2016-08-24 87 views
-2

x,y座標的列表,我還有很長的XY座標列表如下所示:排序在python

>>> 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

你所描述的被稱爲旅行商的問題,是很難在一般的要解決的任務。如果您可以向我們展示一些樣本圖片和相同的樣本數據,也許有人會注意到一種模式,可以讓我們提出一個有效的算法。 –

+0

由於它是一個countour它通常可以假設對於每個點有2個最近鄰 - 所述一個之前和路徑上一前一後 - 並且所述最短路徑也將僅包括這樣的對。因此,您可以使用運行時間爲O(n * n)或更好的貪婪算法。 – janbrohl

+0

你能再解釋一下爲什麼「這個問題看起來對我來說太複雜了」?你有沒有試過他的解決方案,有什麼問題? – Jason

回答

0

如果你的形狀是「簡單的」爲你的榜樣,你可以計算你的點雲(平均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) 
+0

大部分正確的至少[星形](https://en.wikipedia。org/wiki/Star_domain)圖片 – janbrohl

+0

這完全適用於konvex形狀,如果點分佈*很好*這也適用於星形形狀 - 但如果分佈不是*好*找到正確的中心不那麼簡單 – janbrohl

+0

簡單的代碼不起作用,因爲形狀太複雜,而你的第二個建議太多,所以它太慢了。 – feinheitsbrei

0

你描述的問題類似於找到一個凸包。你可以看看here

+0

凸包算法不會考慮'內部'點。他似乎想要他們。 – Cabu

0

您可以通過選擇一個隨機點做到這一點,並找到下一個點,從那裏像

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加速搜索 - 最簡單的情況是沿着一個軸進行二叉樹/二進制搜索 - 但先構建樹/排序點實際上並不快。

如果您countour甚至幾乎凸,@Cabu的解決方案是最快的。

0

更新: 同時我解決它使用的OpenCV的findContours - 輸出就像我whished的座標!