Rilevamento di oggetti utilizzando OpenCV e Swift
Pubblicato: 2022-03-11Swift è con noi da un po' di tempo ormai e, attraverso le sue iterazioni, ci ha portato tutte le caratteristiche di un moderno linguaggio di programmazione orientato agli oggetti. Questi includono opzionali, generici, tuple, struct che supportano metodi, estensioni e protocolli e molti altri. Ma se la tua applicazione si basa su una libreria scritta in C++, non puoi più contare su Swift. Fortunatamente Objective-C++ è qui per aiutarci.
Dalla sua introduzione, Swift ha avuto una grande interoperabilità con Objective-C e useremo Objective-C per collegare Swift con C++. Objective-C++ non è altro che Objective-C con la possibilità di collegarsi al codice C++, e usandolo, in questo post del blog, creeremo una semplice app che riconoscerà il logo Toptal all'interno dell'immagine usando OpenCV. Quando rileviamo quel logo, apriremo la home page di Toptal.
Come affermato nella pagina web di OpenCV:
"OpenCV è stato progettato per l'efficienza computazionale e con una forte attenzione alle applicazioni in tempo reale. Scritta in C/C++ ottimizzato, la libreria può trarre vantaggio dall'elaborazione multi-core".
Questa è un'ottima soluzione se si desidera uno sviluppo rapido e una visione artificiale affidabile.
Creazione del wrapper Objective-C++ OpenCV
In questo tutorial, progetteremo l'applicazione che abbinerà un logo Toptal all'interno di un'immagine e aprirà la pagina Web di Toptal. Per iniziare, crea un nuovo progetto Xcode e configura CocoaPods usando pod init
. Aggiungi OpenCV al pod 'OpenCV
ed esegui l' pod install
nel Terminale. Assicurati di decommentare use_frameworks
! dichiarazione all'interno di Podfile.
Ora, quando abbiamo OpenCV all'interno del progetto Xcode, dobbiamo collegarlo a Swift. Ecco un rapido schema dei passaggi coinvolti:
Passaggio 1: crea una nuova classe Objective-C OpenCVWrapper
. Quando Xcode ti chiede " Vuoi configurare un'intestazione di bridging Objective-C? ” scegli “ Crea bridging header ”. L'intestazione del bridging è il punto in cui importi le classi Objective-C e quindi sono visibili all'interno di Swift.
Passaggio 2: per utilizzare C++ all'interno di Objective-C, dobbiamo modificare l'estensione del file da OpenCVWrapper.m
a OpenCVWrapper.mm
. Puoi farlo semplicemente rinominando il file all'interno del navigatore di progetto di Xcode. L'aggiunta di .mm
come estensione cambierà il tipo di file da Objective-C a Objective-C++.
Passaggio 3: importa OpenCV in OpenCVWrapper.mm
utilizzando la seguente importazione. È importante scrivere il dato import sopra #import "OpenCVWrapper.h"
perché in questo modo evitiamo un noto conflitto BOOL. OpenCV contiene enum che ha il valore NO
che causa un conflitto con il valore di Objective-C BOOL NO
. Se non hai bisogno di classi che usano tale enum, allora questo è il modo più semplice. Altrimenti, devi annullare la definizione di BOOL prima di importare OpenCV.
#ifdef __cplusplus #import <opencv2/opencv.hpp> #import <opencv2/imgcodecs/ios.h> #import <opencv2/videoio/cap_ios.h> #endif
Passaggio 4: aggiungi #import "OpenCVWrapper.h"
all'intestazione del bridging.
Apri OpenCVWrapper.mm
e crea un'interfaccia privata in cui dichiareremo le proprietà private:
@interface OpenCVWrapper() <CvVideoCameraDelegate> @property (strong, nonatomic) CvVideoCamera *videoCamera; @property (assign, nonatomic) cv::Mat logoSample; @end
Per creare CvVideoCamera
, dobbiamo passargli UIImageView
e lo faremo tramite il nostro inizializzatore di designazione.
- (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; }
Al suo interno, configuriamo CvVideoCamera
che esegue il rendering del video all'interno del parentView
specificato e tramite delegato ci invia l'immagine cv::Mat
per l'analisi.

processImage:
metodo proviene dal protocollo CvVideoCameraDelegate
e al suo interno faremo la corrispondenza dei modelli.
- (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]; } }
Innanzitutto, convertiamo l'immagine data in un'immagine in scala di grigi perché all'interno del metodo init abbiamo convertito il nostro modello di logo Toptal che corrisponde all'immagine in scala di grigi. Il prossimo passo è controllare le corrispondenze con una certa soglia che ci aiuterà con il ridimensionamento e gli angoli. Dobbiamo controllare gli angoli perché puoi catturare una foto in vari angoli in cui vogliamo ancora rilevare il logo. Dopo aver raggiunto la soglia indicata, invocheremo il delegato e apriremo la pagina web di Toptal.
Non dimenticare di aggiungere NSCameraUsageDescription
al tuo Info.plist, altrimenti l'applicazione andrà in crash subito dopo aver chiamato [self.videoCamera start];
.
Ora finalmente Swift
Finora ci siamo concentrati su Objective-C++ perché tutta la logica è fatta al suo interno. Il nostro codice Swift sarà abbastanza semplice:
- All'interno di
ViewController.swift
, creaUIImageView
doveCvVideoCamera
eseguirà il rendering del contenuto. - Crea un'istanza di
OpenCVWrapper
e passa ad essa l'istanzaUIImageView
. - Implementa il protocollo
OpenCVWrapperDelegate
per aprire la pagina web di Toptal quando rileviamo il logo.
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 in azione
In questo articolo, abbiamo mostrato come integrare il codice C++ con Swift e creare classi wrapper che sono qui per collegare il codice C++ con Swift. Ora, quando rileviamo il logo Toptal attraverso la fotocamera, apriamo la home page di Toptal.
Per aggiornamenti futuri, potresti voler eseguire la corrispondenza del modello di CV in un thread in background. In questo modo, non bloccherai il thread principale e l'interfaccia utente rimarrà reattiva. Poiché questo è un semplice esempio di OpenCV, la corrispondenza dei modelli potrebbe non avere un maggiore successo, ma lo scopo di questo articolo era mostrarti come iniziare a usarlo.
Se sei ancora interessato all'apprendimento di OpenCV e dei suoi usi più complessi in iOS, ti consiglio il rilevamento di oggetti in tempo reale utilizzando MSER in iOS , che ti guida attraverso il rilevamento delle immagini utilizzando la fotocamera posteriore dell'iPhone.