二つのファイルに保存されている画像を合成する
python version
import numpy as np import cv2 import os import glob import sys import re args = sys.argv filename1 = args[1] filename2 = args[2] outname = args[3] if __name__ == "__main__": new_dir = outname + "/" os.makedirs(new_dir, exist_ok=True) files1 = glob.glob(filename1 + "/*.png") files1.sort() files2 = glob.glob(filename2 + "/*.png") files2.sort() i = 0 for file1, file2 in zip (files1, files2): img1 = cv2.imread(file1) img2 = cv2.imread(file2) merge_img = cv2.add(img1, img2) cv2.imwrite(new_dir + "{:08}.png".format(i), merge_img) i += 1
C++ vesion
前半部分はこの方のコードを拝借。
Python built-in zip() method implemented in C++ · GitHub
#include "opencv2/opencv.hpp" #include "TStyle.h" #include "TFile.h" #include "TTree.h" #include "TMath.h" #include "TRandom.h" #include <stdio.h> #include <stdlib.h> #include <string> #include <iostream> #include <fstream> #include <sstream> #include <math.h> #include <random> #include <vector> #include <sys/stat.h> #include <glob.h> template<typename T> class Zip { public: typedef std::vector<T> container_t; template<typename... Args> Zip(const T& head, const Args&... args) : items_(head.size()), min_(head.size()) { zip_(head, args...); } inline operator container_t() const { return items_; } inline container_t operator()() const { return items_; } private: template<typename... Args> void zip_(const T& head, const Args&... tail) { // If the current item's size is less than // the one stored in min_, reset the min_ // variable to the item's size if (head.size() < min_) min_ = head.size(); for (std::size_t i = 0; i < min_; ++i) { // Use begin iterator and advance it instead // of using the subscript operator adds support // for lists. std::advance has constant complexity // for STL containers whose iterators are // RandomAccessIterators (e.g. vector or deque) typename T::const_iterator itr = head.begin(); std::advance(itr, i); items_[i].push_back(*itr); } // Recursive call to zip_(T, Args...) // while the expansion of tail... is not empty // else calls the overload with no parameters return zip_(tail...); } inline void zip_() { // If min_ has shrunk since the // constructor call items_.resize(min_); } /*! Holds the items for iterating. */ container_t items_; /*! The minimum number of values held by all items */ std::size_t min_; }; template<typename T, typename... Args> typename Zip<T>::container_t zip(const T& head, const Args&... tail) { return Zip<T>(head, tail...); } std::vector<std::string> get_file_path(std::string input_dir, std::string wildcard){ glob_t globbuf; std::vector<std::string> files; glob((input_dir + wildcard).c_str(), 0, NULL, &globbuf); for (int i = 0; i < globbuf.gl_pathc; i++){ files.push_back(globbuf.gl_pathv[i]); } globfree(&globbuf); return files; } int main(int argc, char *argv[]){ std::string input_dir1 = argv[1]; std::string input_dir2 = argv[2]; std::string output_dir = argv[3]; std::string png = "/*.png"; mkdir(Form("result_image/%s", output_dir.c_str()), S_IRWXU | S_IRWXG | S_IRWXO); std::vector<std::string> events1 = get_file_path(input_dir1, png); std::vector<std::string> events2 = get_file_path(input_dir2, png); std::sort(events1.begin(), events1.end()); std::sort(events2.begin(), events2.end()); std::string f; int i = 0; for (auto j : zip(events1, events2)){ std::cout << j[0] << std::endl; std::cout << j[1] << std::endl; cv::Mat img1 = cv::imread(j[0], cv::IMREAD_COLOR); cv::Mat img2 = cv::imread(j[1], cv::IMREAD_COLOR); cv::Mat out_img; cv::add(img1, img2, out_img); cv::imwrite(Form("result_image/%s/%08d.png",output_dir.c_str(), i), out_img); i += 1; } }
素粒子原子核物理解析用 Docker image
Docker imageを作成しました。Dockerhubでも公開しています。
https://hub.docker.com/repository/docker/salt22g/docker-rpg
こちらの記事でも書きましたが、環境構築というのはとても面倒で、パッケージのversionが変わると不具合があったり、何度繰り返しても毎回違うところでつまずくことがあったりします。そこで原子核物理理系大学院生である私が普段使っている解析ツールをDocker imageにしてまとめてみました。その名もdocker-rpg(名前がダサい…??)
想定している使い方
- 中央計算機、サーバーなどでの使用(Linux)
- ローカルでの開発環境テスト
- 開発環境の共有(後輩へのプログラミング講習会etc)
docker-rpgはどんなDocker imageなのか
R - ROOT
[https://root.cern.ch/}
物理解析の基本的な解析ツールで多くの素粒子原子核の研究室で使用されており、大量のデータを解析する際に重宝されます。グラフの描画ツールとしても優秀で、解析と同時に物理学っぽい(怒られそう)図を作ることができます。
P- python3
https://www.python.jp/
現在最も開発が盛んでプログラミングのスタンダードとなっている言語です。初心者にも理解しやすく、画像処理からデータ解析、機械学習まで用途は様々。
筆者の主言語もpython3です。
G- Geant4
https://geant4.web.cern.ch
モンテカルロシミュレーションによって粒子の物理過程をシミュレーションできるツールです。
物理実験はもちろん医療分野においてもよく用いられています。
これらの開発ツールは導入が面倒です。
macOSではhomebrewがあるので簡単になっていますが、他の人と共有しているLinuxサーバーでは制約の大きさから依存関係のパッケージをインストールできず導入ができないことがあります。
また、ROOTとGeant4についてはWindows PC上では上級者でないと動かすのはなかなか大変です。
基本的にLinuxの仮想環境またはWSLを立ち上げる必要があります。
phst.hateblo.jp
(↑こちらの記事は参考になります。)
そこで、是非このDocker imageで開発環境をサボりましょう。
pythonのglobで末端のファイル名だけ取得したい
pythonのglob.globによるファイル名の一括取得。便利ですよね。
glob君に任せ切りにしていたら一瞬迷ったのでメモとして残します。
全然大した話ではないのですし、もっと良いやり方があるかもしれません。
2020/05/17 15:20 追記
os.path.basename()
これで行けるらしい。俺がアホだった。
cv2.imwrite(new_dir + os.path.basename(file1), merge_img)
問題
こんな処理をしたい時がありました。
- 二つのファイルに同じ名前だが異なる処理をした画像がある。
- これらのファイルから同じ名前の画像を読み込み横につなげる。
- 別のファイルに元と同じ名前で書き出す。
ファイル構造はこんな感じ。名前は連番ではない場合もあります。
current_dir/ ┠ gattai.py ┠ images_A/ ┃ ┠ abcdefg.png ┃ ┠ hijk.png ┃ ┠ lmnopq.png ┃ ┠ 以下略 ┠ images_B/ ┃ ┠ abcdefg.png ┃ ┠ hijk.png ┃ ┠ lmnopq.png ┃ ┠ 以下略
↑を↓にしたい。
current_dir/ ┠ gattai.py ┠ images_A/ #略 ┠ images_B/ #略 ┠ images_C/ #自分で名前をつけて作成 ┃ ┠ abcdefg.png #処理されて合体した画像 ┃ ┠ hijk.png ┃ ┠ lmnopq.png ┃ ┠ 以下略
最初は何も考えずこんなコードを書きました。
import numpy as np import cv2 import os import glob import sys import re args = sys.argv filename1 = args[1] #images_A filename2 = args[2] #images_B outname = args[3] #images_C if __name__ == "__main__": new_dir = outname + "/" os.makedirs(new_dir, exist_ok=True) #出力用のファイルを生成 files1 = glob.glob(filename1 + "/*.png") #ファイル内の画像のパスを取得 files1.sort() files2 = glob.glob(filename2 + "/*.png") files2.sort() for file1, file2 in zip(files1, files2): #読み込む画像パスを名前をループで取得 img1 = cv2.imread(file1) img2 = cv2.imread(file2) images = [img1, img2] merge_img = np.concatenate(images, axis=1) cv2.imwrite(new_dir + file1, merge_img)
実行してみると…
python3 gattai.py images_A images_B images_C
実はこの部分がまずい。
new_dir + file1 ## => images_C/images_A/hogehoge.png
globで取得したfile1には
images_A/hogehoge.png
というように相対パスが全て書き込まれます。
os.makedirs(new_dir, exist_ok=True)
で作成したのはimages_Cまでなのでその中のimages_Aは作成されておらず画像は保存できません。
解決
pythonの文字列分割機能を使って解決しました。
qiita.com
phrase = "If you can dream it you can do it" # 「split」使用 .split(区切り文字, 分割数) print(phrase.split()) ## => ["If","you","can","dream","it","you","can","do","it"] word = "HelloWorld" # 「rsplit」使用 .rsplit(区切り文字, 分割数) print(word.rsplit("l",1)) ## => ['HelloWor', 'd']
今回globで取得したパスは
/images_A/hogehoge.png
取得したいのはhogehoge.pngの部分なので、"/"で区切った末端の部分を取れば良いですね。
phrase.split("/") ## => ["", "images", "hogehoge.png"] phrase.split("/")[-1] ## => ["hogehoge.png"]
ということで最後のcv2.imwriteをこのように書き換えました。
cv2.imwrite(new_dir + file1.split("/")[-1], merge_img)
これで当初の狙い通りの出力になりました。めでたし。
Docker locale設定のerrror
Docker image作成時に起きたlocaleに関するerrorを解決しました。
ググっても「Ubuntuの日本語化」に記事ばかりで遠回りになってしまい、しばらくうまく行きませんでした。
最終的に解決法がわかったのでこの記事に書いておきます。
環境
hostOS: macOS & ubuntu(どちらでも同じ症状)
Docker version: 19.03.8
元にしたDocker image: ubuntu:18.04(公式)
hub.docker.com
症状
# docker run -it hogehoge manpath: can't set the locale; make sure $LC_* and $LANG are correct bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
使っている分には影響はなかったがなんか気持ち悪い。
調べてみるとlocale(言語設定)の問題らしい。
# locale locale: Cannot set LC_CTYPE to default locale: No such file or directory locale: Cannot set LC_MESSAGES to default locale: No such file or directory locale: Cannot set LC_ALL to default locale: No such file or directory LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_CTYPE="en_US.UTF-8" LC_NUMERIC="en_US.UTF-8" LC_TIME="en_US.UTF-8" LC_COLLATE="en_US.UTF-8" LC_MONETARY="en_US.UTF-8" LC_MESSAGES="en_US.UTF-8" LC_PAPER="en_US.UTF-8" LC_NAME="en_US.UTF-8" LC_ADDRESS="en_US.UTF-8" LC_TELEPHONE="en_US.UTF-8" LC_MEASUREMENT="en_US.UTF-8" LC_IDENTIFICATION="en_US.UTF-8" LC_ALL=en_US.UTF-8
正しく設定できていないとのこと。
使えるlocaleを確認すると…
# locale -a C C.UTF-8 POSIX
え…デフォルトに英語ないの??!…公式imageなのに…
解決法1
Dockerfikeに以下の記述を追加
# locale RUN apt-get update RUN apt-get install -y locales RUN locale-gen en_US.UTF-8
これでen_US.UTF-8の環境が構築されるのでerrorが消える。
解決法2
コンテナ内で以下のコマンドを叩く
# apt-get update # apt-get install -y locales # locale-gen en_US.UTF-8
これで起動時のerrorも消えてスッキリしました。
↓解決後
# locale LANG=ja_JP.UTF-8 LANGUAGE=en_US.UTF-8 LC_CTYPE="en_US.UTF-8" LC_NUMERIC="en_US.UTF-8" LC_TIME="en_US.UTF-8" LC_COLLATE="en_US.UTF-8" LC_MONETARY="en_US.UTF-8" LC_MESSAGES="en_US.UTF-8" LC_PAPER="en_US.UTF-8" LC_NAME="en_US.UTF-8" LC_ADDRESS="en_US.UTF-8" LC_TELEPHONE="en_US.UTF-8" LC_MEASUREMENT="en_US.UTF-8" LC_IDENTIFICATION="en_US.UTF-8" LC_ALL=en_US.UTF-8 # locale -a C C.UTF-8 en_US.utf8 POSIX
ちゃんと追加されてますね。
大学院生Macユーザーへ Dockerの勧め
最近研究でDockerを使うようになり、Linuxサーバー上で実行環境を作成するなどしていました。
使っているうちに「Docker面白いな」と感じたので手持ちのMac上でも使えるようにして詳しくなりたいと思った次第です。
Dockerとはなんぞ。
qiita.com
色々なサイトで紹介されています。上記のサイトは余計な絵が少なくておすすめ。
筆者はDockerを環境構築をサボれるツールだと考えています。
「全く同じ開発環境(Docker image)をどのマシンでも使うことができる。」
これはチームでの開発はもちろんですが、理系大学院生にとって後輩への引継ぎ、指導が楽になることを意味します。
研究室で必要なツールを1から後輩のPCにインストールするとなると指導や質問対応に多くの時間を割かねばなりません。
それはそれぞれのPCの環境(OSやバージョン)が異なることが大きな理由の一つです。
特にMacユーザーの私がプログラミングの講習会をする際には、Windowsユーザーのことも考える必要がありました。
手元の動かせるWindowsマシンで環境構築を試さなければなりません。
Dockerを導入すれば同一の開発環境をWindowsでもmacOSでも準備できます。
今回はまず、MacOSへの導入についてです。
Dockerの導入
まずはmacOSにインストール。
環境はこちらの記事を参照。今回もHomebrewを使います。
salt22g.hatenablog.jp
% brew install docker % brew cask install docker
インストールできたか確認。
% docker --version Docker version 19.03.8, build afacb8b
Dockerの主なコマンドはこちらのサイトによくまとまっています。
qiita.com
Docker imageを作成、起動
今回作ったDocker imageについては後日詳しくお話しします。
Dockerfileの作り方についてもこのブログで公開する予定です。
Dockerfileを作成したフォルダに移動して
% docker build -t <イメージ名> .
イメージが作成できているか確認してみましょう。
% docker images REPOSITORY TAG IMAGE ID CREATED SIZE 自分が作ったイメージ名 latest イメージID 作成された時刻 ファイルサイズ ubuntu 18.04 c3c304cb4f22 2 weeks ago 64.2MB
自分が作ったイメージの名前が表示されればOKです。
早速起動しましょう。
% docker run -it <イメージ名> [root@8fc1f79b0233] /workspace
docker runには様々なoptionがあるのでそれはまたどこかで…
自分のイメージをDockerhubにpush
Docker hub
作成したimageをクラウド上で管理するのがDocker hubです。
他の人が作ったimageはもちろん。公式imageも掲載されています。
今回は筆者が作ったimageをpushしてみます。
アカウントを作成
hub.docker.com
予めdockerhubでリポジトリを作成。
% docker tag <ビルドしたimageの名前> <DockerhubのユーザID>/<さっきDockerhubで作ったリポジトリ名>:<tag名(latestとするのが一般的)>
% docker login -p="Dockerhubのパスワード" -u="Dockerhubのユーザー名" WARNING! Using --password via the CLI is insecure. Use --password-stdin. Login Succeeded
なんかWARNINGが出たけれど無視。
Login Succeededが出ればOK。
docker push salt22g/docker-rpg:latest
GUIを使ってみる
まずはsocatをインストール。
brew install socat
XQuartzもインストール。
salt22g.hatenablog.jp
この記事でも紹介したソフトです。
sshで接続した先の画像やGUIを取得するサーバーみたいなものです(多分)
brew install caskroom/cask/brew-cask brew cask install xquartz open -a XQuartz #普通にlaunchpadから起動してもOK
Docker側から見たMac(hostマシーン)のIPアドレスを取得。
ifconfig en0 | grep inet | awk '$1=="inet" {print $2}' xxx.xxx.xxx.xxx #ipアドレス
socatを起動。
socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"
ターミナルから返答がないはずなので別ターミナルからdockerを以下のオプション付きで起動。
docker run -it <イメージ名>-e DISPLAY="xxx.xxx.xxx.xxx:0"
dockerコンテナ内で作成したグラフが表示できるか見てみましょう。
後輩や部下の環境構築に困っている方々いかがでしょう。
Docker内でのopencv buildでerror…
とあるDocker imageをbuildしようとしていたところ…
opencvのbuild中にerrorが
Dockerfileはこんな感じ…
FROM ubuntu:18.04 --中略-- # install & build opencv RUN git clone https://github.com/opencv/opencv.git ~/opencv RUN git clone https://github.com/opencv/opencv_contrib.git ~/opencv_contrib RUN cd ~/opencv && \ mkdir build && \ cd build && \ cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local \ -D INSTALL_PYTHON_EXAMPLES=ON \ -D INSTALL_C_EXAMPLES=OFF \ -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules ~/opencv \ -D BUILD_EXAMPLES=ON .. && \ make -j4 && \ make install && \ /sbin/ldconfig --以下略--
makeの部分でerrorが出た模様。
c++: internal compiler error: Killed (program cc1plus)
Please submit a full bug report,
with preprocessed source if appropriate.
See <file:///usr/share/doc/gcc-7/README.Bugs> for instructions.
make[2]: *** [modules/python2/CMakeFiles/opencv_python2.dir/__/src2/cv2.cpp.o] Error 4
make[1]: *** [modules/python2/CMakeFiles/opencv_python2.dir/all] Error 2
CMakeFiles/Makefile2:19251: recipe for target 'modules/python2/CMakeFiles/opencv_python2.dir/all' failed
make[1]: *** Waiting for unfinished jobs....
error文で検索すると。
github.com
ちゃんと読んでないけどRAMが何たらかんたらと書いてある。
Linuxのサーバ上では同じDockerfileでbuildできていたのでおそらくPCの性能的な問題。
Macのスペック
OS: macOS Catalina 10.15.4
機種名: iMac(21.5-inch, 2019)
プロセッサ: 3.2 GHz 6Core Intel Core i7
メモリ: 16 GB 2667 MHz DDR4
グラフィックス: Radeon Pro 555X 2 GB
コア数は足りているはずなのにうまく行ってない。
ということで
make -j4 → make -j2
ちゃんとmakeできました。
まだしっかり調べていないですが、docker内でのcpu使用に制限があるのか…
またわかったら記事にします。
Geant4 cmakeでerror…(macOS)
Geant4のフレームワークをcmakeしようとしたところここでエラーが…
Failed to find "gl.h" in CMake Error at /usr/local/opt/qt/lib/cmake/Qt5Gui/Qt5GuiConfigExtras.cmake:9 (message): Failed to find "gl.h" in "/System/Library/Frameworks/OpenGL.framework/Headers;/System/Library/Frameworks/AGL.framework/Headers". Call Stack (most recent call first): /usr/local/opt/qt/lib/cmake/Qt5Gui/Qt5GuiConfig.cmake:202 (include) /usr/local/Cellar/geant4/10.5.1/lib/Geant4-10.5.1/Geant4Config.cmake:449 (find_package) CMakeLists.txt:13 (find_package)
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/OpenGL.framework/Headers /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/AGL.framework/Headers
なんか場所が変わっていたらしい。
ちなみにGeant4はhomebrewを使ってインストールしたもの。
/usr/local/opt/qt/lib/cmake/Qt5Gui/Qt5GuiConfigExtras.cmake
このファイルを見に行くと
set(_GL_INCDIRS "/System/Library/Frameworks/OpenGL.framework/Headers" "/System/\ Library/Frameworks/AGL.framework/Headers")
ということでここを編集してみる
一応元のファイルは残して…
set(_GL_INCDIRS "/System/Library/Frameworks/OpenGL.framework/Headers" "/System/Library/Frameworks/AGL.framework/Headers" "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/AGL.framework/Headers" "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/OpenGL.framework/Headers")
このように編集
再度
cmake .. -- Configuring done -- Generating done -- Build files have been written to: /Users/hogehoge/
やったぜ。