仿射变换(affine transform)
仿射变换的变换矩阵统一用
[
a
b
c
d
e
f
0
0
1
]
\left[\begin{array}{lll}a & b & c \\ d & e & f \\ 0 & 0 & 1\end{array}\right]
⎣⎡ad0be0cf1⎦⎤来描述,不同基础变换的
a
,
b
,
c
,
d
,
e
,
f
a, b, c, d, e, f
a,b,c,d,e,f约束不同。所以有以下关系:
[
x
′
y
′
1
]
=
[
m
11
m
12
t
x
m
21
m
22
t
x
0
0
1
]
[
x
y
1
]
\left[\begin{array}{l} x^{\prime} \\ y^{\prime} \\ 1 \end{array}\right]=\left[\begin{array}{lll} m_{11} & m_{12} & t_{x}\\ m_{21} & m_{22} & t_{x} \\ 0 & 0 & 1 \end{array}\right]\left[\begin{array}{l} x \\ y \\ 1 \end{array}\right]
⎣⎡x′y′1⎦⎤=⎣⎡m11m210m12m220txtx1⎦⎤⎣⎡xy1⎦⎤
注意:当位移值
t
x
t_{x}
tx和
t
y
t_{y}
ty位于第3列时,上面变换矩阵是左乘。
也可以表示为:
[
x
y
1
]
×
[
m
11
m
21
0
m
12
m
22
0
t
x
t
y
1
]
=
[
x
′
y
′
1
]
\left[\begin{array}{lll} x & y & 1 \end{array}\right] \times\left[\begin{array}{ccc} m_{11} & m_{21} & 0 \\ m_{12} & m_{22} & 0 \\ t_{x} & t_{y} & 1 \end{array}\right]=\left[\begin{array}{lll} x^{\prime} & y^{\prime} & 1 \end{array}\right]
[xy1]×⎣⎡m11m12txm21m22ty001⎦⎤=[x′y′1]
位移值 t x t_{x} tx和 t y t_{y} ty位于第3行时,上面变换矩阵是右乘。可以看到变换矩阵其实是进行了转置。在不同库中,例如threejs:2维向量应用3 x 3矩阵,其源码使用的是左乘,所以我们表示时使用前者。
注意:上面旋转矩阵的
s
i
n
θ
sin\theta
sinθ前的负号情况。在常规坐标系下,顺时针旋转角度为正才这么表示。我们更常用的表示是逆时针为正,所以有:
[
cos
θ
−
sin
θ
0
sin
θ
cos
θ
0
0
0
1
]
\left[\begin{array}{ccc} \cos \theta & -\sin \theta & 0 \\ \sin \theta & \cos \theta & 0 \\ 0 & 0 & 1 \end{array}\right]
⎣⎡cosθsinθ0−sinθcosθ0001⎦⎤
更多参考:仿射变换及其变换矩阵的理解
在本文,将仿射变换表示为:
[
M
11
M
12
M
13
M
21
M
22
M
23
0
0
1
]
\left[\begin{array}{ccc} M_{11} & M_{12} & M_{13} \\ M_{21} & M_{22} & M_{23} \\ 0 & 0& 1 \\ \end{array}\right]
⎣⎡M11M210M12M220M13M231⎦⎤
在求解时表示为:
[
M
11
M
12
M
13
M
21
M
22
M
23
]
\left[\begin{array}{ccc} M_{11} \\ M_{12} \\ M_{13} \\ M_{21} \\ M_{22} \\ M_{23} \\ \end{array}\right]
⎣⎢⎢⎢⎢⎢⎢⎡M11M12M13M21M22M23⎦⎥⎥⎥⎥⎥⎥⎤
在给出3对数据点个时(不共线),我们可以列出6个方程组
M
11
x
0
+
M
11
y
0
+
M
13
=
x
p
0
M
21
x
0
+
M
21
y
0
+
M
23
=
y
p
0
M
11
x
1
+
M
11
y
1
+
M
13
=
x
p
1
M
21
x
1
+
M
21
y
1
+
M
23
=
y
p
1
M
11
x
2
+
M
11
y
2
+
M
13
=
x
p
2
M
21
x
2
+
M
21
y
2
+
M
23
=
y
p
2
M_{11}x_{0} + M_{11}y_{0} + M_{13} = x_{p0} \\ M_{21}x_{0} + M_{21}y_{0} + M_{23} = y_{p0} \\ M_{11}x_{1} + M_{11}y_{1} + M_{13} = x_{p1} \\ M_{21}x_{1} + M_{21}y_{1} + M_{23} = y_{p1} \\ M_{11}x_{2} + M_{11}y_{2} + M_{13} = x_{p2} \\ M_{21}x_{2} + M_{21}y_{2} + M_{23} = y_{p2} \\
M11x0+M11y0+M13=xp0M21x0+M21y0+M23=yp0M11x1+M11y1+M13=xp1M21x1+M21y1+M23=yp1M11x2+M11y2+M13=xp2M21x2+M21y2+M23=yp2
M
x
=
x
p
Mx = x_p
Mx=xp
x
x
x表示原始坐标,
M
M
M表示仿射变换矩阵,
x
p
x_p
xp表示变换后坐标
编程中把变换矩阵M描述为待求量X,表示:
[x0 y0 1 0 0 0 ] [M11] [xp0]
[0 0 0 x0 y0 1 ] [M12] [yp0]
[x1 y1 1 0 0 0 ] [M13] [xp1]
[0 0 0 x1 y1 1 ] * [M21] = [yp1]
[x2 y2 1 0 0 0 ] [M22] [xp2]
[0 0 0 x2 y2 1 ] [M23] [yp2]
A * X = B
3个点不共线时,A满秩,所|A|不为0,直接对A求逆,便可解出X,得到仿射变换的6个自由度值。
下面使用最小二乘分析数据点大于和小于3时,求解方法。
最小二乘
线性最小二乘法是将数据与未知参数之间的关系以线性形式表达的一种拟合数据模型的方法。
A x = b J = argmin x ∥ A x − b ∥ 2 \begin{array}{l} A x=b \\ J = \underset{x}{\operatorname{argmin}}\|A x-b\|^{2}\\ \end{array} Ax=bJ=xargmin∥Ax−b∥2
取极值,求导要为0
∂ J ∂ a = A T ( A x − b ) = 0 A T A x = A T b x = ( A T A ) − 1 A T b \frac{\partial J}{\partial a}=A^{T}(A x-b)=0\\ A^{T}A x = A^{T}b\\ x = (A^{T} A)^{-1} A^{T} b ∂a∂J=AT(Ax−b)=0ATAx=ATbx=(ATA)−1ATb
A的秩>3时,
∣
A
T
A
∣
|A^{T} A|
∣ATA∣不为0,所以可以求得
(
A
T
A
)
−
1
(A^{T} A)^{-1}
(ATA)−1,也就有
x
x
x
A的秩<2时,
∣
A
T
A
∣
|A^{T} A|
∣ATA∣可能为0,需要加正则项来确定唯一。
以下示例展示的matlab代码表明:当数据点刚好为3个时,且不共线,秩为3,可以直接求逆:
function M= affine_least_square(x0,y0, x1,y1, x2,y2, xp0,yp0, xp1,yp1, xp2,yp2)
% This function finds the affine transform between three points
% an affine transformation with a 3x3 affine transformation matrix:
%
% [M11 M12 M13]
% [M21 M22 M23]
% [M31 M32 M33]
%Since the third row is always [0 0 1] we can skip that.
% [x0 y0 1 0 0 0 ] [M11] [xp0]
% [0 0 0 x0 y0 1 ] [M12] [yp0]
% [x1 y1 1 0 0 0 ] [M13] [xp1]
% [0 0 0 x1 y1 1 ] * [M21] = [yp1]
% [x2 y2 1 0 0 0 ] [M22] [xp2]
% [0 0 0 x2 y2 1 ] [M23] [yp2]
% A * X = B
%A -> 6x6
%X -> 6x1 in fact
%X=A\B
A=[x0 y0 1 0 0 0 ; 0 0 0 x0 y0 1 ; x1 y1 1 0 0 0 ; 0 0 0 x1 y1 1 ; x2 y2 1 0 0 0;0 0 0 x2 y2 1];
B=[xp0; yp0; xp1; yp1; xp2 ; yp2 ];
% X=A\B;
% X=inv(A)*B;
X=pinv(A)*B;
M11 =X(1,1);
M12 =X(2,1);
M13 =X(3,1);
M21 =X(4,1);
M22 =X(5,1);
M23 =X(6,1);
M=[ M11 M12 M13; M21 M22 M23; 0 0 1];
end
当3个数据点共线时,其秩变为2,行列式值为0,即不可逆。但是可以求A的伪逆(广义逆矩阵),得到其中的一个解。当数据点多余3个且秩大于3时,便不能求得A的逆矩阵,此时也需要求伪逆来解。
线性最小二乘法是将数据与未知参数之间的关系以线性形式表达的一种拟合数据模型的方法。
A x = b X ∗ = argmin x ∥ A x − b ∥ 2 = ( A T A ) − 1 A T b \begin{array}{l} A x=b \\ X^{*}=\underset{x}{\operatorname{argmin}}\|A x-b\|^{2}=\left(A^{T} A\right)^{-1} A^{T} b \end{array} Ax=bX∗=xargmin∥Ax−b∥2=(ATA)−1ATb
import * as math from "mathjs";
export default class MathUtils{
constructor(){
}
/**
* Get generalized inverse use QR decomposition
* @param A arbitrary matrix
* @return matrix:PInvA of APInvAA=A | APInvA=I
*/
static pInvQR(A: math.Matrix | math.MathArray): math.Matrix | math.MathArray{
// more reference:
// https://blog.csdn.net/xingozd/article/details/50417233
const QR = math.qr(A);
const Q = QR.Q;
const R = QR.R;
const RT = math.transpose(R);
const QT = math.transpose(Q);
const InvR = math.multiply(math.inv(math.multiply(RT, R)), RT);
const PInvA = math.multiply(InvR, QT);
return PInvA;
}
/**
* This function finds the affine transform between three points
* an affine transformation with a 3x3 affine transformation matrix:
* [M11 M12 M13]
* [M21 M22 M23]
* [M31 M32 M33]
* Since the third row is always [0 0 1] we can skip that.
* [x0 y0 1 0 0 0 ] [M11] [xp0]
* [0 0 0 x0 y0 1 ] [M12] [yp0]
* [x1 y1 1 0 0 0 ] [M13] [xp1]
* [0 0 0 x1 y1 1 ] * [M21] = [yp1]
* [x2 y2 1 0 0 0 ] [M22] [xp2]
* [0 0 0 x2 y2 1 ] [M23] [yp2]
* A * X = BapplyMatrix3
* @param pointPairs [A B]
* @return matrix:x of Ax = B
*/
static affineLeastSquare(pointPairs: [[number, number], [number, number]][]): math.Matrix{
// more reference:
// http://ros-developer.com/2017/12/26/finding-affine-transform-with-linear-least-squares/
// https://www.cnblogs.com/AndyJee/p/5053354.html
let A: number[][] = [];
let B: number[] = [];
pointPairs.forEach(pointPair => {
let p1 = pointPair[0];
let p2 = pointPair[1];
A.push([p1[0], p1[1], 1, 0, 0, 0]);
A.push([0, 0, 0, p1[0], p1[1], 1]);
B.push(p2[0]);
B.push(p2[1]);
});
// (A^TA)^(-1)
let AT = math.transpose(A);
let ATA = math.multiply(AT, A);
let ATAInv: math.Matrix | math.MathArray;
try {
ATAInv = math.inv(ATA) as math.MathArray
}catch (e) {
ATAInv = this.pInvQR(ATA)//这里应该使用正则项求解,要么直接在最开始就是用pinv求解
}
// X = (A^TA)^(-1)A^TB
let X = math.multiply(math.multiply(ATAInv, AT), B) as number[];
let x = math.matrix([
[X[0], X[1], X[2]],
[X[3], X[4], X[5]],
[0, 0, 1]
]);
return x;
}
}
伪逆
伪逆矩阵和最小二乘估计
相关性质
矩阵的逆、伪逆、左右逆,最小二乘,投影矩阵
https://blog.csdn.net/baidu_38172402/article/details/82931879?utm_medium=distribute.pc_relevant.none-task-blog-title-2&spm=1001.2101.3001.4242
伪逆矩阵(广义逆矩阵
https://www.jianshu.com/p/609fa0cce409
含义:
伪逆矩阵的意义及求法? - 破魔之箭的回答 - 知乎
https://www.zhihu.com/question/47688307/answer/107477245
解释最小二乘法
https://zhuanlan.zhihu.com/p/85327669
矩阵求导解最小二乘问题
https://blog.csdn.net/ACdreamers/article/details/44662633?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param
求伪逆
求伪逆的三种方法:直接,SVD,QR及具体的应用
https://blog.csdn.net/xingozd/article/details/50417233?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param
C++SVD分解求伪逆 (Eigen库)(附C++代码)
https://blog.csdn.net/weixin_44344462/article/details/88879356
js计算
https://mathnotepad.com/