Detectarea obiectelor folosind OpenCV și Swift

Publicat: 2022-03-11

Swift 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:

  1. În ViewController.swift , creați UIImageView unde CvVideoCamera va reda conținut.
  2. Creați o instanță a OpenCVWrapper și transmiteți-i instanța UIImageView .
  3. 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.

Detectarea siglei Toptal cu OpenCV Swift

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.