双目相机、SLAM、极线几何与相机标定总结
双目相机、SLAM、极线几何与相机标定总结
目录
- 为什么 SLAM 常把图像变成灰度图
- 双目深度中的 d、f、B、Z
- 什么是极平面、极线和图像平面
- 什么是去畸变、双目矫正和极线对齐
- ORB-SLAM3 双目特征匹配的大致流程
- 为什么右目畸变错误会影响 SLAM 和手部轨迹
- 矫正后没有黑边是否代表操作错误
- 焦距到底决定什么
- 为什么焦距更大时深度分辨能力更好
- 标定误差、重投影误差与极线误差
- 如何在真实场景验证极线误差
- 标定阶段与运行阶段的正确参数组合
- AprilTag 检测与 solvePnP 的关系
- 最常见的错误与检查清单
1. 为什么 SLAM 常把图像变成灰度图
传统几何 SLAM 主要关心的是角点、边缘、局部纹理、明暗变化、同一特征在前后帧的位置变化,以及同一特征在左右目图像中的位置差。
这些信息通常用单通道灰度图就能表达,因此很多 SLAM 系统会把彩色图:
B、G、R 三个通道转换成灰度图:
Gray 一个通道OpenCV 中常见写法:
gray=cv2.cvtColor(color_image,cv2.COLOR_BGR2GRAY)灰度图并不是只有纯黑和纯白,而是:
0:黑色255:白色- 中间数值:不同程度的灰色
常见灰度转换近似为:
[
Gray=0.299R+0.587G+0.114B
]
灰度化的主要优点:
- 数据量约降为彩色图的三分之一
- 特征提取和匹配更快
- 内存占用更小
- 左右相机色彩差异影响更小
- 传统 ORB、FAST 等特征本身主要依赖亮度结构
颜色并不是完全没用。语义 SLAM、目标检测、手部识别、彩色指套识别等任务仍可使用彩色图。
推荐并行流程:
灰度左右图 → 双目 SLAM → 相机轨迹、地图点、深度 彩色左目图 → 手部、AprilTag、物体检测2. 双目深度中的 d、f、B、Z
双目矫正后,某个空间点在左右图中的位置分别为:
[
p_L=(u_L,v_L)
]
[
p_R=(u_R,v_R)
]
理想情况下:
[
v_L\approx v_R
]
视差定义为:
[
d=u_L-u_R
]
双目深度公式:
[
Z=\frac{fB}{d}
]
| 符号 | 含义 | 常见单位 |
|---|---|---|
| (d) | 左右图中同一真实点的水平像素差 | px |
| (f) | 矫正图像对应的像素焦距 | px |
| (B) | 左右相机光心之间的物理距离,即基线 | m、mm |
| (Z) | 该点沿相机前方方向的深度 | m、mm |
2.1 视差 d 的直观含义
例如同一个桌角:
左图横坐标:uL = 450 右图横坐标:uR = 370.4则:
[
d=450-370.4=79.6\text{ px}
]
意思是:同一个真实桌角,在左右两张矫正后的图像中,水平方向错开了 79.6 个像素。
它不是左右相机的真实距离。
2.2 基线 B 的含义
例如:
[
B=0.1\text{ m}=100\text{ mm}
]
它是左右相机光心之间的真实物理距离,可以通过机械尺寸或双目标定外参获得。
因此:
- (d) 是图像中的像素错位
- (B) 是现实世界中的相机间距
二者不是同一个量,但存在关系:
[
d=\frac{fB}{Z}
]
2.3 近大远小
在焦距和基线固定时:
[
d\text{ 大}\Rightarrow Z\text{ 小}
]
[
d\text{ 小}\Rightarrow Z\text{ 大}
]
通俗地说:近处物体在左右图中“跳得多”,远处物体“跳得少”。
3. 什么是极平面、极线和图像平面
3.1 左右图像平面
图像平面是相机模型中的几何平面。
通俗理解:
- 左相机有一张“成像画布”
- 右相机有一张“成像画布”
空间中的一个点会分别投影到两张画布上。
实际数字图像是该连续平面被像素化后的结果:
三维空间 ↓ 投影 连续图像平面 ↓ 采样 数字图像中的像素坐标 (u, v)工程上可以近似理解为:
- 左图像平面对应左目图像
- 右图像平面对应右目图像
3.2 极平面
设:
- 左相机光心为 (O_L)
- 右相机光心为 (O_R)
- 空间点为 (P)
三点:
[
O_L,\ O_R,\ P
]
确定一个平面,这个平面叫做极平面。
3.3 极线
极平面分别与左右图像平面相交,产生两条直线:
- 左极线
- 右极线
极线的含义是:已知左图中的一个点后,它在右图中的对应点理论上只能出现在对应极线上,而不是出现在右图任意位置。
未经双目矫正时,极线可能是斜线;经过双目矫正后,极线通常变成水平线。
4. 什么是去畸变、双目矫正和极线对齐
这三个概念要分开。
4.1 去畸变 Undistortion
单个镜头会产生径向畸变、切向畸变等。
去畸变使用该相机的:
- 内参 (K)
- 畸变参数 (D)
把弯曲的直线和错误的像素位置修正到理想针孔相机模型中。
左右相机各有自己的:
[
K_L,D_L
]
[
K_R,D_R
]
4.2 双目矫正 Stereo Rectification
双目矫正还要使用左右相机之间的外参:
[
R,T
]
它把左右相机虚拟地旋转到一个更方便匹配的姿态,使得:
- 两个图像平面共面
- 两个水平轴平行
- 同一个空间点在左右图中的纵坐标接近
- 极线变成水平线
OpenCV 常见流程:
R1,R2,P1,P2,Q,roi1,roi2=cv2.stereoRectify(K1,D1,K2,D2,image_size,R,T)然后生成映射表:
map1_x,map1_y=cv2.initUndistortRectifyMap(K1,D1,R1,P1,image_size,cv2.CV_32FC1)map2_x,map2_y=cv2.initUndistortRectifyMap(K2,D2,R2,P2,image_size,cv2.CV_32FC1)执行重映射:
left_rect=cv2.remap(left_raw,map1_x,map1_y,cv2.INTER_LINEAR)right_rect=cv2.remap(right_raw,map2_x,map2_y,cv2.INTER_LINEAR)4.3 极线对齐
双目矫正后的理想条件:
[
v_L^{rect}\approx v_R^{rect}
]
于是左右匹配只需沿水平方向寻找:
二维搜索:上、下、左、右都找 变成 一维搜索:只在同一水平行附近左右找这就是极线对齐带来的主要价值。
5. ORB-SLAM3 双目特征匹配的大致流程
双目 SLAM 通常不是只检测左目,也不是简单在右图中寻找“相同亮度的像素”。
更准确的流程是:
左图提取 ORB 特征点和描述子 右图提取 ORB 特征点和描述子 ↓ 以左目特征为主建立匹配 ↓ 在右图相同水平极线附近筛选候选 ↓ 比较 ORB 描述子 ↓ 局部灰度块进一步细化 ↓ 得到亚像素右目坐标 ↓ 计算视差和深度5.1 为什么不是“相同亮度”
单个像素亮度相同,并不代表是同一个空间点。
ORB 描述子比较的是特征点周围的局部明暗结构。
例如:
左目描述子:101101001011... 右目候选A:101001111011... 右目候选B:101101001001...通过汉明距离判断谁更相似。
通常还会限制:
- 极线位置
- 合理视差范围
- 特征尺度
- 描述子距离
- 局部图块差异
5.2 为什么常常只显示左目特征
很多系统以左目为参考相机。
最后保存的信息可能是:
左目像素坐标 右目匹配坐标 视差 深度因此查看器只显示左目,不代表右目没有参与计算。
6. 为什么右目畸变错误会影响 SLAM 和手部轨迹
误差链路可以写成:
右目畸变参数错误 → 右图去畸变位置错误 → 双目矫正后无法正确极线对齐 → 左右特征匹配减少或错配 → 视差错误 → 三角化深度和地图点错误 → ORB-SLAM3 相机位姿优化受影响 → SLAM 世界坐标系中的手部轨迹继承相机位姿误差例如本来应该:
左图点:(450, 320) 右图点:(370, 320)右目畸变错误后可能变成:
右图错误位置:(373, 326)此时:
- 水平位置错误,导致视差错误
- 垂直位置错误,导致极线误差
水平视差:
[
d=450-373=77
]
纵向极线误差:
[
e_{\text{epi}}=|320-326|=6\text{ px}
]
如果 SLAM 输出的相机世界位姿为:
[
T_{\text{world}\leftarrow\text{camera}}
]
手部相机坐标为:
[
P_{\text{camera}}
]
则:
[
P_{\text{world}}
T_{\text{world}\leftarrow\text{camera}}
P_{\text{camera}}
]
相机位姿错了,手部世界坐标也会继承该误差。
7. 矫正后没有黑边是否代表操作错误
不一定。
去畸变或双目矫正后,理论上可能出现黑边,因为输出画布中的某些位置在原图中没有对应像素。
但如果使用了以下方式,图像可以没有黑边:
- 放大
- 自动裁剪
- 有效 ROI
alpha=0- 公共有效区域
- 查看器只显示有效部分
OpenCV 中:
new_K,roi=cv2.getOptimalNewCameraMatrix(K,D,image_size,alpha,image_size)常见含义:
| alpha | 效果 |
|---|---|
| 0 | 尽量没有黑边,但会裁剪或放大 |
| 1 | 尽量保留完整视野,但可能有黑边 |
| 0~1 | 折中 |
双目矫正中也有类似的alpha参数。
7.1 裁剪是否破坏几何真值
正确理解:
裁剪会损失边缘视野,但不必然破坏保留区域的几何正确性。
只要后续参数同步更新:
- 新焦距正确
- 新主点正确
- 新投影矩阵正确
- 左右坐标系一致
极线仍可对齐。
7.2 真正危险的情况
图像已经经过:
- 去畸变
- 双目矫正
- 裁剪
- 缩放
却仍然使用原始:
[
K,D
]
这会造成参数与图像坐标不一致。
例如左边裁掉 50 像素:
[
c_x’=c_x-50
]
如果又缩放 0.8 倍:
[
f_x’=0.8f_x
]
[
f_y’=0.8f_y
]
[
c_x’=0.8(c_x-50)
]
[
c_y’=0.8(c_y-y_{\text{crop}})
]
8. 焦距到底决定什么
相机投影公式:
[
u=f_x\frac{X}{Z}+c_x
]
[
v=f_y\frac{Y}{Z}+c_y
]
焦距决定:
- 空间角度对应多少像素
- 目标在图像中占多大
- 相机视场角有多宽
- 同样空间运动产生多大像素位移
- 双目视差有多明显
8.1 焦距与视场角
近似关系:
[
\mathrm{FOV}
2\arctan\left(\frac{\text{传感器尺寸}}{2f}\right)
]
因此:
- 焦距大:视野窄,目标更大
- 焦距小:视野宽,目标更小
8.2 物理焦距与像素焦距
物理焦距:
2.8 mm 4 mm 8 mm像素焦距:
fx = 800 px fy = 798 px二者关系大致为:
[
f_x=
\frac{f_{\text{mm}}}
{\text{单像素物理宽度}_{\text{mm}}}
]
SLAM、双目深度和solvePnP通常使用像素焦距。
9. 为什么焦距更大时深度分辨能力更好
物体真实深度不会因为焦距改变而改变。
“深度分辨能力更好”是指:同样的真实深度变化,在图像中会造成更明显的视差变化,因此更容易区分相近的两个深度。
双目关系:
[
d=\frac{fB}{Z}
]
在 (B) 和 (Z) 固定时:
[
f\text{ 越大}\Rightarrow d\text{ 越大}
]
9.1 数值例子
设:
[
B=0.1\text{ m}
]
[
Z=2\text{ m}
]
焦距 400 px
[
d=\frac{400\times0.1}{2}=20\text{ px}
]
若视差误差为 1 px:
[
Z_{19}=\frac{400\times0.1}{19}\approx2.105\text{ m}
]
[
Z_{21}=\frac{400\times0.1}{21}\approx1.905\text{ m}
]
深度误差约 10 cm 量级。
焦距 800 px
[
d=\frac{800\times0.1}{2}=40\text{ px}
]
若视差误差同样为 1 px:
[
Z_{39}=\frac{800\times0.1}{39}\approx2.051\text{ m}
]
[
Z_{41}=\frac{800\times0.1}{41}\approx1.951\text{ m}
]
深度误差约 5 cm 量级。
近似误差传播:
[
|\Delta Z|
\approx
\frac{Z^2}{fB}|\Delta d|
]
因此:
[
f\text{ 越大}\Rightarrow |\Delta Z|\text{ 越小}
]
前提是视差匹配误差 (\Delta d) 基本相同。
注意:单纯把数字图像resize放大,并不会产生新的真实光学信息,也不会真正提升深度精度。
10. 标定误差、重投影误差与极线误差
10.1 标定中的重投影误差
标定时使用的是原始带畸变图像。
流程:
原始畸变图像 → 检测棋盘格、ChArUco 等角点 → 得到观测像素位置 → 拟合 K、D、每张图的 R、t → 把已知三维角点重新投回原始图像 → 比较预测点和检测点检测到的实际图像角点:
[
p_{\text{obs}}=(u_{\text{obs}},v_{\text{obs}})
]
标定模型预测位置:
[
p_{\text{proj}}=(u_{\text{proj}},v_{\text{proj}})
]
单点重投影误差:
[
e=
\sqrt{
(u_{\text{obs}}-u_{\text{proj}})^2+
(v_{\text{obs}}-v_{\text{proj}})^2
}
]
例如:
[
p_{\text{obs}}=(500.3,320.7)
]
[
p_{\text{proj}}=(499.8,321.1)
]
则:
[
e\approx0.64\text{ px}
]
总 RMS 重投影误差:
[
\mathrm{RMS}
\sqrt{
\frac{1}{N}
\sum_{i=1}{N}e_i2
}
]
它表示标定模型与标定图中检测结果的一致程度。
它不直接等于真实三维测量误差,也不等于双目极线误差。
10.2 极线误差
双目矫正后,对应点应该满足:
[
v_L^{rect}\approx v_R^{rect}
]
每个点的垂直极线误差可定义为:
[
e_{\text{epi},i}
|v_{L,i}{rect}-v_{R,i}{rect}|
]
例如:
左图角点:(450.3, 320.2) 右图角点:(372.8, 320.7)则:
[
e_{\text{epi}}=|320.2-320.7|=0.5\text{ px}
]
横坐标差 (u_L-u_R) 是视差,纵坐标差 (v_L-v_R) 才是矫正后的极线误差。
11. 如何在真实场景验证极线误差
应该使用没有参加标定的独立数据。
推荐:
- 30~100 对同步左右图
- 棋盘格
- ChArUco
- 多 AprilTag 板
- 覆盖中央、四角、近中远距离和不同倾角
完整流程:
独立左右同步图 → 使用标定参数做双目矫正 → 在左右矫正图中检测相同编号角点 → 计算每个点的纵坐标差 → 统计 mean、median、RMS、P95、max → 按图像位置画误差分布11.1 计算代码
from__future__importannotationsimportnumpyasnpdefcompute_rectified_epipolar_error(points_left:np.ndarray,points_right:np.ndarray,)->dict[str,float]:""" 输入: points_left: (N, 2),左矫正图中的 (u, v) points_right: (N, 2),右矫正图中的对应 (u, v) 输出: 极线误差统计,单位为像素 """points_left=np.asarray(points_left,dtype=np.float64)points_right=np.asarray(points_right,dtype=np.float64)ifpoints_left.shape!=points_right.shape:raiseValueError("左右对应点数量或形状不一致")ifpoints_left.ndim!=2orpoints_left.shape[1]!=2:raiseValueError("输入必须为形状 (N, 2)")iflen(points_left)==0:raiseValueError("没有有效对应点")errors=np.abs(points_left[:,1]-points_right[:,1])return{"count":float(len(errors)),"mean_px":float(np.mean(errors)),"median_px":float(np.median(errors)),"rms_px":float(np.sqrt(np.mean(errors**2))),"p95_px":float(np.percentile(errors,95)),"max_px":float(np.max(errors)),}11.2 推荐统计指标
至少报告:
- 有效对应点数量
- 平均误差 mean
- 中位数 median
- RMS
- P95
- 最大误差 max
- 图像中央和四角分区误差
- 静态状态
- 动态状态
经验参考:
| 平均垂直极线误差 | 大致评价 |
|---|---|
< 0.3 px | 很好 |
0.3~0.5 px | 较好 |
0.5~1.0 px | 通常可用 |
1~2 px | 建议排查 |
> 2 px | 双目匹配通常明显受影响 |
这只是经验值,最终要结合:
- 图像分辨率
- 焦距
- 基线
- 工作距离
- SLAM 匹配阈值
- 所需深度精度
11.3 静态与动态分开测试
静态测试主要反映:
- 内参
- 畸变参数
- 双目外参
- 矫正算法
动态测试还会包含:
- 左右时间不同步
- 滚动快门
- 运动模糊
- 支架形变
- 自动曝光差异
- 自动对焦变化
如果静态很好、动态变差,问题可能主要在同步和成像时序,而不是标定本身。
12. 标定阶段与运行阶段的正确参数组合
这是整个流程最容易混淆的地方。
12.1 标定阶段
第一次标定时还没有可靠的 (K,D),因此不能先正确去畸变再标定。
正确流程:
原始畸变图 → 直接检测标定板角点 → 拟合 K、D、R、t → 用 K、D 把三维角点投影回原始畸变图 → 计算重投影误差检测棋盘格角点本身通常不需要输入相机参数:
ret,corners=cv2.findChessboardCorners(gray,pattern_size)12.2 运行阶段
运行时可以选两种正确做法。
方案 A:在原始图上检测
原始畸变图 → 检测 Tag 四角 → 使用原始 K、D 做 solvePnPsuccess,rvec,tvec=cv2.solvePnP(object_points,image_points_raw,K_original,D_original,)方案 B:先去畸变或双目矫正,再检测
原始图 → 使用 K、D 去畸变或双目矫正 → 在处理后的图上检测 Tag → 使用处理后图像对应的新内参 → 畸变设为零success,rvec,tvec=cv2.solvePnP(object_points,image_points_rectified,K_rectified,None,)核心对应关系:
| 图像坐标类型 | 应使用的相机参数 |
|---|---|
| 原始畸变图 | 原始 (K) + 原始 (D) |
| 单目去畸变图 | (K_{\text{new}}) + 零畸变 |
| 双目矫正左图 | 从 (P_1) 提取的新内参 + 零畸变 |
| 双目矫正右图 | 从 (P_2) 提取的新内参 + 零畸变 |
双目矫正左图的新内参通常可写为:
[
K_{\text{rect,left}}=P_1[:,0:3]
]
13. AprilTag 检测与 solvePnP 的关系
要区分两个步骤。
13.1 Tag 检测
输入图像,输出:
- Tag ID
- 四个角点像素坐标
这一阶段主要依赖:
- 黑白边缘
- 方形轮廓
- 编码内容
- 局部纹理
通常不需要输入 (K,D)。
13.2 姿态估计
根据:
- Tag 四个图像角点
- Tag 真实边长
- 对应图像坐标系的相机内参
- 图像是否还包含畸变
计算:
[
rvec,\ tvec
]
如果 Tag 物体坐标以中心为原点,tvec表示 Tag 中心在相机坐标系中的位置。
OpenCVsolvePnP的关系:
[
X_c=RX_o+t
]
其中:
- (X_o):Tag 坐标系中的点
- (X_c):相机坐标系中的点
- (R,t):Tag 到相机的位姿
如果物体点单位是 mm,则tvec也是 mm。
13.3 关键原则
角点检测: “角点在图像哪里?” 姿态估计: “Tag 离相机多远、朝向如何?”检测阶段通常不依赖相机参数;姿态估计阶段必须使用和图像坐标一致的相机参数。
14. 最常见的错误与检查清单
14.1 常见错误
错误 1:矫正图继续使用原始 K、D
图像已去畸变 + solvePnP 仍使用原始 K、D会产生二次畸变补偿和坐标不一致。
错误 2:图像裁剪后主点未修改
裁掉左侧 (x_0) 像素后:
[
c_x’=c_x-x_0
]
裁掉顶部 (y_0) 像素后:
[
c_y’=c_y-y_0
]
错误 3:图像缩放后焦距和主点未缩放
缩放比例为 (s_x,s_y):
[
f_x’=s_xf_x
]
[
f_y’=s_yf_y
]
[
c_x’=s_xc_x
]
[
c_y’=s_yc_y
]
错误 4:左右使用不同 ROI,但未同步坐标
左右图必须在统一的矫正坐标定义下做视差比较。
错误 5:只看重投影误差,不测真实极线误差
重投影误差小,不保证双目外参和真实使用状态一定好。
错误 6:只看平均误差
必须同时看:
- median
- RMS
- P95
- max
- 图像位置分布
错误 7:只做静态测试
头部设备还应测试:
- 转头
- 快速平移
- 动态光照
- 运动模糊
- 时间同步
14.2 推荐检查清单
相机状态
- 分辨率与标定一致
- 裁剪方式与标定一致
- 自动对焦关闭或固定
- 曝光和增益尽量固定
- 电子防抖关闭
- 数字变焦关闭
- 左右时间同步正常
- 机械支架无松动
标定参数
- 左相机 (K_1,D_1) 对应左图
- 右相机 (K_2,D_2) 对应右图
- 双目外参 (R,T) 方向正确
- 基线单位正确
- 图像尺寸与标定尺寸一致
- 矫正后使用 (P_1,P_2) 对应的新内参
- 矫正图畸变设置为零
极线验证
- 使用独立验证图像
- 左右角点编号严格对应
- 计算 (|v_L-v_R|)
- 统计 mean、median、RMS、P95、max
- 检查图像中央与四角
- 静态和动态分别测试
- 可视化水平线和误差热图
SLAM 验证
- 左右匹配数量是否正常
- 深度分布是否合理
- 近距离与远距离深度是否稳定
- 转头时轨迹是否异常抖动
- 纯旋转时是否出现明显虚假平移
- 地图点是否在边缘区域异常
- 手部世界坐标是否继承头部轨迹漂移
最终核心总结
- 灰度化是为了减少计算量并保留几何特征。
- 视差 (d) 是同一真实点在左右图中的水平像素差。
- 基线 (B) 是左右相机光心之间的真实物理距离。
- 焦距 (f) 决定空间角度被映射成多少像素。
- 极线限定了对应点在另一张图中的可能位置。
- 双目矫正使对应极线水平化,满足 (v_L\approx v_R)。
- 右目畸变错误会破坏极线对齐、视差和三角化深度。
- 矫正后无黑边不一定是错误,可能只是裁剪或放大了有效区域。
- 裁剪可以保留几何正确性,但必须同步更新内参和主点。
- 焦距增大不会改变真实深度,但会让视差变化更明显,从而降低相同像素误差带来的深度误差。
- 重投影误差是模型预测像素和标定图中检测像素之间的距离。
- 极线误差是矫正后左右对应点的纵坐标差。
- 标定时在原始畸变图上检测角点。
- 运行时原始图使用原始 (K,D),矫正图使用新内参和零畸变。
- Tag 检测和 Tag 姿态估计是两个不同步骤。
最重要的参数一致性原则:
原始图坐标 ↔ 原始 K、D 去畸变图坐标 ↔ 新 K、零畸变 双目矫正图坐标 ↔ P1/P2 对应的新内参、零畸变