在二进制图像中找到一条曲线的末端

4
我正在寻找一种算法来检测曲线的端点。我将把二进制图像转换为坐标点云,需要找到线段的端点以启动另一个算法。
我考虑对于每个点,取N个最近的1像素的向量平均值,并认为具有最长向量的像素必须是端点,因为如果一个点在直线中间,则向量的平均值将会抵消。然而,我认为这可能是图像处理领域中已知的问题,所以我想在这里发表一下,看看是否有人知道“正确”的算法。enter image description here

这条线是二进制图像吗?如果是,端点检测就很简单(只有一个邻居像素),你的问题将是算法是否会生成突出部分。 - Malcolm McLean
2个回答

2
如果线条只有一到两个像素宽,你可以使用Malcolm McLean在评论中提出的方法。否则,一种方法是为每个红色像素计算与其距离最远的同一组件中的红色像素,以及该最远像素的距离。(用图论术语来说,这两个像素之间的距离是每个像素的离心率。)长线附近的像素将具有最大的离心率,因为它们和线另一端的点之间的最短路径很长。(请注意,无论最大离心率是多少,至少会有两个像素具有该值,因为从a到b的距离与从b到a的距离相同。)
如果有n个红色像素,则所有离心率(和相应的最远像素)可以在O(n^2)时间内计算:依次为每个像素启动一个BFS,并将您找到的最深节点作为其最远像素(可能有几个;任何一个都可以)。每个BFS运行时间为O(n),因为任何像素上只有常数数量的边(取决于如何建模像素连接)。
为了保证鲁棒性,您可以考虑选择前10或50(等等)像素对,并检查它们是否形成了2个分离良好、定义良好的聚类。然后,您可以将每个聚类中的平均位置作为您的2个端点。

这条线比两个像素要粗得多,因此我实现了类似您建议的东西。看起来似乎很有效! - Peter Greaves
很高兴听到这个消息! :) - j_random_hacker

0
如果您将细化应用于线条,使得您的线条只有一个像素宽度,您可以利用OpenCV中的morphologyEX并使用MORPH_HITMISS。实质上,您为每个可能的角落(共有8个可能)创建一个模板(内核或滤波器),并通过每个模板进行卷积。每次卷积的结果在内核匹配的位置为1,否则为0。因此,如果您认为自己能够更好地完成工作,您也可以手动执行相同的操作。
以下是一个示例。它以任何由零和一组成的图像作为输入图像,其中线条只有一个像素宽度。
import numpy as np
import cv2
import matplotlib.pylab as plt

def find_endoflines(input_image, show=0):

<pre><code>kernel_0 = np.array((
        [-1, -1, -1],
        [-1, 1, -1],
        [-1, 1, -1]), dtype="int")

kernel_1 = np.array((
        [-1, -1, -1],
        [-1, 1, -1],
        [1,-1, -1]), dtype="int")

kernel_2 = np.array((
        [-1, -1, -1],
        [1, 1, -1],
        [-1,-1, -1]), dtype="int")

kernel_3 = np.array((
        [1, -1, -1],
        [-1, 1, -1],
        [-1,-1, -1]), dtype="int")

kernel_4 = np.array((
        [-1, 1, -1],
        [-1, 1, -1],
        [-1,-1, -1]), dtype="int")

kernel_5 = np.array((
        [-1, -1, 1],
        [-1, 1, -1],
        [-1,-1, -1]), dtype="int")

kernel_6 = np.array((
        [-1, -1, -1],
        [-1, 1, 1],
        [-1,-1, -1]), dtype="int")

kernel_7 = np.array((
        [-1, -1, -1],
        [-1, 1, -1],
        [-1,-1, 1]), dtype="int")

kernel = np.array((kernel_0,kernel_1,kernel_2,kernel_3,kernel_4,kernel_5,kernel_6, kernel_7))
output_image = np.zeros(input_image.shape)
for i in np.arange(8):
    out = cv2.morphologyEx(input_image, cv2.MORPH_HITMISS, kernel[i,:,:])
    output_image = output_image + out

return output_image

if show == 1:
    show_image = np.reshape(np.repeat(input_image, 3, axis=1),(input_image.shape[0],input_image.shape[1],3))*255
    show_image[:,:,1] = show_image[:,:,1] -  output_image *255
    show_image[:,:,2] = show_image[:,:,2] -  output_image *255
    plt.imshow(show_image)

即使有多条相互交叉且形状奇怪的线路,这也能正常工作。 - NinjasAtWork

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