You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从赛道黑白图像提取1像素宽坐标并生成有序数据集

从黑白赛道图像生成单像素连续坐标数据集并按赛道走向排序

一、提取单像素连续赛道坐标

你之前用ImageMagick提取所有黑色像素的方法,本质是获取了赛道的区域像素,而非单像素线条。要得到连续的单像素坐标,核心是对赛道进行骨架提取(细化),把宽赛道压缩成单像素的连续线条。推荐用Python+OpenCV实现,可控性更强:

  1. 图像预处理:
    • 读取灰度图,通过二值化区分赛道(黑色)和背景(白色),反转后让赛道变为白色像素便于处理。
    • 用形态学操作清理噪声:开运算去掉背景小噪点,闭运算填补赛道的细微缺口,保证赛道连续。
  2. 骨架提取:
    • 用OpenCV的cv2.ximgproc.thinning()函数直接生成单像素骨架,这一步能精准得到赛道的核心线条,避免冗余像素。
  3. 提取坐标:
    • 找出骨架上所有白色像素的坐标,注意OpenCV的坐标是(y,x)格式,需要转换成常规的(x,y)。

二、按赛道走向排序坐标

骨架提取后的坐标是无序的,需要按赛道的环形/线性走向排序,这里用贪心路径跟踪算法:

  1. 选择起点:可以选赛道最左、最右或最上的像素,也可以手动指定一个明显的起点(比如赛道起点)。
  2. 路径跟踪:
    • 从起点出发,每次在当前点的8邻域内寻找未被访问过的骨架像素。
    • 优先选择与上一步方向连续的像素(通过方向向量点积判断相似度),保证走向的连贯性。
    • 如果遇到分支或断点,自动寻找最近的未访问像素,确保覆盖整个赛道。
  3. 闭环处理:如果是环形赛道,最终会回到起点,确保所有像素都被访问。

完整代码实现

import cv2
import numpy as np

# 读取图像并预处理
img = cv2.imread("O0HQM.png", cv2.IMREAD_GRAYSCALE)
# 二值化:反转后赛道为白色,背景为黑色
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
# 形态学清理:去噪+补缺口
kernel = np.ones((3,3), np.uint8)
binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
# 提取单像素骨架
skeleton = cv2.ximgproc.thinning(binary)

# 转换为(x,y)坐标列表
coords = np.argwhere(skeleton == 255)
coords = [(x, y) for y, x in coords]
coords_set = set(coords)  # 用于快速查找

# 按赛道走向排序坐标
def sort_raceline(coords):
    # 选起点:最左侧的像素(可根据需求调整)
    start = min(coords, key=lambda p: p[0])
    current = start
    visited = {start}
    sorted_list = [start]
    
    # 8邻域方向
    neighbors = [(-1,-1), (-1,0), (-1,1),
                 (0,-1),          (0,1),
                 (1,-1),  (1,0), (1,1)]
    
    while len(visited) < len(coords):
        # 找当前点的未访问邻域
        candidates = []
        for dx, dy in neighbors:
            neighbor = (current[0]+dx, current[1]+dy)
            if neighbor in coords_set and neighbor not in visited:
                candidates.append(neighbor)
        
        if not candidates:
            # 处理断点,找最近的未访问像素
            unvisited = [p for p in coords if p not in visited]
            current = min(unvisited, key=lambda p: np.linalg.norm(np.array(p)-np.array(current)))
            visited.add(current)
            sorted_list.append(current)
            continue
        
        # 优先选择方向连续的像素
        if len(sorted_list) > 1:
            prev_point = sorted_list[-2]
            prev_dir = (current[0]-prev_point[0], current[1]-prev_point[1])
            # 计算方向相似度(点积越大越接近)
            def get_similarity(pt):
                curr_dir = (pt[0]-current[0], pt[1]-current[1])
                return prev_dir[0]*curr_dir[0] + prev_dir[1]*curr_dir[1]
            current = max(candidates, key=get_similarity)
        else:
            # 第一步任选邻域点
            current = candidates[0]
        
        visited.add(current)
        sorted_list.append(current)
    
    return sorted_list

# 执行排序并保存为CSV
sorted_coords = sort_raceline(coords)
with open("raceline.csv", "w") as f:
    f.write("x,y\n")
    for x, y in sorted_coords:
        f.write(f"{x},{y}\n")

注意事项

  • 二值化阈值(127)可根据图像实际明暗调整,确保赛道和背景完全分离。
  • 形态学核的大小((3,3))可根据赛道宽度调整,宽赛道可适当放大核尺寸。
  • 如果赛道有分支,可手动指定起点或修改排序逻辑,优先跟踪主赛道。

内容的提问来源于stack exchange,提问作者jenototh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 18:56:00