KAN保姆级解析(2)--KAN源码中的BSpline插值

引言

  KAN源码中KANlayer模块涉及到了许多coef2curvecoef2curve操作,这里实际就是在利用Bspline曲线进行插值或者说拟合,以下介绍相关的BSpline插值知识。后一篇中我们将结合相关代码详细解释Bspline是如何融入到KAN的计算过程中的。

Bspline曲线

 对于Bspline曲线的构造我们只需要给定一系列的控制点 p 0 , . . . , p n p_0,...,p_n p0,...,pn(control points)以及节点向量 U = { u 0 , . . , u m + 1 } U=\{u_0,..,u_{m+1}\} U={u0,..,um+1}(knot vector),以及次数 k k k即可进行计算。
 首先,BSpline曲线的基础计算公式是:
C ( u ) = ∑ i = 0 n p i N i , k ( u ) C(u)=\sum_{i=0}^{n} p_iN_{i,k}(u) C(u)=i=0npiNi,k(u)
其中 p i p_{i} pi人为选取或者求取的控制点, N i , k ( t ) N_{i,k}(t) Ni,k(t) k k k次( k + 1 k+1 k+1阶)基函数(这里, k k k次的意思是 N i , k ( t ) N_{i,k}(t) Ni,k(t)中包含了 t t t k k k次项 t k t^k tk,这里我们假定 k k k从0开始取值)。
 在这一公式的基础上我们只需要确定控制点以及基函数,就可以计算给定 t t t时的曲线点位置。后者可以通过Cox-de Boor递归公式来确定:

N i , 0 ( u ) = { 1 i f   u i ≤ u < u i + 1 a n d u i < u i + 1 0 o t h e r w i s e N i , j ( u ) = u − u i u i + j − u i N i , j − 1 ( u ) + u i + j + 1 − u u i + j + 1 − u i + 1 N i + 1 , j − 1 ( u ) , \begin{aligned} &N_{i,0}\left(u\right) \left.=\left\{\begin{matrix}{1}&{\mathrm{if~}u_i\leq u<u_{i+1} \mathrm{and} u_i<u_{i+1}}\\\\{0}&{\mathrm{otherwise}}\\\end{matrix}\right.\right. \\ &N_{i,j}(u) =\frac{u-u_{i}}{u_{i+j}-u_{i}} N_{i,j-1} (u)+\frac{u_{i+j+1}-u}{u_{i+j+1}-u_{i+1}} N_{i+1,j-1} (u), \end{aligned} Ni,0(u)= 10if uiu<ui+1andui<ui+1otherwiseNi,j(u)=ui+juiuuiNi,j1(u)+ui+j+1ui+1ui+j+1uNi+1,j1(u),

节点向量

这里需要注意的是,节点向量的设置是比较有讲究的,假定需要 m + 1 m+1 m+1个节点,通常有以下几个方案

  1. [ 0 , 1 ] [0,1] [0,1]范围内进行划分(通常就是等距);这样形成了open Bspline曲线,不经过控制点折线的第一段和最后一段;
  2. k + 1 k+1 k+1个节点设置为0, u 0 = u 1 = , . . , = u k = 0 u_0=u_1=,..,=u_k=0 u0=u1=,..,=uk=0,中间进行采样(通常就是等距),后 k + 1 k+1 k+1个节点设置为1, u m − k − 1 = , . . = u m = 1 u_{m-k-1}=,..=u_{m}=1 umk1=,..=um=1;这样形成了 clamped B-spline曲线,它会穿过第一个和最后一个控制点,并且和第一段最后一段折线相切;
  3. 重复多个节点。这样形成closed B-spline曲线.

次数、控制点数目和节点数目的关系1

 次数 k k k,控制点数目 n + 1 n+1 n+1和节点数目 m + 1 m+1 m+1需要满足 m = n + k + 1 m=n+k+1 m=n+k+1,也就是说如果给出了5个控制点,要求计算次数为3的B样条曲线,我们需要自行设置长度为9的节点向量。这是因为由Cox-de Boor递归公式我们可以知道计算 N i , k ( u ) N_{i,k}(u) Ni,k(u),我们需要计算 N i , k − 1 ( u ) N_{i,k-1}(u) Ni,k1(u) N i + 1 , p − 1 ( u ) N_{i+1,p-1}(u) Ni+1,p1(u),可以画出如下计算流图:
在这里插入图片描述

从而得到 m = n + k + 1 m=n+k+1 m=n+k+1
通过推导我们也可以进一步得到以下两个性质:

  1. 基函数 N i , k ( u ) N_{i,k}(u) Ni,k(u) 在区间 [ u i , u i + k + 1 ) [u_i, u_{i+k+1}) [ui,ui+k+1) 上非零。换句话说, N i , k ( u ) N_{i,k}(u) Ni,k(u) k + 1 k+1 k+1 个结节区间 [ u i , u i + 1 ) , [ u i + 1 , u i + 2 ) , … , [ u i + k , u i + k + 1 ) [u_i, u_{i+1}), [u_{i+1}, u_{i+2}), \ldots, [u_{i+k}, u_{i+k+1}) [ui,ui+1),[ui+1,ui+2),,[ui+k,ui+k+1) 上非零。
  2. 在任意结节区间 [ u i , u i + 1 ) [u_i, u_{i+1}) [ui,ui+1) 上,最多有 k + 1 k+1 k+1 个度数为 k k k 的基函数是非零的,即: N i − k , k ( u ) , N i − k + 1 , k ( u ) , N i − k + 2 , k ( u ) , … , N i − 1 , k ( u ) N_{i-k,k}(u), N_{i-k+1,k}(u), N_{i-k+2,k}(u), \ldots, N_{i-1,k}(u) Nik,k(u),Nik+1,k(u),Nik+2,k(u),,Ni1,k(u) N i , k ( u ) N_{i,k}(u) Ni,k(u)

Bspline插值

 KAN中首先使用到的技术就是BSpline插值,整体而言,使用Bspline进行曲线插值的步骤可以总结为如下:

步骤1:数据准备

准备需要拟合的数据点集合 D = { D 0 , D 1 , . . , D n } \mathbb{D}=\{\mathbf{D_0},\mathbf{D_1},..,\mathbf{D_n}\} D={D0,D1,..,Dn},通常是二维或三维空间中的点。

步骤2:节点向量的选择

 选择节点向量,这决定了B样条基函数的形状和数量。节点向量可以是等距的或非等距的,通常情况下选择非等距节点能够更灵活地控制曲线的形状,简单情况下,我们进行等距采样。
 而在节点向量结构方面,我们会选用clamped结构(为什么这么选,其实是没懂的,感觉插值下反正都会经过第一个和最后一个控制点,使用open好像也没差),有 m + 1 m+1 m+1 个结节 u 0 = u 1 = … = u p = 0 u_0=u_1=\ldots=u_p=0 u0=u1==up=0, u p + 1 u_{p+1} up+1, u p + 2 u_{p+2} up+2, … \ldots , u m − p − 1 u_{m-p-1} ump1, u m − p = u m − p + 1 = … = u m = 1 u_{m-p}=u_{m-p+1}=\ldots=u_m=1 ump=ump+1==um=1

步骤3:构建插值矩阵

 根据数据点和节点向量构建插值矩阵。插值矩阵的每一行对应一个数据点,每一列对应一个基函数在该数据点处的取值。插值矩阵每个元素的求取是通过Codx算法得到的,流程如下:

算法描述

输入:n、k、m、u 和 m+1 个闭合结节 { u 0 , . . . , u m } \{ u_0, ..., u_m \} {u0,...,um}
输出:系数 N 0 , k ( u ) N_{0,k}(u) N0,k(u) N 1 , k ( u ) N_{1,k}(u) N1,k(u)、…、 N n , k ( u ) N_{n,k}(u) Nn,k(u) 存储在 N [ 0 ] N[0] N[0] N [ 1 ] N[1] N[1]、…、 N [ n ] N[n] N[n]

算法步骤2
  1. N [ 0.. n ] N[0..n] N[0..n] 初始化为 0; // 初始化

  2. 如果 u = u 0 u = u_0 u=u0,则 // 排除特殊情况

    • N [ 0 ] = 1.0 N[0] = 1.0 N[0]=1.0;
    • 返回
  3. 否则如果 u = u m u = u_m u=um,则

    • N [ n ] = 1.0 N[n] = 1.0 N[n]=1.0;
    • 返回
  4. 结束如果

  5. // 现在 u u u u 0 u_0 u0 u m u_m um 之间

  6. u u u 在结节区间 [ u k , u k + 1 ) [u_k, u_{k+1}) [uk,uk+1) 中;

  7. N [ k ] : = 1.0 N[k] := 1.0 N[k]:=1.0;// 度数 0 的系数

  8. 对于 d d d 从 1 到 k k k 循环执行:// 度数 d d d 从 1 到 k k k

    • 开始
    • N [ k − d ] = u k + 1 − u u k + 1 − u k − d + 1 ∗ N [ ( k − d ) + 1 ] N[k-d] = \frac{u_{k+1}-u}{u_{k+1}-u_{k-d+1}}* N[(k-d)+1] N[kd]=uk+1ukd+1uk+1uN[(kd)+1]; // 右侧(西南角)项
    • 对于 i i i k − d + 1 k-d+1 kd+1 k − 1 k-1 k1 循环执行:// 计算内部项
      • N [ i ] : = u − u i u i + d − u i ∗ N [ i ] + u i + d + 1 − u u i + d + 1 − u i + 1 ∗ N [ i + 1 ] N[i] := \frac{u-u_i}{u_{i+d}-u_{i}} * N[i] +\frac{u_{i+d+1}-u}{u_{i+d+1}-u_{i+1}} * N[i+1] N[i]:=ui+duiuuiN[i]+ui+d+1ui+1ui+d+1uN[i+1];
    • N [ k ] = u − u k u k + d − u i + k ∗ N [ k ] N[k] = \frac{u-u_k}{u_{k+d}-u_{i+k}} * N[k] N[k]=uk+dui+kuukN[k]; // 左上角项
    • 结束
  9. // 数组 N [ 0.. n ] N[0..n] N[0..n] 中存储了系数。

对每前 n + 1 n+1 n+1节点都进行这样的操作(后面的节点会因为计算项数不足被舍掉,所以只会有n+1个这样的求系数操作),我们就能得到插值矩阵:
[
M_k = \begin{bmatrix}
N_{0,k}(u_0) & N_{1,k}(u_0) & \cdots & N_{n,k}(u_0) \
N_{0,k}(u_1) & N_{1,k}(u_1) & \cdots & N_{n,k}(u_1) \
\vdots & \vdots & \ddots & \vdots \
N_{0,k}(u_n) & N_{1,k}(u_n) & \cdots & N_{n,k}(u_n)
\end{bmatrix}
]
其中, u 0 , u 1 , … , u n u_0, u_1, \ldots, u_n u0,u1,,un 是插值点, N i , k ( u ) N_{i,k}(u) Ni,k(u) k k k次B样条基函数在该节点处的取值。

步骤4:控制点的计算

使用最小二乘法,求解线性方程组 D = N P D=NP D=NP其中 N N N是插值矩阵, P P P是控制点的向量, D D D是数据点坐标向量。

步骤5:B样条曲线的计算

对于给出的任意待插值坐标序列 u \mathbf{u} u,使用Cox-de Boor递归公式得到在 n n n个基函数在该位置上的取值,使用插值点和计算得到的控制点 p p p进行加权求和,计算code构建B样条曲线。

总结

 使用Bspline进行插值实际只有两个关键步骤:

  1. 构造节点向量
  2. 计算控制点
    然后基于Cox-de Boor递归公式就可以计算出插值曲线在任意位置的值了。

参考


  1. B-spline Basis Functions: Definition ↩︎

  2. Curve Global Interpolation ↩︎

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BSpline插值算法是一种常用的图像处理算法,用于在给定的离散数据点之间进行插值。它通过使用多项式函数来逼近数据点之间的曲线或曲面。BSpline插值算法具有良好的平滑性和局部逼近性能,因此在图像处理和计算机图形学得到广泛应用。 BSpline插值算法的基本思想是通过在每个数据点附近定义一个局部的多项式函数来逼近数据。这些多项式函数称为基函数,它们的形状由插值的次数决定。BSpline插值算法插值次数越高,逼近的精度越高。 BSpline插值算法的步骤如下: 1. 确定插值的次数,通常用一个整数来表示。 2. 根据插值次数确定基函数的形状。 3. 在每个数据点附近定义一个基函数,并将其与数据点进行加权组合。 4. 根据加权组合的结果,计算插值点的值。 下面是一个使用BSpline插值算法进行图像处理的示例代码: ```python import cv2 import numpy as np # 读取图像 image = cv2.imread('input.jpg') # 将图像转换为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 使用BSpline插值算法对图像进行放大 scale_factor = 2 output = cv2.resize(gray, None, fx=scale_factor, fy=scale_factor, interpolation=cv2.INTER_CUBIC) # 显示结果 cv2.imshow('Input', gray) cv2.imshow('Output', output) cv2.waitKey(0) cv2.destroyAllWindows() ``` 这段代码使用OpenCV库的resize函数,将灰度图像进行放大。其插值方法参数设置为cv2.INTER_CUBIC,表示使用BSpline插值算法进行插值

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值