python - 在 RGB 图像中查找 Point 之间的路径

标签 python image opencv rgb

我有一张 RGB 图像,它是一张包含道路和车站的 map 。

我正在尝试找到彼此最近的车站,这些车站之间有一条路。 道路显示为黑色,车站显示为红色。我添加了名称,这样会更容易理解。

O -> M
A -> B
B -> C
E -> F
G -> H
G -> C
M -> N
.
. 
.

两个站点之间的距离是根据它们之间的道路长度计算的。

解决这个问题的一些想法: 我在考虑从道路上移除车站,我们已经断开了道路,然后使用等高线来计算道路的长度(以像素为单位)。但在某些情况下(如 F 或 E),车站不会完全覆盖道路并且不会将道路打折为三个不同的部分。

请让我知道你的想法,你是如何解决的。

enter image description here

去掉车站的道路会是这样的:

enter image description here

这是没有标签的主图。

enter image description here

如果可能的话,追踪两个站点之间的路径并创建路径图像。例如从站 A 到 C:

enter image description here

最佳答案

哇,解决这个问题很有趣! :-)

我用我的想法扩大红色车站的 Blob ,以确保它们接触到它们相关的道路,并且 @Micka “波前”求解器的想法,用于计算沿路线的距离以形成距离图。

在计算出这些距离图之后,查找从 A 到 B 的距离只需读取 B map 中 A 位置的值即可(反之亦然)。

代码

诚然,代码有点长,但应该添加注释,以便您了解发生了什么。 :)

您可以找到包含额外 Matplotlib 内容以生成诊断和图像的代码 over here at GitHub .

from itertools import count, combinations

import cv2
import numpy as np


def inrange_thresh(image, color, thresh, binarize_thresh=None):
    """
    Apply cv.inRange with a threshold near the given color, optionally threshold the final image.
    """
    min_color = tuple(c - thresh for c in color)
    max_color = tuple(c + thresh for c in color)
    image = cv2.inRange(image, min_color, max_color)
    if binarize_thresh is not None:
        t, image = cv2.threshold(image, binarize_thresh, 255, cv2.THRESH_BINARY)
    return image


def find_feature_bboxes(image):
    """
    Find contours in the image and return their bounding boxes.
    :return: Iterable of (x, y, w, h)
    """
    cnts, *_ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for c in cnts:
        yield cv2.boundingRect(c)


def distance_from_point(input_mask, start_point):
    """
    Build a distance map following truthy paths in the input mask image, starting from start_point.
    :return: Tuple of distance map matrix and infinity value for the matrix
    """
    binary_mask = (input_mask > 127)
    # Figure out a suitably large number to serve as "infinity" for the mask.
    infinite_distance = max(binary_mask.shape) * 2

    # Generate a distance map with unreachable points, then seed it with our start point.
    dist_map = np.full_like(input_mask, infinite_distance, dtype="uint32")
    dist_map[start_point[::-1]] = 0

    # Precompute a structuring element we can use to dilate the "wavefront" to walk along the route with.
    struct_el = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
    for step in count(1):
        # Compute a zero map for new neighboring pixels.
        neighbor_map = np.full_like(dist_map, 0, dtype="uint8")
        # Mask in all of the pixels that were filled in by the last step.
        neighbor_map[dist_map == (step - 1)] = 255
        # Dilate the map with the structuring element so new neighboring pixels would be included.
        neighbor_map = cv2.dilate(neighbor_map, struct_el)

        # Figure out which pixels in the dist map should be filled
        new_dist_mask = (
            (dist_map > step) &  # must be more distant than we've already filled
            (neighbor_map > 0) &  # must be one of these new neighbors
            binary_mask  # must be walkable
        )
        if not np.any(new_dist_mask):
            # If there are no new pixels, we're done.
            break
        dist_map[new_dist_mask] = step
    return (dist_map, infinite_distance)


def main():
    image = cv2.imread("hHwyu.png", cv2.IMREAD_COLOR)

    marker_color = (239, 30, 40)[::-1]  # RGB -> BGR
    route_color = (0, 0, 0)[::-1]

    # Grab a grayscale image of the markers only
    markers = (inrange_thresh(image, marker_color, 5, 5) > 0).astype(np.uint8)
    # Use the center of their bounding boxes as a station location
    station_positions = [(int(x + w / 2), int(y + h / 2)) for x, y, w, h in find_feature_bboxes(markers)]
    station_positions.sort(key=lambda pair: pair[1])

    # Dilate the markers a bit so they'll always sit on the roads, then splat them on.
    # We'll use this as the base map for the contour-walking algorithm so it's all connected.
    markers_dilated = cv2.dilate(markers, cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)))
    routes_binary = inrange_thresh(image, route_color, 25, 0)
    routes_binary[markers_dilated > 0] = 255

    station_infos = []
    for station_id, start_point in enumerate(station_positions, 1):
        print(f"Computing distance map for station {station_id} at {start_point}")
        distmap, inf = distance_from_point(routes_binary, start_point)
        station_infos.append((station_id, start_point, distmap, inf))

    for (sa_id, sa_point, sa_map, sa_inf), (sb_id, sb_point, sb_map, sb_inf) in combinations(station_infos, 2):
        distance = sa_map[sb_point[::-1]]
        if distance >= sa_inf:
            distance = np.inf
        print(f"Distance between {sa_id} ({sa_point}) and {sb_id} ({sb_point}): {distance}")


if __name__ == '__main__':
    main()

输出

输出(格式化为距离矩阵)是

B / A  |    #1    #2    #3    #4    #5    #6    #7    #8    #9   #10   #11
    #1 |      -   356   288   370   212   inf   574   304   inf   455   inf
    #2 |    356     -    68   232   495   inf   436   587   inf   317   inf
    #3 |    288    68     -   164   427   inf   368   519   inf   249   inf
    #4 |    370   232   164     -   509   inf   379   601   inf   260   inf
    #5 |    212   495   427   509     -   inf   713   176   inf   594   inf
    #6 |    inf   inf   inf   inf   inf     -   inf   inf   inf   inf   inf
    #7 |    574   436   368   379   713   inf     -   805   inf   212   inf
    #8 |    304   587   519   601   176   inf   805     -   inf   686   inf
    #9 |    inf   inf   inf   inf   inf   inf   inf   inf     -   inf   114
   #10 |    455   317   249   260   594   inf   212   686   inf     -   inf
   #11 |    inf   inf   inf   inf   inf   inf   inf   inf   114   inf     -

给定距离图和站点,如下面的诊断图像所示。

基于 6、9 和 11 与网络的其余部分断开连接(并且 6 与所有内容断开连接!)并且它们往往在输出中获得 inf 距离这一事实,我'我说这行得通。 :-)

插图

各站距离图

enter image description here

漂亮的动画

这些加速的 gif 说明了左上站的距离图和邻居图“波前”如何工作

距离图构建

enter image description here

邻居图构建

enter image description here

关于python - 在 RGB 图像中查找 Point 之间的路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69552983/

相关文章:

python - Matplotlib 图例垂直旋转

python - CSS 未应用于 Flask 应用程序

image - 如何在Matlab中对图像的某个特定方向进行模糊处理?

php - 快速以肖像模式拍摄照片并上传到服务器 - 上传的图像显示方向错误

android - 在 Android 上实现益智游戏

python - 为什么这个 OpenCV 掩蔽函数不迭代?

python - 使用埃拉托斯特尼筛法给定范围内的素数

c++ - 将视频文件加载到 C++ 中的缓冲区中

c++ - 为什么神经网络不预测?

python - 使用 cx_Oracle 从 Python 连接到 Oracle 时出错