2012-06-21 13 views
5

Estoy desarrollando un proyecto de identificación de componentes usando el paquete javacv (Opencv). He utilizado un método para devolver un conjunto de rectángulos en la imagen como "CvSeq" Lo que necesito saber es cómo hacer las cosas siguientesCómo extraer el ancho y la altura del contorno en javacv?

  • ¿Cómo puedo obtener cada rectángulo de la salida de los métodos (de CvSeq)?
  • ¿Cómo acceder a las longitudes y el ancho del rectángulo?

Este es el método que devuelve los rectángulos

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

Esta es la imagen original muestra que utilicé

enter image description here

Y esta es la imagen que llegué después de dibujar líneas alrededor de los rectángulos coincidentes

enter image description here

En realidad, en las imágenes de arriba me apetece eliminar esos grandes rectángulos y solo necesito identificar otros rectángulos, así que necesito un ejemplo de código para entender cómo archivar los objetivos anteriores. Por favor tenga la amabilidad de compartir su experiencia conmigo. Gracias !

Respuesta

4

OpenCV encuentra los contornos de los objetos blancos en fondo negro. En tu caso, es al revés, los objetos son negros. Y de esa manera, incluso el borde de la imagen también es un objeto. Para evitar eso, simplemente revertir la imagen de manera que el fondo sea negro.

continuación he demostrado que (usando 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) 

Recuerde, en lugar de utilizar la función separada para invertir, lo usé en el umbral. Simplemente convierta el tipo de umbral a BINARY_INV (es decir, '1').

Ahora usted tiene una imagen de la siguiente manera:

enter image description here

Ahora nos encontramos con los contornos. Luego, para cada contorno, lo aproximamos y verificamos si es un rectángulo observando la longitud del contorno aproximado, que debería ser cuatro para un rectángulo.

Si se sacan, se obtiene así:

enter image description here

Y al mismo tiempo, también encontramos la rect delimitación de cada contorno. El rect delimitador tiene una forma como esta: [punto inicial x, punto inicial y, ancho de rect, altura de rect]

Para obtener el ancho y la altura.

A continuación se muestra el código:

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:

Después de algunos comentarios, comprendí que, el verdadero objetivo de la pregunta es para evitar grandes rectángulos y seleccionar los más pequeños solamente.

Se puede hacer utilizando los valores rec rectificados que obtuvimos. es decir, seleccione solo aquellos rectángulos cuya longitud sea menor que un valor umbral, o ancho o área. Como ejemplo, en esta imagen, el área que tomé debería ser inferior a 10000. (Una estimación aproximada). Si es menor que 10000, debe seleccionarse y denotarlo en color rojo, de lo contrario, candidato falso, representado en color azul (solo para visualización).

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) 

A continuación se muestra la salida que me dieron:

enter image description here

Cómo conseguir que el valor umbral? :

Depende completamente de usted y su aplicación. O puede encontrarlo por métodos de prueba y error. (así lo hice).

Espero que resuelva su problema. Todas las funciones son funciones estándar de opencv. Así que creo que no encontrarás ningún problema para convertir a JavaCV.

+0

En realidad tengo que retirar dos grandes rectángulos en el lado de la imagen y otros pequeños rectángulos deben ser identificados. Por favor, ¿puedes explicar cómo hacer eso usando javacv? –

+0

Lo siento, no lo conseguí. Puede ser que puedas dibujar qué cuadrado necesitas en color verde usando pintura, y subirlo a otro lugar y dar un enlace aquí. –

+2

K, lo tengo ... solo necesitas pequeños rectángulos y evitar grandes rectángulos, ¿verdad? puedes evitarlos si su longitud o anchura es mayor que algún valor de umbral. –

2

he dado cuenta de que hay un error en el código proporcionado en la pregunta:

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

Esto significa que si hay un solo canal, tgray habrá una imagen vacía. Se debería leer:

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); 
} 
Cuestiones relacionadas