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

时间:2019-05-14 13:19:11

标签: python numpy opencv

我有如下图像:

enter image description here

我想要的是获取每个线段的起点和终点的坐标。实际上,我想考虑的事实是,每个极端点应只有一个点属于其附近的线段,而所有其他点都应至少具有2个点。不幸的是,线的粗细不等于一个像素,因此这种推理不成立。

3 个答案:

答案 0 :(得分:2)

您提到的方法应该能很好地工作,您只需要进行形态学运算,然后将线条的宽度减小到一个像素即可。您可以为此使用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)

enter image description here

有关this page的Skimage中的骨架化方法,请参见

答案 1 :(得分:2)

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

  • 加载图像并丢弃多余的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')

enter image description here


请注意,使用 ImageMagick ,您可以从命令行中获得完全相同的结果,而所需的工作却更少:

convert lines.png -alpha off -morphology HMT LineEnds result.png

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

convert result.png txt: | grep "gray(255)"

示例输出

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

另一种方法是使用scipy.ndimage.morphology.binary_hit_or_miss并将您的“命中” 设置为下图中的白色像素,并设置您的“失踪” 为黑色像素:

enter image description here

该图来自Anthony Thyssen的出色材料here


与上述类似,您可以如上所述将“ Hits” “ Misses” 内核与 OpenCV 一起使用here

morphologyEx(input_image, output_image, MORPH_HITMISS, kernel);

我怀疑这将是最快的方法。


关键字:Python,图像,图像处理,行尾,行尾,形态,命中或缺失,HMT,ImageMagick,过滤器。

答案 2 :(得分:1)

我将使用分水岭式算法解决此问题。我在下面介绍了方法,但是该方法仅用于处理单行(多段),因此您需要将图像拆分为单独行的图像。

玩具示例:

0000000
0111110
0111110
0110000
0110000
0000000

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)

输出:

[[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]]

现在的末端由上述数组(在这种情况下为6)中最大值的位置表示。

说明:我正在检查所有白色像素(如果可能)。对于每个这样的像素,我正在“泛洪”图像-我放置一个特殊值(127-与0不同并且与255不同),然后对其进行传播-在每一步中,所有{{1 }}(在冯·诺伊曼的意义上)具有特殊价值的邻居本身成为特殊价值。我正在计算删除所有255所需的步骤。因为如果从头开始(恒速)溢流比在任何其他位置放源要花费更多的时间,那么最大的溢流时间就是生产线的终点。

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