繁体   English   中英

在 B 样条基础上查询双变量样条上的多个点

[英]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 网格。 因此, uv必须按升序排序,并且将对所有对(u[j], v[k])执行评估,输出是值的二维数组。 这不是你想要的; 求值次数的平方可能不好,并且从返回的二维数组中提取您实际想要的值并不容易(它们不一定在对角线上,因为您的 u、v 没有开始排序)。

但是SmoothBivariateSplinecall 方法包括一个布尔参数grid ,将 which 设置为 False 使其仅在(u[j], v[j])对处评估样条。 向量 u、v 不再需要排序,但现在它们必须具有相同的大小。

但是您正在准备自己的tck 这提供了两种方法: SmoothBivariateSpline使用手工 tck 实例化SmoothBivariateSpline 或者读取它的调用方法的源代码并在参数grid设置为 False 时执行它的操作。 我采用了后一种方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM