如何在曲线上均匀分布点?

6
我有一些由XYZ直角坐标点组成的三维任意曲线。这些点不是均匀分布的(存在时间因素)。如何使用给定数量的点“重建”曲线?我看到在3D建模程序中可以实现,所以我相信这是可能的,只是我不知道如何做。

enter image description here

根据回答,我需要用Python,所以我开始尝试将interparc转换为Python。 我已经完成了线性插值。 它可能效率低下且冗余,但也许对某些人有用。http://pastebin.com/L9NFvJyA


我有一些定义曲线的点,需要在这条曲线上均匀分布这些点。但是这个“曲线”并没有被计算出来,它只是一个数据集合。 - user-2147482637
如果您有“定义”曲线的点,那么您应该能够计算出曲线。否则,我不太确定您所说的“重新分配”的意思。 - vroomfondel
我添加了一张图片来展示我所说的内容。这些点沿着曲线并不均匀分布。 - user-2147482637
这张图片展示了为什么你认为自己应该能够在不计算任何东西的情况下做到这一点。但是更多的点确实没有什么区别!无论两个点之间的值如何,你怎么能确定它们之间的绝对值,即使它们只相隔一个微小的epsilon?你仍然需要使用某种回归方法,或者只需取相邻两个点的平均值,以找到更多可以选择使用或不使用的值。http://en.wikipedia.org/wiki/Regression_analysis#Nonlinear_regression - vroomfondel
我想用Python编写,但我可以将Matlab代码转换。 - user-2147482637
显示剩余2条评论
5个回答

11
我会使用我的工具interparc,它专门用于这个目的。它通过2个或多个维度中的一般空间曲线来拟合样条,然后选择在该曲线上等距离的点。在三次样条的情况下,解决方案使用odesolver进行数值积分,因此速度较慢,但仍然相当快。在许多情况下,简单的线性插值(如我在这里使用的)就足够了,并且非常快。
该曲线可能是完全一般化的,甚至可以交叉。我将为一个3D空间曲线给出一个简单的示例:
t = linspace(0,1,500).^3;
x = sin(2*pi*t);
y = sin(pi*t);
z = cos(3*x + y);
plot3(x,y,z,'o')
grid on
box on
view(-9,12)

enter image description here

xyzi = interparc(100,x,y,z,'lin');
plot3(xyzi(:,1),xyzi(:,2),xyzi(:,3),'o')
box on
grid on
view(-9,12)

enter image description here


哇,这看起来非常有前途,曲线不需要闭合吗? - user-2147482637
曲线可以是完全通用的,不需要闭合。但我提供了这种能力,以防第一个和最后一个点相同。 - user85109
2
你有算法写下来吗?我不用Matlab,很难理解正在发生的事情。 - user-2147482637

7

首先,感谢John D'Errico先生为interparc所做的贡献。他真是干得好!

我也曾面临这个问题,但不熟悉MATLAB引擎API。因此,我尝试将interparc Matlab代码的一部分转换为Python(仅包括线性插值器,因为它足以解决我的问题)。

下面是我的代码,希望能帮助所有寻求类似解决方案的Python开发人员:

import numpy as np

def interpcurve(N,pX,pY):
#equally spaced in arclength
N=np.transpose(np.linspace(0,1,N))

#how many points will be uniformly interpolated?
nt=N.size

#number of points on the curve
n=pX.size
pxy=np.array((pX,pY)).T
p1=pxy[0,:]
pend=pxy[-1,:]
last_segment= np.linalg.norm(np.subtract(p1,pend))
epsilon= 10*np.finfo(float).eps

#IF the two end points are not close enough lets close the curve
if last_segment > epsilon*np.linalg.norm(np.amax(abs(pxy),axis=0)):
    pxy=np.vstack((pxy,p1))
    nt = nt + 1
else:
    print('Contour already closed')

pt=np.zeros((nt,2))

#Compute the chordal arclength of each segment.
chordlen = (np.sum(np.diff(pxy,axis=0)**2,axis=1))**(1/2)
#Normalize the arclengths to a unit total
chordlen = chordlen/np.sum(chordlen)
#cumulative arclength
cumarc = np.append(0,np.cumsum(chordlen))

tbins= np.digitize(N,cumarc) # bin index in which each N is in

#catch any problems at the ends
tbins[np.where(tbins<=0 | (N<=0))]=1
tbins[np.where(tbins >= n | (N >= 1))] = n - 1      

s = np.divide((N - cumarc[tbins]),chordlen[tbins-1])
pt = pxy[tbins,:] + np.multiply((pxy[tbins,:] - pxy[tbins-1,:]),(np.vstack([s]*2)).T)

return pt 

我很快就会上传,但是我和一些学生一起工作,已经想出了完整的代码。不过,我认为这对许多人都很有用。 - user-2147482637
1
我不得不将你在两个“tbins [np.where ...]”行中的“|”运算符更改为np.bitwise_or; 否则它们不会按照你的意图工作(我想)。 我还注释掉了曲线关闭块,因为它对于我的非闭合曲线应用程序创建了奇怪的行为。 否则,干得好。 我希望有人能够将整个interparc代码python化,因为它非常有用。 - Jon
2
除了之前的评论,我还必须在摆脱曲线闭合块后像这样更改以下行: s = np.divide((N - cumarc[tbins-1]),chordlen[tbins-1]) pt = pxy[tbins-1,:] + np.multiply((pxy[tbins,:] - pxy[tbins-1,:]),(np.vstack([s]*ndim)).T)。现在,这与原始的Matlab函数线性插值给出相同的结果。 - bluelizard
这对我非常有效,但在运行interpcurve之前需要将输入归一化为相等的幅度。这只是提醒未来的用户。 - Oak Nelson

2
你的“曲线”是连接一系列点的线段。每个线段都有一个长度,你的曲线总长度是这些线段长度的求和。
因此,计算 d = totalCurveLength / (numberOfPoints - 1),并将曲线分成(numberOfPoints - 1)长度为d的块。

0
作为后续,interparc假设您的点按路径顺序列出(即点从起点到终点沿曲线追踪)。如果使用真实世界的数据,情况可能不是这样的。
如果您知道点的连接性(即哪些点连接形成弦),您可以使用libigl按路径顺序对点进行排序:
import igl

path, _, _ = igl.edges_to_path(edges)  # edges = N x 2 array of point indices

points = points[path]

# Now use `interparc`...

如果你不知道如何建立连接,可以尝试使用pykdtree
  • 创建一个名为edges的空的N X 2数组
  • points数据集中随机选择一个点(比如points的索引5
  • 使用KDTree找到数据中距离最近的两个邻居的索引
  • 将索引对存储在edges中 (例如,如果points[5]points[10]points[3]相连,你可以这样做:
edges = np.append(edges, [[5, 10], [5, 3]], axis=0)

points中弹出5103的点,并继续在点103处执行算法,直到没有剩余的点为止。
以上只是一个想法。

0

不太确定我是否理解了,但是可以尝试存储点与点之间的增量而不是实际数据,然后从增量重建曲线,这样就不会有空白区域了。但是这将改变曲线的形状。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接