OpenCV が Objective-C / Swift で使えるようになります & 使ってみた

*2020年6月13日：一部加筆・修正しました。*

2020年4月27日、OpenCV にこんな PR が現れました。
[Objc binding by komakai · Pull Request #17165 · opencv/opencv](https://github.com/opencv/opencv/pull/17165)

これまで、OpenCV には Java のラッパーが含まれていたため、Android で簡単に OpenCV を利用することができましたが、iOS、macOS では Objective-C、Swift で使うために自分で C++ を呼び出すためのラッパーを書く必要がありました。そこで、[@komakai](https://github.com/komakai) さんによって提案されたのがこの PR でした。

そして今日 2020年6月9日、その PR が無事に master ブランチへマージされ、自分で Objective-C++ を書くことなく、Objective-C、Swift で使うことができるようになりました🎉

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr">あ！！！！！！！<br>Merged!!!!!!!!!!<br><br>Objc binding by komakai · Pull Request #17165 · opencv/opencv<a href="https://t.co/uQLcKlBxoN">https://t.co/uQLcKlBxoN</a> <a href="https://t.co/zQvA29BNSl">pic.twitter.com/zQvA29BNSl</a></p>&mdash; treastrain / Tanaka Ryoga (@treastrain) <a href="https://twitter.com/treastrain/status/1270062033497518080?ref_src=twsrc%5Etfw">June 8, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

今回は iOS プロジェクトで Swift から OpenCV を使うデモを行ってみたいと思います。

# 環境

```
$ swift --version
Apple Swift version 5.2.4 (swiftlang-1103.0.32.9 clang-1103.0.32.53)

$ cmake --version   
cmake version 3.17.3

$ python --version      
Python 2.7.18

$ xcode-select --version
xcode-select version 2373
```

- macOS Catalina 10.15.5 (19F101)
- Xcode 11.5 (11E608c)

# OpenCV をダウンロードする
OpenCV 4.4 に含まれると思われますが、記事執筆時点では Release に含まれていないので、git clone で master ブランチのものを持ってきます。

```
$ xcode-select --install
$ git clone https://github.com/opencv/opencv.git
```

# OpenCV.framework をビルドする
続いて OpenCV.framework をビルドします。

```
./build_framework.py <outputdir>
```

を実行する必要があるので、私の環境では以下のようにしました。時間がかかるのでゆっくり待ちましょう。

```
$ cd opencv/platforms/ios
$ ./build_framework.py .

Using IPHONEOS_DEPLOYMENT_TARGET=8.0
Using iPhoneOS ARCHS=['armv7', 'armv7s', 'arm64']
Using iPhoneSimulator ARCHS=['i386', 'x86_64']
... つづく
```

## CMAKE_Swift_COMPILER が見つからないエラー
私ははじめ CMake のバージョンを  3.15.5 で実行していたのですが、`No CMAKE_Swift_COMPILER could be found.` となってしまい、フレームワークをビルドすることができませんでした。PR の中でもこのエラーは認識されていて、しばらくは 3.16 以降が必要となっていましたが、その後最小サポートバージョンが 3.15 に引き下げられました。でもまだエラーが出る気がするんだけど…？
このエラーについて私は 3.17 を使うことで回避しました。

# Xcode プロジェクトに opencv2.framework を導入
`<outputdir>` で指定したディレクトリに `opencv2.framework` が出来ているので、Xcode プロジェクトに導入します。libc++.tbd も追加します。
<img width="1074" alt="001.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/f8a1e048-85a9-611c-e10d-ed5c317db87b.png">

# Swift で opencv2 を import
ここで Swift で `import opencv2` をします。またコードを書いていきます。自分で Objective-C++ のラッパーを書かなくていいのはものすごく楽…。これまでは Objective-C++ を書いて、 Bridging Header も書いてからようやく Swift を書く…という流れでしたが、より簡単になりました。

```swift
import UIKit
import opencv2

class ViewController: UIViewController {
    
    @IBOutlet weak var srcImageView: UIImageView!
    @IBOutlet weak var dstImageView: UIImageView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let image = UIImage(named: "サンプル画像")!
        self.srcImageView.image = image
        self.dstImageView.image = self.convertColor(source: image)
    }
    
    func convertColor(source srcImage: UIImage) -> UIImage {
        let srcMat = Mat(uiImage: srcImage)
        let dstMat = Mat()
        Imgproc.cvtColor(src: srcMat, dst: dstMat, code: .COLOR_RGB2GRAY)
        return dstMat.toUIImage()
    }
}
```

## ~~unrecognized selector sent to instance~~
~~しかしながら、記事執筆時点では `let srcMat = Mat(uiImage: srcImage)` の部分で `unrecognized selector sent to instance` の実行時エラーが発生します。
実装は `opencv/modules/imgcodecs/misc/objc/ios/Mat+Converters.mm` にあり、そこで `UIImageToMat()` を呼び出しているわけですが…~~

# "Other Linker Flags" に `-all_load` を追加する
`unrecognized selector sent to instance` の実行時エラーが発生する問題があるとしていましたが、"Other Linker Flags" に `-all_load` を追加する必要がありました。
![スクリーンショット 2020-06-13 0.17.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/f59ffd23-af7d-ca35-2edd-7ac67e5365d3.png)
（Thanks [@komakai](https://github.com/komakai) ! [`Mat initWithUIImage:` does not work (Objective-C / Swift) · Issue #17532 · opencv/opencv](https://github.com/opencv/opencv/issues/17532)）

# 実行結果
自分で Objective-C++ のラッパーを書くことなく、OpenCV を使うことができました！！
<img width="375" alt="IMG_8007-2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/8edd90b8-349d-1252-df6a-c1d775b25c21.png">

# 他のサンプル
フレームワークをビルドする際に指定した <outputdir> には opencv2.framework の他に samples があり、その中に ColorBlobDetection と FaceDetection の2つが入っています。Sign して opencv2.framework のパスを指定するだけで iOS App を Run できるので、こちらを試してみるのもいいですね。