计算边缘线斜率的有效方法

3
我使用Python 2-7和OpenCV 3.2对图像执行了边缘检测,得到了如下图片所示的结果,即一像素宽的边缘不一定封闭(可能有“松散的端点”),并且可能有空洞。

enter image description here

现在我想获得这些边缘的“导数”,也就是每个点的“斜率”,如下图所示:

enter image description here

目前,我唯一能做到的是在本地进行操作。对于边缘的每个点(在下一个“放大”图片中为红色),我创建一个围绕它的圆圈(粉色),使用该圆圈遮罩边缘以获取红点的邻居,然后计算这两个邻居的斜率。

enter image description here

然而,如果边缘有洞(通常会出现这种情况)或靠近其他边缘(通常也是如此),掩蔽所有点会变得非常混乱,所以我想知道是否有更好的方法。我的第一个想法是样条插值,但您需要为给定边缘提供一个有序的点列表作为输入,除非使用像素邻居跟踪算法,否则很难实现。我还想到了findContours,但它需要封闭的边缘,否则它会产生一个一像素宽的边缘轮廓,即两条线在边缘的两侧开始于任意位置,简而言之,这是一团糟。是否有比我当前方法更清晰和更有效的方法来实现我想要的?OpenCV是否有任何资源,还是它在边缘检测后就完成了工作(我认为后者更有可能!)?附言:“我认为没有更好的方法”是我愿意接受的答案!

最简单的方法是将您的对象表示为轮廓,但正如您所说,您的对象可能具有孔,并且可能非常难以将其表示为“连接”的轮廓。 - Micka
感谢您的回复。即使没有孔,我的对象也不一定是封闭的,它们大多是松散的边缘。因此,findContours()并不能帮助我。我已经编辑了我的问题以考虑这一点。 - Soltius
你仍然可以通过“轮廓”(沿着曲线的点位置的有序列表)来表示曲线段,但是你必须以不同的方式计算它们(而不是使用findContours)。之后计算斜率变得简单,也许总体更有效。但仍然存在将边缘分组到曲线段的问题... - Micka
是的,对于“将边缘分组成曲线”的步骤,我想不到其他方法,只能使用低级算法,通过计算每个像素的邻居数量来找到极点,然后迭代地查找下一个邻居...这种算法可能在计算效率上不够高,并且在连接性有些复杂时容易出错。 - Soltius
斜率可以被看作是一条直线的倾斜度,谈论曲线的斜率是没有意义的(只有切线上的“线”斜率)。由于你的轮廓中有曲线,你可能想将每个轮廓分成“直”的线段,每个线段都有一个定义它的斜率。是这样吗? - Pedro Batista
显示剩余3条评论
1个回答

0

所以,如果我理解正确的话,你需要一个没有空洞的有序点列表,因为在那之后,你似乎知道如何继续获得你的结果。所以,你应该专注于获取一个有序无间隙的列表。

FindContours确实输出了一个有序列表,但可能不是你需要的顺序。它按从上到下/从左到右的优先级分组连接像素。因此,它按顺序扫描每一行,当它遇到白色像素时,它会找到第一个轮廓。因此,在你的图像中,它找到的第一个轮廓实际上是右边的那个,因为它的Y值最接近0。

对于这个特定的图像,如果你将它旋转90度,你会意识到它实际上会按照你需要的方式排序你的轮廓和点。但这种情况总是成立吗?只有你自己能够判断。如果有一种预处理方法可以应用于你的图像,可以保证findContours以正确的方式对你的像素进行排序,那么其余的工作就很容易了。如果没有,我建议你创建自己的像素连接算法,使其按照你需要的方式工作,因为你所有的问题都取决于获得一个有序列表。

一旦你有了有序列表,只需插值缺失的像素即可。

如果您有一个有序像素集,“关闭间隙”很容易,因为您只需要找到间隙并在它们之间插值作为近似,这可能不会损害您的算法。

1
我认为你是正确的,但正如我在我的问题中所说(已编辑),对于一个1像素宽的非封闭线条使用findContour确实很丑陋。而且我的图像并不总是这样... 但现在我更好地了解了问题所在以及我能做什么和不能做什么! - Soltius
作为旁注,我使用findContours描述1像素宽的线条时没有遇到任何问题,实际上我正在使用它来计算物体的尺寸。所以我不知道那里可能出了什么问题。很高兴能帮上忙,干杯。 - Pedro Batista

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接