3. 仿射变换数学原理:平移、旋转、缩放、剪切变换的矩阵表示

各位工程师朋友,欢迎来到第三章。上一章我们聊了九点标定的整体流程,今天咱们把镜头拉近,专门看看背后的数学引擎——仿射变换。

说实话,我刚入行时觉得矩阵变换就是纯理论,跟现场调试八竿子打不着。直到有一次在3C产线做手机中框定位,怎么调参数都对不准,最后发现是忽略了剪切变形。嗯,从那以后我再也不敢小看这4x4矩阵了。

3.1 什么是仿射变换?一句话说清楚

仿射变换,说白了就是“线性变换 + 平移”。它保证两件事:

  • 直线还是直线(不会变成曲线)
  • 平行线还是平行线(不会变成相交线)

你想想看,在机器视觉里,我们处理的都是刚体或近似刚体的零件。相机拍到的图像,跟模板之间无非就是:挪了个位置(平移)、转了个角度(旋转)、放大或缩小(缩放)、或者镜头有点歪(剪切)。这些操作,仿射变换全包了。

核心公式:

在二维平面上,仿射变换可以写成:

[ x' ]   [ a  b  tx ] [ x ]
[ y' ] = [ c  d  ty ] [ y ]
[ 1  ]   [ 0  0  1  ] [ 1 ]

其中 (x, y) 是原始坐标,(x', y') 是变换后坐标。矩阵中的 a, b, c, d 控制线性部分,tx, ty 控制平移。

3.2 四种基本变换的矩阵拆解

咱们一个一个来看。我个人习惯把这四种变换记成“两动两不动”——平移和旋转是“动位置”,缩放和剪切是“动形状”。

3.2.1 平移变换

最简单的,没有之一。就是把整个图像往右挪 dx,往上挪 dy。

[ 1  0  dx ]
[ 0  1  dy ]
[ 0  0  1  ]

我在项目中遇到过一种情况:相机安装有微小的机械偏差,导致每次拍照都有固定偏移。这时候直接用平移矩阵做补偿,比重新调机械快得多。

3.2.2 旋转变换

绕原点逆时针旋转 θ 角度:

[ cosθ  -sinθ  0 ]
[ sinθ   cosθ  0 ]
[ 0      0     1 ]

避坑指南:我曾经在HALCON里直接用弧度制传参,结果旋转角度完全不对。后来发现HALCON的 hom_mat2d_rotate 默认也是弧度,但很多现场工程师习惯用角度。我的建议是——统一用弧度,在代码开头写一行注释说明,避免后续维护的人踩坑。

3.2.3 缩放变换

沿 x 轴缩放 sx 倍,沿 y 轴缩放 sy 倍:

[ sx  0   0 ]
[ 0   sy  0 ]
[ 0   0   1 ]

注意:如果 sx ≠ sy,图像会变形。这在标定中其实很常见——比如相机芯片的像素不是正方形,或者镜头有畸变。

3.2.4 剪切变换

这个很多人容易忽略。剪切变换让图像沿一个方向“倾斜”:

[ 1   shx  0 ]   (沿x方向剪切)
[ 0   1    0 ]
[ 0   0    1 ]

[ 1   0    0 ]   (沿y方向剪切)
[ shy 1    0 ]
[ 0   0    1 ]

你想想看,如果相机安装时跟运动平面不平行,拍出来的图像就会有剪切变形。我遇到过一条自动化产线,视觉引导总是偏几毫米,查了三天,最后发现是相机支架被撞歪了0.5度——就是这点剪切,让所有标定数据全废了。

3.3 变换的复合:矩阵乘法顺序很重要

实际应用中,我们很少只用一种变换。比如“先旋转再平移”和“先平移再旋转”,结果完全不同。

重要提醒:矩阵乘法不满足交换律。在HALCON中,复合变换的顺序是“从右往左”读。例如:

H_final = H_translation * H_rotation * H_scaling

意思是:先缩放,再旋转,最后平移。这个顺序我建议你写在注释里,不然三个月后自己都看不懂。

下面这张图展示了四种基本变换以及它们的复合效果:

仿射变换四种基本操作及复合变换 原始 平移 旋转 缩放 剪切 复合 复合变换注意 先缩放 → 再旋转 → 最后平移 顺序不同,结果不同

3.4 HALCON中的矩阵操作实战

理论讲完了,咱们看看在HALCON里怎么写。我个人习惯用 hom_mat2d_identity 初始化单位矩阵,然后一步步往上乘。

* 初始化单位矩阵
hom_mat2d_identity (HomMat2D)

* 先缩放:x方向0.5,y方向0.8
hom_mat2d_scale (HomMat2D, 0.5, 0.8, 0, 0, HomMat2D)

* 再旋转:30度(注意是弧度!)
hom_mat2d_rotate (HomMat2D, rad(30), 0, 0, HomMat2D)

* 最后平移:x方向100,y方向200
hom_mat2d_translate (HomMat2D, 100, 200, HomMat2D)

* 应用变换到点
affine_trans_point_2d (HomMat2D, 50, 60, Qx, Qy)

我的调试小技巧:每次写完变换,先用 affine_trans_point_2d 测试几个已知点。比如原点 (0,0) 经过平移后应该变成 (tx, ty),如果不对,八成是矩阵顺序搞反了。

3.5 逆变换:从像素坐标回到世界坐标

九点标定的核心,其实就是求一个从像素坐标到世界坐标的仿射变换矩阵。但有时候我们需要反过来——比如机器人已经走到某个位置,想知道它在图像里对应哪个像素。

逆变换的矩阵就是原矩阵的逆矩阵:

H_inv = hom_mat2d_invert (HomMat2D)

这里有个坑:如果原矩阵的缩放因子接近0(比如 sx=0),矩阵就不可逆了。我在项目中遇到过因为标定板放得太远,导致图像上特征点几乎重合,矩阵接近奇异——最后算出来的逆变换全是NaN。嗯,这种情况只能重新采集标定点。

3.6 本章小结:一张表记住所有

变换类型 矩阵形式 自由度 现场常见场景
平移 [1 0 tx; 0 1 ty; 0 0 1] 2 相机安装偏移补偿
旋转 [cosθ -sinθ 0; sinθ cosθ 0; 0 0 1] 1 零件角度偏差校正
缩放 [sx 0 0; 0 sy 0; 0 0 1] 2 不同分辨率相机切换
剪切 [1 shx 0; 0 1 0] 或 [1 0 0; shy 1 0] 2 相机倾斜、镜头畸变
复合 上述矩阵的乘积 6 九点标定实际应用