OpenCV Canny + 分水岭
Posted
技术标签:
【中文标题】OpenCV Canny + 分水岭【英文标题】:OpenCV Canny + Watershed 【发布时间】:2014-01-11 09:47:49 【问题描述】:我正在使用精明的边缘检测和寻找轮廓功能(都是 OpenCV)来为分水岭变换创建标记。一切正常,但我对结果不是 100% 满意。原因是缺少一些边缘,因此丢失了重要信息。更详细地说,我得到了一堆窗口(前视图),它们是矩形,在分水岭变换之后,我最终得到了这样的结果:
但我宁愿有漂亮的矩形,它们是完整的,不是向一侧开放的。在保持不规则形状的同时(房子前面的灌木丛,汽车..)有什么想法可以解决这个问题吗?我想过用网格覆盖整个图像,但我不能让它工作。
非常感谢。
这是我的代码:
Mat gray;
cvtColor(im, gray, CV_BGR2GRAY);
// Use Canny instead of threshold to catch squares with gradient shading
Mat bw;
Canny(gray, bw, 0, 100, 5, true);
// Find contours
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( bw, contours, hierarchy,
CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
// watershed
Mat markers(bw.size(), CV_32S);
markers = Scalar::all(0);
int idx = 0;
int compCount = 0;
for( ; idx >= 0; idx = hierarchy[idx][0], compCount++ )
if (fabs(contourArea(contours[compCount])) < min_size )
continue;
drawContours(markers, contours, idx, Scalar::all(compCount+1), 1, 8, hierarchy, INT_MAX);
watershed( im, markers );
根据要求,这是原始图像、我想要获取的图像和我的输出:
我想要这样的分割(虽然过度分割并没有什么坏处,我只需要确保,我得到所有的细节):
虽然我得到这样的东西: (请忽略颜色,它们对于这个问题并不重要,只是我整个程序的结果)。这只是一个例子,如果你愿意,我可以给你更多,也请看一下 etrims 数据集,我所有的图片都来自那里。
【问题讨论】:
这样的事情在很大程度上取决于实际的源图像,所以如果可以的话,您可能需要附上一个或多个示例。 添加了一些图片,一般我使用的是etrims数据集,可以在这里找到:ipb.uni-bonn.de/projects/etrims_db 请为您的第一张图片添加输入图片 OpenCV 没有内置的分割算法。您可以在这里查看:eecs.berkeley.edu/Research/Projects/CS/vision/bsds 在不同的算法中,您将能够找到其中一些的开源代码。不管上述情况如何,如果您决定从某种双边/均值偏移过滤开始自己实现某些东西,那将会有所帮助。 基本问题之一是窗户的反射,以及立面的亮度梯度。尝试使用GMM 更好地模拟这些变化。 【参考方案1】:两件事-
1) 如前所述,边缘检测会导致拾取虚假边缘。
2) 使用这些边缘作为分水岭分割的标记会导致过度分割,因为每个标记都会在输出中产生一个分割区域。
策略 -
(i) 预处理:对图像进行大量平滑处理(通过重构的形态开口可用于均匀化强度,而不会显着影响您感兴趣的边缘)。
(ii) 标记:我不使用边缘作为种子,而是使用局部极值。理想情况下,我们希望每个要分割的区域都有一个标记。
(iii) 分割:从步骤 (i) 中找到图像的梯度幅度(范围滤波也是一个不错的选择)并将其用作分割函数。
使用此策略,我得到以下细分。
或者,在步骤 (i) 之后,您可以使用 Canny 边缘检测并进行一些形态学清理(以填充轮廓并移除剩余的边缘)。这就是我得到的。
这些并不完全是预期的分割(未检测到汽车等某些对象),但这是一个好的开始。
编辑:用于生成图像的 MATLAB 代码 -
% convert to grayscale
img = rgb2gray(origImg);
% create an appropriate structuring element
w_size = 20;
seSquare = strel('square', w_size);
% opening by reconstruction - to smooth dark regions
imgEroded = imerode(img, seSquare);
imgRecon = imreconstruct(imgEroded, img);
% invert and repeat - to smooth bright regions
imgReconComp = imcomplement(imgRecon);
imgEroded2 = imerode(imgReconComp, seSquare);
imgRecon2 = imreconstruct(imgEroded2, imgReconComp);
% get foreground markers
fgm = imregionalmax(imgRecon2);
% get background markers - this step can be skipped
% in which case only fgm would be the marker image
% and the segmentation would be different
distTrans = bwdist(fgm);
wLines= watershed(distTrans);
bgm = wLines == 0;
% get the segmentation function and impose markers
% perform watershed segmentation
seSquare3 = strel('square', 3);
rangeImg = rangefilt(imgRecon2, getnhood(seSquare3));
segFunc = imimposemin(rangeImg, fgm | bgm);
grayLabel = watershed(segFunc);
rgbLabel= label2rgb(grayLabel);
figure, imshow(rgbLabel); title('Output using Watershed')
% alternatively, extract edges from the preprocessed image
% perform morph cleanup
bwEdges = edge(imgRecon2, 'canny');
bwFilled = imfill(bwEdges, 'holes');
bwRegions = imopen(bwFilled, seSquare3);
grayLabel = bwlabel(bwRegions);
rgbLabel = label2rgb(grayLabel, 'jet', 'k');
figure, imshow(rgbLabel); title('Output using Canny')
【讨论】:
虽然我在一年多前发布了这个,但问题从未完全解决,所以这对我来说真的很有趣。你能给我一些时间,也许把你的代码贴在某个地方吗?我认为我仍然存在的主要问题是我会错过很多物体(汽车,...)。我不介意将汽车识别为多个对象(过度分割),但我必须以某种方式识别它(而不仅仅是忽略它)。与天空和街道相同(参见参考图片)。 图像是使用 MATLAB 代码生成的,我不想发布,因为您的代码是 C++。使用 OpenCV 编写等效代码看起来并不难,尽管它需要实现“形态重建”部分。看到这个-answers.opencv.org/question/35224/morphological-reconstruction。如果您认为有帮助,我可以编辑我的答案以包含 MATLAB 代码。 如果您可以链接到您的代码会很好,我会在将其转换为 c++ 代码之前在 Matlab 中使用它。 谢谢 - 玩了一段时间。首先,我认为使用形态开放来消除所有噪声是一个好主意 - 不知道为什么我必须在之后重建图像?其次,我尝试了一些其他图像,不幸的是结果并不令人满意(ETrims 数据集)。我觉得这可能只有在我调整每个图像的参数时才有效,这是不可行的。还有一个 matlab 教程,我想仔细看看(第一个结果看起来很有希望)。很遗憾我现在没有太多时间,所以可能需要一段时间才能发布更新 在上述技术中,重建是打开操作的一部分。在正常开口中,腐蚀之后是膨胀。在通过重建打开时是相同的,只是膨胀受到蒙版图像的限制(由imreconstruct
完成)。看到这个-blogs.mathworks.com/steve/2008/07/14/opening-by-reconstruction。确实,您需要大量的参数调整,尤其是当您的输入图像存在可变性时。您可能需要研究监督技术。【参考方案2】:
从所需输出和程序输出的外观来看,边缘检测器似乎正在寻找虚假边缘。 Canny 边缘检测器包含一个低通滤波器,但在实际运行 Canny 边缘检测器之前执行单独的高斯低通滤波步骤可能会有所帮助。
除此之外,很难达到预期的结果。例如,查看图片中最顶部的窗口。它们有不同的颜色——框架、框架的阴影和窗口。边缘检测器会将这些颜色的边界检测为边缘。
【讨论】:
我尝试平滑图像并使用所有 3 个颜色通道进行边缘检测。结果略有改善。我认为现在就可以了,但如果有人知道更好的方法,我会很高兴听到。以上是关于OpenCV Canny + 分水岭的主要内容,如果未能解决你的问题,请参考以下文章
youcans 的 OpenCV 例程200篇184.鼠标交互标记的分水岭算法
youcans 的 OpenCV 例程200篇183.基于轮廓标记的分水岭算法
youcans 的 OpenCV 例程200篇182.基于形态学梯度的分水岭算法