juliaからOpenCVを使う（opencv_contrib版）ビルド編

# はじめに

juliaでOpenCVを使うために以前から [CxxWrap.jlを使ったラッパーを書いたり](https://qiita.com/TakekazuKATO/items/f577ff9320a3e8afafca)していました。OpenCVの関数が膨大なのでヘッダファイルからラッパーを自動生成するようなスクリプトを書いていたんですが、いろんな関数や構造体に対応しているうちにスクリプトも肥大化し収集が付かなくなっていました。

公式のpythonバインディングを生成しているスクリプトを利用するように書き直した方がいいかなと思いつつ手を出していなかったんですが、そうこうしているうちにopencv_contribにjulia bindingが入ったようなので、今後はそちらを使った方が良さそうということで、試してみました。

OpenCVのバージョンは4.5.0、juliaのバージョンは1.5.3で試しました。[公式ドキュメント](https://docs.opencv.org/master/d8/da4/tutorial_julia.html)では、julia-1.4以降を推奨しています。
下記で試した環境は MacOS (Big Sur)ですが、Ubuntuでもほぼ同じでした。

今回はjuliaバインディングのビルド方法を説明します。基本的には[公式ドキュメント](https://docs.opencv.org/master/d8/da4/tutorial_julia.html)に書いていますが、若干修正が必要だったのと、今回opencv自体はbrew でインストールしてあるため、juliaバインディングだけビルドする方法を説明します。

基本的に、opencv_contribはopencvとともにビルドすることを想定していて、opencv_contribだけ、さらにその中の１個のモジュールだけビルドする方法は用意されていません。

# juliaとCxxWrap.jlの準備

juliaはできるだけ最新をインストールしておきましょう。Julia公式ページの[ダウンロード](https://julialang.org/downloads/)から各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の引数で指定します。基本的には[公式チュートリアル](https://docs.opencv.org/master/d8/da4/tutorial_julia.html)に書いてある通りなのですが、ここには書いていない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
$ 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
$ 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))
```

２回目からはPrecompileは実行されません。const cv = OpenCVとしておけば、opencv-pythonと同じように使えます。OpenCV_logo_white_600x.pngは、OpenCVのロゴ画像をあらかじめフォルダにおいておきました。うまくいけば画面にロゴが表示されます。

# おわりに

長くなったので、今回はここまで。juliaからみてOpenCVの画像(cv::Mat型)がどう見えているかなど、使い勝手については[別の記事](https://qiita.com/TakekazuKATO/items/332563d51391c0cce8e2)に書きました。