1. 鱼眼镜头去畸变:为什么cv::fisheye::undistortPoints这么关键?
如果你玩过运动相机、全景摄像头,或者捣鼓过机器人上的“眼睛”,那你肯定见过那种画面边缘被严重拉伸、扭曲成圆形的图像。这就是鱼眼镜头带来的效果,视野是真的大,但畸变也是真的猛。在计算机视觉的实际项目里,比如SLAM(同步定位与地图构建)、三维重建或者高精度的测量,我们第一步往往就是得把这些扭曲的点给“掰直”了,还原它们本来的位置。OpenCV里的 cv::fisheye::undistortPoints 函数,就是干这个活的得力工具。
但这里有个坑,我踩过,估计很多新手朋友也绕不过去。这个函数输出的结果,到底是啥坐标?是直接能在你屏幕上画出来的像素坐标吗?很多时候并不是。它默认给你的是归一化坐标,这是一个位于相机前方虚拟平面上的点,单位是米(或者说,是相对于焦距的比例),而不是我们熟悉的图像上的第几行第几列像素。如果你没注意到这一点,直接把结果拿去用,后续的匹配、计算距离会全乱套,误差大得离谱。我自己就曾经因为这个问题,调试了一整天的代码,最后发现是坐标系统没对齐,那种感觉真是让人哭笑不得。
所以,今天我们就来彻底搞懂 cv::fisheye::undistortPoints,核心就是弄明白它内部到底做了什么,以及如何通过那个关键的 P矩阵,把去畸变后的归一化坐标,正确无误地转换回我们最终需要的图像像素坐标。理解了这一点,你才算真正掌握了这个函数,而不是仅仅停留在“调用一下”的层面。
2. 函数参数深度拆解:不只是K和D那么简单
先来看看函数的原型,这是理解一切的起点。在OpenCV的文档里,它的声明大致是这样的:
void cv::fisheye::undistortPoints(
InputArray distorted,
OutputArray undistorted,
InputArray K,
InputArray D,
InputArray R = noArray(),
InputArray P = noArray()
);
看起来参数不少,我们一个一个掰开揉碎了说。distorted 和 undistorted 最好理解,就是输入的被畸变“折磨”的点,和输出的被“矫正”后的点,通常我们用 std::vector<cv::Point2f> 来存储。
K矩阵(内参矩阵),这是相机的身份证。它描述了相机如何把三维世界投影到二维图像上。一个典型的K矩阵长这样:
[ fx, 0, cx ]
[ 0, fy, cy ]
[ 0, 0, 1 ]
这里的 fx, fy 是焦距(以像素为单位),cx, cy 是图像的主点(通常接近图像中心)。这个矩阵是去畸变计算的基础,因为畸变模型本身就是基于这个内参定义的。
D矩阵(畸变系数),这是鱼眼镜头的“病历本”。对于鱼眼模型,OpenCV通常使用4个系数 (k1, k2, k3, k4) 来描述其畸变。函数正是利用这些系数,结合K矩阵,来反推一个畸变点在没有畸变时应该在哪里。
R矩阵(旋转矩阵),这个参数在 undistortPoints 里其实用得不多。它主要用于立体视觉中对极线校正的场景,你可以简单理解为给整个相机坐标系做个旋转。在单目相机单纯去畸变的场景下,我们通常直接传入 cv::noArray() 或者一个单位矩阵就行。
最后,也是最关键、最容易让人迷惑的 P矩阵(投影矩阵)。原始文章里反复强调的坑,就出在这里。它的默认值是 cv::noArray()。很多教程和早期代码都直接这么用,结果导致很多人(包括曾经的我)以为输出的 undistorted 就是像素坐标。大错特错!当P使用默认值时,函数输出的点位于归一化平面上。什么是归一化平面?想象一下,在相机


783

被折叠的 条评论
为什么被折叠?



