从针孔到畸变:单目、双目与深度相机的成像原理与坐标变换全解析

张开发
2026/6/2 16:06:28 15 分钟阅读
从针孔到畸变:单目、双目与深度相机的成像原理与坐标变换全解析
1. 相机成像的基础针孔模型与坐标系想象一下小时候玩过的小孔成像实验在黑暗的房间里蜡烛的光线通过一个小孔在对面白纸上形成倒立的火焰影像。这就是现代相机最基础的针孔模型Pinhole Model。虽然现在相机都用透镜替代了针孔但这个模型仍然是理解成像原理的黄金标准。在实际应用中我们需要四个关键坐标系来描述这个成像过程世界坐标系就像地球上的经纬度用来标记物体在三维空间中的绝对位置。比如自动驾驶中可以用它记录行人、车辆的位置坐标。相机坐标系以相机镜头中心为原点Z轴指向拍摄方向。我在调试工业相机时常需要手动测量这个坐标系与物体的相对位置。图像坐标系以成像平面中心为原点用毫米单位表示。这个坐标系在镜头标定时特别重要。像素坐标系最终照片上的像素位置左上角为原点(0,0)。处理OpenCV图像时我们操作的就是这个坐标系。坐标变换就像语言翻译先把世界坐标翻译成相机坐标外参矩阵再转换为图像坐标透视投影最后转为像素坐标内参矩阵。这个过程中内参矩阵包含焦距、主点等相机固有参数而外参矩阵则记录相机在空间中的位置和角度。2. 当理想遇到现实透镜与畸变模型真实的相机不可能真的用针孔成像——那样进光量太小照片会非常暗。于是我们给相机装上透镜但这带来了新的问题畸变。就像透过鱼眼镜头看世界直线会变成曲线。我在做无人机视觉系统时曾遇到过典型的桶形畸变拍摄的建筑物边缘像被吸向中心一样弯曲。这种径向畸变Radial Distortion通常由透镜曲率引起离图像中心越远越明显。其数学模型可以用多项式表示x_corrected x(1 k1*r² k2*r⁴ k3*r⁶) y_corrected y(1 k1*r² k2*r⁴ k3*r⁶)其中k1、k2、k3是需要标定的参数。另一种切向畸变就像把照片斜着撕开又拼回去源于透镜与传感器不平行。工厂装配时的微小偏差就会导致这种问题其校正公式包含p1、p2两个参数x_corrected x [2p1*y p2*(r²2x²)] y_corrected y [p1*(r²2y²) 2p2*x]实际项目中我通常用OpenCV的cv2.calibrateCamera()一次性估计所有畸变参数。有个实用技巧拍摄棋盘格时要让标定板尽量充满整个画面特别是边缘区域这样标定结果更准确。3. 单目相机的局限与突破普通手机摄像头就是典型的单目相机它最大的短板是丢失深度信息。还记得针孔模型那个Zc吗在投影到二维图像时这个深度值被压缩掉了。这就好比看一幅风景画很难判断远处的山距离我们到底有多远。但人类视觉系统给了我们启发虽然单目相机无法直接测距但可以通过一些先验知识来估算。比如已知物体实际大小如人脸平均宽度多帧图像的运动视差Structure from Motion深度学习模型预测深度图我在开发AR应用时就结合IMU传感器数据和单目SLAM算法实现了不错的深度估计效果。不过要注意这些方法都有误差累积问题长时间跟踪可能漂移。4. 双目视觉给相机装上两只眼模仿人类双眼的双目相机通过两个并排放置的摄像头像我们左右眼一样形成视差disparity。这个视差d与物体深度z成反比z (f * b) / d其中f是焦距b是两个相机的基线距离相当于人眼间距。实测中我发现几个关键点基线距离b越大测距范围越远无人机常用10-20cm匹配算法对精度影响极大SGBM比BM算法效果更好纹理少的区域如白墙很难计算视差有个有趣的实验尝试用双目相机测量手指距离。你会发现当手指离相机很近时视差会突然跳变——这是因为超过了最大可测量范围。这也解释了为什么VR设备需要根据瞳距调整镜头间距。5. 深度相机的黑科技RGB-D相机如Kinect、RealSense是更直接的解决方案它们通过主动发射红外光来测量距离。目前主流技术有两种结构光Structured Light投射特定图案通过形变计算深度飞行时间Time-of-Flight测量光线往返时间我在做机械臂抓取时对比过这两种技术结构光在近距离0.3-3米精度更高但室外强光下会失效ToF相机工作距离更远但容易受多重反射干扰最新的iPhone面容识别就用到了结构光技术。而自动驾驶激光雷达本质上是高级版的ToF系统。不过要注意透明物体如玻璃和强吸光材料黑绒布仍是所有深度相机的克星。6. 从理论到实践相机标定全流程理解了原理后让我们看看如何实际操作。相机标定就像给相机做体检目的是确定内参和畸变参数。以下是经过多个项目验证的标准流程准备标定板建议使用不对称的棋盘格图案我用的是7x9格每个方格30mm采集数据在不同角度拍摄15-20张照片确保标定板覆盖整个画面区域运行标定import cv2 objpoints [] # 3D点 imgpoints [] # 2D点 # 检测角点 ret, corners cv2.findChessboardCorners(gray, (9,7), None) if ret: objpoints.append(objp) imgpoints.append(corners) # 标定 ret, mtx, dist, rvecs, tvecs cv2.calibrateCamera( objpoints, imgpoints, gray.shape[::-1], None, None)验证结果重投影误差应小于0.1像素可以用cv2.projectPoints()可视化检查常见坑点标定板要足够平整褶皱会影响角点检测光照要均匀避免反光或阴影相机对焦要锁定避免不同照片焦距变化7. 三维重建的基石坐标变换实战掌握了相机模型后就能进行更高级的应用——将二维图像坐标转换回三维世界坐标。这个逆过程需要一些假设条件比如已知地面平面方程。一个实用的单目测距方法假设目标物体在地平面上Z0通过homography矩阵将图像点映射到世界坐标计算与相机的水平距离# 已知相机高度h和俯仰角theta R cv2.Rodrigues(rvec)[0] # 旋转矩阵 t np.array([[0], [-h*np.cos(theta)], [h*np.sin(theta)]]) # 图像点转世界坐标 uv_point np.array([[u], [v], [1]]) tempMat np.linalg.inv(mtx).dot(uv_point) s (t[2] - R[2,:].dot(t)) / (R[2,:].dot(tempMat)) world_point np.linalg.inv(R).dot(s * tempMat - t)在车载环视系统项目中我用这个方法实现了车位检测误差控制在10cm以内。关键是要准确测量相机的安装高度和角度任何微小偏差都会被距离放大。

更多文章