使用 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 網頁上所述:

“OpenCV 專為提高計算效率而設計,重點關注實時應用程序。該庫採用優化的 C/C++ 編寫,可以利用多核處理。”

如果您想要快速開發和可靠的計算機視覺,這是一個很好的解決方案。

創建 OpenCV Objective-C++ 包裝器

在本教程中,我們將設計應用程序來匹配圖像中的 Toptal 徽標並打開 Toptal 網頁。 首先,創建一個新的 Xcode 項目並使用pod init設置 CocoaPods。 將 OpenCV 添加到 Podfile 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 。 在#import "OpenCVWrapper.h"上方編寫給定的導入很重要,因為這樣我們可以避免眾所周知的 BOOL 衝突。 OpenCV 包含值NO的枚舉,這會導致與 Objective-C BOOL 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; }

在其中,我們配置CvVideoCamera ,它在給定的parentView中渲染視頻,並通過委託向我們發送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 的網頁。

不要忘記將NSCameraUsageDescription添加到您的 Info.plist 中,否則,您的應用程序將在調用[self.videoCamera start]; .

現在終於 Swift

到目前為止,我們專注於 Objective-C++,因為所有邏輯都在其中完成。 我們的 Swift 代碼將相當簡單:

  1. ViewController.swift中,創建CvVideoCamera將呈現內容的UIImageView
  2. 創建一個OpenCVWrapper實例並將UIImageView實例傳遞給它。
  3. 當我們檢測到徽標時,執行OpenCVWrapperDelegate協議以打開 Toptal 的網頁。
 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) } }

OpenCV Swift 在行動

在本文中,我們展示瞭如何將 C++ 代碼與 Swift 集成,並創建用於將 C++ 代碼與 Swift 連接起來的包裝類。 現在,當我們通過攝像頭檢測到 Toptal Logo 時,我們打開 Toptal 的主頁。

使用 OpenCV Swift 檢測 Toptal 的徽標

對於未來的更新,您可能希望在後台線程中運行 CV 模板匹配。 這樣,您不會阻塞主線程,並且 UI 將保持響應。 因為這是一個簡單的 OpenCV 示例,模板匹配可能不會特別成功,但本文的目的是向您展示如何開始使用它。

如果您仍然對學習 OpenCV 及其在 iOS 中更複雜的用途感興趣,我推薦在 iOS 中使用 MSER 進行實時對象檢測,它會引導您使用 iPhone 的後置攝像頭進行圖像檢測。