分拣点形成一条连续的线 [英] sorting points to form a continuous line

查看:171
本文介绍了分拣点形成一条连续的线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个表示线骨架的(x,y)坐标列表。
该列表直接从二进制映像获得:

  import numpy as np 
list = np。其中(img_skeleton> 0)

现在列表中的点根据它们在图像中的位置进行排序沿着其中一个轴。



我想对列表进行排序,使订单代表沿线的平滑路径。 (目前不是线条向后弯曲的情况)。
随后,我想为这些点拟合样条。



使用arcPy描述并解决了类似的问题



当我将列表发送到scipy的样条拟合程序时,我遇到了问题,因为这些点没有'订购'就行了:



解决方案

我事先为长期答案道歉:P(问题不在于 简单)。



让我们从重新措辞开始。找到连接所有点的线,可以重新表示为图中的最短路径问题,其中(1)图节点是空间中的点,(2)每个节点连接到其2个最近邻居,( 3)最短路径仅通过每个节点。最后一个约束是非常重要的(并且非常难以优化)。本质上,问题是找到长度 N 的排列,其中排列是指每个节点的顺序( N b

查找所有可能的排列并评估其成本太贵(有 N!排列,如果我没错,这对于问题来说太大了)。 Bellow我提出了一种方法,找到 N 最佳排列(每个 N 点的最佳排列)然后找到最小化错误/成本的排列(从那些 N )。



1。使用无序点创建随机问题



现在,让我们开始创建一个示例问题:

 导入matplotlib.pyplot为plt 
import numpy as np

x = np.linspace(0,2 * np.pi,100)
y = np。 sin(x)

plt.plot(x,y)
plt.show()



此处,未分类的点数 [ x,y] 模拟一行中连接的空格中的随机点:

  idx = np .random.permutation(x.size)
x = x [idx]
y = y [idx]

plt.plot(x,y)
plt.show ()



然后问题是订购那些po注册以恢复原始订单,以便正确绘制线。



2。在节点之间创建2-NN图



我们可以先重新排列 [N,2] 数组中的点数:

  points = np.c_ [x,y] 

然后,我们可以从创建最近邻居图开始,将每个节点连接到其最近的2个邻居:

 来自sklearn.neighbors import NearestNeighbors 

clf = NearestNeighbors(2).fit(points)
G = clf.kneighbors_graph()

G 是稀疏 N x N 矩阵,其中每一行代表一个节点,列的非零元素与这些点的欧几里德距离。



我们可以然后使用 networkx 从这个稀疏矩阵构造一个图形:

  import networkx as nx 

T = nx.from_scipy_sparse_matrix(G)



3。从源代码中找到最短路径



并且,这里开始 magic :我们可以使用



嗯,不是太糟糕,但我们可以注意到重建不是最佳的。这是因为无序列表中的点 0 位于该行的中间,即它首先向一个方向行进,然后返回并在另一个方向上结束方向。



4。从所有来源找到成本最低的路径



因此,为了获得最佳订单,我们可以获得所有节点的最佳订单:

  paths = [list(nx.dfs_preorder_nodes(T,i))for i in range(len(points))] 

现在我们有从 N = 100 节点,我们可以丢弃它们并找到最小化连接之间距离的节点(优化问题):

  mindist = np.inf 
minidx = 0

我在范围内(len(点)):
p = paths [i]#节点顺序
ordered = points [ p]#ordered nodes
#通过点(i)和(i + 1)之间的欧几里德距离之和找到该订单的成本
cost =(((ordered [: - 1] - ordered [ 1:])** 2).sum(1))。sum()
如果成本< mindist:
mindist = cost
minidx = i

积分是为每个最佳路径,然后计算成本(通过计算所有点对之间的欧氏距离 i i + 1 )。如果路径从 start end 点开始,它将具有最小的成本,因为所有节点将是连续的。另一方面,如果路径从位于线路中间的节点开始,则某些点的成本将非常高,因为它需要从线路的末端(或开始)行进到初始路径。探索另一个方向的位置。最小化该成本的路径是从最佳点开始的路径。

  opt_order = paths [minidx] 

现在,我们可以正确重建订单:

  xx = x [opt_order] 
yy = y [opt_order]

plt.plot(xx,yy)
plt.show()


I have a list of (x,y)-coordinates that represent a line skeleton. The list is obtained directly from a binary image:

import numpy as np    
list=np.where(img_skeleton>0)

Now the points in the list are sorted according to their position in the image along one of the axes.

I would like to sort the list such that the order represents a smooth path along the line. (This is currently not the case where the line curves back). Subsequently, I want to fit a spline to these points.

A similar problem has been described and solved using arcPy here. Is there a convenient way to achieve this using python, numpy, scipy, openCV (or another library?)

below is an example image. it results in a list of 59 (x,y)-coordinates.

when I send the list to scipy's spline fitting routine, I am running into a problem because the points aren't 'ordered' on the line:

解决方案

I apologize for the long answer in advance :P (the problem is not that simple).

Lets start by rewording the problem. Finding a line that connects all the points, can be reformulated as a shortest path problem in a graph, where (1) the graph nodes are the points in the space, (2) each node is connected to its 2 nearest neighbors, and (3) the shortest path passes through each of the nodes only once. That last constrain is a very important (and quite hard one to optimize). Essentially, the problem is to find a permutation of length N, where the permutation refers to the order of each of the nodes (N is the total number of nodes) in the path.

Finding all the possible permutations and evaluating their cost is too expensive (there are N! permutations if I'm not wrong, which is too big for problems). Bellow I propose an approach that finds the N best permutations (the optimal permutation for each of the N points) and then find the permutation (from those N) that minimizes the error/cost.

1. Create a random problem with unordered points

Now, lets start to create a sample problem:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)

plt.plot(x, y)
plt.show()

And here, the unsorted version of the points [x, y] to simulate a random points in space connected in a line:

idx = np.random.permutation(x.size)
x = x[idx]
y = y[idx]

plt.plot(x, y)
plt.show()

The problem is then to order those points to recover their original order so that the line is plotted properly.

2. Create 2-NN graph between nodes

We can first rearrange the points in a [N, 2] array:

points = np.c_[x, y]

Then, we can start by creating a nearest neighbour graph to connect each of the nodes to its 2 nearest neighbors:

from sklearn.neighbors import NearestNeighbors

clf = NearestNeighbors(2).fit(points)
G = clf.kneighbors_graph()

G is a sparse N x N matrix, where each row represents a node, and the non-zero elements of the columns the euclidean distance to those points.

We can then use networkx to construct a graph from this sparse matrix:

import networkx as nx

T = nx.from_scipy_sparse_matrix(G)

3. Find shortest path from source

And, here begins the magic: we can extract the paths using dfs_preorder_nodes, which will essentially create a path through all the nodes (passing through each of them exactly once) given a starting node (if not given, the 0 node will be selected).

order = list(nx.dfs_preorder_nodes(T, 0))

xx = x[order]
yy = y[order]

plt.plot(xx, yy)
plt.show()

Well, is not too bad, but we can notice that the reconstruction is not optimal. This is because the point 0 in the unordered list lays in the middle of the line, that is way it first goes in one direction, and then comes back and finishes in the other direction.

4. Find the path with smallest cost from all sources

So, in order to obtain the optimal order, we can just get the best order for all the nodes:

paths = [list(nx.dfs_preorder_nodes(T, i)) for i in range(len(points))]

Now that we have the optimal path starting from each of the N = 100 nodes, we can discard them and find the one that minimizes the distances between the connections (optimization problem):

mindist = np.inf
minidx = 0

for i in range(len(points)):
    p = paths[i]           # order of nodes
    ordered = points[p]    # ordered nodes
    # find cost of that order by the sum of euclidean distances between points (i) and (i+1)
    cost = (((ordered[:-1] - ordered[1:])**2).sum(1)).sum()
    if cost < mindist:
        mindist = cost
        minidx = i

The points are ordered for each of the optimal paths, and then a cost is computed (by calculating the euclidean distance between all pairs of points i and i+1). If the path starts at the start or end point, it will have the smallest cost as all the nodes will be consecutive. On the other hand, if the path starts at a node that lies in the middle of the line, the cost will be very high at some point, as it will need to travel from the end (or beginning) of the line to the initial position to explore the other direction. The path that minimizes that cost, is the path starting in an optimal point.

opt_order = paths[minidx]

Now, we can reconstruct the order properly:

xx = x[opt_order]
yy = y[opt_order]

plt.plot(xx, yy)
plt.show()

这篇关于分拣点形成一条连续的线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆