iOS(swift)でOpenCVを使うシンプルなサンプル
ふとiOSでOpenCVを使いたくなったのですが、iOSのプログラミング経験がなかったので、0から調べつつサンプルを作ってみました。
他にも同様のことをやっている記事がいくつかありましたが、情報を整理する意味でも自分なりにまとめることにしました。
swiftからOpenCVを使って入力画像をグレースケール変換するだけのシンプルなサンプルです。(Xcodeの使い方が多め)
CocoaPodsのセットアップ
CocoaPodsはiOSのライブラリ管理ツールです。OpenCVはCocoaPods経由でインストールします。
CocoaPodsのインストールにはRubyGemsを用います。
以下のコマンドを実行してセットアップします。
$ sudo gem install cocoapods
$ pod setup
プロジェクトの作成
次にXcodeを立ち上げます。バージョンはVersion 9.2
を使いました。
Xcodeを起動すると以下のウインドウが表示されるので、Create a new Xcode projectをクリックします。
Single View Applicationは何もないまっさらなアプリで一番シンプルなので、それを選択し次へをクリック。
続いて、プロジェクトの詳細設定です。
今回はシミュレータでのみの実行なので、Teamは必要ありません。(実機で動作させたい場合に必要になります)
ProductNameはOpenCVSwiftSample、Languageはswiftとしました。その他は初期設定のままです。適当なディレクトリを指定して作成をしましょう。
CocoaPodsを用いてOpenCVをインストール
ここで一度Xcodeを閉じます。
ターミナルから先ほどXcodeプロジェクトを作成したディレクトリ(.xcodeprojがあるディレクトリ)に移動して以下を実行します。
$ pod init
するとPodfileというファイルが作成されるので、これを以下のように編集します。
(OpenCV3系だとO Linker Errorなどの不具合があるため、今回は2系を入れます)
target 'OpenCVSwiftSample' do
use_frameworks!
# Pods for opencvSwiftTest
platform :ios, "9.0"
pod 'OpenCV', '2.4.9'
end
下記のコマンドを実行すればOpenCVのインストールが始まります。
$ pod install
Objective-CからOpenCVを呼び出す
Swiftではc言語で記述されたOpenCVを実行することができないので、Objective-Cの中にOpenCVの処理を記述し、それをSwiftから呼び出します。
まずFinderからOpenCVSwiftSample.xcworkspace
を開き、Xcodeを起動します。(.xcodeprojでないので注意)
OpenCVSwiftSampleのディレクトリを右クリックしてNew FileからCocoa Touch Class
を選択し、nextと進みます。
ClassはOpenCVSample
、Subclass of はNSObject
、Languageは Objective-C
を選択してください。
nextをクリックしcreateを押すと、以下のようなダイアログが立ち上がります。
SwiftからObjective-Cを実行するにはbridging headerなるものが必要で、それをXcodeが自動で生成してくれます。
ファイル名は{project-name}-Bridging-Header.h
といったものになります。
詳しくはこちらに記載されています。
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
※ここでCocoa Touch Class
ではなくObjective-C
を選んでしまうとBridge-headerは自動で生成されないようです。
また今回はOpenCVのC++ライブラリを用いるため、Objective-C++
というものを用います。
こちらは作成されたOpenCVSample.m
のファイル拡張子を.mm
に変更するだけでObjective-C++として使用できます。
bridging headerファイルで先ほど作成したOpenCVSample.hファイルをインポートします。
#import "OpenCVSample.h"
次にヘッダーファイルにメソッドを宣言します。
#import <UIKit/UIKit.h>
@interface OpenCVSample : NSObject
+(UIImage *)GrayScale:(UIImage *)image;
@end
続いて実装ファイルです。
処理としてはUIImagをmatrix(cv::Mat)に変換して画像処理を行なった後にUIImageに再変換しています。
#import <opencv2/opencv.hpp>
#import <opencv2/highgui/ios.h>
#import "OpenCVSample.h"
@implementation OpenCVSample
+(UIImage *)GrayScale:(UIImage *)image{
// convert image to mat
cv::Mat mat;
UIImageToMat(image, mat);
// convert mat to gray scale
cv::Mat gray;
cv::cvtColor(mat, gray, CV_BGR2GRAY);
// convert to image
UIImage * grayImg = MatToUIImage(gray);
return grayImg;
}
@end
swiftからObjective-Cを実行
プロジェクト作成時に自動で生成されているViewControllerで先ほど実装したGrayScaleメソッドを呼び出します。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var myImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let image = myImageView.image;
let gray = OpenCVSample.grayScale(image)
myImageView.image = gray;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
UIImageViewに紐づけた画像をグレースケールに変換して設定し直しています。
このとき、Objective-Cで設定したメソッド名が変わるので注意してください。今回は小文字になっているだけですが、メソッド名が長いと結構変更されてしまうようです。
どうやらSwiftからObjective-Cのメソッドを呼び出す場合、Xcodeがメソッド名を最適化してしまうようです。
(めちゃくちゃハマりました。)
続いてストーリーボードに画像を配置しましょう。
Main.storyboardを開きます。最初は空のViewControllerがあるのみなので、右側のユーティリティ領域のペインからImageViewを選択してストーリーボードのViewControllerにドラッグ&ドロップします。
ストーリーボードのImageViewとViewControllerのmyImageView変数を紐づけます。
@IBOutlet
と記述された変数はストーリーボードと紐づけることができます。
ViewController.swiftを開きます。この時にoptionキーを押しながら開くとウインドウが左右分割して開くので紐付けが便利になります。
@IBOutlet
が記述されている行の左端に白丸が表示されているのでそれをドラッグアンドドロップし、UIImageViewに紐付けます。
処理を行う画像をXcodeプロジェクトに追加します。これはXcodeにドラッグアンドドロップでいけます。
ストーリーボードのImageViewを選択し、右側のユーティリティ領域のペインから先ほど追加した画像を選択します。(今回だとcat.jpg)
するとストーリーボードに画像が表示されます。
ここでXcodeの実行ボタンを押しましょう。
シミュレータが立ち上がって、先ほどの画像がグレースケール変換されたものが無事表示されました。