检测图像中线的起点和终点(numpy数组)

Tom*_*ini 6 python opencv numpy

我有一个像下面这样的图像:

在此处输入图片说明

我想要的是获取每个段的起点和终点的坐标。实际上我的想法是考虑这样一个事实,即每个极值点应该只有一个点属于其附近的线段,而所有其他点应该至少有 2 个。不幸的是,这条线的厚度不等于一个像素,所以这个推理不成立。

Mar*_*ell 8

这是一个相当简单的方法:

  • 加载图像并丢弃多余的 alpha 通道
  • 骨架化
  • 过滤器寻找具有中心像素集的 3x3 邻域和其他邻域

#!/usr/bin/env python3

import numpy as np
from PIL import Image
from scipy.ndimage import generic_filter
from skimage.morphology import medial_axis

# Line ends filter
def lineEnds(P):
    """Central pixel and just one other must be set to be a line end"""
    return 255 * ((P[4]==255) and np.sum(P)==510)

# Open image and make into Numpy array
im = Image.open('lines.png').convert('L')
im = np.array(im)

# Skeletonize
skel = (medial_axis(im)*255).astype(np.uint8)

# Find line ends
result = generic_filter(skel, lineEnds, (3, 3))

# Save result
Image.fromarray(result).save('result.png')
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


请注意,您可以使用ImageMagick从命令行获得完全相同的结果,而且工作量要少得多,如下所示:

convert lines.png -alpha off -morphology HMT LineEnds result.png
Run Code Online (Sandbox Code Playgroud)

或者,如果您希望它们作为数字而不是图像:

convert result.png txt: | grep "gray(255)"
Run Code Online (Sandbox Code Playgroud)

样本输出

134,78: (65535)  #FFFFFF  gray(255)    <--- line end at coordinates 134,78
106,106: (65535)  #FFFFFF  gray(255)   <--- line end at coordinates 106,106
116,139: (65535)  #FFFFFF  gray(255)   <--- line end at coordinates 116,139
196,140: (65535)  #FFFFFF  gray(255)   <--- line end at coordinates 196,140
Run Code Online (Sandbox Code Playgroud)

Another way of doing it is to use scipy.ndimage.morphology.binary_hit_or_miss and set up your "Hits" as the white pixels in the below image and your "Misses" as the black pixels:

在此处输入图片说明

The diagram is from Anthony Thyssen's excellent material here.


In a similar vein to the above, you could equally use the "Hits" and "Misses" kernels above with OpenCV as described here:

morphologyEx(input_image, output_image, MORPH_HITMISS, kernel);
Run Code Online (Sandbox Code Playgroud)

I suspect this would be the fastest method.


Keywords: Python, image, image processing, line ends, line-ends, morphology, Hit or Miss, HMT, ImageMagick, filter.


fir*_*ant 5

你说的方法应该很好用,你只需要在之前做一个形态学操作,将线条的宽度减少到一个像素。您可以为此使用 scikit-image:

from skimage.morphology import medial_axis
import cv2

# read the lines image
img = cv2.imread('/tmp/tPVCc.png', 0)

# get the skeleton
skel = medial_axis(img)

# skel is a boolean matrix, multiply by 255 to get a black and white image
cv2.imwrite('/tmp/res.png', skel*255)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

有关skimage 中的骨架化方法,请参阅此页面


Daw*_*weo 1

我会用分水岭式算法来解决这个问题。我在下面描述了方法,但是创建它是为了仅处理单(多段)线,因此您需要将图像分割为单独线的图像。

玩具示例:

0000000
0111110
0111110
0110000
0110000
0000000
Run Code Online (Sandbox Code Playgroud)

其中0表示黑色,1表示白色。

现在我的解决方案的实现:

import numpy as np
img = np.array([[0,0,0,0,0,0,0],
[0,255,255,255,255,255,0],
[0,255,255,255,255,255,0],
[0,255,255,0,0,0,0],
[0,0,0,0,0,0,0]],dtype='uint8')

def flood(arr,value):
    flooded = arr.copy()
    for y in range(1,arr.shape[0]-1):
        for x in range(1,arr.shape[1]-1):
            if arr[y][x]==255:
                if arr[y-1][x]==value:
                    flooded[y][x] = value
                elif arr[y+1][x]==value:
                    flooded[y][x] = value
                elif arr[y][x-1]==value:
                    flooded[y][x] = value
                elif arr[y][x+1]==value:
                    flooded[y][x] = value
    return flooded

ends = np.zeros(img.shape,dtype='uint64')

for y in range(1,img.shape[0]-1):
    for x in range(1,img.shape[1]-1):
        if img[y][x]==255:
            temp = img.copy()
            temp[y][x] = 127
            count = 0
            while 255 in temp:
                temp = flood(temp,127)
                count += 1
            ends[y][x] = count

print(ends)
Run Code Online (Sandbox Code Playgroud)

输出:

[[0 0 0 0 0 0 0]
 [0 5 4 4 5 6 0]
 [0 5 4 3 4 5 0]
 [0 6 5 0 0 0 0]
 [0 0 0 0 0 0 0]]
Run Code Online (Sandbox Code Playgroud)

现在结束由上面数组中最大值的位置表示(6在本例中)。

解释:我正在检查所有白色像素作为可能的结束。对于每个这样的像素,我是“淹没”图像 - 我放置特殊值(127- 不同于0和 不同于255),然后传播它 - 在每一步中,所有255具有特殊值的邻居(在冯·诺依曼的意义上)本身都变成特殊值。我正在计算删除所有255. 因为如果您从末端开始(恒定速度)泛洪,则比在任何其他位置有源时需要更多时间,因此最大泛洪次数是线路的末端。

我必须承认,我没有对此进行深入测试,因此我不能保证在特殊情况下正确工作,例如自相交线的情况。我也意识到我的解决方案的粗糙性,特别是在检测邻居和传播特殊值方面,因此请随意改进它。我假设所有边框像素都是黑色的(没有线条接触图像的“框架”)。