【问题标题】:OpenCV Edge/Border detection based on color基于颜色的 OpenCV 边缘/边界检测
【发布时间】:2015-03-19 22:23:15
【问题描述】:

我对 OpenCV 还很陌生,很高兴能了解更多信息。我一直在玩弄勾勒边缘、形状的想法。

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

谢谢!

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

例如这些汉堡肉饼。 OpenCV 会检测到肉饼,并勾勒出它的轮廓。

原图:

【问题讨论】:

  • 使用findContours 确定所有形状的轮廓:docs.opencv.org/modules/imgproc/doc/… - 接下来,使用drawContours 以您想要的任何颜色绘制每个轮廓:docs.opencv.org/modules/imgproc/doc/…。在我看到您要处理的图像是什么样子之前,我真的不能说更多。
  • @rayryeng 添加了一张图片以供参考。感谢您的帮助
  • 没问题!是否可以显示没有红色轮廓的原始图像?一旦我明白了,我就有了一些我想尝试的想法,当我有什么想法时,我会发布一些东西来帮助你!
  • 刚刚添加了原图。
  • 请不要使用 C api(cvCanny 之类的东西),而是尽可能切换到 c++ api,这样更不容易出错,更容易学习/使用!!

标签: c++ opencv edge-detection


【解决方案1】:

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

如果您将图像转换为 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];

色调通道:

饱和通道:

价值渠道:

通常,如果您对分割“颜色”(例如所有红色对象)感兴趣,首先要查看色调通道。一个问题是,色调是一个圆形/角度值,这意​​味着最高值与最低值非常相似,这会导致肉饼边缘出现明亮的伪影。为了克服特定值的这种情况,您可以移动整个色调空间。如果移动 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;
    }

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

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

您可以看到这些区域比真正的肉饼要大一些,这可能是因为肉饼周围的地面上有微小的反射,但我不确定这一点。也许这只是因为 jpeg 压缩伪影;)

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

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

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

在这个阶段你有优势。考虑到边缘还不是轮廓。如果您直接从边缘提取轮廓,它们可能不会被关闭/分离等:

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

您可以在绘制之前通过检查cv::contourArea(contoursH[i]) &gt; 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.

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

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

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

注意扩张和内轮廓的东西有点模糊,所以它可能不适用于不同的图像,如果初始边缘更好地放置在对象边界周围,它可能 1. 不需要进行扩张和内轮廓事情和 2. 如果仍然需要,在这种情况下,扩张会使对象变小(幸运的是,这对于给定的示例图像非常有用。

编辑:关于 HSV 的一些重要信息:色调通道将为每个像素提供光谱的颜色,即使饱和度非常低(=灰色/白色)或者颜色非常低(值),所以经常需要对饱和度和值通道进行阈值化以找到一些特定的颜色!这可能比我在代码中使用的膨胀更容易处理,也更容易处理。

【讨论】:

  • 这个答案令人难以置信。 -- 我现在只是在吹毛求疵,但是如何在彩色图像上叠加轮廓?
  • 正是我要做的。昨晚没时间。不错的工作!顺便说一句,我对您的 C api 评论大笑。顺便说一句,一个漂亮的答案。让我想起了我的风格!
  • 移动 50 的原因是什么?这仅仅是因为您正在寻找的颜色(以及明亮的伪影)恰好接近 0 / 360 边界吗? 50 是一个让所有红色都连续的好数字吗?大概如果您正在寻找另一种颜色(例如蓝色,大约 240),那么完全不需要移动?
  • 是的,如果您正在寻找不低于 0 或不超过 360(或在 opencv 中为 180)的色调范围,则移动不会带来任何东西。您可以评估 0 个区域以获得相同的结果,而不是移动(如果范围低于 0 或超过 360/180)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-27
  • 1970-01-01
  • 2021-09-01
  • 2017-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多