我需要构造一个3D B-spline曲面,并在各种参数坐标下对其进行多次采样。我找到的最接近的解决方案是使用bisplev,它期望由tck计算的bsplprep输入。不幸的是,我不能使用该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查询?

最佳答案

简短答案:更换

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

但是call method of SmoothBivariateSpline包含一个布尔参数grid,将其设置为False会使它仅对(u[j], v[j])对中的样条曲线求值。向量u,v不再需要排序,但是现在它们的大小必须相同。

但是您正在准备自己的tck。这提供了两种方法:尝试使用手工tck实例化SmoothBivariateSpline;或读取its call method的来源,并执行参数grid设置为False时的操作。我采用了后一种方法。

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

10-09 13:26