【问题标题】:Sorting a list of xy-coordinates in python在python中对xy坐标列表进行排序
【发布时间】:2016-12-31 07:08:00
【问题描述】:

我有一长串 xy 坐标,如下所示:

>>> data = [(x1,y1),(x2,y2),(x3,y3),...]

每对坐标代表图像中轮廓的一个点,我想对它们进行排序,就像它们沿着轮廓排列(最短路径)一样。轮廓的形状很复杂(它是一个国家的形状),这就是为什么ConvexHull 不会工作。

我试过这段代码,但不够精确:

>>> import math
>>> import matplotlib.patches as patches
>>> import pylab

>>> pp=[(1,1),(2,3),(3,4)]

# compute centroid
>>> cent=(sum([p[0] for p in pp])/len(pp),sum([p[1] for p in pp])/len(pp))
# sort by polar angle
>>> pp.sort(key=lambda p: math.atan2(p[1]-cent[1],p[0]-cent[0]))
# plot points
>>> pylab.scatter([p[0] for p in pp],[p[1] for p in pp])
# plot polyline

>>> pylab.gca().add_patch(patches.Polygon(pp,closed=False,fill=False))
>>> pylab.grid()
>>> pylab.show()

我已经尝试过this case 中的建议 但效果不佳,因为我的坐标列表太长了。

由于点彼此非常接近,this question 的解决方案 对我来说可能看起来太复杂了。

This might illustrate my problem

【问题讨论】:

  • 你描述的这个任务叫做旅行商问题,一般很难解决。如果您可以向我们展示一些示例图像和相同的示例数据,也许有人会注意到一种模式,可以让我们提出一种有效的算法。
  • 因为它是一个计数,通常可以假设对于每个点有 2 个最近的邻居——路径上的一个之前和之后的一个——并且最短路径也将只包括这样的对.因此,您可以使用运行时间为 O(n*n) 或更好的贪心算法。
  • 你能解释一下为什么“这个问题对我来说似乎太复杂了”吗?你有没有试过他的解决方案,有什么问题?

标签: python sorting coordinates shortest-path


【解决方案1】:

如果您的形状是“简单”的示例,您可以计算点云的中心(X 和 Y 的平均值)。按从中心到点的角度对点进行排序,如果其中两个具有相同的角度,则按与中心的距离对它们进行排序。这应该可以解决问题。

center = functools.reduce(lambda p1, p2: (p1[0]+p2[0], p1[1]+p2[1]), data)
center = (center[0] / len(data), center[1] / len(data))

def angle(p1, p2):
    return math.atan2(p2[1], p2[0]) - math.atan2(p1[1], p1[0])

answer = sorted(data, key=lambda p: (angle(p, center), distance(p, center)))

对于更复杂的形状,我有另一种算法,我称之为放气船体。从你的云壳中,你将它放气,直到它接触到所有剩余的点:

def deflate_hull(points):
    hull = convex_hull(points)

    for p in hull:
        points.remove(p)

    while points:
        l = len(hull)
        _, p, i = min((distance(hull[i-1], p) + distance(p, hull[i]) - distance(hull[i-1], hull[i]), p, i) 
                      for p in points 
                      for i in range(l))
        points.remove(p)
        hull = hull[:i] + [p] + hull[i:]

    return hull

def convex_hull(points):
    if len(points) <= 3:
        return points
    upper = half_hull(sorted(points))
    lower = half_hull(reversed(sorted(points)))
    return upper + lower[1:-1]

def half_hull(sorted_points):
    hull = []
    for C in sorted_points:
        while len(hull) >= 2 and turn(hull[-2], hull[-1], C) <= -1e-6: 
            hull.pop()
        hull.append(C)
    return hull

def turn(A, B, C):
    return (B[0]-A[0]) * (C[1]-B[1]) - (B[1]-A[1]) * (C[0]-B[0]) 

def distance(p1, p2):
    return math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)

answer = deflate_hull(data)

【讨论】:

  • 对于至少star-shaped 图片大部分是正确的
  • 这完全适用于凸形形状,如果点分布很好,这也适用于星形形状 - 但如果分布不很好 i> 找到正确的中心并不那么简单
  • 简单的代码行不通,因为形状太复杂,第二个建议点太多,所以太慢了。
【解决方案2】:

您描述的问题类似于寻找凸包。你可以看看here

【讨论】:

  • 凸包算法不会考虑“内部”点。看来他都想要。
【解决方案3】:

您可以通过选择一个随机点并从那里找到下一个点来做到这一点

def distance(a,b):
    pass
    #propably something with math.hypot for euclidean norm

points=set([(1,2),(2,3)])
current=points.pop()
path=[current]
while points:
    current=min(points,key=(lambda p: distance(current,p))
    points.remove(current)
    path.append(current)

这不是一个快速算法(大约 O(n*n)),但它很简单。

您可以通过使用 kd-tree 来加快搜索速度 - 最简单的情况是仅沿一个轴进行二叉树/二叉搜索 - 但首先构建树/对点进行排序实际上并不快。

如果你的计数几乎是凸的,@Cabu 的解决方案是最快的。

【讨论】:

    【解决方案4】:

    更新: 同时,我通过使用 OpenCV 的 findContours 解决了这个问题——它可以像我希望的那样输出坐标!

    【讨论】:

      猜你喜欢
      • 2021-11-04
      • 1970-01-01
      • 1970-01-01
      • 2021-10-02
      • 1970-01-01
      • 2014-02-07
      • 2013-10-12
      • 1970-01-01
      • 2023-01-11
      相关资源
      最近更新 更多