Download - OpenCV_forms detection

Transcript
  • 7/25/2019 OpenCV_forms detection

    1/10

    Corso diElaborazione ed Interpretazione delle

    Immagini Digitali

    A.A. 2014/2015

    N2:

    Video-Analysis: Circle Detection

    Autore:

    Valerio Colamatteo

    Sommario:

    Lesercitazione N 2 richiede lideazione, in ambiente di programmazioneC++/OpenCV,di un meccanismo di tracking video delle traiettorie di alcune

    figure geometriche. richiesta inoltre la determinazione in tempo reale deirelativi centroidi e la stampa a video di alcune informazioni specifiche. Linputvideo di riferimento il flusso webcam.

  • 7/25/2019 OpenCV_forms detection

    2/10

  • 7/25/2019 OpenCV_forms detection

    3/10

    Descrizione dell esercitazione

    Questa seconda esercitazione, come immediatamente evidente dallintestazione, evidenziaun netto scollamento teorico/didattico dalla precedente muovendo dalle tematiche di pi diretta enaturale argomentazione del corso di insegnamento afferente. Onde abbracciare dunque spazi di piampia operativit, la stessa si sofferma su tematiche di approfondimento collaterali per la materia inesame ma che, stante lindubbia componente ludica di cui risultano intrinsecamente portatrici, sidimostrano comunque tali da suscitare, nei pi, vivo apprezzamento e rappresentare cos fonte dicuriosit didattica1.

    Lesercitazione richiedeva, come suggerito dallintitolazione della stessa, di effettuare il trackingvideo di oggetti circolari rappresentati, contestualmente ad altre figure geometriche, su una stampaappositamente ideata e fornita dal docente come riferimento. La fonte video designata la webcam

    del proprio PC.Operativamente il problema si risolto come segue.

    La prima cosa che si fatto stata quella di convertire il flusso delle immagini acquisite ininput dalla webcam da rgba scala di grigio2. Limmagine appena convertita stata poi binarizzata3sfruttando lalgoritmo di Otsu, il quale determina il valore di soglia dellimmagine dallanalisidistributiva dei livelli di grigio allinterno delle varie componenti di sfondo e di oggetto della stessaimmagine.Dopodich si usata la funzione findContours()4 per lindividuazione dei contornidellimmagine binarizzata.

    Con lindividuazione degli stessi e la loro conseguente allocazione in una struttura apposita di tipovector5si chiude la parte iniziale dellesercitazione6.

    1Di riflesso, dunque, stante la diversa architettura teorica di base, il taglio argomentativo caratterizzante la prima diquesto ciclo di relazioni dedicate agli approfondimenti didattici non trover, in questa sede, uguale accoglimento,ritenendo pleonastico per le finalit della presente fornire gli approfondimenti teorici altrimenti dovuti. Ci detto, latrattazione evolver ugualmente in maniera da potersi ritenere certamente non eccessivamente liquidatoria di alcunidegli aspetti pi delicati per loggetto di analisi.

    2 Prototipo della funzione utilizzata: void cvtColor(InputArray src, OutputArray dst, int code, intdstCn=0);

    3Prototipo della funzione utilizzata: double cvThreshold(const CvArr* src, CvArr* dst, double threshold,double max_value, int threshold_type);

    4 Prototipo della funzione utilizzata: void findContours(InputOutputArray image, OutputArrayOfArrayscontours, OutputArray hierarchy, int mode, int method, Point offset=Point());

    Tale funzione opera solo su immagini binarie di input che abbiano le cui componenti connesse siano bianche su sfondonero, ci ha allora indotto, previa corretto utilizzo di tale funzione, la generazione preventiva del negativo della stessaimmagine.5Maggiori e pi puntali riferimenti, come al solito, sono rimandati alla sezione tre della relazione o direttamente alcodice sorgente in allegato.6 In questa prima parte si deliberatamente deciso di non fornire ulteriori e pi profondi riferimenti a quanto fatto

    poich tali primi passaggi sono in parte da ritenersi frutto sia di un comune convenire di idee e proposte in aula con ildocente, sia in parte frutto delle preziose linee di indirizzamento fornite in allegato al testo dellesercitazione. Gli aspettiritenuti invece coredellesercitazione sono quelli immediatamente seguenti, alla cui, si spera esaustiva, argomentazionesi dedicher tutta la parte rimanente di tale lavoro.

  • 7/25/2019 OpenCV_forms detection

    4/10

    Oggetto di intensa attivit elaborativa stata, in questa fase, lideazione di un metodo efficace didiscernimento dei contorni delle diverse forme geometriche raffigurate sulla apposita stampacampione.

    Nella successiva sezione si analizzeranno invece i risultati effettivamente ottenuti.Le specifiche dellesercitazione richiedevano esplicitamente solo lidentificazione della figura di

    forma circolare. Una volta ottenuto il tracciamento della stessa, si cercato di trovare unametodologia unica che permettesse di identificare correttamente anche le altre figure polinomialidella stampa campione. In merito a ci la soluzione tentata stata quella di affidarelidentificazione della figura al geometrica al numero di vertici da essa posseduta (le immagini sullastampa hanno tutte un diverso numero di vertici e questo un elemento di semplificazione a mio

    parere7). Per identificare il numero di vertici si fatto della funzione approxPolyDP()8. Essa, inrealt, risulta implementata allinterno della libreria OpenCV con finalit differenti da quelle con cui stata invece impiegata in tale lavoro. Essa sostanzialmente si occupa di approssimare i contorni diuna immagine a quelli di una figura polinomiale secondo un margine di tolleranza prefissabile. Icontorni approssimati vengono salvati in una struttura dati vector (approx) appositamente creata.Allora, inserendo tale funzione in un ciclo for( ) e ciclandone lazione in base al numero di

    contorni individuati, si riesce a determinare il numero di contorni di volta in volta approssimatidalla funzione semplicemente ispezionando le dimensioni di approx(approx.size( )).Successivamente si pu procedere allidentificazione del centroide e alla stampa delle informazionirichieste dalla traccia, cercando comunque di mantenere sempre degli elementi di differenziazione

    per la figura circolare. Tale discorso verr ripreso e ultimato nelle seguenti sezioni.

    Ulteriore problema a questo punto stata la caratterizzazione del centroide dei vari poligoni. A talproposito si sfruttata la funzione (suggerita dal testo dellesercitazione) boundingRect() pertrovare il rettangolo che meglio circoscrivesse di volta in volta i vari poligoni. La stessa funzionerestituisce un oggetto di classe Rect. possibile accedere alle informazioni specifiche di taleoggetto attraverso gli appositi metodi boundingRect(.).x e boundingRect(.).y. Con civengono, rispettivamente restituite in uscita le coordinate xe ydellestremo superiore sinistro delrettangolo. A questo punto, attraverso i metodi boundingRect(.).windth eboundingRect(.).heightsi ricavano anche le lunghezze delle due dimensioni del rettangolo. Lestesse allora, se divise per 2 e sommate alle coordinate dellestremo superiore sinistro delrettangolo, consentono di determinare univocamente le coordinate del punto centrale per ogni

    poligono che venga circoscritto dal rettangolo diboundingRect(.).

    7In caso di pi poligoni con lo stesso numero di vertici si sarebbe dovuto probabilmente implementare un meccanismodi valutazione degli angoli interni di ogni figura, aumentando cos non di poco la complessit analitica del tutto.

    8Prototipo della funzione utilizzata : void approxPol yDP ( InputArray curve, OutputArray approxCurve, double epsilon,bool closed );

  • 7/25/2019 OpenCV_forms detection

    5/10

  • 7/25/2019 OpenCV_forms detection

    6/10

    Descrizione dei risultati

    Veniamo ora allanalisi dei risultati ottenuti.

    Facendo partire il programma si intuisce innanzitutto come effettivamente la strategia preposta diidentificazione delle diverse forme poligonali abbia funzionato. Le figure vengono riconosciute inmaniera distinta come previsto dal programma. Ci che per appare ancor pi evidente come inrealt laccuratezza determinativa dei vari centroidi e conseguentemente la stampa delleinformazioni necessarie siano non correttamente distribuite sulla superfice video. Il punto in cui difatto il sistema ideato in parte fallisce a mio parere legato allerrore di approssimazione dellafunzione approxPolyDP(). Si cercato, a tal proposito di legare il margine di errore al profilo

    perimetrale di ogni specifico poligono e lo si fatto in questo modo:

    - Utilizzando una apposita funziona per la determinazione del perimetro di una curva chiusa(quale sono considerabili in effetti i poligoni in questione). La funzione di specie la

    seguente: double arcLength(InputArray curve, bool closed);

    - Il valore restituito da arcLength() stato poi ponderato per un fattore di riduzione

  • 7/25/2019 OpenCV_forms detection

    7/10

    Listati dei programmi

    Listato numero 1: funzione circleFinder()

    cv::Mateiid::p2::circleFinder(constcv::Mat& frame) throw(eiid::failure){

    // create output imagecv::Matout = frame;

    cv::cvtColor(frame,out,CV_RGB2GRAY); // converto l'immagine acquisita dalla webcam

    in una a scala di grigio

    cv::threshold(out, out, 0, 255, CV_THRESH_BINARY| CV_THRESH_OTSU);// binarizzazione dell'immagine con algoritmo di OTSU

    out=255-out; // genero il negativo dellimmagine per poterla passare findContours()

    // devo copiare l'immagine prima di passarla a findcountourscv::Matout_contours;out.copyTo(out_contours);

    cv::findContours(out_contours,countours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);cv::Matout2;

    frame.copyTo(out2);

    std::vector contoursOUT;std::vector approx;

    floatt=0.05; doublet2=(double) t;

    // ciclo for() in cui applico approxPolyDP per ogni contornofor(inti = 0; i < countours.size(); i++){

    cv::approxPolyDP(countours[i], approx, arcLength(countours[i], true)*, true);

    if(approx.size() == 3){printf("TRIANgolo\n");

    cv::drawContours(out2,countours,i,cv::Scalar(255,0,0),2);

    chars5[20];sprintf(s5," TRIANG.");

    eiid::p2::Draw(out2,countours,i,s5);}

    if(approx.size() == 5){printf("Pentagono\n");

    cv::drawContours(out2,countours,i,cv::Scalar(255,0,0),2);

  • 7/25/2019 OpenCV_forms detection

    8/10

    chars5[20];sprintf(s5," PENT.");

    eiid::p2::Draw(out2,countours,i,s5);

    }

    if(approx.size() == 4){printf("QUADRATO\n");

    cv::drawContours(out2,countours,i,cv::Scalar(255,0,0),2);

    chars5[20];sprintf(s5," QUADR.");

    eiid::p2::Draw(out2,countours,i,s5);

    }if(approx.size() > 10){

    printf("STELLA....\n");

    cv::drawContours(out2,countours,i,cv::Scalar(255,0,0),2); // Triangles

    chars5[20];sprintf(s5," STELLA");

    eiid::p2::Draw(out2,countours,i,s5);

    }else{

    doublearea = cv::contourArea(countours[i]);

    cv::Rectr = cv::boundingRect(countours[i]);

    intradius = r.width / 2;

    if(std::abs(1 - (area / (CV_PI* std::pow(radius, 2))))

  • 7/25/2019 OpenCV_forms detection

    9/10

    Praticamente quello che faccio semplicemente, ad ogni passo del ciclofor( ), controllare il numerodi vertici approssimati. Ci mi consente di poter gestire individualmente i vari poligoni,richiamando una ulteriore funzione, appositamente definita, che sar commentata nel prossimolistato. Allinterno delle varie sezioni (if( )) di codice dedicate ai singoli poligoni, si disegnanoopportunamente i contorni (drawContours() 10) e viene poi definita una stringa di caratteri da passare

    alla funzione draw() che sar poi visualizzata in real-time sullo schermo nei pressi del relativocentroide. La procedura differisce leggermente solo per il cerchio, per il quale il controllo prima seil raggio dei due poligoni (quello del contorno originale e quello approssimato) coincidono a menodellerrore 3. In particolare per il calcolo dellarea si usato lapposita funzione contourArea()11.

    Infine il listato della funzione Draw()e, con esso, si si conclude anche questa seconda relazione.

    Listato numero 2: funzione Draw()

    voideiid::p2::Draw(cv::Matm,std::vectorcountours,inti,char* s)throw(eiid::failure){

    doublearea = cv::contourArea(countours[i]);intalt=(cv::boundingRect(countours[i]).height/2);intlarg=(cv::boundingRect(countours[i]).width/2);

    intpointX=cv::boundingRect(countours[i]).x;intpointY=cv::boundingRect(countours[i]).y;

    intsumX=alt+pointX;intsumY=alt+pointY;

    cv::Pointpt5;pt5.x=sumX;pt5.y=sumY;

    if(std::strcmp(s," CERCHIO.")){

    cv::circle(m,pt5,1,cv::Scalar(0,255,0),8);sprintf(s+strlen(s),", area= %d",(int)area);cv::putText(m,s, pt5, 2, 0.4, cv::Scalar(0,255,0), 1);

    }

    else {

    cv::circle(m,pt5,1,cv::Scalar(255,0,0),8);sprintf(s+strlen(s),", area= %d",(int)area);cv::putText(m,s, pt5, 2, 0.4, cv::Scalar(255,0,0), 1);

    }

    }

    10 Prototipo della funzione utilizzata : void drawContours(InputOutputArray image, InputArrayOfArrayscontours, int contourIdx, const Scalar& color, int thickness=1, int lineType=8, InputArrayhierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point());

    11Prototipo della funzione utilizzata : double contourArea( InputArray contour, bool oriented=false );

  • 7/25/2019 OpenCV_forms detection

    10/10

    La funzione riceve in ingresso limmagine video su cui operare, il vettore dei contorni, lindicerelativo al contorno di interesse e un puntatore alla stringa da stampare a video.Per prima cosa, allinterno di essa, mi determino le coordinate del centroide secondo le modalit giispezionate nelle precedenti sezioni, e poi, a seconda della stringa ricevuta in ingresso, stampo leinformazioni richieste dallesercitazione (area della figura) in maniera particolareggiata (per colore)

    unicamente se si tratta del cerchio. Tale funzione viene continuamente richiamata ad ogni avvenutaindividuazione di uno dei poligoni ricercati.