[英]Querying multiple points on a bivariate spline in the B-spline basis
I need to construct a 3D B-spline
surface and sample it multiple times at various parametric coordinates.我需要构建一个 3D
B-spline
曲面并在各种参数坐标下对其进行多次采样。 The nearest solution I found was to use bisplev
, which expects a tck
input calculated by bsplprep
.我找到的最接近的解决方案是使用
bisplev
,它需要由bsplprep
计算的tck
输入。 Unfortunately i cannot use that tck
component because it produces a surface that passes through the control points, while what I want is a surfaces computed in the B-spline
basis
.不幸的是,我无法使用该
tck
组件,因为它产生了一个通过控制点的曲面,而我想要的是在B-spline
basis
计算的曲面。 So I manually construct the tck
input bsplev
can use to produce the desired surface.所以我手动构建了
tck
输入bsplev
可以用来生成所需的表面。
Unfortunately, I can't figure out how to do this without using 2 nested loops: 1 for each uv
query, and one for each spacial component.不幸的是,如果不使用 2 个嵌套循环,我无法弄清楚如何做到这一点:每个
uv
查询 1 个,每个空间组件一个。 The latter is acceptable but the former is very slow when dealing with very large query arrays.后者是可以接受的,但前者在处理非常大的查询数组时非常慢。
Here's the code:这是代码:
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
Test:测试:
# 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
Speed test:速度测试:
import cProfile
cProfile.run('bivariate_bspline(cv,u,v,uCount,vCount,uDegree,vDegree)') # 0.929 seconds
So nearly 1 sec for 10k samples, where the bisplev
call takes the lion's share of computation time because it's being called 10k times for each space component.因此对于 10k 个样本,将近 1 秒,其中
bisplev
调用占用了大部分计算时间,因为每个空间组件都调用了 10k 次。
I did try to replace the for j in xrange(u.shape[0]):
loop with a single bisplev
call giving it the u and v arrays in one go, but that raises a ValueError: Invalid input data
at scipy\\interpolate\\_fitpack_impl.py", line 1048, in bisplev
.我确实尝试将
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
。
Is there a way to get rid both, or at minimum the uv
query loop and do all the uv
queries in a single vectorized operation?有没有办法摆脱两者,或者至少摆脱
uv
查询循环并在单个矢量化操作中执行所有uv
查询?
Short answer: replace简答:替换
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)
with和
for i in xrange(cv.shape[1]):
position[:, i] = si.dfitpack.bispeu(u_kv, v_kv, cv[:,i], uDegree, vDegree, u, v)[0]
bisplev
does accept arrays as si.bisplev(u, v, tck)
, but it interprets them as defining an xy-grid. bisplev
确实接受数组为si.bisplev(u, v, tck)
,但它将它们解释为定义一个 xy 网格。 Hence, u
and v
must be sorted in ascending order, and evaluation will be performed on all pairs (u[j], v[k])
, the output being a 2D array of values.因此,
u
和v
必须按升序排序,并且将对所有对(u[j], v[k])
执行评估,输出是值的二维数组。 This is not what you want;这不是你想要的; squaring the number of evaluations is probably bad, and it's not going to be easy to extract the values you actually want from the 2D array returned (they are not necessarily on the diagonal, since your u, v were not sorted to begin with).
求值次数的平方可能不好,并且从返回的二维数组中提取您实际想要的值并不容易(它们不一定在对角线上,因为您的 u、v 没有开始排序)。
But the call method of SmoothBivariateSpline includes a boolean parameter grid
, setting which to False makes it just evaluate the spline at (u[j], v[j])
pairs.但是SmoothBivariateSpline的call 方法包括一个布尔参数
grid
,将 which 设置为 False 使其仅在(u[j], v[j])
对处评估样条。 The vectors u, v no longer need to be sorted, but now they must be of the same size.向量 u、v 不再需要排序,但现在它们必须具有相同的大小。
But you are preparing your own tck
.但是您正在准备自己的
tck
。 This presents two approaches: seek to instantiate SmoothBivariateSpline
with hand-made tck;这提供了两种方法:
SmoothBivariateSpline
使用手工 tck 实例化SmoothBivariateSpline
; or read the source of its call method and do what it does when the parameter grid
is set to False.或者读取它的调用方法的源代码并在参数
grid
设置为 False 时执行它的操作。 I went with the latter approach.我采用了后一种方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.