[英]Querying multiple points on a bivariate spline in the B-spline basis
我需要构建一个 3D B-spline
曲面并在各种参数坐标下对其进行多次采样。 我找到的最接近的解决方案是使用bisplev
,它需要由bsplprep
计算的tck
输入。 不幸的是,我无法使用该tck
组件,因为它产生了一个通过控制点的曲面,而我想要的是在B-spline
basis
计算的曲面。 所以我手动构建了tck
输入bsplev
可以用来生成所需的表面。
不幸的是,如果不使用 2 个嵌套循环,我无法弄清楚如何做到这一点:每个uv
查询 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]):
loop 替换为单个bisplev
调用, bisplev
给它 u 和 v 数组,但这会引发ValueError: Invalid input data
at scipy\\interpolate\\_fitpack_impl.py", line 1048, in bisplev
。
有没有办法摆脱两者,或者至少摆脱uv
查询循环并在单个矢量化操作中执行所有uv
查询?
简答:替换
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
确实接受数组为si.bisplev(u, v, tck)
,但它将它们解释为定义一个 xy 网格。 因此, u
和v
必须按升序排序,并且将对所有对(u[j], v[k])
执行评估,输出是值的二维数组。 这不是你想要的; 求值次数的平方可能不好,并且从返回的二维数组中提取您实际想要的值并不容易(它们不一定在对角线上,因为您的 u、v 没有开始排序)。
但是SmoothBivariateSpline的call 方法包括一个布尔参数grid
,将 which 设置为 False 使其仅在(u[j], v[j])
对处评估样条。 向量 u、v 不再需要排序,但现在它们必须具有相同的大小。
但是您正在准备自己的tck
。 这提供了两种方法: SmoothBivariateSpline
使用手工 tck 实例化SmoothBivariateSpline
; 或者读取它的调用方法的源代码并在参数grid
设置为 False 时执行它的操作。 我采用了后一种方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.