基於顏色的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 联系我们: