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

标签 python numpy scipy

我需要构建一个 3D B-spline表面并在各种参数坐标处对其进行多次采样。我找到的最接近的解决方案是使用 bisplev ,预计为 tck输入由 bsplprep 计算得出。不幸的是我不能使用那个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 call 占用了大部分计算时间,因为每个空间组件都会调用它 10k 次。

我确实尝试更换 for j in xrange(u.shape[0]):循环使用单个 bisplev调用一次给它 u 和 v 数组,但这会引发 ValueError: Invalid input datascipy\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]) 进行评估,输出是一个二维值数组。这不是你想要的;对评估次数进行平方可能很糟糕,并且从返回的 2D 数组中提取您实际想要的值并不容易(它们不一定位于对角线上,因为您的 u, v 一开始就没有排序)。

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

但你正在准备自己的tck 。这提供了两种方法:寻求实例化SmoothBivariateSpline手工制作的tck;或阅读 its call method 的来源并执行参数 grid 时的操作设置为 False。我选择了后一种方法。

关于python - 在 B 样条基础上查询双变量样条上的多个点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46983476/

相关文章:

python - matplotlib 中共享轴方形子图的新 pythonic 样式?

python - 如何在 scipy 中集成一个 numpy 数组?

python - 在 Python 中使用 Marionette/Selenium 的多个 Firefox 实例

python - 为什么在 python 中使用已知数组初始化函数内的参数也会更改数组的值?

python - 在接下来的 10 个字符中查找 python 中的各种字符串重复

python - 在 numpy 2D 矩阵中计算 "holes"

python - 使用 Django Admin 上传文件

python - 生成具有正态分布噪声和均值函数的数据

python - 使用 Numba 的 @jit 导致数学与 Python 中使用的 Numpy 的 float32 不一致

python - scipy:spearmanr返回值的重要性(相关性)