【问题标题】:Finding a path between Point in a RGB image在 RGB 图像中查找点之间的路径
【发布时间】:2021-10-13 09:29:41
【问题描述】:

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

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

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

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

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

请告诉我你的想法,你如何解决它。

通过移除车站道路将是这样的:

这是没有标签的主图。

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

【问题讨论】:

  • 嗯,你当然可以种植红色斑点(遮罩、侵蚀、复合材料),这样它们就可以一直覆盖道路。
  • 1.从一个红点开始。 2.创建一个距离那个红点最近的地图D,用INF初始化,将D中的红点位置初始化为0;用 0 初始化 cDist。 3. 虽然未完成:对于 D 中的 cDist 值元素的邻居并且在图像中为黑色的每个像素:设置为 cDist+1;更新 cDist=cDist+1; 4. 对所有其他红点重复 5. 每个红点都有一个距离和可达性地图。
  • @Micka,你能再解释一下吗,我没有理解正确。请您用 sudo 代码或代码解释一下。
  • 你真的只有有那个图像吗? (顺便说一下,我们能看到实际图像吗?)如果你没有在上面写站名,你怎么知道哪个站是哪个?
  • 名称不重要。可以为他们分配一个 ID。重要的是哪个连接到哪个。我正在处理这张图片作为样本。我没有其他图片。

标签: python image opencv rgb


【解决方案1】:

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

我使用了我的想法,即扩大红色车站斑点以确保它们接触到与之相关的道路,并使用 @Micka 的“波前”求解器的想法来计算沿路线的距离以形成距离图.

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

代码

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

您可以找到包含额外 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 距离的事实,我会说这是可行的。 :-)

插图

各车站的距离图

漂亮的动画

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

距离图构建

邻域图构建

【讨论】:

  • 知道如何从一个站点追踪到另一个站点并创建路径图像吗?我添加了另一张图片,以便您更好地理解我的问题。例如,这是从 A 站到 C 站的路径。
  • 我会使用寻路算法(例如 A*)来找到最短路径。
猜你喜欢
  • 1970-01-01
  • 2017-07-03
  • 1970-01-01
  • 1970-01-01
  • 2012-03-21
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 2017-12-06
相关资源
最近更新 更多