2014-04-20 7 views
5

Sto facendo un progetto in cui devo ispezionare il blister farmaceutico per i tablet mancanti.Uso di opencv matchtemplate per l'ispezione del blister

Sto tentando di utilizzare la funzione matchTemplate di opencv. Lasciami mostrare il codice e quindi alcuni risultati.

int match(string filename, string templatename) 
{ 
    Mat ref = cv::imread(filename + ".jpg"); 
    Mat tpl = cv::imread(templatename + ".jpg"); 
    if (ref.empty() || tpl.empty()) 
    { 
     cout << "Error reading file(s)!" << endl; 
     return -1; 
    } 

    imshow("file", ref); 
    imshow("template", tpl); 

    Mat res_32f(ref.rows - tpl.rows + 1, ref.cols - tpl.cols + 1, CV_32FC1); 
    matchTemplate(ref, tpl, res_32f, CV_TM_CCOEFF_NORMED); 

    Mat res; 
    res_32f.convertTo(res, CV_8U, 255.0); 
    imshow("result", res); 

    int size = ((tpl.cols + tpl.rows)/4) * 2 + 1; //force size to be odd 
    adaptiveThreshold(res, res, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, size, -128); 
    imshow("result_thresh", res); 

    while (true) 
    { 
     double minval, maxval, threshold = 0.8; 
     Point minloc, maxloc; 
     minMaxLoc(res, &minval, &maxval, &minloc, &maxloc); 

     if (maxval >= threshold) 
     { 
      rectangle(ref, maxloc, Point(maxloc.x + tpl.cols, maxloc.y + tpl.rows), CV_RGB(0,255,0), 2); 
      floodFill(res, maxloc, 0); //mark drawn blob 
     } 
     else 
      break; 
    } 

    imshow("final", ref); 
    waitKey(0); 

    return 0; 
} 

E qui ci sono alcune immagini.

Il "campione" immagine di un buon blister:

good pack

Il modello ritagliata dall'immagine "campione":

template

Risultato immagine "campione" con:

sample result

viene rilevato

tablet mancante da questo pacchetto:

missing detected

Ma qui sono i problemi:

fail 1

fail 2

Io attualmente non ho alcuna idea del perché questo accade . Qualsiasi suggerimento e/o aiuto è apprezzato.

Il codice originale che ho seguito e modificato è qui: http://opencv-code.com/quick-tips/how-to-handle-template-matching-with-multiple-occurences/

+0

Ho modificato le immagini per voi. +1 per una domanda eccellente – Bojangles

+0

Die lo provi con diverse soglie? 0,8 il massimo puoi andare? Inoltre, muori provi gli altri metodi di correlazione incrociata come SQDIFF_NORMED invece di CCOEFF_NORMED? – HugoRune

+0

Esaminando il codice più da vicino, si converte il risultato della correlazione in 8 bit e quindi si esegue una soglia adattiva su di esso, quindi l'immagine che si passa a MinMaxLoc è già binarizzata, quindi la modifica della soglia 0.8 non avrà alcun effetto. Non sono sicuro di ciò che guadagni, non cercando i massimi nel res_32f originale ottieni risultati migliori? – HugoRune

risposta

0

Hai provato l'algoritmo di Surf al fine di ottenere i descrittori più dettagliate? Puoi provare a raccogliere il descrittore sia per l'immagine di esempio completa che vuota. Ed esegui un'azione diversa per ognuno degli oggetti thr rilevati.

1

Ho trovato una soluzione per la mia domanda. Ho solo bisogno di applicare il rilevatore di bordo Canny su entrambe le immagini e template prima di lanciarli per abbinare la funzione Template. Il codice completo funzionante:

int match(string filename, string templatename) 
{ 
    Mat ref = cv::imread(filename + ".jpg"); 
    Mat tpl = cv::imread(templatename + ".jpg"); 
    if(ref.empty() || tpl.empty()) 
    { 
     cout << "Error reading file(s)!" << endl; 
     return -1; 
    } 

    Mat gref, gtpl; 
    cvtColor(ref, gref, CV_BGR2GRAY); 
    cvtColor(tpl, gtpl, CV_BGR2GRAY); 

    const int low_canny = 110; 
    Canny(gref, gref, low_canny, low_canny*3); 
    Canny(gtpl, gtpl, low_canny, low_canny*3); 

    imshow("file", gref); 
    imshow("template", gtpl); 

    Mat res_32f(ref.rows - tpl.rows + 1, ref.cols - tpl.cols + 1, CV_32FC1); 
    matchTemplate(gref, gtpl, res_32f, CV_TM_CCOEFF_NORMED); 

    Mat res; 
    res_32f.convertTo(res, CV_8U, 255.0); 
    imshow("result", res); 

    int size = ((tpl.cols + tpl.rows)/4) * 2 + 1; //force size to be odd 
    adaptiveThreshold(res, res, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, size, -64); 
    imshow("result_thresh", res); 

    while(1) 
    { 
     double minval, maxval; 
     Point minloc, maxloc; 
     minMaxLoc(res, &minval, &maxval, &minloc, &maxloc); 

     if(maxval > 0) 
     { 
      rectangle(ref, maxloc, Point(maxloc.x + tpl.cols, maxloc.y + tpl.rows), Scalar(0,255,0), 2); 
      floodFill(res, maxloc, 0); //mark drawn blob 
     } 
     else 
      break; 
    } 

    imshow("final", ref); 
    waitKey(0); 

    return 0; 
} 

Qualsiasi suggerimento per il miglioramento è apprezzato. Sono molto preoccupato per le prestazioni e la robustezza del mio codice, quindi sono alla ricerca di tutte le idee.

Ora ci sono 2 cose che mi fanno venire i nervi: la soglia Canny inferiore e la costante negativa sulla funzione AdaptiveThreshold.

Edit: Ecco il risultato, come lei ha chiesto :)

Template:

template

immagine di prova, mancano 2 compresse:

test, missing 2 tablets

risultati Canny di modello e immagine di prova:

Canny of template

Canny of test

risultato matchTemplate (convertito in CV_8U):

matchTemplate

Dopo adaptiveThreshold:

thresholded

risultato finale:

result

+0

Ottimo lavoro! Puoi fornire i tuoi risultati con noi! – Hadi

+0

@ Costantino: aggiunte le immagini dei risultati;) – vuanhtung2004

1

Non penso che la soglia adattativa sia una buona scelta.

Quello che devi fare qui è chiamato soppressione non massima. Hai un'immagine con più massimi locali e desideri rimuovere tutti i pixel che non sono massimi locali.

cv::dilate(res_32f, res_dilated, null, 5); 
cv::compare(res_32f, res_dilated, mask_local_maxima, cv::CMP_GE); 
cv::set(res_32f, 0, mask_local_maxima) 

Ora tutti i pixel nell'immagine res_32f che non sono massimi locali sono impostati su zero. Tutti i pixel massime sono ancora al loro valore originale, in modo da poter regolare la soglia più avanti nella linea di

double minval, maxval, threshold = 0.8; 

Tutti massimi locali dovrebbe ora essere circondato da abbastanza zeri che il floodfill non si estenderà troppo lontano.

Ora penso che dovresti essere in grado di regolare la soglia per escludere tutti i falsi positivi.


Se questo non è sufficiente, qui è un altro suggerimento:

invece di uno solo modello, vorrei correre la ricerca con più modelli; il modello attuale e uno con un tablet sul lato destro e sul lato sinistro del pacchetto. A causa della prospettiva queste compresse sembrano un po 'diverse. Tieni traccia dei tablet trovati in modo da non rilevare più volte il tablet Smae.

Con questi modelli multipli è possibile aumentare ulteriormente la soglia.


Un ulteriore affinamento: se il rilevamento è ancora troppo irregolare, provare a sfocatura il modello e la ricerca di immagini con una sfocatura gaussiana. Questo rimuoverà i minimi dettagli e il rumore che potrebbe generare la funzione matchTemplate, lasciando intatte le strutture più grandi.

L'utilizzo di un filtro canny invece mi sembra inaffidabile: sembra basarsi sul fatto che una regione compressa della tavoletta avrà più bordi al centro. Ma non sono sicuro che questo sarà sempre il caso; e scarti molte informazioni sul colore e la luminosità con il filtro canny, quindi mi aspetterei risultati peggiori.

(che detto, se funziona per voi, funziona)

+0

Grazie per il vostro aiuto. Potresti dare un'altra occhiata alla mia risposta, ho aggiunto le foto dei risultati. La soppressione locale non massima funzionerà? So che attualmente il mio codice può solo gestire "silhouette" di tablet. Ma almeno restituisce risultati migliori rispetto a MatchTemplate con immagini a colori/grigi originali. Sto pianificando di aggiungere un altro giro di controllo di sagome abbinate per il colore in caso di pastiglie di colore sbagliato mescolate in. – vuanhtung2004

+0

Vorrei testare il tuo approccio ma non riesco a trovare 'cv :: set'. Gli altri metodi sono documentati su http://docs.opencv.org/modules/imgproc/doc/filtering.html#dilate e http://docs.opencv.org/modules/core/doc/operations_on_arrays.html#compare – handle

+0

@handle Probabilmente l'ho ricordato male, o è stato rinominato nel wrapper C# che uso; è chiamato [setTo] (http://docs.opencv.org/modules/core/doc/basic_structures.html#mat-setto) – HugoRune