一个矩形三个顶点(0,0), (50, 0), (50, 50)
, 变换后为(30, 30), (30, 130), (130, 130)
, 求其仿射矩阵。
我们分别设起始和结束矩阵的坐标为:, 变换后的加一个prime()符号,以此类推。
要知道,一个3X2的矩阵是不可能右乘一个矩阵得到一个3X2的矩阵(只能左乘一个3X3的),
然后,每一个新坐标,都是由原坐标的(x, y)经过变换得到(x', y‘),即使是新坐标的X值,也是需要原坐标的(x, y)值参与过来进行变化的(乘以合适的系数),然后还要加上偏移的系数,以x'
为例,应该是这样:
我们根据矩阵特征,补一个1,构造这个矩阵看看效果:
这只是把三个x给变换出来了,其实你也可以认为这是把y给变换出来了(因为原理一样,只是系数不同)。
做到这一步,我们已经知道要如何求y坐标了,即我们只补一列的话,只能得到一个坐标的x值(或y值),要求另一半,根据坐标相乘的原理,看来只能把前三列置零,再把后三列复制进去了(这样仿射矩阵也就变成6X1了),其实就是上面矩阵乘法的重复,只不过交错一下形成x,y交错的排列:
原理当然就是把第一个公式补全:
最小二乘的公式如下:
推导过程见此
奇异矩阵没有逆矩阵,会出现无法求解的问题,也就是该方法对数据是有约束的,这个有解,另议。
我们把A和Y都做出来了,直接套用公式即可,为了编程方便,我们把前后矩阵设为A和B,仿射矩阵为M,就成了:
import numpy as np
A = [[0,0], [50, 0], [50, 50]]
B = [[30, 30], [130, 30], [130, 130]]
# 分别整理成上面分析的6x6和6x1的矩阵
# 先定义变量保留6个坐标的值
(ax, ay), (bx, by), (cx, cy) = A
(ax1, ay1), (bx1, by1), (cx1, cy1) = B
A = np.array([
[ax, ay, 1, 0, 0, 0],
[0, 0, 0, ax, ay, 1],
[bx, by, 1, 0, 0, 0],
[0, 0, 0, bx, by, 1],
[cx, cy, 1, 0, 0, 0],
[0, 0, 0, cx, cy, 1]
])
B = np.array([ax1, ay1, bx1, by1, cx1, cy1]).reshape(6, 1) # 比手写6X1矩阵要省事
M = np.linalg.inv(A.T @ A) @ A.T @ B # 套公式
M.reshape(2, 3)
输出:
array([[ 2., 0., 30.],
[ 0., 2., 30.]])
上就是最小二乘的一个应用,也给了一篇链接介绍推导,后来我翻阅学习线代时的笔记,其实有从投影方面给的解释,直观易懂,于是另写了篇博文来介绍这个推导。