获取二值图像上两点之间的像素索引

问题描述

很抱歉,如果有与此类似的问题,但我找不到。

我的问题很简单。我有如下图所示的二元曲线图像。我想在同一条曲线上的给定两点之间找到白色像素位置。我正在使用 Python,但任何有关算法的建议都会非常有帮助。

input image

import numpy as np
import cv2
import matplotlib.pyplot as plt


curve = cv2.imread('curve.png',-1)

pts = np.array([[31,14],[34,51]])

y,x = np.nonzero(curve)

fig,ax=plt.subplots()

ax.scatter(pts[:,1],pts[:,0],s=10,c='b')

for xx,yy in zip(x,y):
    if 51>xx>14:
        ax.scatter(xx,yy,c='r')
ax.imshow(curve,cmap='gray')

plt.show()

蓝点是给定的点,红点是我想得到的点。在代码中,我添加了 if 部分只是为了显示我想要得到的内容

enter image description here

我正在处理skeleton化的图像。因此,我正在为许多二值图像上的许多曲线寻找一种合理的方法。你知道做这种事情的任何算法/方法吗?提前致谢。

解决方法

解决此问题的最佳方法(还不是 cv2numpy)是使用广度优先搜索。 A* 算法不会总是返回最小路径,而且它更复杂。此外,由于像素之间没有权重,Dijkstra 对这个问题来说太复杂了。

以下是一些 Python 代码,用于进行原始广度优先搜索,以获得起点和终点之间的最短路径。请注意,路径数组包含开始和结束之间的所有内容,而不是开始/结束本身。也很容易调整以包括开始和结束。

import numpy as np
import cv2
import matplotlib.pyplot as plt
from collections import deque
import sys

curve = cv2.imread('curve.png',-1)
height,width = len(curve),len(curve[0])

# The start and end point you're looking at
start,end = (31,14),(34,51)

# All 8 directions
delta = [(-1,-1),(-1,0),1),(0,(1,-1)]

# Store the results of the BFS as the shortest distance to start
grid = [[sys.maxsize for _ in range(width)] for _ in range(height)]
grid[start[0]][start[1]] = 0

# The actual BFS algorithm
bfs = deque([start])
found = False
while len(bfs) > 0:
    y,x = bfs.popleft()
    # We've reached the end!
    if (y,x) == end:
        found = True
        break

    # Look all 8 directions for a good path
    for dy,dx in delta:
        yy,xx = y + dy,x + dx
        # If the next position hasn't already been looked at and it's white
        if 0 <= yy < height and 0 <= xx < width and grid[y][x] + 1 < grid[yy][xx] and curve[yy][xx] != 0:
            grid[yy][xx] = grid[y][x] + 1
            bfs.append((yy,xx))

if found:
    # Now rebuild the path from the end to beginning
    path = []
    y,x = end
    while grid[y][x] != 0:
        for dy,dx in delta:
            yy,x + dx
            if 0 <= yy < height and 0 <= xx < width and grid[yy][xx] == grid[y][x] - 1:
                path.append((yy,xx))
                y,x = yy,xx
    # Get rid of the starting point from the final path
    path.pop()

    # Display the final path on the plot
    fig,ax = plt.subplots()
    ax.scatter([start[1],end[1]],[start[0],end[0]],s=10,c='b')
    for y,x in path:
        ax.scatter(x,y,c='r')
    ax.imshow(curve,cmap='gray')

    plt.show()
else:
    print(f'No path found between {start} and {end}')

这是一个很好的方法,因为它使用了 O(height * width) 最差的时间复杂度。由于您的图像主要是骨架,因此它的运行速度应该比平均速度快得多。

,

使用 cv2.floodFill 是个好主意。 该问题通常称为寻找测地曲线。这是我的代码:

import numpy as np
import cv2

img=cv2.imread('UA3xU.png',cv2.IMREAD_GRAYSCALE)
pts = np.array([[31,14],[34,51]])
mask = np.zeros((img.shape[0]+2,img.shape[1]+2),np.uint8)

mask1_image = img.copy()
mask1_image[pts[0,0],pts[0,1]]=0 # split curve in first point 
cv2.floodFill(mask1_image,mask.copy(),(pts[1,1],pts[1,0]),flags=8)

mask2_image = img.copy()
mask2_image[pts[1,1]]=0 # split curve in second point
cv2.floodFill(mask2_image,(pts[0,flags=8)

# combine images
all_mask=cv2.bitwise_or(mask1_image,mask2_image)
out=cv2.bitwise_xor(img,all_mask)
cv2.imwrite('curve_between_two_points.png',out)

为了可靠地将曲线分成几部分,您可以不使用点,而是使用更大的 3x3 元素,例如。

之前和之后:

enter image description here enter image description here