鱼眼镜头在机器人导航、VR全景拍摄等领域越来越常见,但它的超大视场角也给双目测距带来了独特挑战。去年参与一个室内机器人项目时,我花了两周时间反复调试StereoSGBM参数,才让鱼眼双目的测距精度达到实用水平。本文将分享参数调优过程中的关键发现,特别是那些文档里没写的实战细节。
鱼眼镜头的畸变模型与普通针孔相机截然不同。OpenCV的fisheye模块虽然提供了标定工具,但实际使用时有几个坑需要注意:
projectPoints反向验证重投影误差。我们遇到过一次K3系数异常大的情况,导致边缘点重投影误差超过5像素,重新标定后解决。鱼眼双目标定的核心代码如下:
cpp复制// 单目标定
double rms = fisheye::calibrate(
objectPoints, imagePoints,
imageSize, K, D,
rvecs, tvecs,
fisheye::CALIB_RECOMPUTE_EXTRINSIC | fisheye::CALIB_FIX_SKEW
);
// 双目标定
rms = fisheye::stereoCalibrate(
objectPoints, imagePoints1, imagePoints2,
K1, D1, K2, D2,
imageSize, R, T,
fisheye::CALIB_FIX_INTRINSIC // 保持单目标定结果
);
注意:鱼眼镜头的畸变系数D通常有4个参数(k1,k2,k3,k4),而普通相机模型只有5个(k1,k2,p1,p2,k3)
StereoSGBM有12个可调参数,经过上百次测试后,我发现这三个参数对鱼眼图像影响最大:
| 参数 | 典型值范围 | 影响效果 | 鱼眼场景注意事项 |
|---|---|---|---|
| blockSize | 3-11(奇数) | 窗口越大噪声越少但边缘越模糊 | 鱼眼边缘畸变区需要更大窗口 |
| numDisparities | 16的整数倍 | 值越大检测距离越远 | 需匹配焦距和基线长度 |
| uniquenessRatio | 5-15 | 值越大误匹配越少但空洞越多 | 纹理缺乏区域要调高 |
blockSize的权衡:在走廊场景测试发现,当blockSize从5增加到9时,墙面区域的噪声点减少40%,但门框边缘变得模糊。最终我们采用动态调整策略——中心区域用blockSize=5,边缘区域用blockSize=9。
numDisparities的坑:一开始设成16×10=160,结果近处物体出现阶梯状视差。后来发现鱼眼镜头的有效视差范围其实更小,改成16×6=96后效果更好,计算量还减少了37%。
经过多个场景实测,总结出以下配置组合:
python复制sgbm.setBlockSize(7)
sgbm.setNumDisparities(80) # 16×5
sgbm.setUniquenessRatio(10)
sgbm.setSpeckleWindowSize(200) # 鱼眼图像需要更大值
纹理丰富区域(如地毯、书架):
弱纹理区域(如白墙):
python复制sgbm.setMode(StereoSGBM.MODE_SGBM_3WAY)
sgbm.setBlockSize(11)
sgbm.setP1(8×3×11²=2904) # 更大窗口需要调整P1/P2
原始视差图常有噪声和空洞,这几个后处理方法很实用:
视差滤波:WLS滤波能保留边缘同时平滑区域
cpp复制Ptr<DisparityWLSFilter> wls_filter = createDisparityWLSFilter(sgbm);
wls_filter->setLambda(8000.0);
wls_filter->setSigmaColor(1.5);
wls_filter->filter(left_disp, left_img, filtered_disp);
空洞填充:对于深度连续区域(如墙面),用邻域均值填充比直接插值更合理
边缘锐化:对矫正后的鱼眼图像先做unsharp masking,可提升5-8%的匹配准确率
实测发现,经过优化的鱼眼双目系统在3米范围内能达到2%的相对精度,满足我们扫地机器人的避障需求。最意外的是,适当调高speckleWindowSize到200-300,反而比默认值100更适合鱼眼图像的散斑过滤。