我需要构建一个3D B-spline
曲面,并在不同的参数坐标处对其进行多次采样。我找到的最近的解决方案是使用bisplev
,它需要bsplprep
计算的tck
输入。不幸的是我不能使用tck
组件,因为它会产生一个穿过控制点的表面,而我想要的是在B-spline
basis
中计算的表面。因此,我手动构建tck
输入bsplev
可用于生成所需的曲面。
不幸的是,我不知道如何在不使用2个嵌套循环的情况下做到这一点:每个uv
查询1个,每个空间组件1个。后者是可以接受的,但前者在处理非常大的查询数组时非常慢。
以下是代码:
import numpy as np
import scipy.interpolate as si
def bivariate_bspline(cv,u,v,uCount,vCount,uDegree,vDegree):
# cv = grid of control vertices
# u,v = list of u,v component queries
# uCount, vCount = number of control points along the u and v directions
# uDegree, vDegree = curve degree along the u and v directions
uMax = uCount-uDegree # Max u parameter
vMax = vCount-vDegree # Max v parameter
# Calculate knot vectors for both u and v
u_kv = np.clip(np.arange(uCount+uDegree+1)-uDegree,0,uCount-uDegree) # knot vector in the u direction
v_kv = np.clip(np.arange(vCount+vDegree+1)-vDegree,0,vCount-vDegree) # knot vector in the v direction
# Compute queries
position = np.empty((u.shape[0], cv.shape[1]))
for i in xrange(cv.shape[1]):
tck = (u_kv, v_kv, cv[:,i], uDegree,vDegree)
for j in xrange(u.shape[0]):
position[j,i] = si.bisplev(u[j],v[j], tck)
return position
测试:
# A test grid of control vertices
cv = np.array([[-0.5 , -0. , 0.5 ],
[-0.5 , -0. , 0.33333333],
[-0.5 , -0. , 0. ],
[-0.5 , 0. , -0.33333333],
[-0.5 , 0. , -0.5 ],
[-0.16666667, 1. , 0.5 ],
[-0.16666667, -0. , 0.33333333],
[-0.16666667, 0.5 , 0. ],
[-0.16666667, 0.5 , -0.33333333],
[-0.16666667, 0. , -0.5 ],
[ 0.16666667, -0. , 0.5 ],
[ 0.16666667, -0. , 0.33333333],
[ 0.16666667, -0. , 0. ],
[ 0.16666667, 0. , -0.33333333],
[ 0.16666667, 0. , -0.5 ],
[ 0.5 , -0. , 0.5 ],
[ 0.5 , -0. , 0.33333333],
[ 0.5 , -0.5 , 0. ],
[ 0.5 , 0. , -0.33333333],
[ 0.5 , 0. , -0.5 ]])
uCount = 4
vCount = 5
uDegree = 3
vDegree = 3
n = 10**4 # make 10k random queries
u = np.random.random(n) * (uCount-uDegree)
v = np.random.random(n) * (vCount-vDegree)
bivariate_bspline(cv,u,v,uCount,vCount,uDegree,vDegree) # will return n correct samples on a b-spline basis surface
速度测试:
import cProfile
cProfile.run('bivariate_bspline(cv,u,v,uCount,vCount,uDegree,vDegree)') # 0.929 seconds
10k样本接近1秒,其中bisplev
调用占用了大部分计算时间,因为每个空间分量调用10k次。
我确实尝试将for j in xrange(u.shape[0]):
循环替换为单bisplev
次调用,一次性为其提供u和v数组,但在ValueError: Invalid input data
处引发了scipy\interpolate\_fitpack_impl.py", line 1048, in bisplev
有没有办法摆脱两者,或至少uv
查询循环,并在单个矢量化操作中执行所有uv
查询?
答案 0 :(得分:1)
简短回答:替换
gcc
与
for i in xrange(cv.shape[1]):
tck = (u_kv, v_kv, cv[:,i], uDegree,vDegree)
for j in xrange(u.shape[0]):
position[j,i] = si.bisplev(u[j],v[j], tck)
for i in xrange(cv.shape[1]):
position[:, i] = si.dfitpack.bispeu(u_kv, v_kv, cv[:,i], uDegree, vDegree, u, v)[0]
接受数组为bisplev
,但它将它们解释为定义xy网格。因此,si.bisplev(u, v, tck)
和u
必须按升序排序,并且将对所有对v
执行评估,输出是2D数组值。这不是你想要的;平方评估的数量可能很糟糕,并且从返回的2D数组中提取您实际想要的值并不容易(它们不一定在对角线上,因为您的u,v没有排序开始)。
但是call method of SmoothBivariateSpline包含一个布尔参数(u[j], v[k])
,设置为False使得它只评估grid
对的样条曲线。向量u,v不再需要排序,但现在它们必须具有相同的大小。
但是你正在准备自己的(u[j], v[j])
。这提出了两种方法:寻求用手工制作的tck实例化tck
;或者阅读its call method的来源,并在参数SmoothBivariateSpline
设置为False时执行操作。我采用了后一种方法。