Tutorial OpenCV: Detectarea obiectelor în timp real folosind MSER în iOS

Publicat: 2022-03-11

În ultimii ani, performanța medie a telefonului mobil a crescut semnificativ. Fie că este vorba despre puterea CPU sau capacitatea RAM, acum este mai ușor să faci sarcini grele de calcul pe hardware-ul mobil. Deși aceste tehnologii mobile se îndreaptă în direcția corectă, mai sunt multe de făcut pe platformele mobile, în special odată cu apariția realității augmentate, a realității virtuale și a inteligenței artificiale.

O provocare majoră în viziunea computerizată este detectarea obiectelor de interes în imagini. Ochiul și creierul uman fac o treabă excepțională, iar replicarea acestui lucru în mașini este încă un vis. În ultimele decenii, s-au dezvoltat abordări pentru a imita acest lucru în mașini și este din ce în ce mai bine.

În acest tutorial, vom explora un algoritm utilizat în detectarea blob-urilor în imagini. De asemenea, vom folosi algoritmul, din biblioteca open source, OpenCV, pentru a implementa o aplicație prototip pentru iPhone care folosește camera din spate pentru a achiziționa imagini și a detecta obiecte din ele.

Tutorial OpenCV

OpenCV este o bibliotecă open source care oferă implementări ale unor algoritmi majori de viziune computerizată și de învățare automată. Dacă doriți să implementați o aplicație pentru a detecta fețele, cărți de joc pe o masă de poker sau chiar o aplicație simplă pentru adăugarea de efecte la o imagine arbitrară, atunci OpenCV este o alegere excelentă.

OpenCV este scris în C/C++ și are biblioteci wrapper pentru toate platformele majore. Acest lucru îl face deosebit de ușor de utilizat în mediul iOS. Pentru a-l utiliza într-o aplicație iOS Objective-C, descărcați cadrul OpenCV iOS de pe site-ul oficial. Vă rugăm să vă asigurați că utilizați versiunea 2.4.11 a OpenCV pentru iOS (pe care acest articol presupune că o utilizați), deoarece ultima versiune, 3.0, are unele modificări care distrug compatibilitatea în modul în care sunt organizate fișierele antet. Informații detaliate despre cum să-l instalezi sunt documentate pe site-ul său web.

MSER

MSER, prescurtare de la Maximally Stable Extremal Regions, este una dintre numeroasele metode disponibile pentru detectarea blobului în imagini. Cu cuvinte simple, algoritmul identifică seturi învecinate de pixeli ale căror intensități ale pixelilor la granița exterioară sunt mai mari (cu un anumit prag) decât intensitățile pixelilor la granița interioară. Se spune că astfel de regiuni sunt maxim stabile dacă nu se schimbă mult la o cantitate variabilă de intensități.

Deși există o serie de alți algoritmi de detectare a blob-ului, MSER a fost ales aici deoarece are o complexitate de rulare destul de ușoară de O(n log(log(n))) unde n este numărul total de pixeli de pe imagine. Algoritmul este, de asemenea, robust la estompare și scalare, ceea ce este avantajos atunci când vine vorba de procesarea imaginilor achiziționate prin surse în timp real, cum ar fi camera unui telefon mobil.

În scopul acestui tutorial, vom proiecta aplicația pentru a detecta sigla Toptal. Simbolul are colțuri ascuțite și acest lucru poate conduce să ne gândim la cât de eficienți pot fi algoritmii de detectare a colțurilor în detectarea siglei Toptal. La urma urmei, un astfel de algoritm este atât simplu de utilizat, cât și de înțeles. Deși metodele bazate pe colțuri pot avea o rată de succes ridicată atunci când vine vorba de detectarea obiectelor care sunt distincte de fundal (cum ar fi obiectele negre pe fundal alb), ar fi dificil să se realizeze detectarea în timp real a siglei Toptal în lumea reală. imagini, în care algoritmul ar detecta în mod constant sute de colțuri.

Strategie

machine learning și opencv

Pentru fiecare cadru de imagine pe care aplicația îl dobândește prin intermediul camerei, acesta este convertit mai întâi în tonuri de gri. Imaginile în tonuri de gri au un singur canal de culoare, dar logo-ul va fi vizibil, totuși. Acest lucru face ca algoritmul să se ocupe mai ușor de imagine și reduce semnificativ cantitatea de date pe care algoritmul trebuie să le proceseze pentru un câștig suplimentar mic sau deloc.

În continuare, vom folosi algoritmul implementării OpenCV pentru a extrage toate MSER-urile. În continuare, fiecare MSER va fi normalizat prin transformarea dreptunghiului său minim de limite într-un pătrat. Acest pas este important deoarece logo-ul poate fi achiziționat din unghiuri și distanțe diferite și acest lucru va crește toleranța la distorsiunea perspectivei.

Mai mult, un număr de proprietăți sunt calculate pentru fiecare MSER:

  • Numărul de găuri
  • Raportul dintre suprafața MSER și aria corpului său convex
  • Raportul dintre aria lui MSER și aria dreptunghiului său cu aria minimă
  • Raportul dintre lungimea scheletului MSER și aria MSER
  • Raportul dintre aria MSER și aria celui mai mare contur

aplicații iOS și învățare automată

Pentru a detecta sigla Toptal într-o imagine, proprietățile tuturor MSER-urilor sunt comparate cu proprietățile logo-ului Toptal deja învățate. În scopul acestui tutorial, diferențele maxime permise pentru fiecare proprietate au fost alese empiric.

În cele din urmă, regiunea cea mai asemănătoare este aleasă ca rezultat.

Aplicație iOS

Utilizarea OpenCV din iOS este ușoară. Dacă nu ați făcut-o încă, iată o scurtă descriere a pașilor implicați în configurarea Xcode pentru a crea o aplicație iOS și a utiliza OpenCV în ea:

  1. Creați un nou nume de proiect „SuperCool Logo Detector”. Ca limbă, lăsați Obiective-C selectat.

  2. Adăugați un nou fișier Antet prefix (.pch) și denumiți-l PrefixHeader.pch

  3. Accesați proiectul „SuperCool Logo Detector” Build Target și în fila Build Settings, găsiți setarea „Prefix Headers”. Îl puteți găsi în secțiunea LLVM Language sau utilizați funcția de căutare.

  4. Adăugați „PrefixHeader.pch” la setarea Antetelor de prefix

  5. În acest moment, dacă nu ați instalat OpenCV pentru iOS 2.4.11, faceți-o acum.

  6. Trageți și plasați cadrul descărcat în proiect. Bifați „Linked Frameworks and Libraries” în Setările țintă. (Ar trebui adăugat automat, dar mai bine pentru a fi în siguranță.)

  7. În plus, conectați următoarele cadre:

    • Fundația AV
    • Assets Library
    • CoreMedia
  8. Deschideți „PrefixHeader.pch” și adăugați următoarele 3 rânduri:

     #ifdef __cplusplus #include <opencv2/opencv.hpp> #endif”
  9. Schimbați extensiile fișierelor de cod create automat din „ .m” în „ .mm”. OpenCV este scris în C++ și cu *.mm spui că vei folosi Objective-C++.

  10. Importați „opencv2/highgui/cap_ios.h” în ViewController.h și modificați ViewController pentru a se conforma cu protocolul CvVideoCameraDelegate:

     #import <opencv2/highgui/cap_ios.h>
  11. Deschideți Main.storyboard și puneți un UIImageView pe controlerul de vizualizare inițial.

  12. Faceți o priză la ViewController.mm numită „imageView”

  13. Creați o variabilă „CvVideoCamera *camera;” în ViewController.h sau ViewController.mm și inițializați-l cu o referință la camera din spate:

     camera = [[CvVideoCamera alloc] initWithParentView: _imageView]; camera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack; camera.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480; camera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait; camera.defaultFPS = 30; camera.grayscaleMode = NO; camera.delegate = self;
  14. Dacă construiți proiectul acum, Xcode vă va avertiza că nu ați implementat metoda „processImage” de la CvVideoCameraDelegate. Deocamdată, și de dragul simplității, vom obține doar imaginile de la cameră și le vom suprapune cu un text simplu:

    • Adăugați o singură linie la „viewDidAppear”:
     [camera start];
    • Acum, dacă rulați aplicația, aceasta vă va cere permisiunea de a accesa camera. Și apoi ar trebui să vedeți video de la cameră.

    • În metoda „processImage” adăugați următoarele două linii:

     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));

Cam asta este. Acum aveți o aplicație foarte simplă care desenează textul „Toptal” pe imaginile din cameră. Acum putem construi aplicația noastră de detectare a logo-ului țintă din aceasta mai simplă. Pentru concizie, în acest articol vom discuta doar o mână de segmente de cod care sunt esențiale pentru înțelegerea modului în care funcționează aplicația, în general. Codul de pe GitHub are o cantitate destul de mare de comentarii pentru a explica ce face fiecare segment.

Întrucât aplicația are un singur scop, acela de a detecta sigla Toptal, de îndată ce este lansată, caracteristicile MSER sunt extrase din imaginea șablonului dată și valorile sunt stocate în memorie:

 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];

Aplicația are un singur ecran cu un buton Start/Stop, iar toate informațiile necesare, cum ar fi FPS și numărul de MSER detectate, sunt desenate automat pe imagine. Atâta timp cât aplicația nu este oprită, pentru fiecare cadru de imagine din cameră, este invocată următoarea metodă 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]; }

Această metodă, în esență, creează o copie în tonuri de gri a imaginii originale. Identifică toți MSER-urile și extrage caracteristicile lor relevante, punctează fiecare MSER pentru similitudine cu șablonul și îl alege pe cel mai bun. În cele din urmă, desenează o graniță verde în jurul celui mai bun MSER și suprapune imaginea cu meta informații.

Mai jos sunt definițiile câtorva clase importante și metodele acestora în această aplicație. Scopurile lor sunt descrise în comentarii.

GeometrieUtil.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

După ce totul este conectat împreună, cu această aplicație, ar trebui să puteți utiliza camera dispozitivului iOS pentru a detecta sigla Toptal din diferite unghiuri și orientări.

Detectarea unei imagini (sigla Toptal) pe verticală.

Detectarea unei imagini (sigla Toptal) în diagonală pe o cămașă.

Aplicațiile de realitate augmentată încep cu înțelegerea imaginilor și așa puteți face acest lucru.
Tweet

Concluzie

În acest articol am arătat cât de ușor este să detectezi obiecte simple dintr-o imagine folosind OpenCV. Întregul cod este disponibil pe GitHub. Simțiți-vă liber să bifurcați și să trimiteți solicitări push, deoarece contribuțiile sunt binevenite.

Așa cum este adevărat pentru orice problemă de învățare automată, rata de succes a detectării siglei în această aplicație poate fi crescută prin utilizarea unui set diferit de caracteristici și a unei metode diferite de clasificare a obiectelor. Cu toate acestea, sper că acest articol vă va ajuta să începeți cu detectarea obiectelor folosind MSER și aplicațiile tehnicilor de viziune computerizată, în general.

Lectură suplimentară

  • J. Matas, O. Chum, M. Urban și T. Pajdla. „Stereo de bază larg robust din regiuni extreme maxim stabile.”
  • Neumann, Lukas; Matas, Jiri (2011). „O metodă pentru localizarea și recunoașterea textului în imagini din lumea reală”
Înrudit: Un tutorial introductiv de programare a roboților