【问题标题】:Getting pixel indices between two points on a binary image获取二值图像上两点之间的像素索引
【发布时间】:2021-01-21 12:57:27
【问题描述】:

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

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

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,s=10,c='r')
ax.imshow(curve,cmap='gray')

plt.show()

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

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

【问题讨论】:

  • 快速想法:实际上,为两个给定点着色,并使用具有 8 个连接的cv2.floodFill 为这些点之间的所有像素着色。使用指定颜色对图像进行遮罩,使用np.argwhere获取所有像素的x, y坐标。症结:您需要在两个给定点之间找到一个点。我可以想象使用给定点之一的邻居进行暴力破解...也许,有更聪明的东西...

标签: python image-processing line curve


【解决方案1】:

解决这个问题的最佳方法是使用广度优先搜索,而不是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, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -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, xx = y + dy, 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, s=10, c='r')
    ax.imshow(curve, cmap='gray')

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

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

【讨论】:

  • 哇。这是解决问题的一种非常聪明和简单的方法。我也同意你对其他建议算法的看法,特别是对于这个问题,你的问题更有意义。非常感谢。
【解决方案2】:

使用 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]), 0, flags=8)

mask2_image = img.copy()
mask2_image[pts[1,0], pts[1,1]]=0 # split curve in second point
cv2.floodFill(mask2_image, mask.copy(), (pts[0,1], pts[0,0]), 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 元素。

之前和之后:

【讨论】:

    猜你喜欢
    • 2012-03-24
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 2014-02-27
    • 1970-01-01
    • 1970-01-01
    • 2021-07-04
    • 1970-01-01
    相关资源
    最近更新 更多