OpenCVのセットアップ方法(iOSアプリ用)

OpenCVはオープンソースの画像処理やマシンラーニングを行うためのライブラリです。高機能なライブラリで、画像処理やマシンラーニングの処理を一から全て実装するよりも効率的にアプリ開発を行えます。

高機能なだけではなく、非常に高速です。内部ではOpenCLやSIMD、IPP(Intel Performance Primitive)、スレッドによる並列化などを行い、普通に愚直に実装したら得られない速度を実現しています。

この記事では、OpenCVを使ったiOSアプリを開発するための、OpenCVの開発環境のセットアップ方法を解説します。

macOSアプリのセットアップ方法については、次の記事をご覧ください。

目次

Xcodeのプロジェクト設定

OpenCVをアプリで使用するには、アプリにダウンロードしたフレームワークを組み込みます。以下の様に操作してプロジェクトを設定します。

SDKをダウンロードする

iOS版のSDKはビルド済みのフレームワークがGitHubのReleaseページで公開されています。これをダウンロードします。

Releases · opencv/opencv (GitHub)

フレームワークをコピー

ダウンロードした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を追加します。次のように操作します。

(1) commonフォルダをプロジェクトにドラッグ&ドロップします。

「common」フォルダをドラッグ&ドロップしてプロジェクトに追加する
「common」フォルダをドラッグ&ドロップしてプロジェクトに追加する

(2) 「Create groups」を選択し、アプリのターゲットをチェックして、「Finish」ボタンをクリックします。

オプションを選択して「Finish」ボタンをクリックする
オプションを選択して「Finish」ボタンをクリックする

フレームワークの埋め込み設定する

次のように操作します。

(1) アプリのターゲットの設定を開き、「General」タブを表示します。

(2) 「Frameworks, Libraries and Embedded Content」の「opencv2.framework」の「Embed」の設定を「Do Not Embed」を選択する。

「Do Not Embed」を選択する
「Do Not Embed」を選択する

バイナリーの種類に関するエラーが発生する

次のようなエラーが表示されることがあります。私の場合は、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の不具合のようにも思えるのですが、次のように操作すると回避できます。

(1) ビルド設定の「Build Options」の「Validate Workspace」を「YES」に変更します。

(2) 手順(1)で「YES」に変更した値を「NO」に変更します。

「Validate Workspace」を「YES」に変更してから「NO」に戻す
「Validate Workspace」を「YES」に変更してから「NO」に戻す

このエラーは「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」に指定します。

「Objective-C Bridging Header」を指定する
「Objective-C Bridging 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で実行したときです。綺麗にグレースケールになっています。

iPhone 13 Proでの実行結果
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版のバイナリが動作します。

こちらの記事を参照してください。

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

Akira Hayashiのアバター Akira Hayashi 代表・ソフトウェアエンジニア

アプリ開発が好きなアプリ開発者。このブログは学習メモを記事にしたテックブログです。仕事ではアプリ開発をメインに、技術書の執筆やセミナーの講師などもしています。業務や著書のサイトはこちらです→ アールケー開発

目次