findcontours canny c++ opencv edge-detection

c++ - canny - Detección de borde/borde OpenCV basado en color



findcontours opencv c++ (1)

Soy bastante nuevo en OpenCV, y estoy muy emocionado de aprender más. He estado jugando con la idea de delinear bordes, formas.

Me he encontrado con este código (que se ejecuta en un dispositivo iOS), que utiliza Canny. Me gustaría poder renderizar esto en color y rodear cada forma. ¿Alguien me puede apuntar en la dirección correcta?

¡Gracias!

IplImage *grayImage = cvCreateImage(cvGetSize(iplImage), IPL_DEPTH_8U, 1); cvCvtColor(iplImage, grayImage, CV_BGRA2GRAY); cvReleaseImage(&iplImage); IplImage* img_blur = cvCreateImage( cvGetSize( grayImage ), grayImage->depth, 1); cvSmooth(grayImage, img_blur, CV_BLUR, 3, 0, 0, 0); cvReleaseImage(&grayImage); IplImage* img_canny = cvCreateImage( cvGetSize( img_blur ), img_blur->depth, 1); cvCanny( img_blur, img_canny, 10, 100, 3 ); cvReleaseImage(&img_blur); cvNot(img_canny, img_canny);

Y el ejemplo podría ser estas hamburguesas de hamburguesa. OpenCV detectaría la empanada y la delinearía.

Imagen original:


La información del color a menudo se maneja mediante la conversión a un espacio de color HSV que maneja el "color" directamente en lugar de dividir el color en componentes R / G / B, lo que facilita el manejo de los mismos colores con diferentes brillos, etc.

Si convierte su imagen a HSV obtendrá esto:

cv::Mat hsv; cv::cvtColor(input,hsv,CV_BGR2HSV); std::vector<cv::Mat> channels; cv::split(hsv, channels); cv::Mat H = channels[0]; cv::Mat S = channels[1]; cv::Mat V = channels[2];

Canal de matiz:

Canal de saturación:

Canal de valor:

por lo general, el canal de tono es el primero en mirar si está interesado en segmentar "color" (por ejemplo, todos los objetos rojos). Un problema es que ese tono es un valor circular / angular, lo que significa que los valores más altos son muy similares a los valores más bajos, lo que da como resultado los artefactos brillantes en el borde de las hamburguesas. Para superar esto para un valor particular, puede cambiar todo el espacio de matiz. Si lo cambias a 50 ° obtendrás algo como esto en su lugar:

cv::Mat shiftedH = H.clone(); int shift = 25; // in openCV hue values go from 0 to 180 (so have to be doubled to get to 0 .. 360) because of byte range from 0 to 255 for(int j=0; j<shiftedH.rows; ++j) for(int i=0; i<shiftedH.cols; ++i) { shiftedH.at<unsigned char>(j,i) = (shiftedH.at<unsigned char>(j,i) + shift)%180; }

Ahora puede usar una sencilla detección de bordes para encontrar bordes en el canal de tono:

cv::Mat cannyH; cv::Canny(shiftedH, cannyH, 100, 50);

Puedes ver que las regiones son un poco más grandes que las tortitas reales, lo que podría deberse a los pequeños reflejos en el suelo alrededor de las tortitas, pero no estoy seguro de eso. Tal vez sea solo por los artefactos de compresión jpeg;)

Si, en cambio, usa el canal de saturación para extraer bordes, terminará con algo como esto:

cv::Mat cannyS; cv::Canny(S, cannyS, 200, 100);

Donde los contornos no están completamente cerrados. Quizás pueda combinar el tono y la saturación dentro del preprocesamiento para extraer bordes en el canal del tono, pero solo donde la saturación sea lo suficientemente alta.

En esta etapa tienes aristas. Tenga en cuenta que los bordes no son contornos todavía. Si extrae directamente los contornos de los bordes, es posible que no estén cerrados / separados, etc.

// extract contours of the canny image: std::vector<std::vector<cv::Point> > contoursH; std::vector<cv::Vec4i> hierarchyH; cv::findContours(cannyH,contoursH, hierarchyH, CV_RETR_TREE , CV_CHAIN_APPROX_SIMPLE); // draw the contours to a copy of the input image: cv::Mat outputH = input.clone(); for( int i = 0; i< contoursH.size(); i++ ) { cv::drawContours( outputH, contoursH, i, cv::Scalar(0,0,255), 2, 8, hierarchyH, 0); }

puede eliminar esos contornos pequeños marcando cv::contourArea(contoursH[i]) > someThreshold antes de dibujar. ¿Pero ves las dos empanadas a la izquierda para estar conectado? Aquí viene la parte más difícil ... use algunas heurísticas para "mejorar" su resultado.

cv::dilate(cannyH, cannyH, cv::Mat()); cv::dilate(cannyH, cannyH, cv::Mat()); cv::dilate(cannyH, cannyH, cv::Mat()); Dilation before contour extraction will "close" the gaps between different objects but increase the object size too.

Si extraes contornos de eso se verá así:

Si, en cambio, elige solo los contornos "internos", es exactamente lo que le gusta:

cv::Mat outputH = input.clone(); for( int i = 0; i< contoursH.size(); i++ ) { if(cv::contourArea(contoursH[i]) < 20) continue; // ignore contours that are too small to be a patty if(hierarchyH[i][3] < 0) continue; // ignore "outer" contours cv::drawContours( outputH, contoursH, i, cv::Scalar(0,0,255), 2, 8, hierarchyH, 0); }

Tenga en cuenta que la dilatación y el contorno interno son un poco borrosos, por lo que podría no funcionar con diferentes imágenes y si los bordes iniciales se colocan mejor alrededor del borde del objeto, puede que no sea necesario hacer la dilatura y el contorno interno. Si aún es necesario, el dilatado hará que el objeto sea más pequeño en este escenario (lo que afortunadamente es excelente para la imagen de muestra dada).

EDICIÓN: Alguna información importante sobre HSV: El canal de tono le dará a cada píxel un color del espectro, incluso si la saturación es muy baja (= gris / blanco) o si el color es muy bajo (valor), por lo que a menudo se desea Umbral los canales de saturación y valor para encontrar algún color específico! Esto podría ser mucho más fácil y mucho más manejable que la dilatación que he usado en mi código.