当前位置: 首页 > news >正文

薄板样条(TPS, Thin Plate Spline)数学原理推导

1. TPS 的问题描述

想象一块薄钢板,桌上钉了 NNN 根钉子,已知:

1、钉子 iii 的位置:
xi=(xi,yi)\mathbf{x}_i = (x_i, y_i)xi=(xi,yi)

2、每根钉子想要被拉到高度:
ziz_izi

目标:找出一个光滑的曲面 f(x,y)f(x,y)f(x,y),经过所有钉子,且尽量平滑。

2. 建立TPS模型

2.1 弯曲能量函数

二维 TPS 的弯曲能量(这里不做推导,你只需要知道这个方程表示的是薄板的弯曲程度对应的能量E):
E=∬[(∂2f∂x2)2+2(∂2f∂x∂y)2+(∂2f∂y2)2]dxdyE=\iint\left[\left(\frac{\partial^2f}{\partial x^2}\right)^2+2\left(\frac{\partial^2f}{\partial x\partial y}\right)^2+\left(\frac{\partial^2f}{\partial y^2}\right)^2\right]dxdyE=[(x22f)2+2(xy2f)2+(y22f)2]dxdy

物理含义:板子越弯曲,能量越大。

2.2 目标函数

我们想解这个优化问题,最小化以下目标:

(1)拟合所有钉子高度
f(xi,yi)=zif(x_i, y_i) = z_if(xi,yi)=zi
(2) 保证曲面最光滑 → 最小化弯曲能

所以可以写成如下形式:
Loss=∑i=1N(f(xi,yi)−zi)2  +  λ⋅E(公式1)\text{Loss} =\sum_{i=1}^{N} \left( f(x_i, y_i) - z_i \right)^2\; + \;\lambda \cdot E (公式1)Loss=i=1N(f(xi,yi)zi)2+λE(公式1

  • 第一项:拟合误差
  • 第二项:弯曲能量
  • λ\lambdaλ:平滑参数

带入E,Loss完整形式为:
Loss=min⁡[∑i=1N(f(xi,yi)−zi)2+λ⋅∬(fxx2+2fxy2+fyy2)dxdy](公式2)Loss=\min\left[\sum_{i=1}^N\left(f(x_i,y_i)-z_i\right)^2+\lambda\cdot\iint\left(f_{xx}^2+2f_{xy}^2+f_{yy}^2\right)dxdy\right](公式2)Loss=min[i=1N(f(xi,yi)zi)2+λ(fxx2+2fxy2+fyy2)dxdy](公式2
这是一个变分法问题。

  • 第一个部分 → 拟合数据(这部分很好理解,就是最小二乘法能量最小)
  • 第二个部分 → 惩罚曲面弯曲(为什么是这样?请看附录1)

2.3 进一步简化表达形式

目的:原有表达形式是刚性和非刚性变化混在一起,难以对每一部分单独划分,因此需要将其拆解为仿射变化和非刚性变化,仿射变换只改变平移、缩放;非刚性负责旋转、弯曲等变换。

通过变分法 + 双调和方程,可以将公式1写为(至于如何化简的,感兴趣去看变分法,这里也不推导了):
f(x,y)=Affine+∑wi⋅U(r) f(x,y) = \text{Affine} + \sum w_i \cdot U(r) f(x,y)=Affine+wiU(r)
那么公式2即可写为:
f(x,y)=a1⋅x+a2⋅y+a3+∑j=1NwjU(∥(x,y)−(xj,yj)∥)f(x,y)=a_1\cdot x+a_2\cdot y+a_3+\sum_{j=1}^Nw_jU\left(\left\|(x,y)-(x_j,y_j)\right\|\right)f(x,y)=a1x+a2y+a3+j=1NwjU((x,y)(xj,yj))
其中:

  • a1,a2,a3a_1, a_2, a_3a1,a2,a3:仿射项 (表示平面或倾斜面)

  • wjw_jwj:权重,表示每根钉子对弯曲的贡献

  • U(r)U(r)U(r):核函数:
    U(r)=r2log⁡r,其中r=∥(x,y)−(xj,yj)∥ U(r) = r^2 \log r,其中 r = \| (x, y) - (x_j, y_j) \| U(r)=r2logr,其中r=(x,y)(xj,yj)

3. 建立方程

3.1 约束条件1

因为每根钉子都在平面f(x,y)f(x,y)f(x,y),因此要求:
f(xi,yi)=zif(x_i, y_i) = z_if(xi,yi)=zi
i=1,…,Ni = 1, \dots, Ni=1,,N 都成立。
即:f(xi,yi)=a1xi+a2yi+a3+∑j=1Nwj⋅U(∥(xi,yi)−(xj,yj)∥)f(x_i,y_i)=a_1x_i+a_2y_i+a_3+\sum_{j=1}^Nw_j\cdot U\left(\|(x_i,y_i)-(x_j,y_j)\|\right)f(xi,yi)=a1xi+a2yi+a3+j=1NwjU((xi,yi)(xj,yj))

3.2 约束条件2

非刚性部分不能有刚性的变化量,所有的刚性变化都必须由仿射量提供,这就是边界条件,因此TPS 必须满足

∑j=1Nwj=0,∑j=1Nwj⋅xj=0,∑j=1Nwj⋅yj=0 \sum_{j=1}^{N} w_j = 0, \\ \sum_{j=1}^{N} w_j \cdot x_j = 0,\\ \sum_{j=1}^{N} w_j \cdot y_j = 0 j=1Nwj=0,j=1Nwjxj=0,j=1Nwjyj=0
意思:弯曲部分 不能产生平移或旋转,这些由仿射项负责。

3.3 矩阵形式

(1) 第一部分: a1xi+a2yi+a3a_1 x_i + a_2 y_i + a_3a1xi+a2yi+a3为仿射项

定义仿射矩阵 PPP
P=[x1y11x2y21⋮⋮⋮xNyN1] P = \begin{bmatrix} x_1 & y_1 & 1 \\ x_2 & y_2 & 1 \\ \vdots & \vdots & \vdots \\ x_N & y_N & 1 \end{bmatrix} P=x1x2xNy1y2yN111
定义核矩阵 c=[a1,a2,a3]Tc=[a1,a2,a3]^Tc=[a1,a2,a3]T

a1xi+a2yi+a3a_1 x_i + a_2 y_i + a_3a1xi+a2yi+a3仿射项可记作写为:
Pc=[x1y11x2y21⋮⋮⋮xNyN1][a1a2⋮aN] Pc = \begin{bmatrix} x_1 & y_1 & 1 \\ x_2 & y_2 & 1 \\ \vdots & \vdots & \vdots \\ x_N & y_N & 1 \end{bmatrix} \begin{bmatrix} a_1 \\ a_2 \\ \vdots \\ a_N \end{bmatrix} Pc=x1x2xNy1y2yN111a1a2aN
(2)第二部分:∑j=1Nwj⋅U(∥(xi,yi)−(xj,yj)∥)\sum_{j=1}^{N} w_j \cdot U\bigl( \| (x_i, y_i) - (x_j, y_j) \| \bigr )j=1NwjU((xi,yi)(xj,yj))为非刚性变换

定义核矩阵 KKK
Kij=U(∥(xi,yi)−(xj,yj)∥) K_{ij} = U\bigl(\| (x_i, y_i) - (x_j, y_j) \|\bigr) Kij=U((xi,yi)(xj,yj))
定义核矩阵 W=[w1,w2,…,wN]TW = [w_1, w_2, \ldots, w_N]^TW=[w1,w2,,wN]T

那么,∑j=1Nwj⋅U(∥(xi,yi)−(xj,yj)∥)\sum_{j=1}^{N} w_j \cdot U\bigl( \| (x_i, y_i) - (x_j, y_j) \| \bigr )j=1NwjU((xi,yi)(xj,yj))非刚性项则可以记作:
KW=[K11,K12,…Kij][w1w2⋮wN] KW = \begin{bmatrix} K_{11} , K_{12}, \dots K_{ij} \end{bmatrix} \begin{bmatrix} w_1 \\ w_2 \\ \vdots \\ w_N \end{bmatrix} KW=[K11,K12,Kij]w1w2wN
(3)还差一个是边界条件

PT⋅W=[x1x2x3y1y2y3⋮⋮⋮111][w1w2⋮wN]=0P^T·W=\begin{bmatrix} x_1 & x_2 & x_3 \\ y_1 & y_2 & y_3 \\ \vdots & \vdots & \vdots \\ 1 & 1 & 1 \end{bmatrix} \begin{bmatrix} w_1 \\ w_2 \\ \vdots \\ w_N \end{bmatrix} = 0PTW=x1y11x2y21x3y31w1w2wN=0

(4) 一二部分相加(仿射+非刚性):
f(xi,yi)=a1xi+a2yi+a3+∑j=1Nwj⋅U(∥(xi,yi)−(xj,yj)∥)f(x_i,y_i)=a_1x_i+a_2y_i+a_3+\sum_{j=1}^Nw_j\cdot U\left(\|(x_i,y_i)-(x_j,y_j)\|\right)f(xi,yi)=a1xi+a2yi+a3+j=1NwjU((xi,yi)(xj,yj))
就可以记作:
Z=Pc+KWZ=Pc+KWZ=Pc+KW
边界部分:

PT⋅W=0P^T·W=0PTW=0

(5) 约束1和约束2写成矩阵方程:
[KPPT0]⋅[Wc]=[Z0]\begin{bmatrix}K&P\\P^T&0\end{bmatrix}\cdot\begin{bmatrix}W\\c\end{bmatrix}=\begin{bmatrix}Z\\0\end{bmatrix}[KPTP0][Wc]=[Z0]

4 代码实现

import numpy as np
import matplotlib.pyplot as plt# ------------------------
# 定义控制点
# ------------------------# 控制点坐标 (x, y)
points = np.array([[0, 0],[1, 0],[0, 1],[2, 1],[1, 2],[0, 2]
])# 每个控制点想要达到的高度
heights = np.array([0, 2, 1, 3, 1, 0])N = points.shape[0]# ------------------------
# 计算核矩阵 K
# ------------------------def U(r):# 为避免 log(0),加一个极小值r[r == 0] = 1e-20return r**2 * np.log(r)# pairwise distance matrix
dist_matrix = np.linalg.norm(points[:, np.newaxis, :] - points[np.newaxis, :, :],axis=2
)K = U(dist_matrix)# ------------------------
# 构造 P 矩阵
# ------------------------# 对每个点,P = [x, y, 1]
P = np.hstack((points,np.ones((N, 1))
))# ------------------------
# 拼接完整矩阵
# ------------------------# 大矩阵 A
A_upper = np.hstack((K, P))
A_lower = np.hstack((P.T, np.zeros((3,3))))
A = np.vstack((A_upper, A_lower))# 构造右边的向量
Y = np.concatenate([heights, np.zeros(3)])# ------------------------
# 解方程
# ------------------------params = np.linalg.solve(A, Y)# 提取结果
W = params[:N]
a1, a2, a3 = params[N:]print("权重 W =", W)
print("仿射项 = ", [a1, a2, a3])# ------------------------
# 计算网格上的值
# ------------------------# 建一个网格
grid_x, grid_y = np.meshgrid(np.linspace(-0.5, 3, 50),np.linspace(-0.5, 3, 50)
)grid_points = np.vstack((grid_x.ravel(),grid_y.ravel()
)).T# 计算每个网格点到所有控制点的距离
dists_to_points = np.linalg.norm(grid_points[:, np.newaxis, :] - points[np.newaxis, :, :],axis=2
)U_values = U(dists_to_points)  # shape = (M, N)# 弯曲部分
bending_part = U_values @ W# 仿射部分
affine_part = a1 * grid_points[:,0] + a2 * grid_points[:,1] + a3# 总 TPS
grid_heights = bending_part + affine_part
grid_heights = grid_heights.reshape(grid_x.shape)# ------------------------
# 绘制曲面
# ------------------------fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(grid_x, grid_y, grid_heights, cmap='viridis', alpha=0.8)# 绘制控制点
ax.scatter(points[:,0], points[:,1], heights, color='r', s=50, label='Control Points')ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Height')
ax.set_title('TPS')
ax.legend()
plt.show()

运行结果

权重 W = [-5.6051292e-01 1.9983916e-01 7.2134752e-01 8.2778442e-17
-1.9983916e-01 -1.6083460e-01]
仿射项 = [1.451205059304602, -0.25000000000000006, 1.2499999999999998]

在这里插入图片描述
在这里插入图片描述

http://www.dtcms.com/a/282939.html

相关文章:

  • 从0到1开发网页版五子棋:我的Java实战之旅
  • 【ROS/DDS】FastDDS:C++编写一个发布者和订阅者应用程序(三)
  • OpenCV稠密光流估计的一个类cv::optflow::DenseRLOFOpticalFlow
  • hashMap原理(一)
  • FAISS深度学习指南:构建高效向量检索系统的完整方法论
  • SSH连接复用技术在海外云服务器环境下的稳定性验证与优化方案
  • [时序数据库-iotdb]时序数据库iotdb的安装部署
  • 【C++】迭代器
  • 第五章 管道工程 5.4 管道安全质量控制
  • 【前端】HTML语义标签的作用与实践
  • 想删除表中重复数据,只留下一条,sql怎么写
  • 1688商品API全链路开发实践
  • Reddit Karma是什么?Post Karma和Comment Karma的提升指南
  • 搭建基于Gitee文档笔记自动发布
  • 达梦数据库配置兼容MySQL
  • Vue + Element UI 实现单选框
  • [特殊字符] 第1篇:什么是SQL?数据库是啥?我能吃吗?
  • LeafletJS 进阶:GeoJSON 与动态数据可视化
  • UI测试平台TestComplete:关键字驱动测试技巧
  • 【ArcGISPro】修改conda虚拟安装包路径
  • Mybatis的SQL编写—XML方式
  • 无人机EIS增稳技术要点分析
  • 牛客:HJ26 字符串排序[华为机考][map]
  • web:js提示框、询问框、输入框的使用
  • React 条件渲染完全指南
  • 题解:P13256 [GCJ 2014 #2] Data Packing
  • 新版本Cursor中配置自定义MCP服务器教程,附MCP工具开发实战源码
  • 棱镜观察|比亚迪“全责兜底”智能泊车!“减配”风潮接踵而至
  • realsense应用--rs-distance(距离测量)
  • 中国旅行社协会在京召开“文旅人工智能应用研讨会”,助力文旅创新发展