OpenCVとSwiftを使用したオブジェクト検出

公開: 2022-03-11

Swiftはしばらくの間私たちと一緒にいて、その反復を通して、現代のオブジェクト指向プログラミング言語のすべての機能を私たちにもたらしました。 これらには、オプション、ジェネリック、タプル、メソッドをサポートする構造体、拡張機能、プロトコルなどが含まれます。 ただし、アプリケーションがC ++を使用して作成されたライブラリに依存している場合は、Swiftを信頼できなくなります。 幸いなことにObjective-C++は私たちを助けるためにここにあります。

導入以来、SwiftはObjective-Cとの優れた相互運用性を備えており、Objective-Cを使用してSwiftとC++をブリッジします。 Objective-C ++は、C ++コードとリンクする機能を備えたObjective-Cにすぎません。このブログ投稿では、OpenCVを使用して画像内のToptalロゴを認識するシンプルなアプリを作成します。 そのロゴを検出すると、Toptalホームページを開きます。

OpenCV Webページに記載されているように:

「OpenCVは計算効率を重視して設計されており、リアルタイムアプリケーションに重点を置いています。最適化されたC / C ++で記述されているため、ライブラリはマルチコア処理を利用できます。」

これは、迅速に開発し、信頼性の高いコンピュータービジョンが必要な場合に最適なソリューションです。

OpenCVObjective-C++ラッパーの作成

このチュートリアルでは、画像内のToptalロゴと一致するアプリケーションを設計し、ToptalWebページを開きます。 まず、新しいXcodeプロジェクトを作成し、podinitを使用してCocoaPodsをセットアップしpod init 。 OpenCVをpod 'OpenCVに追加し、ターミナルでpod installを実行します。 必ずuse_frameworksのコメントを外してください! Podfile内のステートメント。

Xcodeプロジェクト内にOpenCVがある場合、Swiftに接続する必要があります。 関連する手順の概要は次のとおりです。

ステップ1:新しいObjective-CクラスOpenCVWrapperを作成します。 Xcodeから「 Objective-Cブリッジヘッダーを設定しますか? 」を選択します。「ブリッジヘッダーの作成」を選択します。 ブリッジヘッダーは、Objective-Cクラスをインポートする場所であり、Swift内に表示されます。

ステップ2: Objective-C内でC ++を使用するには、ファイル拡張子をOpenCVWrapper.mからOpenCVWrapper.mmに変更する必要があります。 Xcodeのプロジェクトナビゲーター内でファイルの名前を変更するだけでそれを行うことができます。 拡張子として.mmを追加すると、ファイルタイプがObjective-CからObjective-C++に変更されます。

ステップ3:次のインポートを使用してOpenCVをOpenCVWrapper.mmにインポートします。 よく知られているBOOLの競合を回避するため、#import #import "OpenCVWrapper.h"の上に特定のインポートを記述することが重要です。 OpenCVには、Objective- NO値との競合を引き起こす値NOを持つ列挙型が含まれています。 このような列挙型を使用するクラスが必要ない場合は、これが最も簡単な方法です。 それ以外の場合は、OpenCVをインポートする前にBOOLの定義を解除する必要があります。

 #ifdef __cplusplus #import <opencv2/opencv.hpp> #import <opencv2/imgcodecs/ios.h> #import <opencv2/videoio/cap_ios.h> #endif

ステップ4:ブリッジヘッダーに#import "OpenCVWrapper.h"を追加します。

OpenCVWrapper.mmを開き、プライベートプロパティを宣言するプライベートインターフェイスを作成します。

 @interface OpenCVWrapper() <CvVideoCameraDelegate> @property (strong, nonatomic) CvVideoCamera *videoCamera; @property (assign, nonatomic) cv::Mat logoSample; @end

CvVideoCameraを作成するには、 UIImageViewを渡す必要があります。これは、指定子イニシャライザーを介して行います。

 - (instancetype)initWithParentView:(UIImageView *)parentView delegate:(id<OpenCVWrapperDelegate>)delegate { if (self = [super init]) { self.delegate = delegate; parentView.contentMode = UIViewContentModeScaleAspectFill; self.videoCamera = [[CvVideoCamera alloc] initWithParentView:parentView]; self.videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack; self.videoCamera.defaultAVCaptureSessionPreset = AVCaptureSessionPresetHigh; self.videoCamera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait; self.videoCamera.defaultFPS = 30; self.videoCamera.grayscaleMode = [NSNumber numberWithInt:0].boolValue; self.videoCamera.delegate = self; // Convert UIImage to Mat and store greyscale version UIImage *templateImage = [UIImage imageNamed:@"toptal"]; cv::Mat templateMat; UIImageToMat(templateImage, templateMat); cv::Mat grayscaleMat; cv::cvtColor(templateMat, grayscaleMat, CV_RGB2GRAY); self.logoSample = grayscaleMat; [self.videoCamera start]; } return self; }

その中で、指定されたparentView内でビデオをレンダリングするCvVideoCameraを構成し、デリゲートを介して分析のためにcv::Mat画像を送信します。

processImage:メソッドはCvVideoCameraDelegateプロトコルからのものであり、その中でテンプレートマッチングを行います。

 - (void)processImage:(cv::Mat&)image { cv::Mat gimg; // Convert incoming img to greyscale to match template cv::cvtColor(image, gimg, CV_BGR2GRAY); // Get matching cv::Mat res(image.rows-self.logoSample.rows+1, self.logoSample.cols-self.logoSample.cols+1, CV_32FC1); cv::matchTemplate(gimg, self.logoSample, res, CV_TM_CCOEFF_NORMED); cv::threshold(res, res, 0.5, 1., CV_THRESH_TOZERO); double minval, maxval, threshold = 0.9; cv::Point minloc, maxloc; cv::minMaxLoc(res, &minval, &maxval, &minloc, &maxloc); // Call delegate if match is good enough if (maxval >= threshold) { // Draw a rectangle for confirmation cv::rectangle(image, maxloc, cv::Point(maxloc.x + self.logoSample.cols, maxloc.y + self.logoSample.rows), CV_RGB(0,255,0), 2); cv::floodFill(res, maxloc, cv::Scalar(0), 0, cv::Scalar(.1), cv::Scalar(1.)); [self.delegate openCVWrapperDidMatchImage:self]; } }

まず、指定された画像をグレースケール画像に変換します。これは、initメソッド内で、Toptalロゴテンプレートマッチング画像をグレースケールに変換したためです。 次のステップは、スケーリングと角度に役立つ特定のしきい値との一致をチェックすることです。 ロゴを検出したいさまざまな角度で写真をキャプチャできるため、角度を確認する必要があります。 指定されたしきい値に達した後、デリゲートを呼び出してToptalのWebページを開きます。

NSCameraUsageDescriptionをInfo.plistに追加することを忘れないでください。そうしないと、 [self.videoCamera start];

ついにSwift

これまでは、すべてのロジックが内部で行われるため、Objective-C++に焦点を当てていました。 Swiftコードはかなり単純になります。

  1. ViewController.swift内で、 UIImageViewがコンテンツをレンダリングするCvVideoCameraを作成します。
  2. OpenCVWrapperのインスタンスを作成し、 UIImageViewインスタンスを渡します。
  3. OpenCVWrapperDelegateプロトコルを実装して、ロゴを検出したときにToptalのWebページを開きます。
 class ViewController: UIViewController { var wrapper: OpenCVWrapper! @IBOutlet var imageView: UIImageView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. wrapper = OpenCVWrapper.init(parentView: imageView, delegate: self) } } extension ViewController: OpenCVWrapperDelegate { //MARK: - OpenCVWrapperDelegate func openCVWrapperDidMatchImage(_ wrapper: OpenCVWrapper) { UIApplication.shared.open(URL.init(string: "https://toptal.com")!, options: [:], completionHandler: nil) } }

OpenCVSwiftの動作

この記事では、C ++コードをSwiftと統合し、C++コードをSwiftとブリッジするためのラッパークラスを作成する方法を示しました。 これで、カメラからToptalロゴを検出すると、Toptalのホームページが開きます。

OpenCVSwiftを使用したToptalのロゴの検出

将来の更新のために、バックグラウンドスレッドでCVテンプレートマッチングを実行することをお勧めします。 このように、メインスレッドをブロックすることはなく、UIの応答性は維持されます。 これはOpenCVの単純な例であるため、テンプレートマッチングはそれほど成功しない可能性がありますが、この記事の目的は、OpenCVの使用を開始する方法を示すことでした。

OpenCVとiOSでのより複雑な使用法についてまだ興味がある場合は、iOSでMSERを使用したリアルタイムオブジェクト検出をお勧めします。これにより、iPhoneのリアカメラを使用した画像検出について説明します。