R2D*_*2D2 2 python rgb opencv image
我有一个 RGB 图像,它是一张包含道路和车站的地图。
我正在尝试找到距离最近的车站,并且它们之间有一条路。道路显示为黑色,车站显示为红色。我添加了名称,这样会更容易理解。
O -> M
A -> B
B -> C
E -> F
G -> H
G -> C
M -> N
.
.
.
Run Code Online (Sandbox Code Playgroud)
两个车站之间的距离是根据它们之间的道路长度计算的。
解决这个问题的一些想法:我正在考虑从道路上移除车站,并且我们断开了道路,然后使用轮廓来计算道路的长度(以像素为单位)。但在某些情况下(例如 F 或 E),车站不会完全覆盖道路,并且不会将道路打折为三个不同的部分。
请告诉我你的想法,你如何解决它。
移除车站道路后将是这样的:
这是没有标签的主图像。
如果可能的话,追踪两个站点之间的路径并创建路径图像。例如从A站到C站:
AKX*_*AKX 10
哇,解决这个问题很有趣!:-)
我用我的想法来扩大红色车站斑点,以确保它们接触到它们相关的道路,并使用@Micka的“波前”解算器的想法来计算沿路线的距离,以形成距离图。
计算完这些距离图后,查找从 A 到 B 的距离只需读取 B 图中 A 位置处的值即可(反之亦然)。
不可否认,代码有点长,但应该进行注释,以便您可以弄清楚发生了什么。:)
您可以在 GitHub 上找到包含额外 Matplotlib 内容的代码,用于生成诊断和图像。
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()
Run Code Online (Sandbox Code Playgroud)
输出(格式为距离矩阵)是
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 -
Run Code Online (Sandbox Code Playgroud)
给定距离地图和站点,如下诊断图像所示。
基于 6、9 和 11 与网络的其余部分断开连接(并且 6 与所有网络断开连接!)以及这些往往会inf在输出中获得距离的事实,我认为这是可行的。:-)
这些加速的 gif 说明了左上站的距离图和邻居图“波前”是如何工作的
| 归档时间: |
|
| 查看次数: |
901 次 |
| 最近记录: |