基于颜色的OpenCV Edge / Border检测

[英]OpenCV Edge/Border detection based on color


I'm fairly new to OpenCV, and very excited to learn more. I've been toying with the idea of outlining edges, shapes.

我是OpenCV的新手,很高兴能够学到更多东西。我一直在想着勾勒出边缘,形状的想法。

I've come across this code (running on an iOS device), which uses Canny. I'd like to be able to render this in color, and circle each shape. Can someone point me in the right direction?

我遇到过这个代码(在iOS设备上运行),它使用了Canny。我希望能够以彩色渲染它,并圈出每个形状。有人能指出我正确的方向吗?

Thanks!

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

And example might be these burger patties. OpenCV would detect the patty, and outline it.enter image description here

例如可能是这些汉堡肉饼。 OpenCV会检测到patty,并概述它。

Original Image:

enter image description here

1 个解决方案

#1


59  

Color information is often handled by conversion to HSV color space which handles "color" directly instead of dividing color into R/G/B components which makes it easier to handle same colors with different brightness etc.

颜色信息通常通过转换为HSV颜色空间来处理,该颜色空间直接处理“颜色”而不是将颜色分成R / G / B组件,这使得更容易处理具有不同亮度的相同颜色等。

if you convert your image to HSV you'll get this:

如果您将图像转换为HSV,您将得到:

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

Hue channel:

enter image description here

Saturation channel:

enter image description here

Value channel:

enter image description here

typically, the hue channel is the first one to look at if you are interested in segmenting "color" (e.g. all red objects). One problem is, that hue is a circular/angular value which means that the highest values are very similar to the lowest values, which results in the bright artifacts at the border of the patties. To overcome this for a particular value, you can shift the whole hue space. If shifted by 50° you'll get something like this instead:

通常,如果您对分割“颜色”(例如所有红色对象)感兴趣,则首先要查看色调通道。一个问题是,色调是圆形/角度值,这意​​味着最高值与最低值非常相似,这导致肉饼边缘处的明亮伪影。要克服特定值,可以移动整个色调空间。如果偏移50°,你会得到类似的东西:

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

enter image description here

now you can use a simple canny edge detection to find edges in the hue channel:

现在您可以使用简单的canny边缘检测来查找色调通道中的边缘:

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

enter image description here

You can see that the regions are a little bigger than the real patties, that might be because of the tiny reflections on the ground around the patties, but I'm not sure about that. Maybe it's just because of jpeg compression artifacts ;)

你可以看到这些区域比真正的小馅饼要大一点,这可能是因为小馅饼周围地面的微小反射,但我不确定。也许这仅仅是因为jpeg压缩工件;)

If you instead use the saturation channel to extract edges, you'll end up with something like this:

如果你改为使用饱和度通道来提取边缘,你最终会得到这样的结果:

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

enter image description here

where the contours aren't completely closed. Maybe you can combine hue and saturation within preprocessing to extract edges in the hue channel but only where saturation is high enough.

轮廓没有完全关闭的地方。也许您可以在预处理中组合色调和饱和度来提取色调通道中的边缘,但仅限于饱和度足够高的位置。

At this stage you have edges. Regard that edges aren't contours yet. If you directly extract contours from edges they might not be closed/separated 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);
 }

enter image description here

you can remove those small contours by checking cv::contourArea(contoursH[i]) > someThreshold before drawing. But you see the two patties on the left to be connected? Here comes the hardest part... use some heuristics to "improve" your result.

您可以通过在绘制之前检查cv :: contourArea(contoursH [i])> someThreshold来删除这些小轮廓。但是你看到左边的两个肉饼要连接?这是最难的部分......使用一些启发式方法来“改善”你的结果。

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.

enter image description here

if you extract contours from that it will look like this:

如果从中提取轮廓,它将如下所示:

enter image description here

If you instead choose only the "inner" contours it is exactly what you like:

如果您只选择“内部”轮廓,那么它就是您喜欢的:

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

enter image description here

mind that the dilation and inner contour stuff is a little fuzzy, so it might not work for different images and if the initial edges are placed better around the object border it might 1. not be necessary to do the dilate and inner contour thing and 2. if it is still necessary, the dilate will make the object smaller in this scenario (which luckily is great for the given sample image.).

请注意,扩张和内部轮廓的东西有点模糊,所以它可能不适用于不同的图像,如果初始边缘更好地放置在物体边界周围,它可能1.没有必要做扩张和内部轮廓的事情和2如果仍然需要,在这种情况下,扩张会使物体变小(幸运的是,这对于给定的样本图像来说是很好的。)。

EDIT: Some important information about HSV: The hue channel will give every pixel a color of the spectrum, even if the saturation is very low ( = gray/white) or if the color is very low (value) so often it is desired to threshold the saturation and value channels to find some specific color! This might be much easier and much more stavle to handle than the dilation I've used in my code.

编辑:有关HSV的一些重要信息:色调通道将为每个像素提供光谱的颜色,即使饱和度非常低(=灰色/白色)或颜色非常低(值),因此通常需要阈值饱和度和值通道找到一些特定的颜色!与我在代码中使用的扩张相比,这可能更容易处理,也更容易处理。


注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2015/03/19/2ff39e2587dbe2038d537d0e6a1fa081.html



 
  © 2014-2022 ITdaan.com 联系我们: