Reputation: 1090
I got a list of points by extracting the edge of a image, like that:But it is not well ordered, so if I connect it as a line, it will be:
Thus I want to sort this list if points. Like, start with point_0, find which one has the shortest distance with it, say, point_3, then find which one's closest to point_3 then continue... To sort the points, I wrote this:
import matplotlib.pyplot as plt
import numpy as np
import math
def dist(now, seek):
return math.sqrt((now[0] - seek[0])**2 + (now[1] - seek[1])**2)
def sortNearest(x, y):
if len(x) != len(y):
raise Exception('Error! Array length do not match!')
return False
xNew = []; yNew = []
nearest = 0 #record which point is nearest
now = [x[0], y[0]] #start point index
seekValue = 0
while len(x) > 0:
distance = (max(x) - min(x)) + (max(y) - min(y))
for seek in range(len(x)): # other
temp = dist(now, [x[seek], y[seek]])
if temp < distance and temp != 0.0:
distance = temp
seekValue = x[seek]
xNew.append(now[0]);
yNew.append(now[1]);
if len(x) > 0:
x.remove(now[0])
y.remove(now[1])
if len(x) > 0:
nearest = x.index(seekValue)
now = [x[nearest], y[nearest]]
x = list(xNew); y = list(yNew)
return xNew, yNew
x, y = getBorder('large.png', maxRes = 125)
x, y = sortNearest(x, y)
But that doesn't work well, I came up with this:
Which is obviously incorrect, if I zoom in, see:
If my code runs what I want, point_644 should connect 620 or 675, any but 645... What's wrong with it?
Upvotes: 2
Views: 586
Reputation: 4017
I think what you want to do can be optimized with numpy and scipy:
import numpy as np
import scipy.spatial.distance as distance
import matplotlib.pyplot as plt
points = np.random.random((6,2))
dists =distance.pdist(points)
m=np.argsort(distance.squareform(dists))[:,1:]
order = [0,m[0,0]]
next_point = order[-1]
while len(order)<len(points):
row = m[next_point]
i = 0
while row[i] in order:
i += 1
order.append(row[i])
next_point = order[-1]
order.append(0)
ordered=points[order]
plt.plot(ordered[:,0], ordered[:,1], 'o-')
The idea underlying this code is the following. First you calculate all the distances. Then you use argsort
to get the indices that would order each row. You can remove the first column, as each point is closest to itself. We know that. Then you look which is the next closest point and you add it to the list order
if the point is not there yet. You then go to the row corresponding to this point, and look for the next point. And so on.
If what you are only interested in is just sorting the enclosing set of points, you can use ConvexHull
to find them:
ch = ConvexHull(points)
plt.plot(points[ch.vertices,0], points[ch.vertices,1], 'o-')
Upvotes: 1
Reputation: 2763
I don't know how I would do this in python 3.x, so please forgive changes that I have not made from python 2.7. You'll also want to figure out what point you'd like to start with:
def find_distance(point1, point2):
distance = sqrt(square(point1[0]-point2[0]) + square(point1[1] - point2[1]))
return distance
x, y = getBorder('large.png', maxRes = 125)
points_in_border = [(i,j) for i, j in zip(x,y)]
current_point = points_in_border.pop([0])
points_in_order = [current_point]
while len(points_in_border) > 0:
min_distance = 10000
for point in points_in_border:
if find_distance(current_point, point) < min_distance:
closest_point = point
min_distance = find_distance(current_point, point)
points_in_border.remove(closest_point)
current_point = closest_point
points_in_order.append(closest_point)
Upvotes: 1
Reputation: 9621
Well, point 644 cannot connect to point 620, because 620 is already part of your path.
As for why it connects to 645 instead of the closer 675: in your loop, you aren't actually remembering the index of the closest point, you're only remembering its x coordinate. After the loop, you then locate an arbitrary point with the same x coordinate - it could be anywhere on a vertical line going through the desired point.
Upvotes: 3