游乐游手机版
首页/AI热点日报/热点详情

双目视觉深度检测测距原理详解与算法实现

类型:热点整理2026-07-11
尽管近期主要精力被神经科学、大脑记忆机制以及毕业活动占据了不少,但双目视觉方面的进展仍有必要梳理总结。毕竟上一篇博文发布后,多位同行发来邮件交流探讨,许多之前悬而未决的问题,也在讨论和反复试验中逐渐明晰。 开篇先要感谢 maxwellsdemon 和 wobject,没有大家的共同琢磨,这篇文章也不

尽管近期主要精力被神经科学、大脑记忆机制以及毕业活动占据了不少,但双目视觉方面的进展仍有必要梳理总结。毕竟上一篇博文发布后,多位同行发来邮件交流探讨,许多之前悬而未决的问题,也在讨论和反复试验中逐渐明晰。

开篇先要感谢 maxwellsdemon 和 wobject,没有大家的共同琢磨,这篇文章也不可能成形。

谈到双摄像头测距,首先需要回顾一下原理。翻阅 Learning OpenCV 第416页和418页,可以看到下面这两张图:

图1. 双摄像头模型俯视图

图2, 双摄像头模型立体视图

图1清晰地说明了双摄像头测距的原理,书中给出的 Z 公式如下:

在 OpenCV 中,f 以像素为单位,Tx 的量纲由标定所用的棋盘格实际尺寸和输入值决定,通常设为毫米,若需更高精度可设为0.1毫米。d = xl - xr 同样以像素为单位。分子分母相约后,z 的量纲与 Tx 一致。

图2展示了双摄像头如何获取空间中某点的三维坐标。实际计算均基于相似三角形,具体表达式如 Q 矩阵所示:

空间中某点的三维坐标为 (X/W, Y/W, Z/W)。

因此,要精确计算某点与摄像头的距离,需要获取焦距 f、视差 d、摄像头中心距 Tx 这几个参数。若还想知道 X 和 Y 坐标,则还需知晓左右像平面与立体坐标系原点的偏移 cx 和 cy。f、Tx、cx 和 cy 可通过立体标定获得初始值,再通过立体校准进行优化,使两个摄像头在数学上达到完全平行,左右摄像头的 cx、cy、f 均一致(即图2中左右视图完全平行对准的理想状态)。而立体匹配的任务,就是在上述基础上求出最后一个变量——视差 d(通常需要达到亚像素精度)。这样,计算一个点三维坐标所需的准备工作才算完成。

搞清原理后也就明白了,标定、校准、匹配这几个步骤,本质上都是为了更准确地获取 f、d、Tx、cx、cy。

双目测距的原理就介绍到这里。为避免大家看得头晕,下面用 FAQ 的形式,将实现双摄像头测距过程中遇到的几个疑点串联起来。这些解答仅代表个人理解,如有不当之处,欢迎指正。

Q1:标定时棋盘格大小如何设置,对结果有影响吗?

当然有影响。标定时必须指定棋盘方格的实际长度(通常用毫米,更精确可设为0.1毫米),该值必须与实际长度一致,标定结果才能用于实际距离测量。一般来说,尺寸设置准确后,立体标定得到的 Translation 向量第一个分量 Tx 的绝对值,即为左右摄像头的中心距。可用此值验证立体标定的准确性。例如,我将棋盘格大小设为270(27mm),最终得到的 Tx 为602.8(60.28mm),精度相当不错。

Q2:立体标定得到的 Tx 符号为什么是负的?

说实话,这个问题我也未完全吃透。个人理解是:立体标定得到的 T 向量方向是从右摄像头指向左摄像头(即 Tx 为负),而 OpenCV 坐标系中原点位于左摄像头。因此,在校准时,需要将该向量的三个分量符号全部取反,最终计算出的距离才能为正。

不过还有一个问题:Learning OpenCV 中 Q 的表达式,第四行第三列元素为 -1/Tx,但实际使用时求出的却是 1/Tx。针对这一点,作者与同行讨论后认为,可能是书中 Q 表达式里的负号是为了抵消 T 向量的反方向而设,但在实际编写 OpenCV 代码时,那位朋友并未加入负号。这仅是一己之见,期待更详细的解释。

Q3:cvFindStereoCorrespondenceBM 的输出不是以像素为单位的视差?

在 OpenCV 2.0 中,BM 函数的结果以16位有符号数存储。由于精度要求,所有视差在输出时被放大了16倍(2的4次方)。具体代码如下:

dptr[y*dstep] = (short)(((ndisp - mind - 1 + mindisp)*256 + (d != 0 ? (p-n)*128/d : 0) + 15) >> 4);

可以看到,原始视差左移了8位(256),加上一个修正值后,又右移了4位,最终效果相当于左移4位。因此,实际计算距离时,cvReprojectTo3D 输出的 X/W、Y/W、Z/W 都需要乘以16(即 W 除以16),才能得到正确的三维坐标。

Q4:双摄像头测距时,世界坐标系原点在哪?

世界坐标系的原点位于左摄像头凸透镜的光心。

说到这儿,需要提一下针孔模型。图3中描绘的针孔模型是凸透镜成像的简化版。当物距足够远(远大于两倍焦距)时,凸透镜成像可视为在焦距处的小孔成像。

图3. 针孔模型

实际计算时,为方便起见,我们常将像平面翻转并平移到针孔前,得到一种数学上更简单的等价形式(便于计算相似三角形),如图4所示。

图4. 针孔模型的数学等价形式

对应图2即可理解,世界坐标系原点就是左摄像头针孔模型中的针孔,也就是左摄像头凸透镜的光心。

Q5:f 和 d 的单位是像素,这个像素到底代表什么?如何与毫米换算?

这个问题同样与针孔模型有关。在针孔模型中,光线穿过针孔(即凸透镜中心)后在焦距处成像,因此图3的像平面即为摄像头的 CCD 传感器表面。每个 CCD 都有一定的尺寸和分辨率,由此确定了毫米与像素之间的转换关系。例如,CCD 尺寸为8mm×6mm,分辨率为640×480,那么毫米与像素的换算关系为80 pixel/mm。

实际应用中,我们在数学上将该像平面等效到小孔前(图4),相当于在透镜中心点前假设了一块虚拟的 CCD 传感器。

Q6:为什么 cvStereoRectify 求出的 Q 矩阵中 cx、cy、f 与原来不一样?

这一点前面提到过。实际测量中,由于摄像头摆放的原因,左右摄像头的 f、cx、cy 均不相同。为了使左右视图达到完全平行对准的理想状态,便于数学计算,立体校准实际上是在左右像重合区域最大的前提下,让两个摄像头光轴的前向平行,再让左右摄像头的 f、cx、cy 保持一致。因此,Q 矩阵中的值与两个内参矩阵的值不同,也就不难理解了。

实验结果:

实际测量结果表明,虽然 Block Matching 算法本身的精度有限,但测距结果基本可以接受。结果如图5所示:

图5. OpenCV 双摄像头测距结果

图中从左到右的三个物体分别放置在距离摄像头50cm、75cm和90cm的位置。测距效果尚可。当然,这是较好的结果。由于 BM 算法的限制,同一片点云中相同距离的点通常存在正负2厘米以内的误差。

图6是用双目摄像头测量物体长宽的结果,看起来就不那么准确了……

图6. OpenCV 双摄像头测边长结果

物体宽度为117-88=29mm,但实际宽度是5.2cm;高度为71-13=58mm,实际高度却为13cm。这个误差令人有些困惑。

另外还有一个至今未完全弄明白的问题:双摄像头的中心距,为什么用的是 Tx 而不是 T 向量的长度?因为要使左右视图重合区域最大化,两个摄像头的光轴必须与 T 垂直(如图7所示)。这样,校正后两个摄像头的中心距应该是 T 才对。不知道这种理解是否正确?

图7. 双摄像头立体校准俯视图

来源:https://m.elecfans.com/article/2068629.html

相关热点

继续查看同栏目近期热点。

延伸阅读

补充最近整理过的热点入口。