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

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

2020年4月27日、OpenCV にこんな PR が現れました。
Objc binding by komakai · Pull Request #17165 · opencv/opencv

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

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

今回は 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 も追加します。

Swift で opencv2 を import する

ここで Swift で import opencv2 をします。またコードを書いていきます。自分で Objective-C++ のラッパーを書かなくていいのはものすごく楽…。これまでは Objective-C++ を書いて、 Bridging Header も書いてからようやく 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 を追加する必要がありました。

(Thanks @komakai ! Mat initWithUIImage: does not work (Objective-C / Swift) · Issue #17532 · opencv/opencv

実行結果

自分で Objective-C++ のラッパーを書くことなく、OpenCV を使うことができました!!

他のサンプル

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