4. 单应性矩阵求解:DLT算法、RANSAC鲁棒估计、重投影误差最小化

好,咱们进入正题。单应性矩阵这东西,说白了就是描述两个平面之间的一一映射关系。在标定场景里,它连接了棋盘格平面和图像平面。你想想看,我们拍一张棋盘格照片,角点的世界坐标是已知的,像素坐标也能检测出来——那中间的桥梁,就是单应性矩阵。

我个人习惯把单应性矩阵求解拆成三步走:先用DLT算个初始值,再用RANSAC剔除野点,最后用非线性优化把精度磨到极致。这三板斧下来,基本不会出大问题。

4.1 DLT算法:从线性代数到几何映射

DLT,全称Direct Linear Transform,直接线性变换。名字听着唬人,其实原理很简单——把问题转化成解线性方程组。

单应性矩阵H是一个3x3的矩阵,有8个自由度(去掉一个尺度因子)。一对匹配点可以提供两个方程,所以理论上4对点就能解。但实际中我们会用更多的点,用最小二乘来抵抗噪声。

具体推导我就不啰嗦了,直接说核心:

  • 每对匹配点 (x, y) -> (u, v) 可以构造两个方程
  • 把所有方程堆起来,得到 Ah = 0 的形式
  • 对A做SVD分解,最小奇异值对应的右奇异向量就是H

这里有个坑,我踩过。如果点的坐标数值差异很大(比如世界坐标是毫米级,像素坐标是千像素级),A矩阵的条件数会很大,解出来的H精度堪忧。我的做法是:先做归一化,把坐标缩放到[-1, 1]区间,解完再反归一化回去。这一步叫"归一化DLT",效果立竿见影。

核心要点: DLT是线性解法,速度快,但抗噪能力一般。它适合作为初始估计,不适合直接用于最终结果。

4.2 RANSAC鲁棒估计:把野点揪出来

实际项目中,角点检测不可能100%准确。光照不好、棋盘格反光、运动模糊——这些都会带来野点。如果直接用DLT硬算,一个野点就能把结果带偏。

RANSAC的思路很暴力:随机采样、验证、迭代。具体流程:

  1. 从所有匹配点中随机选4对,用DLT算一个H
  2. 用这个H去投影所有点,算重投影误差
  3. 误差小于阈值的点算内点,记录内点数量
  4. 重复N次,保留内点最多的那组H
  5. 用所有内点重新算一次H(精化)

我曾经在一个项目中,因为标定板贴歪了,导致20%的角点都有系统偏差。第一次跑DLT出来的结果惨不忍睹,重投影误差直接飙到5个像素。后来加上RANSAC,内点率降到95%以上,误差直接回到0.3像素以内。

经验之谈: RANSAC的迭代次数N不是越大越好。我一般用自适应方法:根据当前内点率动态计算所需迭代次数,保证99%的概率能采到一组全内点的样本。这样既省时间,又靠谱。
注意: 阈值的选择很关键。设太小,会把好点误判为野点;设太大,野点会混进来。我通常根据图像分辨率来定:对于1080p的图像,阈值设在1~2个像素比较合理。

4.3 重投影误差最小化:把精度磨到极致

DLT+RANSAC得到的H,精度已经不错了。但如果你追求亚像素级别的标定精度,那就得走最后一步——非线性优化。

优化的目标函数很直观:

minimize Σ || p_i - project(H, P_i) ||²

其中p_i是检测到的像素坐标,P_i是世界坐标,project是投影函数。说白了,就是让所有点的投影误差平方和最小。

我一般用Levenberg-Marquardt算法来解这个优化问题。相比高斯-牛顿,LM在迭代初期更稳定,不容易发散。初始值就用RANSAC精化后的H,通常迭代10~20次就能收敛。

这里有个细节:优化时H的9个参数不是独立的,因为有尺度模糊。我习惯把H的最后一个元素固定为1,或者用单位向量约束。两种做法都可以,看个人喜好。

优化后的效果: 重投影误差可以从0.5像素降到0.1像素以下。对于高精度标定来说,这一步是必须的。

4.4 知识体系总览

下面这张图,是我自己梳理的单应性矩阵求解流程。你可以把它当作一个检查清单:

单应性矩阵求解流程 DLT算法 线性求解初始H RANSAC鲁棒估计 剔除野点,精化H 非线性优化 重投影误差最小化 关键参数与注意事项 • DLT:至少4对点,建议用归一化坐标,SVD求解 • RANSAC:阈值1-2像素,自适应迭代次数,内点率>90% • 优化:LM算法,初始值用RANSAC结果,迭代10-20次 • 最终目标:重投影误差 < 0.1像素(高精度标定)

嗯,到这里单应性矩阵求解的核心内容就讲完了。DLT给个起点,RANSAC清理数据,优化收尾——这套组合拳打下来,标定精度基本稳了。下次你遇到标定不准的问题,不妨按这个流程排查一遍,大概率能找到症结所在。

一个小建议: 在实际工程中,不要只依赖一种方法。我通常会把DLT、RANSAC、优化三个结果都打印出来对比,如果某个步骤的结果异常,能快速定位问题。调试时多留一手,总没错。