如何使图像的不连续轮廓一致?

时间:2017-04-08 12:10:06

标签: image opencv image-processing

在任务中,我得到了一个不连续的边缘图像,怎么能让它关闭?换句话说,使曲线连续。 形状可以是任何形状,导致这个阴影。

enter image description here

6 个答案:

答案 0 :(得分:4)

以下是一些可以帮助您入门的想法。我不喜欢在OpenCV中编写和调试C ++的负载 - 通常人们会提出问题并且永远不会再次登录,或者您花费数小时处理某些事情,然后他们会告诉您他们提供的单个样本图像根本不具代表性他们的实际图像以及花25分钟解释的方法是完全不合适的。

一个想法是形态膨胀 - 您可以使用ImageMagick在命令行执行此操作:

convert gappy.jpg -threshold 50% -morphology dilate disk:5 result.png

enter image description here

另一个想法可能是找到具有Hit-and-Miss形态的所有“line end”像素。这在OpenCV中可用,但我使用ImageMagick来保存编码/调试。结构元素是这样的:

enter image description here

希望您可以看到第一个(最左边)结构元素代表东西线的西端,第二个代表南北线的北端,依此类推。如果你还没有得到它,最后一个是从东北到西南线的西南端​​。

基本上,我发现线条末端然后用蓝色像素扩大它们并将其叠加到原始像素上:

convert gappy.jpg -threshold 50%  \
   \( +clone -morphology hmt lineends -morphology dilate disk:1 -fill blue -opaque white -transparent black \) \
   -flatten result.png

enter image description here

以下是之前和之后的特写:

enter image description here

您还可以使用“peak”结构元素找到没有邻居的单例像素:

enter image description here

然后你可以找到所有的峰值并用红色像素扩大它们:

convert gappy.jpg -threshold 50% \
    \( +clone -morphology hmt Peaks:1.9 -fill red -morphology dilate disk:2  -opaque white -transparent black \) \
    -flatten result.png

enter image description here

以下是前后的特写:

enter image description here

根据原始图像的外观,您可以迭代地应用上述想法,直到轮廓完整为止 - 也许您可以通过充满洪水来检测,并查看轮廓“保持水”没有洪水填充“泄漏”到处都是。

显然你会做红色的山峰,蓝色的线条都是白色的,以完成你的轮廓 - 我只是用彩色来表示我的技术。

答案 1 :(得分:2)

Mark Setchell的答案是一种有趣的方式来学习新的东西。我的方法相当简单直接。

我从头顶上得到了以下解决方案。它涉及一个简单的模糊操作夹在两个形态操作之间

我已经解释了我在代码中所做的事情:

#---- I converted the image to gray scale and then performed inverted binary threshold on it. ----

img = cv2.imread('leaf.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, 1)  

enter image description here

#---- Next I performed morphological erosion for a rectangular structuring element of kernel size 7 ----

kernel = np.ones((7, 7),np.uint8)
erosion = cv2.morphologyEx(thresh, cv2.MORPH_ERODE, kernel, iterations = 2)
cv2.imshow('erosion', erosion )

enter image description here

#---- I then inverted this image and blurred it with a kernel size of 15. The reason for such a huge kernel is to obtain a smooth leaf edge ----

ret, thresh1 = cv2.threshold(erosion, 127, 255, 1)
blur = cv2.blur(thresh1, (15, 15))
cv2.imshow('blur', blur)

enter image description here

#---- I again performed another threshold on this image to get the central portion of the edge ----

ret, thresh2 = cv2.threshold(blur, 145, 255, 0)

#---- And then performed morphological erosion to thin the edge. For this I used an ellipse structuring element of kernel size 5 ----

kernel1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
final = cv2.morphologyEx(thresh2, cv2.MORPH_ERODE, kernel1, iterations = 2)
cv2.imshow(final', final)

enter image description here

希望这会有所帮助:)

答案 2 :(得分:2)

使用Imagemagick

完成它的方法略有不同

1)阈值并扩大轮廓

convert 8y0KL.jpg -threshold 50% -morphology dilate disk:11 step1.gif

enter image description here

2)少量侵蚀

convert step1.gif -morphology erode disk:8 step2.gif

enter image description here

3)用黑色填充1个像素,用白色填充外部并移除填充的1个像素

convert step2.gif -bordercolor black -border 1 -fill white -draw "color 0,0 floodfill" -alpha off -shave 1x1 step3.gif

enter image description here

4)以较小的量侵蚀并获得过渡的白色边缘。请注意,我们从扩张11开始,然后我们被8侵蚀,然后我们现在被3蚀刻。所以8 + 3 = 11应该让我们回到中心线。

convert step3.gif -morphology erode disk:3 -morphology edgein diamond:1 step4.gif

enter image description here

5)创建动画以进行比较

convert -delay 50 8y0KL.jpg step4.gif -loop 0 animation.gif

enter image description here

答案 3 :(得分:2)

这是另一个更加“计算机视觉文学”导向的建议。

作为经验法则的预处理步骤,通常最好将所有边缘变薄,以确保它们的厚度约为1像素。流行的边缘细化方法是non-maximal suppression(NMS)。

然后我将从分析图像开始,找到我拥有的所有连接组件。 OpenCV已经提供了connectedComponents功能。确定连接组件组后,可以将Bezier曲线拟合到每个组。 Graphics Gem书中提供了将贝塞尔曲线拟合到一组2D点的自动方法。他们的方法也有C代码available。拟合Bezier曲线的目的是尽可能多地了解每个组件组。

接下来,您需要将这些Bezier曲线连接在一起。在Shpitalni and Lipson的工作中可以使用端点聚类来连接线的方法。在该文章中,请在名为“实体链接和端点聚类”的部分中查看其自适应聚类方法。

最后,将所有曲线组合在一起,您也可以拟合最终的贝塞尔曲线,以获得一个漂亮而自然的边缘地图。

作为旁注,你可以在cartoon curve extraction看看郑明明的作品。基于OpenCV的代码可用于该方法here too,但会在应用于您的图像后输出以下内容:

enter image description here

<强>声明:
我可以证明Bezier曲线拟合算法的性能,因为我个人使用它并且它工作得很好。 Cheng的曲线提取算法效果也很好,然而,由于使用了梯度检测(它有使细线粗的趋势!),它会产生具有薄轮廓的看起来很糟糕的“斑点”。如果你能找到解决这种“加厚”效果的方法,你可以跳过贝塞尔曲线提取并直接跳到端点聚类中以将曲线连接在一起。

希望这有帮助!

答案 4 :(得分:1)

您可以尝试使用距离变换

% binarize
im=rgb2gray(im); im=im>100;

% Distance transform
bd=bwdist(im);
maxDist = 5;
bd(bd<maxDist)=0;
bw=bwperim(bd); bw=imclearborder(bw);
bw=imfill(bw,'holes');
bw=bwperim(bwmorph(bw,'thin',maxDist));

figure,imagesc(bw+2*im),axis image

enter image description here

答案 5 :(得分:1)

我的建议:

  • 找到端点;在稀疏步骤之后,这些是具有至多一个邻居的像素以丢弃“厚”端点。端点应成对出现。

  • 从所有端点生长数字磁盘,直到遇到另一个不是对等端点的端点。

您可以预先处理该组端点,并为最近邻搜索(例如2D树)做好准备,而不是增长磁盘。您需要修改搜索以避免命中对等。

这种方法不依赖于标准功能,但它具有尊重原始轮廓的优点。

在图片上,原始像素为端点时为白色或绿色。黄色像素是在最近的端点对之间绘制的数字线段。

enter image description here