OpenCVはオープンソースの画像処理やマシンラーニングを行うためのライブラリです。高機能なライブラリで、画像処理やマシンラーニングの処理を一から全て実装するよりも効率的にアプリ開発を行えます。
高機能なだけではなく、非常に高速です。内部ではOpenCLやSIMD、IPP(Intel Performance Primitive)、スレッドによる並列化などを行い、普通に愚直に実装したら得られない速度を実現しています。
この記事では、OpenCVを使ったiOSアプリを開発するための、OpenCVの開発環境のセットアップ方法を解説します。
macOSアプリのセットアップ方法については、次の記事をご覧ください。
Xcodeのプロジェクト設定
OpenCVをアプリで使用するには、アプリにダウンロードしたフレームワークを組み込みます。以下の様に操作してプロジェクトを設定します。
SDKをダウンロードする
iOS版のSDKはビルド済みのフレームワークがGitHubのReleaseページで公開されています。これをダウンロードします。
フレームワークをコピー
ダウンロードしたopencv2.framework
にはヘッダファイルも含まれています。次のようなフォルダ構成になるようにコピーします。
OpenCVTest_iOS
├── OpenCVTest_iOS
│ ├── Assets.xcassets
│ ├── ContentView.swift
│ ├── OpenCVTest_iOSApp.swift
│ └── Preview Content
├── OpenCVTest_iOS.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ └── xcuserdata
└── common
└── opencv2.framework
フレームワークの追加
プロジェクトにopencv2.framework
を追加します。次のように操作します。
フレームワークの埋め込みを設定する
次のように操作します。
バイナリーの種類に関するエラーが発生する
次のようなエラーが表示されることがあります。私の場合は、Xcode 13.4.1 + OpenCV 4.6の組み合わせで発生しました。
Buidling for iOS Simulator, but the linked and embedded framework 'opencv2.framework' was built for iOS + iOS Simulator
エラーの内容を見ると、opencv2.framework
にはiOSデバイスとiOSシミュレーターの2つのバイナリが入っていて、アプリはiOSシミュレーター用にビルドしているというメッセージです。。。?? 何も問題がないのでは??
Xcodeの不具合のようにも思えるのですが、次のように操作すると回避できます。
このエラーは「Validate Workspace」がYESのときに発生するエラーのようです。しかし、初期状態では「NO」と表示されていましたが、何も設定していないときに「NO」になるという意味の表示でした。(太字になっていない)
一度変更して、元に戻すことで、明示的に「NO」を指定するように設定されます。
これは予想ですが、Xcodeはデフォルト設定は「NO」であると想定しているのですが、リンカのデフォルト値は「YES」なのではないかと思われます。
テスト
次のような簡単なコードでOpenCVが動作するか試してみます。OpenCVはC++なので、Objective-C++で書き、Swift側から呼びます。使用する画像はリソースで埋め込んだので、UIImage
で読み込みます。
// OpenCVTest.h
#import <UIKit/UIKit.h>
@interface OpenCVTest : NSObject
+ (nullable UIImage *)filteredImage;
@end
// OpenCVTest.mm
#import "opencv2/opencv.hpp"
#import "opencv2/imgproc.hpp"
#import "opencv2/imgcodecs.hpp"
#import "opencv2/imgcodecs/ios.h"
#import "OpenCVTest.h"
@implementation OpenCVTest
+ (nullable UIImage *)filteredImage
{
UIImage *srcImage = [UIImage imageNamed:@"P4071145"];
cv::Mat srcImageMat;
cv::Mat dstImageMat;
// UIImageからcv::Matに変換する
UIImageToMat(srcImage, srcImageMat);
// 色空間をRGBからGrayに変換する
cv::cvtColor(srcImageMat, dstImageMat, cv::COLOR_RGB2GRAY);
// cv::MatをUIImageに変換する
UIImage *dstImage = MatToUIImage(dstImageMat);
return dstImage;
}
@end
Swift側からObjective-C++のクラスを呼べるように、ブリッジングヘッダーでOpenCVTest.h
をインクルードします。ブリッジングヘッダーは、プロジェクト名-Bridging-Header.h
という名前で作成します。今回のプロジェクトではOpenCVTest_iOS-Bridging-Header.h
です。ブリッジングヘッダーには次のようにコードを書きます。
#import "OpenCVTest.h"
ブリッジングヘッダーは、ターゲットのビルド設定の「Swift Compiler – General」の「Objective-C Briding Header」に指定します。
後は、Swift側から呼びます。私はSwiftUIで試しました。次のようなコードです。
// ContentView.swift
import SwiftUI
struct ContentView: View {
var filteredImage: UIImage? = {
OpenCVTest.filteredImage()
}()
var body: some View {
ZStack {
if self.filteredImage != nil {
Image(uiImage: filteredImage!)
.resizable()
.aspectRatio(filteredImage!.size, contentMode: .fit)
} else {
Text("No Image")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
実機での動作テスト
実機で動かしてみます。次のスクリーンキャプチャはiPhone 13 Proで実行したときです。綺麗にグレースケールになっています。
シミュレータ及びプレビュー上での動作
OpenCV 4.6.0のビルド済みバイナリを使ってシミュレーターで実行しようとすると、次のようなエラーが出力されました。
ld: in /Volumes/Data/src/RK/TechGakuWebSite/SampleCodes/OpenCVTest_iOS/common/opencv2.framework/opencv2(ios_conversions.o), building for iOS Simulator, but linking in object file built for iOS, for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
これはiOSシミュレーター用にビルドしているにも関わらず、iOSデバイス用にリンクしているというエラーです。M1などのApple Siliconマシンの場合、バイナリはarm64
です。そのため、シミュレーター用のビルドにもarm64
版、つまりiOSデバイス用を使用するのでエラーになっています。
iOSシミュレーター用のバイナリをx86_64
に変更することで動作します。Apple Siliconマシン上のシミュレーターでもRosetta2があるので、x86_64
版のバイナリが動作します。
こちらの記事を参照してください。