当前情况:我正在尝试从图像中提取片段。感谢openCV的findContours()
方法,我现在有一个每个轮廓的8连接点列表。但是,这些列表不能直接使用,因为它们包含大量重复项。
问题:给定一个包含重复项的8个连接点的列表,从中提取段。
可能的解决方案:
approxPolyDP()
方法。但是,结果非常糟糕......这是缩放的轮廓:
以下是approxPolyDP()
的结果:( 9段!有些重叠)
但我想要的更像是:
这很糟糕,因为approxPolyDP()
可以在“多个细分”中转换“看起来像几个细分”的内容。但是,我所拥有的是一个点列表,这些点往往会多次迭代。
例如,如果我的观点是:
0 1 2 3 4 5 6 7 8
9
然后,点数列表将为0 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1 9
...如果点数变大(> 100),那么由approxPolyDP()
提取的段很遗憾不会重复(即:它们相互重叠,但不是非常相等,所以我不能只说“删除重复”,而不是像素一样)
approxPolyDP()
segmentation http://img197.imageshack.us/img197/4488/segmentation.png
以下是原始列表的精彩图片(感谢Paint!)以及相关图表。然后,当我在邻居之间添加边缘时。最后,当我删除边缘并制作最小生成树(这里没用)时
总结一下:我有一个乏味的方法,我还没有实现,因为它似乎容易出错。但是,我要求你,StackOverflow的人:是否有其他现有的方法,可能有很好的实现?
编辑:为了澄清,一旦我有了树,我就可以提取“分支”(分支从叶子或链接到3个或更多其他节点的节点开始)然后,openCV的approxPolyDP()
中的算法是{{ 3}},这是维基百科的图片:
通过这张图片,很容易理解为什么当点可能相互重复时它会失败
另一个编辑:在我的方法中,有些内容可能会引起注意。当您考虑位于网格中的点(如像素)时,通常情况下,最小生成树算法没有用,因为有许多可能的最小树
X-X-X-X
|
X-X-X-X
基本上与
非常不同X-X-X-X
| | | |
X X X X
但两者都是最小的生成树
但是,在我的情况下,我的节点很少形成集群,因为它们应该是轮廓,并且已经有一个预先在findContours()
中运行的细化算法。
回答Tomalak的评论:
如果DP算法返回4个段(从点2
到中心的段两次),我会很高兴!当然,有了良好的参数,我可以进入“偶然”我有相同段的状态,我可以删除重复。但是,很明显,算法不是为它而设计的。
这是一个包含太多细分的真实例子:
答案 0 :(得分:16)
使用Mathematica 8,我从图像中的白色像素列表中创建了一个形态图。它在你的第一张图片上工作正常:
创建形态图:
graph = MorphologicalGraph[binaryimage];
然后,您可以查询您感兴趣的图形属性。
这给出了图中顶点的名称:
vertex = VertexList[graph]
边缘列表:
EdgeList[graph]
这给出了顶点的位置:
pos = PropertyValue[{graph, #}, VertexCoordinates] & /@ vertex
这是第一张图片的结果:
In[21]:= vertex = VertexList[graph]
Out[21]= {1, 3, 2, 4, 5, 6, 7, 9, 8, 10}
In[22]:= EdgeList[graph]
Out[22]= {1 \[UndirectedEdge] 3, 2 \[UndirectedEdge] 4, 3 \[UndirectedEdge] 4,
3 \[UndirectedEdge] 5, 4 \[UndirectedEdge] 6, 6 \[UndirectedEdge] 7,
6 \[UndirectedEdge] 9, 8 \[UndirectedEdge] 9, 9 \[UndirectedEdge] 10}
In[26]:= pos = PropertyValue[{graph, #}, VertexCoordinates] & /@ vertex
Out[26]= {{54.5, 191.5}, {98.5, 149.5}, {42.5, 185.5},
{91.5, 138.5}, {132.5, 119.5}, {157.5, 72.5},
{168.5, 65.5}, {125.5, 52.5}, {114.5, 53.5},
{120.5, 29.5}}
鉴于文档http://reference.wolfram.com/mathematica/ref/MorphologicalGraph.html,命令MorphologicalGraph首先通过形态学细化来计算骨架:
skeleton = Thinning[binaryimage, Method -> "Morphological"]
然后检测顶点;它们是分支点和终点:
verteximage = ImageAdd[
MorphologicalTransform[skeleton, "SkeletonEndPoints"],
MorphologicalTransform[skeleton, "SkeletonBranchPoints"]]
然后在分析它们的连通性后链接顶点。
例如,可以先打破顶点周围的结构,然后查找连通的组件,揭示图的边缘:
comp = MorphologicalComponents[
ImageSubtract[
skeleton,
Dilation[vertices, CrossMatrix[1]]]];
Colorize[comp]
魔鬼在细节中,但如果您希望开发自己的实现,这听起来像一个坚实的起点。
答案 1 :(得分:10)
尝试数学形态学。首先,您需要dilate
或close
您的图片填补漏洞。
cvDilate(pimg, pimg, NULL, 3);
cvErode(pimg, pimg, NULL);
我有这个图像
下一步应该是应用thinning算法。不幸的是,它没有在OpenCV
中实现(MATLAB有bwmorph
thin
参数。例如,使用MATLAB,我将图像细化为:
但是OpenCV
所有人都需要基本的形态操作来实现细化(cvMorphologyEx
,cvCreateStructuringElementEx
等)。
另一个想法。
他们说距离变换似乎在这些任务中非常有用。可能是吧。
考虑cvDistTransform
函数。它创建了一个像这样的图像:
然后使用类似cvAdaptiveThreshold
:
那是骨架。我想你可以迭代所有连接的白色像素,找到曲线并滤除小段。
答案 2 :(得分:6)
之前我已经实现了类似的算法,并且我以一种渐进的最小二乘方式实现了它。它运作得相当好。伪代码有点像:
L = empty set of line segments
for each white pixel p
line = new line containing only p
C = empty set of points
P = set of all neighboring pixels of p
while P is not empty
n = first point in P
add n to C
remove n from P
line' = line with n added to it
perform a least squares fit of line'
if MSE(line) < max_mse and d(line, n) < max_distance
line = line'
add all neighbors of n that are not in C to P
if size(line) > min_num_points
add line to L
其中MSE(线)是线的均方误差(与最佳拟合线的平方距离线中所有点的总和),d(线,n)是从点n到这条线。 max_distance的良好值似乎是一个像素左右,而max_mse似乎要小得多,并且将取决于图像中线段的平均大小。对我来说,0.1或0.2像素在相当大的图像中工作。
我一直在使用Canny算子预处理的实际图像上使用它,所以我唯一的结果就是这样。以下是图像上述算法的结果:
也可以快速制作算法。我有C ++实现(由我的工作强制执行的闭源,对不起,否则我会给你)在大约20毫秒内处理上面的图像。这包括应用Canny算子进行边缘检测,因此在你的情况下应该更快。
答案 3 :(得分:0)
您可以先使用随openCV提供的HoughLinesP
从轮廓图像中提取直线:
HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength = 0, double maxLineGap = 0)
如果您选择threshold = 1
和minLineLenght
小,您甚至可以获得所有单个元素。但要小心,因为如果你有很多边缘像素,它会产生很多结果。