You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何用Python的Numpy按顺序获取线条/路径坐标及处理圆形形状

按顺序排列线条/形状的像素坐标解决方案

这是个很常见的计算机视觉问题,我来给你拆解两种场景的具体解决方案:

一、手绘单像素线条的坐标排序

你用numpy.where拿到所有线条像素坐标后,之所以没法直接排序,是因为这个方法返回的是无序的坐标集合——毕竟图像的存储是按行/列来的,和线条的走向没关系。这里有两种靠谱的排序思路:

方法1:基于起点距离排序(适合近似直线的线条)

如果你的线条比较接近直线,直接计算每个点到红色起点的欧氏距离,按距离从小到大排序就足够了。代码示例:

import numpy as np

# 假设img是你的二值图像,线条像素为1,背景为0
y_coords, x_coords = np.where(img == 1)
# 转成(x, y)格式的坐标数组
points = np.column_stack((x_coords, y_coords))

# 假设你已经知道起点(红色像素)坐标
start_point = np.array([x0, y0])

# 计算每个点到起点的距离
distances = np.linalg.norm(points - start_point, axis=1)
# 按距离排序
sorted_points = points[np.argsort(distances)]

⚠️ 注意:如果线条有明显弯曲甚至折返,这个方法会失效,因为有些离起点远的点可能在线条的“分支”上,排序结果会乱。

方法2:路径追踪排序(适合任意弯曲的线条)

这是更可靠的方案,相当于沿着线条一步步“走”:从起点开始,每次找当前点的最近邻未访问点,直到遍历完所有像素。这种方法完全贴合手绘线条的走向,代码示例:

def sort_line_points(points, start_point):
    remaining = points.copy()
    sorted_list = [start_point]
    current = start_point
    
    # 先从剩余点中移除起点
    idx = np.where((remaining == current).all(axis=1))[0][0]
    remaining = np.delete(remaining, idx, axis=0)
    
    while len(remaining) > 0:
        # 计算当前点到所有剩余点的距离
        dists = np.linalg.norm(remaining - current, axis=1)
        # 找到最近的点
        nearest_idx = np.argmin(dists)
        nearest_point = remaining[nearest_idx]
        # 添加到排序列表,移除已访问的点
        sorted_list.append(nearest_point)
        remaining = np.delete(remaining, nearest_idx, axis=0)
        current = nearest_point
    
    return np.array(sorted_list)

# 调用方法得到排序后的坐标
sorted_points = sort_line_points(points, start_point)

二、圆形(闭合形状)的坐标排序

圆形是闭合的,没有天然的起点终点,排序的核心是确定旋转方向(顺时针/逆时针),然后基于极角排序:

  1. 先计算圆形的近似中心:
# 用所有像素坐标的均值作为中心
center = np.mean(points, axis=0)
  1. 计算每个点相对于中心的极角:
# 计算每个点到中心的向量
vectors = points - center
# arctan2(y, x)返回从x轴正方向逆时针旋转的角度(弧度)
angles = np.arctan2(vectors[:, 1], vectors[:, 0])
  1. 按极角排序:
# 逆时针排序(从右水平方向开始逆时针绕圈)
sorted_circle_points = points[np.argsort(angles)]
# 顺时针排序(从右水平方向开始顺时针绕圈)
sorted_circle_points = points[np.argsort(-angles)]

如果你的圆形不是完全闭合(有缺口),可以先指定一个起始点,用线条的路径追踪方法排序;或者结合极角排序后,手动调整起始位置。

额外注意事项

  • 如果线条宽度大于1像素,一定要先提取单像素骨架(比如用OpenCV的cv2.ximgproc.thinning()),否则多像素的排序会完全混乱。
  • 手绘线条可能有噪声点,排序前可以用形态学操作(比如cv2.morphologyEx())移除孤立的小像素块,避免干扰排序结果。

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

火山引擎 最新活动