2012-06-21 16 views
5

Sto sviluppando un progetto sull'identificazione dei componenti usando il pacchetto javacv (Opencv). Ho usato un metodo per restituire un insieme di rettangoli sull'immagine come "CvSeq" Quello che ho bisogno di sapere è come fare le cose seguentiCome estrarre la larghezza e l'altezza del contorno in javacv?

  • Come posso ottenere ogni rettangolo dall'uscita metodi (da CvSeq)?
  • Come accedere alle lunghezze e alla larghezza del rettangolo?

Questo è il metodo che restituisce i rettangoli

public static CvSeq findSquares(final IplImage src, CvMemStorage storage) 
{ 

CvSeq squares = new CvContour(); 
squares = cvCreateSeq(0, sizeof(CvContour.class), sizeof(CvSeq.class), storage); 

IplImage pyr = null, timg = null, gray = null, tgray; 
timg = cvCloneImage(src); 

CvSize sz = cvSize(src.width() & -2, src.height() & -2); 
tgray = cvCreateImage(sz, src.depth(), 1); 
gray = cvCreateImage(sz, src.depth(), 1); 
pyr = cvCreateImage(cvSize(sz.width()/2, sz.height()/2), src.depth(), src.nChannels()); 

// down-scale and upscale the image to filter out the noise 
cvPyrDown(timg, pyr, CV_GAUSSIAN_5x5); 
cvPyrUp(pyr, timg, CV_GAUSSIAN_5x5); 
cvSaveImage("ha.jpg", timg); 
CvSeq contours = new CvContour(); 
// request closing of the application when the image window is closed 
// show image on window 
// find squares in every color plane of the image 
for(int c = 0; c < 3; c++) 
{ 
    IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)}; 
    channels[c] = cvCreateImage(sz, 8, 1); 
    if(src.nChannels() > 1){ 
     cvSplit(timg, channels[0], channels[1], channels[2], null); 
    }else{ 
     tgray = cvCloneImage(timg); 
    } 
    tgray = channels[c]; // try several threshold levels 
    for(int l = 0; l < N; l++) 
    { 
    //    hack: use Canny instead of zero threshold level. 
    //    Canny helps to catch squares with gradient shading 
        if(l == 0) 
       { 
    //    apply Canny. Take the upper threshold from slider 
    //    and set the lower to 0 (which forces edges merging) 
         cvCanny(tgray, gray, 0, thresh, 5); 
    //     dilate canny output to remove potential 
    //    // holes between edge segments 
         cvDilate(gray, gray, null, 1); 
       } 
       else 
       { 
    //    apply threshold if l!=0: 
         cvThreshold(tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY); 
       } 
    //   find contours and store them all as a list 
       cvFindContours(gray, storage, contours, sizeof(CvContour.class), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); 

       CvSeq approx; 

    //   test each contour 
       while (contours != null && !contours.isNull()) { 
         if (contours.elem_size() > 0) { 
          approx = cvApproxPoly(contours, Loader.sizeof(CvContour.class),storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0); 
        if(approx.total() == 4 
          && 
          Math.abs(cvContourArea(approx, CV_WHOLE_SEQ, 0)) > 1000 && 
         cvCheckContourConvexity(approx) != 0 
         ){ 
         double maxCosine = 0; 
         // 
         for(int j = 2; j < 5; j++) 
         { 
      //   find the maximum cosine of the angle between joint edges 
         double cosine = Math.abs(angle(new CvPoint(cvGetSeqElem(approx, j%4)), new CvPoint(cvGetSeqElem(approx, j-2)), new CvPoint(cvGetSeqElem(approx, j-1)))); 
         maxCosine = Math.max(maxCosine, cosine); 
         } 
         if(maxCosine < 0.2){ 
          cvSeqPush(squares, approx); 
         } 
        } 
       } 
       contours = contours.h_next(); 
      } 
     contours = new CvContour(); 
    } 
} 
return squares; 
} 

Questa è l'immagine originale del campione che ho usato

enter image description here

E questa è l'immagine che ho ricevuto dopo le linee di disegno intorno ai rettangoli corrispondenti

enter image description here

In realtà nelle immagini di cui sopra mi sto legando per rimuovere quei rettangoli di grandi dimensioni e ho solo bisogno di identificare altri rettangoli quindi ho bisogno di qualche esempio di codice per capire come archiviare sopra gli obiettivi. Si prega di essere così gentile da condividere la vostra esperienza con me. Grazie !

risposta

4

OpenCV trova i contorni degli oggetti bianchi su sfondo nero. Nel tuo caso è invertito, gli oggetti sono neri. E in questo modo, anche il bordo dell'immagine è anche un oggetto. Quindi per evitare ciò, basta invertire l'immagine in modo tale che lo sfondo sia nero.

Sotto ho dimostrato (utilizzando OpenCV-Python):

import numpy as np 
import cv2 

im = cv2.imread('sofsqr.png') 
img = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) 

ret,thresh = cv2.threshold(img,127,255,1) 

Ricordare, invece di utilizzare la funzione separata per invertire, ho usato in soglia. Basta convertire il tipo di soglia in BINARY_INV (cioè '1').

Ora avete un'immagine come di seguito:

enter image description here

Ora troviamo i contorni. Quindi per ogni contorno, lo approssimiamo e controlliamo se si tratta di un rettangolo osservando la lunghezza del contorno approssimato, che dovrebbe essere quattro per un rettangolo.

Se disegnato, si ottiene in questo modo:

enter image description here

E, allo stesso tempo, troviamo anche la delimitazione rect di ogni contorno. Il rettangolo di delimitazione ha una forma simile a questa: [punto iniziale x, punto iniziale y, larghezza del rettangolo, altezza del rettangolo]

Così si ottiene la larghezza e l'altezza.

Di seguito si riporta il codice:

contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE) 

for cnt in contours: 
    approx = cv2.approxPolyDP(cnt,cv2.arcLength(cnt,True)*0.02,True) 
    if len(approx)==4: 
     cv2.drawContours(im,[approx],0,(0,0,255),2) 
     x,y,w,h = cv2.boundingRect(cnt) 

EDIT:

Dopo alcuni commenti, ho capito che il vero obiettivo della questione è quello di evitare grandi rettangoli e selezionare solo quelli più piccoli.

Può essere eseguito utilizzando i valori limite che abbiamo ottenuto. cioè, seleziona solo quei rettangoli la cui lunghezza è inferiore a un valore di soglia, larghezza o area. Ad esempio, in questa immagine, ho preso l'area dovrebbe essere inferiore a 10000. (Una stima approssimativa). Se è inferiore a 10000, dovrebbe essere selezionato e lo denotiamo in rosso, altrimenti, candidato falso, rappresentato in colore blu (solo per visualizzazione).

for cnt in contours: 
    approx = cv2.approxPolyDP(cnt,cv2.arcLength(cnt,True)*0.02,True) 
    if len(approx)==4: 
     x,y,w,h = cv2.boundingRect(approx) 
     if w*h < 10000: 
      cv2.drawContours(im,[approx],0,(0,0,255),-1) 
     else: 
      cv2.drawContours(im,[approx],0,(255,0,0),-1) 

Di seguito è riportato l'output che ho ottenuto:

enter image description here

come ottenere quel valore di soglia? :

Dipende completamente da te e dalla tua applicazione. Oppure puoi trovarlo per metodi di prova ed errore. (l'ho fatto)

Spero che questo risolva il tuo problema. Tutte le funzioni sono funzioni opencv standard. Quindi penso che non troverai alcun problema a convertire in JavaCV.

+0

In realtà ho bisogno di rimuovere due grandi rettangoli in parte l'immagine e altri piccoli rettangoli dovrebbero essere identificati. Per favore, puoi spiegare come farlo usando javacv. –

+0

Mi dispiace, non l'ho capito. Può essere che tu possa disegnare quale quadrato hai bisogno di colore verde usando la vernice, e caricare da qualche parte e dare il link qui. –

+2

K, ho capito .. hai bisogno solo di piccoli rettangoli ed evita rettangoli di grandi dimensioni, giusto? puoi evitarli se la loro lunghezza o larghezza è maggiore di qualche valore di soglia. –

2

appena notato che v'è un errore nel codice fornito nella domanda:

IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)}; 
channels[c] = cvCreateImage(sz, 8, 1); 
if(src.nChannels() > 1){ 
    cvSplit(timg, channels[0], channels[1], channels[2], null); 
}else{ 
    tgray = cvCloneImage(timg); 
} 
tgray = channels[c]; 

Ciò significa che se v'è un solo canale, tgray sarà un'immagine vuota. Dovrebbe essere visualizzato:

IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)}; 
channels[c] = cvCreateImage(sz, 8, 1); 
if(src.nChannels() > 1){ 
    cvSplit(timg, channels[0], channels[1], channels[2], null); 
    tgray = channels[c]; 
}else{ 
    tgray = cvCloneImage(timg); 
} 
Problemi correlati