はじめに
juliaでOpenCVを使うために以前から CxxWrap.jlを使ったラッパーを書いたりしていました。OpenCVの関数が膨大なのでヘッダファイルからラッパーを自動生成するようなスクリプトを書いていたんですが、いろんな関数や構造体に対応しているうちにスクリプトも肥大化し収集が付かなくなっていました。
公式のpythonバインディングを生成しているスクリプトを利用するように書き直した方がいいかなと思いつつ手を出していなかったんですが、そうこうしているうちにopencv_contribにjulia bindingが入ったようなので、今後はそちらを使った方が良さそうということで、試してみました。
OpenCVのバージョンは4.5.0、juliaのバージョンは1.5.3で試しました。公式ドキュメントでは、julia-1.4以降を推奨しています。
下記で試した環境は MacOS (Big Sur)ですが、Ubuntuでもほぼ同じでした。
今回はjuliaバインディングのビルド方法を説明します。基本的には公式ドキュメントに書いていますが、若干修正が必要だったのと、今回opencv自体はbrew でインストールしてあるため、juliaバインディングだけビルドする方法を説明します。
基本的に、opencv_contribはopencvとともにビルドすることを想定していて、opencv_contribだけ、さらにその中の1個のモジュールだけビルドする方法は用意されていません。
juliaとCxxWrap.jlの準備
juliaはできるだけ最新をインストールしておきましょう。Julia公式ページのダウンロードから各OS用のバイナリをダウンロードできます。今回は執筆時点で最新の 1.5.3 をインストールしておきました。
juliaバインディングではCxxWrap.jlを使うので、これをあらかじめインストールしておきます。
$ julia
...
julia> ]
julia> add CxxWrap
さらにCxxWrap.jlのライブラリ(libcxxwrap_julia)が置かれている場所を調べます。
$ julia
...
julia> using CxxWrap
julia> CxxWrap.CxxWrapCore.prefix_path()
"/Users/myname/.julia/artifacts/6017255205dc4fbf4d962903a855a0c631f092dc"
結果表示されたディレクトリがビルドに必要になるので控えておきます。
opencvとopencv_contribのソースファイルのダウンロード&展開
ビルドにはopencv_contribだけでなく、opencvのソースファイルも必要になります。これらをダウンロードして展開しておきます。
opencvは現時点で最新のリリースである4.5.0のソースをダウンロードします。
$ curl -L https://github.com/opencv/opencv/archive/4.5.0.zip -o opencv-4.5.0.zip
$ unzip opencv-4.5.0.zip
opencv_contribも4.5.0用をダウンロードして、同じフォルダに展開しておきます。
$ curl -L https://github.com/opencv/opencv_contrib/archive/4.5.0.zip -o opencv_contrib-4.5.0.zip
$ unzip opencv_contrib-4.5.0.zip
ビルドの準備
まずは、ビルド用のフォルダを作成します。
$ mkdir opencv-julia
フォルダ名はなんでもいいです。このフォルダ内でcmakeを実行してビルドの準備をします。このときに、opencv_contribを一緒にビルドするように設定することと、julib関係のモジュールの設定をcmakeの引数で指定します。基本的には公式チュートリアルに書いてある通りなのですが、ここには書いていないJlCxx_DIRを指定しないとビルドできませんでした。
$ cd opencv-julia
$ cmake -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib-4.5.0/modules -DWITH_JULIA=ON -DJlCxx_DIR=/Users/myname/.julia/artifacts/6017255205dc4fbf4d962903a855a0c631f092dc/lib/cmake/JlCxx -DJULIA_PKG_INSTALL_PATH=/Users/myname/.julia/packages ../opencv-4.5.0
OPENCV_EXTRA_MODULES_PATHではダウンロードしたopencv_contribのmodulesを指定し、WITH_JULIA=ONでjuliaモジュールを有効にします。
JlCxx_DIRには、先ほどCxxWrap.CxxWrapCore.prefix_path()で調べておいたフォルダに /lib/cmake/JlCxx を追加したフォルダを指定します。
JULIA_PKG_INSTALL_PATHはビルドしたjuliaのパッケージをインストールする場所なので、デフォルトのパッケージフォルダを指定しています。
うまくいけば、cmakeのメッセージに
-- OpenCV modules:
-- To be built: alphamat aruco bgsegm bioinspired calib3d ccalib core datasets dnn dnn_objdetect dnn_superres dpm face features2d flann freetype fuzzy gapi hdf hfs highgui img_hash imgcodecs imgproc intensity_transform julia line_descriptor mcc ml objdetect optflow phase_unwrapping photo plot python2 python3 quality rapid reg rgbd saliency sfm shape stereo stitching structured_light superres surface_matching text tracking ts video videoio videostab xfeatures2d ximgproc xobjdetect xphoto
cmakeの長いメッセージの後ろの方に、このようなメッセージがでるので、この中にjuliaという文字があれば正しく設定できています。
ビルド
通常このフォルダのままmake, make installすればビルド、インストールができますが、これだとopencv自体とopencv_contribのmodulesを全てビルド、インストールしてしまいます。
今回opencv自体はbrewで別途インストールしているため、Juliaバインディングだけビルド&インストールしたいのですが、該当するモジュールのビルドフォルダでmakeすればそのモジュール関係をビルドしてくれるようです。
$ cd modules/julia
$ make
...
ようするにjuliaモジュールのフォルダでmakeを実行すれば、juliaモジュールと、それに必要なライブラリやモジュールだけをビルドすることができます。
必要なモジュールだけとはいってもopencv本体のほとんどをビルドすることになるので、ここはかなり時間がかかります、気長に待ってください。
...
[100%] Built target opencv_julia
Scanning dependencies of target opencv_test_julia
[100%] Building Julia tests
[100%] Built target opencv_test_julia
インストール
最後にmake installでインストールですが、下記のように最後止まってしまいます。
$ make install
...
Install the project...
-- Install configuration: "Release"
-- Up-to-date: /Users/myname/.julia/packages
-- Installing: /Users/myname/.julia/packages/install_package.jl
-- Installing: /Users/myname/.julia/packages/OpenCV
-- Installing: /Users/myname/.julia/packages/OpenCV/Manifest.toml
-- Installing: /Users/myname/.julia/packages/OpenCV/Artifacts.toml
-- Installing: /Users/myname/.julia/packages/OpenCV/Project.toml
-- Installing: /Users/tkato/.julia/packages/OpenCV/src/lib/libopencv_julia.dylib
ERROR: LoadError: expected the file `src/OpenCV.jl` to exist for package `OpenCV` at `/Users/tkato/.julia/packages/OpenCV`
...
このあとインストーラのjuliaスクリプトのスタックトレースが続きます。
理由はわからない(~~無理矢理サブフォルダでmakekしたせい?~~ビルドフォルダからOpenCV含め全体をビルド&インストールしても同じように止まりました)ですが、パッケージのjuliaコードがJULIA_PKG_INSTALL_PATHで指定したフォルダにコピーされないみたいです。そこで、これらを手動でコピーしてからmake installをします。
$ cp ../../OpenCV/src/*.jl /Users/myname/.julia/packages/OpenCV/src/
$ make install
...
Install the project...
-- Install configuration: "Release"
-- Up-to-date: /Users/myname/.julia/packages
-- Installing: /Users/myname/.julia/packages/install_package.jl
-- Installing: /Users/myname/.julia/packages/OpenCV
-- Installing: /Users/myname/.julia/packages/OpenCV/Manifest.toml
-- Installing: /Users/myname/.julia/packages/OpenCV/Artifacts.toml
-- Installing: /Users/myname/.julia/packages/OpenCV/Project.toml
-- Installing: /Users/myname/.julia/packages/OpenCV/src/lib/libopencv_julia.dylib
Path `/Users/myname/.julia/packages/OpenCV` exists and looks like the correct package. Using existing path.
Resolving package versions...
Updating `~/.julia/environments/v1.5/Project.toml`
[c5c8e1f8] + OpenCV v0.1.0 `~/.julia/packages/OpenCV`
Updating `~/.julia/environments/v1.5/Manifest.toml`
[c5c8e1f8] + OpenCV v0.1.0 `~/.julia/packages/OpenCV`
-- Install output: ["/Users/myname/.julia/packages/OpenCV"]
無事インストールできました。juliaでパッケージを読み込んでみましょう。
$ julia
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.5.2 (2020-09-23)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |
julia> using OpenCV
[ Info: Precompiling OpenCV [c5c8e1f8-82bd-4b4b-a82d-991e5c6b68de]
初回はPrecompileが働いてエラーが出なければ成功です。brewでインストールしたopencvのバージョンが違っていればエラーが出るはずなので、その最新の4.5.0にアップグレードしておきましょう。
使ってみる
試しに画像を読み込んでディスプレイに表示してみましょう。
$ julia
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.5.2 (2020-09-23)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |
julia> using OpenCV
julia> const cv = OpenCV
OpenCV
julia> img = cv.imread("OpenCV_logo_white_600x.png");
julia> cv.imshow("window name", img)
julia> cv.waitKey(Int32(0))
2回目からはPrecompileは実行されません。const cv = OpenCVとしておけば、opencv-pythonと同じように使えます。OpenCV_logo_white_600x.pngは、OpenCVのロゴ画像をあらかじめフォルダにおいておきました。うまくいけば画面にロゴが表示されます。
おわりに
長くなったので、今回はここまで。juliaからみてOpenCVの画像(cv::Mat型)がどう見えているかなど、使い勝手については別の記事に書きました。