OpenCVは、画像処理やコンピュータビジョンのための強力なオープンソースライブラリである。iOSアプリにOpenCVを組み込むことで高度な画像処理や分析を実現できる。
この記事では、iOSアプリにCocoaPodsを使ってOpenCVを組み込み、画像をグレースケール(白黒化)する方法を紹介する。
CocoaPodsを使ってOpenCVをインストールする
OpenCVを組み込むには、CocoaPodsが便利だ。CocoaPodsはiOSとmacOSのための依存関係マネージャーで、ライブラリやフレームワークをプロジェクトに簡単に組み込める。
プロジェクトのPodfileにpod 'OpenCV'
を追加し、pod install
コマンドでOpenCVをインストールする。インストール後、.xcworkspaceファイルを開く。ここまでで OpenCVの導入は完了だ。
注意:CocoaPodsでインストールするOpenCVのバージョンが古い可能性がある
2024年1月3日現在、CocoaPodsでOpenCVをインストールした場合4.3.0
がインストールされるが、これは2020年4月にリリースされたバージョンでかなり古い。GitHubのReleaseページにて配布されている最新版は4.9.0
となっている。最新版を使いたい場合は手作業でframeworkをプロジェクトに追加した方が良いだろう。
最新のOpenCVを自前でビルドして、iOSアプリに組み込む方法は別途以下の記事で紹介した。
自前でOpenCVをビルドするのはやや難易度が高いため、さっとOpenCVを試したいのであれば、CocoaPodsでのインストールでも構わないと思う。
OpenCVを使った画像処理クラスを作成する
iOSアプリでは画像をUIImageオブジェクトとして扱うが、OpenCVでは直接UIImageオブジェクトを扱えない。OpenCVを使って画像処理を行うには、UIImage
からcv::Mat
へ変換し、画像処理をおこない、cv::Mat
からUIImage
へ再変換する。下図の手順で変換をおこなう。
OpenCVを使ってグレースケール変換するためのステップは以下の通り。
- Assets.xcassetsにサンプル画像の追加
- Objective-C++クラスとBridgingファイルの追加
- グレースケール変換処理の実装
- Swiftコードから参照できるようにBridgingファイルにヘッダーの追加
- SwiftUI(画面)で画像処理関数を実行
1. Assets.xcassetsにサンプル画像を追加
Assets.xcassetsにサンプル画像を追加した。ここでは名前を sample
とした。
2. Objective-C++クラスとBridgingファイルを追加する
画像処理クラスをObjective-CまたはObjective-C++で書く場合、Swiftプロジェクトでそのクラスを扱う場合にBridgingファイルが必要となる。Objective-CコードとSwiftコードを繋ぐための橋(Bridge)になるファイルである。この記事ではプロジェクトに初めてObjective-Cのファイルを追加する際に表示される自動生成ダイアログを利用して、Bridgingファイルを追加する。
New File... から Objective-C Fileを選択し、ファイルを追加する。ここでは名前を ImageProcessor.m
とした。
プロジェクトに初めてObjective-Cファイルを追加するとダイアログが表示される。Create Bridging Headerボタンをクリックして、プロジェクトにBridgingファイルを追加する。自動生成されたBridgingファイルは、{プロジェクト名}-Bridging-Header.h
という名前になっている。
さきほど追加したImageProcessor.m
の拡張子を変更しImageProcessor.mm
とした。
3. 画像処理クラスの実装
与えられたUIImageオブジェクトを画像処理し、処理後のUIImageオブジェクトを返す関数を実装する。前述の通り、UIImage -> cv:Mat -> (画像処理) -> cv:Mat -> UIImageの変換をおこなう。
ここで扱う画像処理はグレースケール変換で、グレースケールとは各ピクセルの色をグレーの異なる階調で表現することだ。OpenCVを使ってカラー画像をグレースケールに変換するする方法は以下の通り。
ImageProcessor.mm
画像処理をおこなうクラスである。UIImageToCVMat:
と CVMatToUIImage:
は、UIImageとcv::Mat の相互変換をおこなう関数だが冗長になってしまうため本記事では省略する。詳細についてはサンプルコードを参考にして欲しい。
単純な画像変換については「グレースケールに変換」の箇所を書き換えるだけで、様々なバリエーションの画像処理を実装できるだろう。
#import "opencv2/opencv.hpp"
#import "ImageProcessor.h"
@implementation ImageProcessor
+ (UIImage *) convertToGrayscale:(UIImage *) image {
cv::Mat srcMat, grayMat;
srcMat = [self UIImageToCVMat: image];
// グレースケールに変換
cv::cvtColor(srcMat, grayMat, cv::COLOR_BGR2GRAY);
return [self CVMatToUIImage:grayMat];
}
+ (cv::Mat) UIImageToCVMat:(UIImage *) image {
// UIImage を cv::Mat に変換する
}
+ (UIImage *) CVMatToUIImage:(cv::Mat) cvMat {
// cv::Mat を UIImage に変換する処理
}
@end
ImageProcessor.h
#ifndef ImageProcessor_h
#define ImageProcessor_h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface ImageProcessor : NSObject
+ (UIImage *) convertToGrayscale:(UIImage *) image;
@end
#endif /* ImageProcessor_h */
4. Swiftコードから参照できるようにBridgingファイルにヘッダーの追加
SwiftコードからImageProcessor#convertToGrayscale:
を扱うため、BridgingファイルでImageProcessor.hをインポートする。
OpenCVSample-Bridging-Header.h
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "ImageProcessor.h"
5. 画面側で画像処理を実行する
このサンプルでは、SwiftUIで画面を実装している。「to grayscale」ボタンをタップすると、ImageProcessor#convertToGrayscale
を実行し、加工後の画像を表示する。
import SwiftUI
struct ContentView: View {
@State private var image = UIImage(named: "sample")
@State private var text: String?
var body: some View {
VStack {
if let image = image {
Image(uiImage: image)
.resizable()
.frame(width: 200, height: 200)
.scaledToFit()
}
if let text = text {
Text(text)
}
Button("to grayscale") {
action()
}
Button("clear") {
clear()
}
}
.padding()
}
private func action() {
guard let source = UIImage(named: "sample") else {
text = "画像の取得に失敗"
return
}
guard let image = ImageProcessor.convert(toGrayscale: source) else {
text = "画像の変換に失敗"
return
}
self.image = image
text = nil
}
private func clear() {
image = UIImage(named: "sample")
text = nil
}
}
動作確認
サンプルアプリを実行し、to grayscaleボタンをタップすると下図のように変化する。
まとめ
以上で、iOSアプリにCocoaPodsを使ってOpenCVを導入し、画像をグレースケール化することができるようになった。このコードをベースにすることで、さらに高度な画像処理や解析機能の開発が可能となるだろう。
この記事で解説に使ったサンプルコードに関しては、以下のリポジトリで公開している。