Tutorial OpenCV: rilevamento di oggetti in tempo reale utilizzando MSER in iOS

Pubblicato: 2022-03-11

Negli ultimi anni, le prestazioni medie dei telefoni cellulari sono aumentate in modo significativo. Che si tratti della potenza della CPU o della capacità della RAM, ora è più facile eseguire attività di elaborazione pesanti su hardware mobile. Sebbene queste tecnologie mobili stiano andando nella giusta direzione, c'è ancora molto da fare sulle piattaforme mobili, soprattutto con l'avvento della realtà aumentata, della realtà virtuale e dell'intelligenza artificiale.

Una delle principali sfide nella visione artificiale è rilevare oggetti di interesse nelle immagini. L'occhio e il cervello umani fanno un lavoro eccezionale e replicarlo nelle macchine è ancora un sogno. Negli ultimi decenni, sono stati sviluppati approcci per imitare questo nelle macchine e sta migliorando.

In questo tutorial esploreremo un algoritmo utilizzato per rilevare i blob nelle immagini. Utilizzeremo anche l'algoritmo, dalla libreria open source, OpenCV, per implementare un prototipo di applicazione per iPhone che utilizza la fotocamera posteriore per acquisire immagini e rilevare gli oggetti in esse contenuti.

Tutorial OpenCV

OpenCV è una libreria open source che fornisce implementazioni dei principali algoritmi di computer vision e machine learning. Se desideri implementare un'applicazione per rilevare volti, giocare a carte su un tavolo da poker o anche una semplice applicazione per aggiungere effetti a un'immagine arbitraria, OpenCV è un'ottima scelta.

OpenCV è scritto in C/C++ e dispone di librerie wrapper per tutte le principali piattaforme. Ciò lo rende particolarmente facile da usare all'interno dell'ambiente iOS. Per utilizzarlo all'interno di un'applicazione iOS Objective-C, scarica OpenCV iOS Framework dal sito Web ufficiale. Assicurati di utilizzare la versione 2.4.11 di OpenCV per iOS (che questo articolo presume tu stia utilizzando), poiché l'ultima versione, 3.0, ha alcune modifiche che interrompono la compatibilità nel modo in cui sono organizzati i file di intestazione. Informazioni dettagliate su come installarlo sono documentate sul suo sito web.

MSER

MSER, abbreviazione di Maximally Stable Extremal Regions, è uno dei tanti metodi disponibili per il rilevamento dei blob all'interno delle immagini. In parole semplici, l'algoritmo identifica insiemi contigui di pixel le cui intensità dei pixel del limite esterno sono superiori (di una determinata soglia) rispetto alle intensità dei pixel del limite interno. Si dice che tali regioni siano massimamente stabili se non cambiano molto su una quantità variabile di intensità.

Sebbene esistano numerosi altri algoritmi di rilevamento blob, MSER è stato scelto qui perché ha una complessità di runtime abbastanza leggera di O(n log(log(n))) dove n è il numero totale di pixel sull'immagine. L'algoritmo è anche robusto per sfocare e ridimensionare, il che è vantaggioso quando si tratta di elaborare immagini acquisite tramite sorgenti in tempo reale, come la fotocamera di un telefono cellulare.

Ai fini di questo tutorial, progetteremo l'applicazione per rilevare il logo di Toptal. Il simbolo ha spigoli vivi e ciò può portare a pensare a quanto efficaci possano essere gli algoritmi di rilevamento degli angoli nel rilevare il logo di Toptal. Dopotutto, un tale algoritmo è sia semplice da usare che da capire. Sebbene i metodi basati sugli angoli possano avere un'elevata percentuale di successo quando si tratta di rilevare oggetti nettamente separati dallo sfondo (come oggetti neri su sfondi bianchi), sarebbe difficile ottenere il rilevamento in tempo reale del logo di Toptal nel mondo reale immagini, in cui l'algoritmo rileverebbe costantemente centinaia di angoli.

Strategia

apprendimento automatico e opencv

Per ogni fotogramma dell'immagine che l'applicazione acquisisce tramite la fotocamera, viene prima convertito in scala di grigi. Le immagini in scala di grigi hanno un solo canale di colore, ma il logo sarà comunque visibile. Ciò rende più facile per l'algoritmo gestire l'immagine e riduce significativamente la quantità di dati che l'algoritmo deve elaborare con un guadagno aggiuntivo minimo o nullo.

Successivamente, utilizzeremo l'implementazione di OpenCV dell'algoritmo per estrarre tutti gli MSER. Successivamente, ogni MSER verrà normalizzato trasformando il suo rettangolo di delimitazione minimo in un quadrato. Questo passaggio è importante perché il logo può essere acquisito da diverse angolazioni e distanze e ciò aumenterà la tolleranza alla distorsione prospettica.

Inoltre, per ogni MSER vengono calcolate alcune proprietà:

  • Numero di fori
  • Rapporto tra l'area di MSER e l'area del suo scafo convesso
  • Rapporto tra l'area di MSER e l'area del suo rettangolo di area minima
  • Rapporto tra la lunghezza dello scheletro MSER e l'area del MSER
  • Rapporto tra l'area di MSER e l'area del suo contorno più grande

applicazioni ios e machine learning

Per rilevare il logo di Toptal in un'immagine, le proprietà di tutti i MSER vengono confrontate con le proprietà del logo Toptal già apprese. Ai fini di questo tutorial, sono state scelte empiricamente le differenze massime consentite per ciascuna proprietà.

Infine, come risultato viene scelta la regione più simile.

Applicazione iOS

Usare OpenCV da iOS è facile. Se non l'hai ancora fatto, ecco una breve descrizione dei passaggi coinvolti nella configurazione di Xcode per creare un'applicazione iOS e utilizzare OpenCV al suo interno:

  1. Crea un nuovo nome per il progetto "SuperCool Logo Detector". Come lingua, lascia selezionato Objective-C.

  2. Aggiungere un nuovo file di intestazione del prefisso (.pch) e denominarlo PrefixHeader.pch

  3. Vai nel progetto "SuperCool Logo Detector" Build Target e nella scheda Impostazioni build, trova l'impostazione "Intestazioni prefisso". Puoi trovarlo nella sezione Lingua LLVM o utilizzare la funzione di ricerca.

  4. Aggiungi "PrefixHeader.pch" all'impostazione delle intestazioni del prefisso

  5. A questo punto, se non hai installato OpenCV per iOS 2.4.11, fallo ora.

  6. Trascina e rilascia il framework scaricato nel progetto. Controlla "Strutture e librerie collegate" nelle impostazioni di destinazione. (Dovrebbe essere aggiunto automaticamente, ma è meglio essere sicuri.)

  7. Inoltre, collega i seguenti framework:

    • Fondazione AV
    • AssetsLibrary
    • CoreMedia
  8. Apri "PrefixHeader.pch" e aggiungi le seguenti 3 righe:

     #ifdef __cplusplus #include <opencv2/opencv.hpp> #endif”
  9. Modificare le estensioni dei file di codice creati automaticamente da “ .m” a “ .mm”. OpenCV è scritto in C++ e con *.mm stai dicendo che utilizzerai Objective-C++.

  10. Importa "opencv2/highgui/cap_ios.h" in ViewController.h e cambia ViewController per renderlo conforme al protocollo CvVideoCameraDelegate:

     #import <opencv2/highgui/cap_ios.h>
  11. Apri Main.storyboard e metti un UIImageView sul controller di visualizzazione iniziale.

  12. Crea una presa su ViewController.mm denominata "imageView"

  13. Crea una variabile "CvVideoCamera *camera;" in ViewController.ho ViewController.mm e inizializzarlo facendo riferimento alla fotocamera posteriore:

     camera = [[CvVideoCamera alloc] initWithParentView: _imageView]; camera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack; camera.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480; camera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait; camera.defaultFPS = 30; camera.grayscaleMode = NO; camera.delegate = self;
  14. Se crei il progetto ora, Xcode ti avviserà che non hai implementato il metodo "processImage" da CvVideoCameraDelegate. Per ora, e per semplicità, acquisiremo le immagini dalla fotocamera e le sovrapporremo con un semplice testo:

    • Aggiungi una singola riga a "viewDidAppear":
     [camera start];
    • Ora, se esegui l'applicazione, ti verrà chiesto il permesso di accedere alla fotocamera. E poi dovresti vedere il video dalla fotocamera.

    • Nel metodo "processImage" aggiungi le seguenti due righe:

     const char* str = [@"Toptal" cStringUsingEncoding: NSUTF8StringEncoding]; cv::putText(image, str, cv::Point(100, 100), CV_FONT_HERSHEY_PLAIN, 2.0, cv::Scalar(0, 0, 255));

Questo è praticamente tutto. Ora hai un'applicazione molto semplice che disegna il testo "Toptal" sulle immagini dalla fotocamera. Ora possiamo creare il nostro logo di destinazione che rileva l'applicazione da questa più semplice. Per brevità, in questo articolo discuteremo solo una manciata di segmenti di codice che sono fondamentali per capire come funziona l'applicazione, nel complesso. Il codice su GitHub ha una discreta quantità di commenti per spiegare cosa fa ogni segmento.

Poiché l'applicazione ha un solo scopo, rilevare il logo di Toptal, non appena viene avviata, le funzionalità MSER vengono estratte dall'immagine del modello data e i valori vengono archiviati in memoria:

 cv::Mat logo = [ImageUtils cvMatFromUIImage: templateImage]; //get gray image cv::Mat gray; cvtColor(logo, gray, CV_BGRA2GRAY); //mser with maximum area is std::vector<cv::Point> maxMser = [ImageUtils maxMser: &gray]; //get 4 vertices of the maxMSER minrect cv::RotatedRect rect = cv::minAreaRect(maxMser); cv::Point2f points[4]; rect.points(points); //normalize image cv::Mat M = [GeometryUtil getPerspectiveMatrix: points toSize: rect.size]; cv::Mat normalizedImage = [GeometryUtil normalizeImage: &gray withTranformationMatrix: &M withSize: rect.size.width]; //get maxMser from normalized image std::vector<cv::Point> normalizedMser = [ImageUtils maxMser: &normalizedImage]; //remember the template self.logoTemplate = [[MSERManager sharedInstance] extractFeature: &normalizedMser]; //store the feature [self storeTemplate];

L'applicazione ha solo una schermata con un pulsante Start/Stop e tutte le informazioni necessarie, come FPS e numero di MSER rilevati, vengono disegnate automaticamente sull'immagine. Finché l'applicazione non viene arrestata, per ogni fotogramma dell'immagine nella fotocamera viene richiamato il seguente metodo processImage:

 -(void)processImage:(cv::Mat &)image { cv::Mat gray; cvtColor(image, gray, CV_BGRA2GRAY); std::vector<std::vector<cv::Point>> msers; [[MSERManager sharedInstance] detectRegions: gray intoVector: msers]; if (msers.size() == 0) { return; }; std::vector<cv::Point> *bestMser = nil; double bestPoint = 10.0; std::for_each(msers.begin(), msers.end(), [&] (std::vector<cv::Point> &mser) { MSERFeature *feature = [[MSERManager sharedInstance] extractFeature: &mser]; if(feature != nil) { if([[MLManager sharedInstance] isToptalLogo: feature] ) { double tmp = [[MLManager sharedInstance] distance: feature ]; if ( bestPoint > tmp ) { bestPoint = tmp; bestMser = &mser; } } } }); if (bestMser) { NSLog(@"minDist: %f", bestPoint); cv::Rect bound = cv::boundingRect(*bestMser); cv::rectangle(image, bound, GREEN, 3); } else { cv::rectangle(image, cv::Rect(0, 0, W, H), RED, 3); } // Omitted debug code [FPS draw: image]; }

Questo metodo, in sostanza, crea una copia in scala di grigi dell'immagine originale. Identifica tutti gli MSER ed estrae le loro caratteristiche rilevanti, valuta ogni MSER per la somiglianza con il modello e sceglie quello migliore. Infine, disegna un confine verde attorno al miglior MSER e sovrappone l'immagine con le meta informazioni.

Di seguito sono riportate le definizioni di alcune classi importanti e dei relativi metodi in questa applicazione. I loro scopi sono descritti nei commenti.

GeometryUtil.h

 /* This static class provides perspective transformation function */ @interface GeometryUtil : NSObject /* Return perspective transformation matrix for given points to square with origin [0,0] and with size (size.width, size.width) */ + (cv::Mat) getPerspectiveMatrix: (cv::Point2f[]) points toSize: (cv::Size2f) size; /* Returns new perspecivly transformed image with given size */ + (cv::Mat) normalizeImage: (cv::Mat *) image withTranformationMatrix: (cv::Mat *) M withSize: (float) size; @end

MSERManager.h

 /* Singelton class providing function related to msers */ @interface MSERManager : NSObject + (MSERManager *) sharedInstance; /* Extracts all msers into provided vector */ - (void) detectRegions: (cv::Mat &) gray intoVector: (std::vector<std::vector<cv::Point>> &) vector; /* Extracts feature from the mser. For some MSERs feature can be NULL !!! */ - (MSERFeature *) extractFeature: (std::vector<cv::Point> *) mser; @end

MLManager.h

 /* This singleton class wraps object recognition function */ @interface MLManager : NSObject + (MLManager *) sharedInstance; /* Stores feature from the biggest MSER in the templateImage */ - (void) learn: (UIImage *) templateImage; /* Sum of the differences between logo feature and given feature */ - (double) distance: (MSERFeature *) feature; /* Returns true if the given feature is similar to the one learned from the template */ - (BOOL) isToptalLogo: (MSERFeature *) feature; @end

Dopo che tutto è stato collegato insieme, con questa applicazione, dovresti essere in grado di utilizzare la fotocamera del tuo dispositivo iOS per rilevare il logo di Toptal da diverse angolazioni e orientamenti.

Rilevare un'immagine (il logo Toptal) verticalmente.

Rilevare un'immagine (il logo Toptal) in diagonale su una maglietta.

Le app di realtà aumentata iniziano con la comprensione delle immagini, ed è così che puoi farlo.
Twitta

Conclusione

In questo articolo abbiamo mostrato quanto sia facile rilevare oggetti semplici da un'immagine usando OpenCV. L'intero codice è disponibile su GitHub. Sentiti libero di inviare e inviare richieste push, poiché i contributi sono i benvenuti.

Come per qualsiasi problema di apprendimento automatico, la percentuale di successo del rilevamento del logo in questa applicazione può essere aumentata utilizzando un diverso insieme di funzionalità e un metodo diverso per la classificazione degli oggetti. Tuttavia, spero che questo articolo ti aiuti a iniziare con il rilevamento di oggetti utilizzando MSER e le applicazioni delle tecniche di visione artificiale, in generale.

Ulteriori letture

  • J. Matas, O. Chum, M. Urban e T. Pajdla. "Robusto stereo di base ampio da regioni estremali massimamente stabili."
  • Neumann, Lukas; Matas, Jiri (2011). "Un metodo per la localizzazione e il riconoscimento del testo nelle immagini del mondo reale"
Correlati: un tutorial introduttivo alla programmazione dei robot