答案 0 :(得分:4)
以下是一些可以帮助您入门的想法。我不喜欢在OpenCV中编写和调试C ++的负载 - 通常人们会提出问题并且永远不会再次登录,或者您花费数小时处理某些事情,然后他们会告诉您他们提供的单个样本图像根本不具代表性他们的实际图像以及花25分钟解释的方法是完全不合适的。
一个想法是形态膨胀 - 您可以使用ImageMagick在命令行执行此操作:
convert gappy.jpg -threshold 50% -morphology dilate disk:5 result.png
另一个想法可能是找到具有Hit-and-Miss形态的所有“line end”像素。这在OpenCV中可用,但我使用ImageMagick来保存编码/调试。结构元素是这样的:
希望您可以看到第一个(最左边)结构元素代表东西线的西端,第二个代表南北线的北端,依此类推。如果你还没有得到它,最后一个是从东北到西南线的西南端。
基本上,我发现线条末端然后用蓝色像素扩大它们并将其叠加到原始像素上:
convert gappy.jpg -threshold 50% \
\( +clone -morphology hmt lineends -morphology dilate disk:1 -fill blue -opaque white -transparent black \) \
-flatten result.png
以下是之前和之后的特写:
您还可以使用“peak”结构元素找到没有邻居的单例像素:
然后你可以找到所有的峰值并用红色像素扩大它们:
convert gappy.jpg -threshold 50% \
\( +clone -morphology hmt Peaks:1.9 -fill red -morphology dilate disk:2 -opaque white -transparent black \) \
-flatten result.png
以下是前后的特写:
根据原始图像的外观,您可以迭代地应用上述想法,直到轮廓完整为止 - 也许您可以通过充满洪水来检测,并查看轮廓“保持水”没有洪水填充“泄漏”到处都是。
显然你会做红色的山峰,蓝色的线条都是白色的,以完成你的轮廓 - 我只是用彩色来表示我的技术。
答案 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)
#---- 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 )
#---- 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)
#---- 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)
希望这会有所帮助:)
答案 2 :(得分:2)
使用Imagemagick
完成它的方法略有不同1)阈值并扩大轮廓
convert 8y0KL.jpg -threshold 50% -morphology dilate disk:11 step1.gif
2)少量侵蚀
convert step1.gif -morphology erode disk:8 step2.gif
3)用黑色填充1个像素,用白色填充外部并移除填充的1个像素
convert step2.gif -bordercolor black -border 1 -fill white -draw "color 0,0 floodfill" -alpha off -shave 1x1 step3.gif
4)以较小的量侵蚀并获得过渡的白色边缘。请注意,我们从扩张11开始,然后我们被8侵蚀,然后我们现在被3蚀刻。所以8 + 3 = 11应该让我们回到中心线。
convert step3.gif -morphology erode disk:3 -morphology edgein diamond:1 step4.gif
5)创建动画以进行比较
convert -delay 50 8y0KL.jpg step4.gif -loop 0 animation.gif
答案 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,但会在应用于您的图像后输出以下内容:
<强>声明:强>
我可以证明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
答案 5 :(得分:1)