想象一下小时候玩过的"小孔成像"实验——在纸箱上戳个小洞,对面墙上就能出现倒立的影像。现代相机的核心原理与此完全相同,只是用镜头替代了针孔,用CMOS传感器替代了墙面。这个看似简单的模型,却是整个计算机视觉的基石。
在实际工程中,我们常用右手坐标系定义相机空间:Z轴指向镜头前方,X轴向右,Y轴向下。假设空间点P坐标为(X,Y,Z),经过焦距为f的镜头后,在成像平面形成倒立像P'。根据相似三角形原理,可以得到X'=fX/Z和Y'=fY/Z这两个关键公式。不过要注意,原始模型得到的是倒像,而现代相机都会通过软件自动翻转图像,所以我们在推导时可以省略负号,直接把成像平面对称到镜头前方,这样计算更直观。
我第一次做相机标定时,就曾困惑为什么公式里没有负号。后来拆解工业相机才发现,厂商在固件里已经做了图像翻转处理。这个细节提醒我们:理论模型需要根据实际硬件特性进行调整。
成像平面上的坐标还是物理尺寸(毫米),而我们需要的是像素坐标。这就引出了两个关键转换:
合并这两个变换,得到:
code复制u = αf(X/Z) + cx = fx(X/Z) + cx
v = βf(Y/Z) + cy = fy(Y/Z) + cy
其中fx=αf,fy=βf。这个转换引出了相机内参矩阵K:
code复制K = [[fx, 0, cx],
[0, fy, cy],
[0, 0, 1]]
在无人机视觉项目中,我发现内参的cx/cy并不总是图像中心。某次使用广角镜头时,cx比理论中心偏右15像素,这是因为镜头光学中心与传感器几何中心存在偏差。这个偏差虽然不大,但对立体视觉匹配的精度影响显著。
获取准确的内参需要标定过程。推荐使用OpenCV的calibrateCamera函数,配合棋盘格标定板。这里分享几个实用技巧:
python复制fx_guess = (sensor_width_pixels * focal_length_mm) / sensor_width_mm
cx_guess = sensor_width_pixels / 2
某次给机械臂装双目相机时,我发现标定误差始终在1.2像素以上。后来发现是标定板平整度问题——A4纸打印的棋盘格在湿度变化时会产生形变。改用玻璃基板后,重投影误差直接降到0.3像素以下。
视场角(FOV)直接决定相机"看"的范围,计算公式看似简单却容易踩坑:
但实际工程中,我们常用像素坐标计算:
python复制import numpy as np
hfov = 2 * np.arctan2(cx, fx) * 180/np.pi
vfov = 2 * np.arctan2(cy, fy) * 180/np.pi
在VR头盔光学设计时,发现FOV计算值与实测值偏差达5度。排查发现公式默认假设主点(cx,cy)在图像中心,而广角镜头的光学中心偏移会导致实际FOV扩大。修正后的公式需要加入补偿因子:
code复制实际_hfov = 2 * arctan((image_width - cx)/fx)
案例1:焦距混淆
有次调试工业相机时,发现35mm镜头计算的FOV比实际小。原因是镜头标注的是35mm等效焦距,而实际焦距需要根据传感器尺寸换算。对于1/2.3英寸传感器,转换系数约5.62,实际焦距=35/5.62≈6.2mm。
案例2:非矩形像素
某些安防相机的像素不是正方形。这时需要区分fx和fy:某型号相机fx=1200,fy=1180,如果错误地认为fx=fy,会导致FOV计算误差约1.5度。
案例3:镜头畸变
广角镜头的桶形畸变会使边缘FOV扩大10%-15%。精确计算需要先去除畸变,或者采用经验公式补偿。一个实用的补偿公式是:
code复制实际_fov = 标称_fov * (1 + 0.12*(distortion_coeff)^2)
在机器人导航系统中,相机FOV与其他参数存在复杂关联:
分辨率与检测距离:
code复制最小检测像素 = 2 # 目标至少占2×2像素
最远检测距离 = (目标实际宽度 * 焦距) / (最小检测像素 * 像素尺寸)
双目基线计算:
code复制最佳基线距离 = (检测距离 * 像素尺寸) / (2 * tan(FOV/2))
运动模糊控制:
code复制最大允许快门 = (像素尺寸 * 运动容差) / (目标速度 * tan(FOV/2))
在AGV导航项目里,我们通过调整焦距和传感器尺寸的组合,最终在2米检测距离和60cm盲区之间找到了平衡点。这个案例让我深刻理解到,相机选型本质是多参数优化问题。