非有理 B 样条曲线插值方法——给定点数据的全局曲线插值(参数化插值方法)

一、B 样条基函数的定义和性质

1.1、B 样条基函数的定义

U = { u 0 , u 1 , ⋯   , u m } U=\{u_0,u_1,\cdots,u_m\} U={u0,u1,,um} 是一个单调不减的实数序列,即 u i ≤ u i + 1 , i = 0 , 1 , ⋯   , m − 1 u_i\leq u_{i+1},i=0,1,\cdots,m-1 uiui+1,i=0,1,,m1。其中, u i u_i ui 称为节点 U U U 称为节点矢量,用 N i , p ( u ) N_{i,p}(u) Ni,p(u) 表示第 i i i p p p 次( p + 1 p+1 p+1 阶)B 样条基函数,其定义为(也称为 Cox-deBoor 公式
N i , 0 ( u ) = { 1 , u i ≤ u < u i + 1 , 0 , e l s e N i , p ( u ) = u − u i u i + p − u i N i , p − 1 ( u ) + u i + p + 1 − u u i + p + 1 − u i + 1 N i + 1 , p − 1 ( u ) (1) \begin{aligned} N_{i,0}(u)&=\begin{cases}1,\quad &u_i\leq u< u_{i+1},\\ 0,&else \end{cases}\\[3ex] N_{i,p}(u)&=\frac{u-u_i}{u_{i+p}-u_i}N_{i,p-1}(u)+\frac{u_{i+p+1}-u}{u_{i+p+1}-u_{i+1}}N_{i+1,p-1}(u)\tag{1} \end{aligned} Ni,0(u)Ni,p(u)={1,0,uiu<ui+1,else=ui+puiuuiNi,p1(u)+ui+p+1ui+1ui+p+1uNi+1,p1(u)(1)

由上述定义式可知:

  1. 事先指定节点矢量 U U U 和次数 p p p, 即可计算出对应的一组基函数;
  2. 半开区间 u ∈ [ u i , u i + 1 ) u\in [u_i,u_{i+1}) u[ui,ui+1) 称为第 i i i 个节点区间,它的长度可以为零,因为相邻节点可以是相同的;
  3. 计算 p p p 次基函数的过程生成一个如下形式的三角形阵列:(通常将 N i , p ( u ) N_{i,p}(u) Ni,p(u) 写为 N i , p N_{i,p} Ni,p
    N 0 , 0 N 0 , 1 N 1 , 0 N 0 , 2 N 1 , 1 N 0 , 3 N 2 , 0 N 1 , 2 N 2 , 1 N 1 , 3 N 3 , 0     N 2 , 2 ⋮ N 3 , 1 ⋮ N 4 , 0 ⋮    N_{0,0}\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\\[1ex] N_{0,1}\quad\quad\quad\quad\quad\quad\\[1ex] N_{1,0}\quad\quad\quad\quad N_{0,2}\quad\quad\quad\quad\quad\quad\\[1ex] \quad\quad\quad\quad\quad\quad N_{1,1}\quad\quad\quad\quad N_{0,3}\quad\quad\quad\quad\quad\quad\\[1ex] N_{2,0}\quad\quad\quad\quad N_{1,2}\quad\quad\quad\quad\quad\quad\\[1ex] \quad\quad\quad\quad\quad\quad N_{2,1}\quad\quad\quad\quad N_{1,3}\quad\quad\quad\quad\quad\quad\\[1ex] N_{3,0}\ \ \ \quad\quad\quad N_{2,2}\quad\quad\vdots\quad\quad\quad\quad\\[1ex] \quad\quad\quad\quad\quad\quad N_{3,1}\quad\quad\vdots\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad \\[1ex] N_{4,0}\quad\quad\vdots\quad\quad\quad\quad\quad\quad\quad\quad\quad\ \ N0,0N0,1N1,0N0,2N1,1N0,3N2,0N1,2N2,1N1,3N3,0   N2,2N3,1N4,0  

例1. 令 U = { u 0 = 0 , u 1 = 0 , u 2 = 0 , u 3 = 1 , u 4 = 1 , u 5 = 1 } , p = 2 U=\{u_0=0,u_1=0,u_2=0,u_3=1,u_4=1,u_5=1\},p=2 U={u0=0,u1=0,u2=0,u3=1,u4=1,u5=1},p=2,我们分别计算 0 次、1 次和 2 次的 B 样条基函数。
N 0 , 0 = N 1 , 0 = 0 , − ∞ < u < ∞ N 2 , 0 = { 1 , 0 ≤ u < 1 0 , 其他 N 3 , 0 = N 4 , 0 = 0 , − ∞ < u < ∞ N 0 , 1 = u − 0 0 − 0 N 0 , 0 + 0 − u 0 − 0 N 1 , 0 = 0 , − ∞ < u < ∞ N 1 , 1 = u − 0 0 − 0 N 1 , 0 + 1 − u 1 − 0 N 2 , 0 = { 1 − u , 0 ≤ u < 1 0 , 其他 N 2 , 1 = u − 0 1 − 0 N 2 , 0 + 1 − u 1 − 1 N 3 , 0 = { u , 0 ≤ u < 1 0 , 其他 N 3 , 1 = u − 1 1 − 1 N 3 , 0 + 1 − u 1 − 1 N 4 , 0 = 0 , − ∞ < u < ∞ N 0 , 2 = u − 0 0 − 0 N 0 , 1 + 1 − u 1 − 0 N 1 , 1 = { ( 1 − u ) 2 , 0 ≤ u < 1 0 , 其他 N 1 , 2 = u − 0 1 − 0 N 1 , 1 + 1 − u 1 − 0 N 2 , 1 = { 2 u ( 1 − u ) , 0 ≤ u < 1 0 , 其他 N 2 , 2 = u − 0 1 − 0 N 2 , 1 + 1 − u 1 − 1 N 3 , 1 = { u 2 , 0 ≤ u < 1 0 , 其他 \begin{aligned} &N_{0,0}=N_{1,0}=0,\quad -\infty<u<\infty\\[2ex] &N_{2,0}=\begin{cases}1,\quad &0\leq u<1\\[2ex] 0,&其他 \end{cases}\\[2ex] &N_{3,0}=N_{4,0}=0,\quad -\infty<u<\infty\\[2ex] &N_{0,1}=\frac{u-0}{0-0}N_{0,0}+\frac{0-u}{0-0}N_{1,0}=0,\quad -\infty<u<\infty\\[2ex] &N_{1,1}=\frac{u-0}{0-0}N_{1,0}+\frac{1-u}{1-0}N_{2,0}=\begin{cases}1-u,\quad &0\leq u<1\\[2ex]0,&其他\end{cases}\\[2ex] &N_{2,1}=\frac{u-0}{1-0}N_{2,0}+\frac{1-u}{1-1}N_{3,0}=\begin{cases}u,\quad &0\leq u<1\\[2ex]0,&其他\end{cases}\\[2ex] &N_{3,1}=\frac{u-1}{1-1}N_{3,0}+\frac{1-u}{1-1}N_{4,0}=0,\quad -\infty<u<\infty\\[2ex] &N_{0,2}=\frac{u-0}{0-0}N_{0,1}+\frac{1-u}{1-0}N_{1,1}=\begin{cases}(1-u)^2,\quad &0\leq u<1\\[2ex]0,&其他\end{cases}\\[2ex] &N_{1,2}=\frac{u-0}{1-0}N_{1,1}+\frac{1-u}{1-0}N_{2,1}=\begin{cases}2u(1-u),\quad &0\leq u<1\\[2ex]0,&其他\end{cases}\\[2ex] &N_{2,2}=\frac{u-0}{1-0}N_{2,1}+\frac{1-u}{1-1}N_{3,1}=\begin{cases}u^2,\quad &0\leq u<1\\[2ex]0,&其他\end{cases}\\[2ex] \end{aligned} N0,0=N1,0=0,<u<N2,0= 1,0,0u<1其他N3,0=N4,0=0,<u<N0,1=00u0N0,0+000uN1,0=0,<u<N1,1=00u0N1,0+101uN2,0= 1u,0,0u<1其他N2,1=10u0N2,0+111uN3,0= u,0,0u<1其他N3,1=11u1N3,0+111uN4,0=0,<u<N0,2=00u0N0,1+101uN1,1= (1u)2,0,0u<1其他N1,2=10u0N1,1+101uN2,1= 2u(1u),0,0u<1其他N2,2=10u0N2,1+111uN3,1= u2,0,0u<1其他

1.2、B 样条基函数的性质

P1 局部支撑性

如何 u ∉ [ u i , u i + p + 1 ] u\notin[u_i,u_{i+p+1}] u/[ui,ui+p+1],则 N i , p ( u ) = 0 N_{i,p}(u)=0 Ni,p(u)=0,如下图所示, N 1 , 3 N_{1,3} N1,3 N 1 , 0 , N 2 , 0 , N 3 , 0 N_{1,0},N_{2,0},N_{3,0} N1,0,N2,0,N3,0 N 4 , 0 N_{4,0} N4,0 的线性组合,因此 N 1 , 3 N_{1,3} N1,3 仅在 u ∈ [ u 1 , u 5 ) u\in[u_1,u_5) u[u1,u5) 上非零。
在这里插入图片描述

P2

在任意给定的节点区间 [ u i , u j ) [u_i,u_j) [ui,uj) 内,最多 p + 1 p+1 p+1 N i , p N_{i,p} Ni,p 是非零的,它们是 N i − p , ⋯   , N i , p N_{i-p},\cdots,N_{i,p} Nip,,Ni,p

P3 非负性

对于所有 i , p i,p i,p u u u,有 N i , p ( u ) ≥ 0 N_{i,p}(u)\geq 0 Ni,p(u)0

P4 规范性

对于任意的节点区间 [ u i , u i + 1 ) [u_i,u_{i+1}) [ui,ui+1),当 u ∈ [ u i , u i + 1 ) u\in[u_i,u_{i+1}) u[ui,ui+1) 时, ∑ j = i − p i N j , p = 1 \sum_{j=i-p}^iN_{j,p}=1 j=ipiNj,p=1,即同一次数的基函数和为 1。

P5 可微性

在节点区间内部, N i , p ( u ) N_{i,p}(u) Ni,p(u) 是无限次可微的(在每个节点区间内部,它是一个多项式)。在节点处 N i , p ( u ) N_{i,p}(u) Ni,p(u) p − k p-k pk 次连续可微的,其中 k k k 是节点的重复度。因此,增加次数将提高曲线的连续性,而增加节点的重复度则使连续性降低。

P6

非周期节点矢量具有如下形式:
U = { a , ⋯   , a ⏟ p + 1 , u p + 1 , ⋯   , u m − p − 1 , b , ⋯   , b ⏟ p + 1 } (2) U=\{\underbrace{a,\cdots,a}_{p+1},u_{p+1},\cdots,u_{m-p-1},\underbrace{b,\cdots,b}_{p+1}\}\tag{2} U={p+1 a,,a,up+1,,ump1,p+1 b,,b}(2)
定义在非周期节点矢量上,设节点个数是 m + 1 m+1 m+1,则存在 n + 1 n+1 n+1 个基函数(每个控制点对应一个基函数),这里 n = m − p − 1 n=m-p-1 n=mp1;并且, N 0 , p ( a ) = 1 , N n , p ( b ) = 1 N_{0,p}(a)=1,N_{n,p}(b)=1 N0,p(a)=1Nn,p(b)=1 N i , p ( a ) = 0 , i ≠ 0 N_{i,p}(a)=0,i\neq 0 Ni,p(a)=0,i=0 N i , p ( b ) = 0 , i ≠ n N_{i,p}(b)=0,i\neq n Ni,p(b)=0,i=n

二、B 样条基函数的计算

U = { u 0 , u 1 , ⋯   , u m } U=\{u_0,u_1,\cdots,u_m\} U={u0,u1,,um} 为形如(2)式的节点矢量,假设我们感兴趣的是 p p p 次基函数,并假设 u u u 是固定的, u ∈ [ u i , u i + 1 ) u\in[u_i,u_{i+1}) u[ui,ui+1)。我们给出以下两个算法,分别用来计算:

  • 计算 u u u 所属的节点区间的下标 i i i
  • 基于(1)式计算 N i − p , p , ⋯   , N i , p N_{i-p,p},\cdots,N_{i,p} Nip,p,,Ni,p

由性质 P2 和假设 u ∈ [ u i , u i + 1 ) u\in[u_i,u_{i+1}) u[ui,ui+1),我们可以只把注意力集中在计算函数 N i − p , p , ⋯   , N i , p N_{i-p,p},\cdots,N_{i,p} Nip,p,,Ni,p 上,因为其他所有函数都为 0,没必要费力去计算它们。因此,计算的第一步就是确定 u u u 所属的节点区间,这可以对节点矢量使用二分法搜索得到。由于我们使用的区间形式为 u ∈ [ u i , u i + 1 ) u\in[u_i,u_{i+1}) u[ui,ui+1),在计算基函数时需要特殊考虑的一个情况是 u = u m u=u_m u=um 的情形。这时,我们直接将节点区间的下标设置为 n = m − p − 1 n=m-p-1 n=mp1

def FindSpan(p, u, U):
    """
    确定参数 u 所在的节点区间的下标
    :param p: 基函数次数
    :param u: 固定点
    :param U: 节点矢量
    :return: u 所属的节点区间的下标
    """
    m = len(U) - 1
    start, end = p, m
    if u == U[-1]:
        return m - p - 1
    while end != start + 1 and end != start:
        mid_index = (start + end) // 2
        if U[mid_index] == u:
            return mid_index
        if u > U[mid_index]:
            start = mid_index
        else:
            end = mid_index
    return start

现在我们来考虑第二个算法。假设 u u u 在第 i i i 个节点区间内,计算所有非零 B 样条基函数的过程可形成如下的一个倒置的三角形:
在这里插入图片描述
我们利用(1)式写出二次基函数的一般形式,有
N i − 2 , 2 ( u ) = u − u i − 2 u i − u i − 2 N i − 2 , 1 ( u ) + u i + 1 − u u i + 1 − u i − 1 N i − 1 , 1 ( u ) N i − 1 , 2 ( u ) = u − u i − 1 u i + 1 − u i − 1 N i − 1 , 1 ( u ) + u i + 2 − u u i + 2 − u i N i , 1 ( u ) N i , 2 ( u ) = u − u i u i + 2 − u i N i , 1 ( u ) + u i + 3 − u u i + 3 − u i + 1 N i + 1 , 1 ( u ) (3) \begin{aligned} &N_{i-2,2}(u)=\frac{u-u_{i-2}}{u_i-u_{i-2}}N_{i-2,1}(u)+\frac{u_{i+1}-u}{u_{i+1}-u_{i-1}}N_{i-1,1}(u)\\[2ex] &N_{i-1,2}(u)=\frac{u-u_{i-1}}{u_{i+1}-u_{i-1}}N_{i-1,1}(u)+\frac{u_{i+2}-u}{u_{i+2}-u_{i}}N_{i,1}(u)\\[2ex] &N_{i,2}(u)=\frac{u-u_{i}}{u_{i+2}-u_{i}}N_{i,1}(u)+\frac{u_{i+3}-u}{u_{i+3}-u_{i+1}}N_{i+1,1}(u)\\[2ex] \end{aligned}\tag{3} Ni2,2(u)=uiui2uui2Ni2,1(u)+ui+1ui1ui+1uNi1,1(u)Ni1,2(u)=ui+1ui1uui1Ni1,1(u)+ui+2uiui+2uNi,1(u)Ni,2(u)=ui+2uiuuiNi,1(u)+ui+3ui+1ui+3uNi+1,1(u)(3)

我们引入符号
l e f t [   j   ] = u − u i + 1 − j , r i g h t [   j   ] = u i + j − u \mathrm{left}[\ j\ ]=u-u_{i+1-j},\quad \mathrm{right}[\ j\ ]=u_{i+j}-u left[ j ]=uui+1jright[ j ]=ui+ju

那么(3)式可以写为
N i − 2 , 2 ( u ) = l e f t [   3   ] r i g h t [   0   ] + l e f t [   3   ] N i − 2 , 1 ( u ) + r i g h t [   1   ] r i g h t [   1   ] + l e f t [   2   ] N i − 1 , 1 ( u ) N i − 1 , 2 ( u ) = l e f t [   2   ] r i g h t [   1   ] + l e f t [   2   ] N i − 1 , 1 ( u ) + r i g h t [   2   ] r i g h t [   2   ] + l e f t [   1   ] N i , 1 ( u ) N i , 2 ( u ) = l e f t [   1   ] r i g h t [   2   ] + l e f t [   1   ] N i , 1 ( u ) + r i g h t [   3   ] r i g h t [   3   ] + l e f t [   0   ] N i + 1 , 1 ( u ) \begin{aligned} &N_{i-2,2}(u)=\frac{\mathrm{left}[\ 3\ ]}{\mathrm{right}[\ 0\ ]+\mathrm{left}[\ 3\ ]}N_{i-2,1}(u)+\frac{\mathrm{right}[\ 1\ ]}{\mathrm{right}[\ 1\ ]+\mathrm{left}[\ 2\ ]}N_{i-1,1}(u)\\[2ex] &N_{i-1,2}(u)=\frac{\mathrm{left}[\ 2\ ]}{\mathrm{right}[\ 1\ ]+\mathrm{left}[\ 2\ ]}N_{i-1,1}(u)+\frac{\mathrm{right}[\ 2\ ]}{\mathrm{right}[\ 2\ ]+\mathrm{left}[\ 1\ ]}N_{i,1}(u)\\[2ex] &N_{i,2}(u)=\frac{\mathrm{left}[\ 1\ ]}{\mathrm{right}[\ 2\ ]+\mathrm{left}[\ 1\ ]}N_{i,1}(u)+\frac{\mathrm{right}[\ 3\ ]}{\mathrm{right}[\ 3\ ]+\mathrm{left}[\ 0\ ]}N_{i+1,1}(u)\\[2ex] \end{aligned} Ni2,2(u)=right[ 0 ]+left[ 3 ]left[ 3 ]Ni2,1(u)+right[ 1 ]+left[ 2 ]right[ 1 ]Ni1,1(u)Ni1,2(u)=right[ 1 ]+left[ 2 ]left[ 2 ]Ni1,1(u)+right[ 2 ]+left[ 1 ]right[ 2 ]Ni,1(u)Ni,2(u)=right[ 2 ]+left[ 1 ]left[ 1 ]Ni,1(u)+right[ 3 ]+left[ 0 ]right[ 3 ]Ni+1,1(u)

基于以上观察设计算法,用来计算所有非零 B 样条基函数。

def BaseFunction(p, u, U):
    """
    计算所有非零 B 样条基函数的值
    :param u: 固定值
    :param p: 基函数次数
    :param U: 节点矢量
    :return: 基函数值数组
    """
    i = FindSpan(p, u, U)
    N = np.zeros(p + 1)
    N[0] = 1.0
    left = np.zeros(p + 1)
    right = np.zeros(p + 1)
    for j in range(1, p + 1):
        left[j] = u - U[i + 1 - j]
        right[j] = U[i + j] - u
        saved = 0.0
        for r in range(j):
            if (right[r + 1] + left[j - r]) == 0:
                temp = 0.0
            else:
                temp = N[r] / (right[r + 1] + left[j - r])
            N[r] = saved + right[r + 1] * temp
            saved = left[j - r] * temp
        N[j] = saved
    return N

三、B 样条曲线的定义和性质

p p p 次 B 样条曲线的定义为
C ( u ) = ∑ i = 0 n N i , p ( u ) P i , a ≤ u ≤ b (1) \pmb C(u)=\sum_{i=0}^nN_{i,p}(u)\pmb P_i,\quad a\leq u\leq b\tag{1} C(u)=i=0nNi,p(u)Pi,aub(1)

这里 P i \pmb P_i Pi 是曲线的控制点 { N i , p ( u ) } \{N_{i,p}(u)\} {Ni,p(u)} 是定义在非周期(并且非均匀)节点矢量
U = ( a , ⋯   , a ⏟ p + 1 , u p + 1 , ⋯   , u m − p − 1 , b , ⋯   , b ⏟ p + 1 ) U=\bigg(\underbrace{a,\cdots,a}_{p+1},u_{p+1},\cdots,u_{m-p-1},\underbrace{b,\cdots,b}_{p+1}\bigg) U=(p+1 a,,a,up+1,,ump1,p+1 b,,b)

(包含 m + 1 m+1 m+1 个节点)上的 p p p 次 B 样条基函数。除非特别声明,通常取 a = 0 , b = 1 a=0,b=1 a=0,b=1。由 P i \pmb P_i Pi 构成的多边形称为控制多边形

对于固定的 u u u 值,计算 B 样条曲线上的对应点需要三步:

  1. 找到 u u u 所在的节点区间;
  2. 计算非零的基函数;
  3. 将非零基函数的值与相应的控制点相乘,再求和。

. U = { 0 , 0 , 0 , 1 , 2 , 3 , 4 , 4 , 5 , 5 , 5 } ,    u = 5 2 ,    p = 2 \quad U=\{0,0,0,1,2,3,4,4,5,5,5\},\ \ u=\dfrac{5}{2},\ \ p=2 U={0,0,0,1,2,3,4,4,5,5,5},  u=25,  p=2
此时, u ∈ [ u 4 , u 5 ) u\in[u_4,u_5) u[u4,u5),则
N 2 , 2 ( 5 2 ) = 1 8 , N 3 , 2 ( 5 2 ) = 6 8 , N 4 , 2 ( 5 2 ) = 1 8 N_{2,2}\Big(\dfrac{5}{2}\Big)=\frac{1}{8},\quad N_{3,2}\Big(\dfrac{5}{2}\Big)=\frac{6}{8},\quad N_{4,2}\Big(\dfrac{5}{2}\Big)=\frac{1}{8} N2,2(25)=81,N3,2(25)=86,N4,2(25)=81

与相应的控制点相乘并求和得
C ( 5 2 ) = 1 8 P 2 + 6 8 P 3 + 1 8 P 4 \pmb C\Big(\dfrac{5}{2}\Big)=\frac{1}{8}\pmb P_2+\frac{6}{8}\pmb P_3+\frac{1}{8}\pmb P_4 C(25)=81P2+86P3+81P4

def CurvePoint(p, U, P, u):
    """
    计算 B 样条曲线上的点
    :param p: 基函数次数
    :param U: 节点矢量
    :param P: 控制点
    :param u: 固定值
    :return: 固定值 u 在 B 样条曲线上的对应值
    """
    N = BaseFunction(p, u, U)
    span = FindSpan(p, u, U)
    C = 0.0
    for i in range(p+1):
        C = C + N[i] * P[span - p + i]
    return C

四、给定点数据的全局曲线插值

假设定义一组数据点 { Q k } , k = 0 , 1 , ⋯   , n \{\pmb Q_k\},k=0,1,\cdots,n {Qk},k=0,1,,n,我们想要用一条 p p p 次非有理 B 样条曲线插值于这些点。如果我们为每一点 Q k \pmb Q_k Qk 指定了一个参数值 u ‾ k \overline{u}_k uk,并且选定了一个合适的节点矢量 U = { u 0 , u 1 , ⋯   , u m } U=\{u_0,u_1,\cdots,u_m\} U={u0,u1,,um},我们就可以建立一个系数矩阵为 ( n + 1 ) × ( n + 1 ) (n+1)\times(n+1) (n+1)×(n+1) 的线性方程组
Q k = C ( u ‾ k ) = ∑ i = 0 n N i , p ( u ‾ k ) P i \pmb Q_k=\pmb C(\overline{u}_k)=\sum_{i=0}^nN_{i,p}(\overline{u}_k)\pmb P_i Qk=C(uk)=i=0nNi,p(uk)Pi

n + 1 n+1 n+1 个控制点 P i \pmb P_i Pi 是未知量,令 r r r Q i \pmb Q_i Qi 的坐标分量的个数(一般是 2、3 或 4),则存在 r r r 个线性方程组,它们具有相同的系数矩阵,每个方程组的解对应于一个坐标分量的设置。

剩下来的问题是如何选择 Q k \pmb Q_k Qk 对应的参数值 u ‾ k \overline{u}_k uk 以及节点矢量 U U U,这将影响到曲线的形状和参数化

2.1、参数值 u ‾ k \overline{u}_k uk 的选择

假定参数都在 u ∈ [ 0 , 1 ) u\in[0,1) u[0,1) 的范围内,通常有三种选择 u ‾ k \overline{u}_k uk 的方法:

1. 均匀参数化
u ‾ 0 = 0 , u ‾ n = 1 , u ‾ k = k n , k = 1 , 2 , ⋯   , n − 1 \overline{u}_0=0,\quad\overline{u}_n=1,\quad\overline{u}_k=\frac{k}{n},\quad k=1,2,\cdots,n-1 u0=0,un=1,uk=nk,k=1,2,,n1

不推荐采用这种方法,因为当数据点分布不均匀时它会产生很奇怪的形状(例如打圈自交)。

2. 弦长参数化:令 d d d 为总弦长
d = ∑ k = 1 n ∣ Q k − Q k − 1 ∣ d=\sum_{k=1}^n\mid\pmb Q_k-\pmb Q_{k-1}\mid d=k=1nQkQk1

u ‾ 0 = 0 , u ‾ n = 1 \overline{u}_0=0,\overline{u}_n=1 u0=0,un=1
u ‾ k = u ‾ k − 1 + ∣ Q k − Q k − 1 ∣ d , k = 1 , 2 , ⋯   , n − 1 \overline{u}_k=\overline{u}_{k-1}+\frac{\mid\pmb Q_k-\pmb Q_{k-1}\mid}{d},\quad k=1,2,\cdots,n-1 uk=uk1+dQkQk1,k=1,2,,n1

这是目前最常用的方法,并且一般用它就足够了

3. 向心参数化:令
d = ∑ k = 1 n ∣ Q k − Q k − 1 ∣ d=\sum_{k=1}^n\sqrt{\mid\pmb Q_k-\pmb Q_{k-1}\mid} d=k=1nQkQk1

u ‾ 0 = 0 , u ‾ n = 1 \overline{u}_0=0,\overline{u}_n=1 u0=0,un=1
u ‾ k = u ‾ k − 1 + ∣ Q k − Q k − 1 ∣ d , k = 1 , 2 , ⋯   , n − 1 \overline{u}_k=\overline{u}_{k-1}+\frac{\sqrt{\mid\pmb Q_k-\pmb Q_{k-1}\mid}}{d},\quad k=1,2,\cdots,n-1 uk=uk1+dQkQk1 ,k=1,2,,n1

当数据点急转弯变化时,这个方法能得到比弦长参数化更好的结果。

2.2、节点矢量 U U U 的选择

1. 等距分布:即
u 0 = ⋯ = u p = 0 , u m − p = ⋯ = u m = 1 u j + p = j n − p + 1 , j = 1 , 2 , ⋯   , n − p \begin{aligned} &u_0=\cdots=u_p=0,\quad u_{m-p}=\cdots=u_m=1\\[1ex] &u_{j+p}=\frac{j}{n-p+1},\quad j=1,2,\cdots,n-p \end{aligned} u0==up=0,ump==um=1uj+p=np+1j,j=1,2,,np

不推荐采用这种方法,因为它可能导致产生奇异方程组。

2. 平均值分布
u 0 = ⋯ = u p = 0 , u m − p = ⋯ = u m = 1 u j + p = 1 p ∑ i = j j + p − 1 u ‾ i , j = 1 , 2 , ⋯   , n − p \begin{aligned} &u_0=\cdots=u_p=0,\quad u_{m-p}=\cdots=u_m=1\\[1ex] &u_{j+p}=\frac{1}{p}\sum_{i=j}^{j+p-1}\overline{u}_i,\quad j=1,2,\cdots,n-p \end{aligned} u0==up=0,ump==um=1uj+p=p1i=jj+p1ui,j=1,2,,np

这种方法节点矢量能很好地反映 u ‾ k \overline{u}_k uk 的分布情况。

def NodeVector(p, Q):
    """
    准均匀B样条的节点向量计算
    :param p: 基函数次数
    :param Q: 数据点数组
    :return: 节点矢量
    """
    n = Q.shape[1] - 1
    d = 0.0
    for i in range(1, n + 1):  # 计算总弦长
        d += np.linalg.norm(Q[:, i] - Q[:, i - 1])
    Q_parameterize = np.zeros(n + 1)
    Q_parameterize[0] = 0
    Q_parameterize[-1] = 1
    for i in range(1, n):
        Q_parameterize[i] = Q_parameterize[i - 1] + np.linalg.norm(Q[:, i] - Q[:, i - 1]) / d

    m = n + p + 1
    U = np.zeros(m + 1)
    U[m - p:m + 1] = 1
    for i in range(1, n - p + 1):
        U[i + p] = np.sum(Q_parameterize[i:i + p]) / p
    return Q_parameterize, U

五、案例应用

{ Q k } = { ( 0 , 0 ) , ( 3 , 4 ) , ( − 1 , 4 ) , ( − 4 , 0 ) , ( − 4 , − 3 ) , ( − 2 , − 5 ) , ( − 1 , − 7 ) } \{\pmb Q_k\}=\{(0,0),(3,4),(-1,4),(-4,0),(-4,-3),(-2,-5),(-1,-7)\} {Qk}={(0,0),(3,4),(1,4),(4,0),(4,3),(2,5),(1,7)},用三次曲线插值这些点。

完整代码如下

import numpy as np
import matplotlib.pyplot as plt


def FindSpan(p, u, U):
    """
    确定参数 u 所在的节点区间的下标
    :param p: 基函数次数
    :param u: 固定点
    :param U: 节点矢量 行向量
    :return: 节点区间的下标
    """
    m = len(U) - 1
    start, end = p, m
    if u == U[-1]:
        return m - p - 1
    while end != start + 1 and end != start:
        mid_index = (start + end) // 2
        if U[mid_index] == u:
            return mid_index
        if u > U[mid_index]:
            start = mid_index
        else:
            end = mid_index
    return start


def BaseFunction(p, u, U):
    """
    计算所有非零 B 样条基函数的值
    :param u: 固定值
    :param p: 基函数次数
    :param U: 节点矢量
    :return: 基函数值数组
    """
    i = FindSpan(p, u, U)
    N = np.zeros(p + 1)
    N[0] = 1.0
    left = np.zeros(p + 1)
    right = np.zeros(p + 1)
    for j in range(1, p + 1):
        left[j] = u - U[i + 1 - j]
        right[j] = U[i + j] - u
        saved = 0.0
        for r in range(j):
            if (right[r + 1] + left[j - r]) == 0:
                temp = 0.0
            else:
                temp = N[r] / (right[r + 1] + left[j - r])
            N[r] = saved + right[r + 1] * temp
            saved = left[j - r] * temp
        N[j] = saved
    return N


def NodeVector(p, Q):
    """
    准均匀B样条的节点向量计算
    :param p: 基函数次数
    :param Q: 数据点数组
    :return: 节点矢量
    """
    n = Q.shape[1] - 1
    d = 0.0
    for i in range(1, n + 1):  # 计算总弦长
        d += np.linalg.norm(Q[:, i] - Q[:, i - 1])
    Q_parameterize = np.zeros(n + 1)
    Q_parameterize[0] = 0
    Q_parameterize[-1] = 1
    for i in range(1, n):
        Q_parameterize[i] = Q_parameterize[i - 1] + np.linalg.norm(Q[:, i] - Q[:, i - 1]) / d

    m = n + p + 1
    U = np.zeros(m + 1)
    U[m - p:m + 1] = 1
    for i in range(1, n - p + 1):
        U[i + p] = np.sum(Q_parameterize[i:i + p]) / p
    return Q_parameterize, U


def CurvePoint(p, U, P, u):
    """
    计算 B 样条曲线上的点
    :param p: 基函数次数
    :param U: 节点矢量
    :param P: 控制点
    :param u: 固定值
    :return: 固定值 u 在 B 样条曲线上的对应值
    """
    N = BaseFunction(p, u, U)
    span = FindSpan(p, u, U)
    C = 0.0
    for i in range(p+1):
        C = C + N[i] * P[span - p + i]
    return C


if __name__ == '__main__':
    Q = np.array([[0, 3, -1, -4, -4, -2, -1], [0, 4, 4, 0, -3, -5, -7]])
    Q_parameterize, NodeVector = NodeVector(3, Q)
    A = np.zeros((Q.shape[1], Q.shape[1]))
    for i in range(Q.shape[1]):
        temp = BaseFunction(p=3, u=Q_parameterize[i], U=NodeVector)
        span = FindSpan(p=3, u=Q_parameterize[i], U=NodeVector)
        A[i, span - 3:span + 1] = temp

    Px = np.linalg.solve(A, Q[0, :])
    Py = np.linalg.solve(A, Q[1, :])
    print(A)
    print(Q[0, :])
    print(Q[1, :])

    u = np.linspace(0, 1, 1000)
    x = np.zeros(1000)
    y = np.zeros(1000)
    for i in range(len(u)):
        x[i] = CurvePoint(p=3, U=NodeVector, P=Px, u=u[i])
        y[i] = CurvePoint(p=3, U=NodeVector, P=Py, u=u[i])

    plt.plot(x, y, c='r')
    for i in range(Q.shape[1]):
        plt.plot(Q[0, i], Q[1, i], 'go', markerfacecolor='none')
    plt.show()


在这里插入图片描述

  • 24
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值