Detectarea obiectelor folosind OpenCV și Swift
Publicat: 2022-03-11Swift ne este alături de ceva vreme și, prin iterațiile sale, ne-a adus toate caracteristicile unui limbaj modern de programare orientat pe obiecte. Acestea includ opționale, generice, tupluri, structuri care acceptă metode, extensii și protocoale și multe altele. Dar dacă aplicația ta se bazează pe o bibliotecă scrisă folosind C++, atunci nu te mai poți baza pe Swift. Din fericire, Objective-C++ este aici pentru a ne ajuta.
De la introducerea sa, Swift a avut o mare interoperabilitate cu Objective-C și vom folosi Objective-C pentru a lega Swift cu C++. Objective-C++ nu este altceva decât Objective-C cu capacitatea de a se conecta cu codul C++ și, folosindu-l, în această postare de blog, vom crea o aplicație simplă care va recunoaște sigla Toptal în interiorul imaginii folosind OpenCV. Când detectăm acel logo, vom deschide pagina de pornire Toptal.
După cum se menționează pe pagina web OpenCV:
„OpenCV a fost conceput pentru eficiență computațională și cu un accent puternic pe aplicațiile în timp real. Scrisă în C/C++ optimizat, biblioteca poate profita de procesarea multi-core.”
Aceasta este o soluție excelentă dacă doriți o viziune rapidă și fiabilă pentru computer.
Crearea OpenCV Objective-C++ Wrapper
În acest tutorial, vom proiecta aplicația care se va potrivi cu un logo Toptal în interiorul unei imagini și vom deschide pagina web Toptal. Pentru a începe, creați un nou proiect Xcode și configurați CocoaPods folosind pod init
. Adăugați OpenCV la Podfile pod 'OpenCV
și rulați pod install
în Terminal. Asigurați-vă că decomentați use_frameworks
! declarație în Podfile.
Acum, când avem OpenCV în proiectul Xcode, trebuie să-l conectăm cu Swift. Iată o scurtă descriere a pașilor implicați:
Pasul 1: Creați o nouă clasă Objective-C OpenCVWrapper
. Când Xcode vă întreabă „ Doriți să configurați un antet de legătură Objective-C? ” alegeți „ Creați antet de legătură ”. Antetul de legătură este locul în care importați clasele Objective-C și apoi sunt vizibile în Swift.
Pasul 2: Pentru a utiliza C++ în interiorul Objective-C, trebuie să schimbăm extensia fișierului de la OpenCVWrapper.m
la OpenCVWrapper.mm
. Puteți face asta prin simpla redenumire a fișierului în navigatorul de proiecte Xcode. Adăugarea .mm
ca extensie va schimba tipul de fișier din Objective-C în Objective-C++.
Pasul 3: Importați OpenCV în OpenCVWrapper.mm
utilizând următorul import. Este important să scrieți importul dat deasupra #import "OpenCVWrapper.h"
, deoarece astfel evităm un conflict BOOL binecunoscut. OpenCV conține enumerare care are valoarea NO
care provoacă un conflict cu valoarea Objective-C BOOL NO
. Dacă nu aveți nevoie de clase care utilizează o astfel de enumerare, atunci acesta este cel mai simplu mod. În caz contrar, trebuie să nedefiniți BOOL înainte de a importa OpenCV.
#ifdef __cplusplus #import <opencv2/opencv.hpp> #import <opencv2/imgcodecs/ios.h> #import <opencv2/videoio/cap_ios.h> #endif
Pasul 4: Adăugați #import "OpenCVWrapper.h"
la antetul de legătură.
Deschideți OpenCVWrapper.mm
și creați o interfață privată în care vom declara proprietăți private:
@interface OpenCVWrapper() <CvVideoCameraDelegate> @property (strong, nonatomic) CvVideoCamera *videoCamera; @property (assign, nonatomic) cv::Mat logoSample; @end
Pentru a crea CvVideoCamera
, trebuie să îi transmitem UIImageView
și vom face asta prin inițializatorul nostru de designator.
- (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; }
În interiorul acestuia, configurăm CvVideoCamera
care redă video în interiorul parentView
dat și prin delegat ne trimite imaginea cv::Mat
pentru analiză.

processImage:
metoda este din protocolul CvVideoCameraDelegate
, iar în interiorul acestuia, vom face potrivirea șablonului.
- (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]; } }
În primul rând, convertim imaginea dată în imagine în tonuri de gri, deoarece în cadrul metodei init am convertit imaginea de potrivire a șablonului logo Toptal în tonuri de gri. Următorul pas este să verificăm potrivirile cu un anumit prag, care ne va ajuta cu scalarea și unghiurile. Trebuie să verificăm unghiurile pentru că poți face o fotografie în diverse unghiuri în care încă vrem să detectăm logo-ul. După atingerea pragului dat, vom invoca delegatul și vom deschide pagina web a Toptal.
Nu uitați să adăugați NSCameraUsageDescription
la Info.plist, altfel aplicația dvs. se va bloca imediat după apelarea [self.videoCamera start];
.
Acum, în sfârșit Swift
Până acum ne-am concentrat pe Objective-C++ pentru că toată logica este făcută în interiorul lui. Codul nostru Swift va fi destul de simplu:
- În
ViewController.swift
, creațiUIImageView
undeCvVideoCamera
va reda conținut. - Creați o instanță a
OpenCVWrapper
și transmiteți-i instanțaUIImageView
. - Implementați protocolul
OpenCVWrapperDelegate
pentru a deschide pagina web a Toptal atunci când detectăm sigla.
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 în acțiune
În acest articol, am arătat cum puteți să integrați codul C++ cu Swift și să creați clase de wrapper care sunt aici pentru a lega codul C++ cu Swift. Acum, când detectăm sigla Toptal prin cameră, deschidem pagina de pornire a Toptal.
Pentru actualizări viitoare, poate doriți să rulați potrivirea șablonului de CV într-un fir de fundal. În acest fel, nu veți bloca firul principal și interfața de utilizare va rămâne receptivă. Deoarece acesta este un exemplu simplu de OpenCV, potrivirea șablonului poate să nu aibă succes, dar scopul acestui articol a fost să vă arate cum puteți începe să îl utilizați.
Dacă încă sunteți interesat să învățați OpenCV și utilizările sale mai complexe în iOS, vă recomand Detectarea obiectelor în timp real folosind MSER în iOS , care vă ghidează prin detectarea imaginii folosind camera din spate a iPhone-ului.