QiuQiu

Robotics - Calibrartion - Realsense D435 as example

相机内参、D435 双目深度与针孔模型

相机正前方,也就是相机 ZZ 轴指向的方向,投影到图像上的那个像素点。

更严格地说,它是:

相机光轴与等效成像平面的交点,在像素坐标系中的坐标。

语义的 debug

内参是相机属性的一部分,但不是所有相机属性都叫内参。

例如,物理焦距 ff、Sensor 的 pixel pitch、分辨率、曝光时间、快门类型都是相机或成像系统的属性,但狭义的相机内参通常指:

fx,fy,cx,cy f_x,\quad f_y,\quad c_x,\quad c_y

以及有时单独列出的 skew 和畸变参数。

像素与 D435 内部的多个相机

D435 内部有多个成像单元:

面对 D435 正面看,深度模组大致排列为:

右红外相机红外投影器左红外相机RGB 相机 \text{右红外相机} \rightarrow \text{红外投影器} \rightarrow \text{左红外相机} \rightarrow \text{RGB 相机}

这里的 Left/Right 是按照相机模组朝外观察的视角命名的;人面对相机时,左右视觉上会反过来。

每个相机都可以有自己的一套内参,两个相机之间还会有外参。因此不能把 RGB 相机的内参直接当成左、右红外相机的内参。

D435 如何得到深度

D435 的工作方式是:

  1. 红外投影器向物体表面投射人眼不可见的红外点阵。
  2. 左、右红外相机同时拍摄场景。
  3. D4 芯片对两幅图像进行校正并寻找对应点,计算视差 dd
  4. 根据双目几何计算深度。

对于已经极线校正的数字图像,常用公式是:

Z=fxBd Z=\frac{f_xB}{d}

其中:

因为 fxf_xdd 都以 pixel 为单位,它们的像素单位会相消,所以 ZZBB 使用相同的物理长度单位。

需要注意:D435 本质上仍然是双目立体视觉。投影器只是给白墙、无纹理零件等表面人工增加纹理,并不是像 ToF 那样直接测量光的飞行时间。关掉投影器以后,只要环境本身纹理足够,仍然可以计算深度。

Pinhole model

主平面

真实镜头可能包含多块透镜:

外界光线透镜 1透镜 2透镜 3Sensor \text{外界光线} \rightarrow \text{透镜 1} \rightarrow \text{透镜 2} \rightarrow \text{透镜 3} \rightarrow \text{Sensor}

光线在每块透镜上都会发生折射,因此很难用一块真实玻璃的位置代表整个镜组。光学上会把复杂镜组等效为一个简化系统:

复杂镜组等效主平面与焦距 \text{复杂镜组} \Longrightarrow \text{等效主平面与焦距}

主平面不是一块真实存在的玻璃,而是数学上的参考平面。

严格来说,厚透镜或复杂镜组通常有前、后两个主平面;这里为了理解相机投影,先使用简化的等效薄透镜或针孔模型。

焦距

焦距的理论定义:

当来自无限远物体的平行光经过镜头后,会聚到焦点;相应主平面到焦点的距离就是有效焦距。

f=主平面到焦点的物理距离 \boxed{f=\text{主平面到焦点的物理距离}}

对已经组装好的 RealSense,计算机视觉中通常不执着于单独测量物理焦距 ff,而是直接标定:

fx,fy,cx,cy f_x,\quad f_y,\quad c_x,\quad c_y

真实复杂镜头可以近似理解为:

理想针孔相机+畸变模型 \boxed{\text{理想针孔相机}+\text{畸变模型}}

Pixel pitch

单个 Sensor 像素是 Sensor 表面的一个感光格子,内部通常包含光电二极管等微型结构。

例如:

pixel pitch=3 μm \text{pixel pitch}=3\ \mu\text{m}

这种 Sensor 可以想象成一面铺满相同瓷砖的墙。

定义:

如果原始 Sensor 像素与输出图像像素一一对应,sx,sys_x,s_y 就是物理 pixel pitch。实际系统还可能进行 binning、裁剪、去畸变或缩放,因此 Sensor 像素和输出图片像素不一定一一对应。

在这种情况下,公式中的 sx,sys_x,s_y 更准确地理解为:

一个输出图像像素所对应的有效 Sensor 物理尺寸。

主点 (cx,cy)(c_x,c_y)

(cx,cy)(c_x,c_y) 是像素坐标系中的一个点,表示相机光轴与成像平面的交点,也叫主点。

理想装配下,它应该接近图像正中央。按照“整数坐标位于像素中心”的约定,更严格地写是:

cxW12,cyH12 c_x\approx\frac{W-1}{2},\qquad c_y\approx\frac{H-1}{2}

很多资料会简写成 W/2,H/2W/2,H/2;两种写法相差半个像素,来源是像素中心与图像边界的坐标约定不同。

但真实相机中通常会有偏差,原因包括:

所以需要通过标定得到更准确的 (cx,cy)(c_x,c_y)。相机标定一般同时估计:

fx,fy,cx,cy f_x,\quad f_y,\quad c_x,\quad c_y

以及畸变参数。

在 RealSense SDK 的 `rs2_intrinsics` 中:

从物理成像平面到像素坐标

1. 在相机坐标系中建立坐标

将相机坐标系原点设在相机的等效光心:

一个相机坐标系中的三维点写成:

Pc=(X,Y,Z) \mathbf P_c=(X,Y,Z)

计算机视觉通常使用位于光心前方的虚拟成像平面,这样可以避免真实 Sensor 位于镜头后方所带来的图像倒置符号。针孔投影得到等效成像平面上的物理位置:

xsensor=fXZ,ysensor=fYZ x_{\mathrm{sensor}}=f\frac{X}{Z},\qquad y_{\mathrm{sensor}}=f\frac{Y}{Z}

这里全部使用相同的物理长度单位,例如 mm:

这是相似三角形直接得到的:

xsensorf=XZ \frac{x_{\mathrm{sensor}}}{f}=\frac{X}{Z}

这一阶段使用的是物理焦距 ff

2. 但计算机图像不用 mm 表示位置

计算机看到的坐标是:

(u,v)=(420,250) (u,v)=(420,250)

单位是 pixel,而不是:

(x,y)=(1.26 mm,0.75 mm) (x,y)=(1.26\ \text{mm},0.75\ \text{mm})

假设一个输出像素对应的有效物理宽度是:

sx=0.003 mm/pixel s_x=0.003\ \text{mm/pixel}

那么成像平面上的物理横坐标与像素横坐标满足:

xsensor=(ucx)sx x_{\mathrm{sensor}}=(u-c_x)s_x

所以:

ucx=xsensorsx u-c_x=\frac{x_{\mathrm{sensor}}}{s_x}

再代入针孔投影:

xsensor=fXZ x_{\mathrm{sensor}}=f\frac{X}{Z}

得到:

ucx=fsxXZ u-c_x=\frac{f}{s_x}\frac{X}{Z}

为了简化书写,定义:

fx=fsx \boxed{f_x=\frac{f}{s_x}}

于是:

u=fxXZ+cx \boxed{u=f_x\frac{X}{Z}+c_x}

同理:

fy=fsy \boxed{f_y=\frac{f}{s_y}}
v=fyYZ+cy \boxed{v=f_y\frac{Y}{Z}+c_y}

所以除以 sx,sys_x,s_y 的原因非常简单:

把 Sensor 上以毫米表示的位置换算成以像素表示的位置。

ucxu-c_x 表示什么?

ucxu-c_x 表示成像点相对于主点的水平像素偏移量

xsensor=fX/Zx_{\mathrm{sensor}}=fX/Zfxf_x 吗?

不是。

xsensor=fXZ x_{\mathrm{sensor}}=f\frac{X}{Z}

这里:

fxf_x 是:

fx=fsx \boxed{f_x=\frac{f}{s_x}}

单位是 pixel。

两层关系是:

xsensor=fXZ单位:mm x_{\mathrm{sensor}}=f\frac{X}{Z} \qquad\text{单位:mm}

然后除以单个像素对应的有效物理宽度:

ucx=xsensorsx=fsxXZ=fxXZ单位:pixel u-c_x =\frac{x_{\mathrm{sensor}}}{s_x} =\frac{f}{s_x}\frac{X}{Z} =f_x\frac{X}{Z} \qquad\text{单位:pixel}

所以对应关系是:

ffxxsensorucx \boxed{f\longleftrightarrow f_x} \qquad \boxed{x_{\mathrm{sensor}}\longleftrightarrow u-c_x}

不是:

xsensor=fx x_{\mathrm{sensor}}=f_x

一句话记忆:

fX/ZfX/Z 算出成像平面上偏了多少毫米;fxX/Zf_xX/Z 算出图像上偏了多少像素。

为什么 fxf_x 只是一个系数,却叫内参?

数学上:

fx=fsx f_x=\frac{f}{s_x}

确实可以看成一个合并后的系数。

但内参不是指相机内部真实存在的零件,而是:

描述这台相机几何成像行为的模型参数。

fxf_x 虽然只是一个系数,却决定了三维方向在图像水平方向上会被展开成多少像素。

fxf_x 实际描述什么?

投影公式是:

ucx=fxXZ u-c_x=f_x\frac{X}{Z}

其中:

XZ \frac{X}{Z}

描述三维点相对于光轴的水平方向,也可以写成:

XZ=tanθx \frac{X}{Z}=\tan\theta_x

于是:

ucx=fxtanθx u-c_x=f_x\tan\theta_x

因此 fxf_x 表示:

三维观察方向发生一定变化时,图像坐标会变化多少像素。

例如,同一个三维方向:

XZ=0.1 \frac{X}{Z}=0.1

相机 A:

fx=600 f_x=600

得到:

ucx=60 pixels u-c_x=60\ \text{pixels}

相机 B:

fx=1000 f_x=1000

得到:

ucx=100 pixels u-c_x=100\ \text{pixels}

相机 B 会把同样的视角偏移展开成更多像素,看起来更加“放大”,视场角也更窄。

所以 fxf_x 可以理解为:

水平方向的角度/方向像素位移的缩放比例 \boxed{ \text{水平方向的角度/方向} \longrightarrow \text{像素位移的缩放比例} }

为什么不直接把 ffsxs_x 分开放进内参?

因为计算机视觉最终观察到的是数字图像中的像素位置,投影公式里总是以这个组合出现:

fsx \frac{f}{s_x}

相机仅根据标定图片通常可以可靠估计:

fx=fsx f_x=\frac{f}{s_x}

但不能仅凭图片唯一分解出:

f=多少 mm f=\text{多少 mm}

以及:

sx=多少 mm/pixel s_x=\text{多少 mm/pixel}

例如:

2 mm0.003 mm/pixel=666.7 pixels \frac{2\ \text{mm}}{0.003\ \text{mm/pixel}} =666.7\ \text{pixels}

下面这组参数也会产生相同的 fxf_x

4 mm0.006 mm/pixel=666.7 pixels \frac{4\ \text{mm}}{0.006\ \text{mm/pixel}} =666.7\ \text{pixels}

对针孔投影公式来说,这两种组合的结果相同。算法真正需要、也真正能从图像几何中识别的是它们的比值 fxf_x

如果已经从 Sensor 规格表知道真实 pixel pitch,并且确认输出图像没有额外缩放或裁剪,那么可以用 ffxsxf\approx f_xs_x 反推出物理焦距;但这使用了图片以外的额外硬件信息。

uucxc_x 属于同一个坐标空间

图像宽度为 W=1280W=1280 时,通常:

u[0,1279] u\in[0,1279]

cxc_x 也是横向像素坐标,例如:

cx=638.7 c_x=638.7

例如:

u=700,cx=638.7 u=700,\qquad c_x=638.7

则:

ucx=61.3 pixels u-c_x=61.3\ \text{pixels}

表示目标点位于主点右侧 61.361.3 个像素。像素坐标可以是小数,因为标定和特征检测可以达到亚像素精度。

因此:

uucxc_x 属于同一个坐标空间,但 uu 是变量,cxc_x 是固定内参。

“只是系数”为什么也可以是参数?

“参数”本来就是模型中控制行为的系数。

例如:

y=ax+b y=ax+b

其中:

在相机模型中:

u=fxXZ+cx u=f_x\frac{X}{Z}+c_x

它们共同决定相机如何把三维方向映射到像素。

为什么属于“内”参?

判断标准不是它是不是物理零件,而是它描述的是相机自身的成像关系,还是相机相对于外界的位置。

如果只是把 RealSense 从桌子左边移到右边,同时不改变成像 profile:

而如果更换镜头、Sensor,或者改变图像分辨率、裁剪和缩放方式:

所以它们描述的是相机自身在当前成像模式下的几何映射,而不是相机在世界中的位置,因此属于内参。

狭义的内参矩阵

一般形式是:

K=[fxγcx 0fycy 001] K= \begin{bmatrix} f_x&\gamma&c_x\ 0&f_y&c_y\ 0&0&1 \end{bmatrix}

其中 γ\gamma 是像素坐标轴的 skew。现代数字相机通常近似有 γ=0\gamma=0,于是:

K=[fx0cx 0fycy 001] K= \begin{bmatrix} f_x&0&c_x\ 0&f_y&c_y\ 0&0&1 \end{bmatrix}

这里要避免混淆:

两者不是同一个量。

畸变参数

此外还有畸变参数。例如 OpenCV 常见的 Brown–Conrady 参数:

D=(k1,k2,p1,p2,k3) D=(k_1,k_2,p_1,p_2,k_3)

其中:

RealSense 的具体 distortion model 需要以对应 stream profile 返回的模型为准,不能只看到五个系数就默认所有 stream 都使用完全相同的公式方向。

完整投影过程

完整投影过程可以理解为:

(X,Y,Z)(XZ,YZ)(xd,yd)(u,v) (X,Y,Z) \longrightarrow \left(\frac{X}{Z},\frac{Y}{Z}\right) \longrightarrow (x_d,y_d) \longrightarrow (u,v)

| 表示 | 所在空间/坐标 | 单位 | 含义 |

| (X,Y,Z)(X,Y,Z) | 相机三维坐标系 | m 或 mm | 点相对于相机光心的位置 |

| (xn,yn)=(X/Z,Y/Z)(x_n,y_n)=(X/Z,Y/Z) | 理想归一化图像坐标 | 无量纲 | 理想针孔模型中观察射线与虚拟 Z=1Z=1 平面的交点 |

| (xd,yd)(x_d,y_d) | 畸变后的归一化图像坐标 | 无量纲 | 真实镜头畸变后,该方向在归一化平面上的等效位置 |

| (u,v)(u,v) | 图像像素坐标系 | pixel | 最终落在数字图片中的位置 |

这里的 (xn,yn)(x_n,y_n)(xd,yd)(x_d,y_d) 都是投影模型中的中间坐标,不是直接拿尺子在外界测到的点。

内参矩阵本质上就是缩放和偏移

先得到归一化图像坐标:

xn=XZ,yn=YZ x_n=\frac{X}{Z},\qquad y_n=\frac{Y}{Z}

经过畸变模型后得到 (xd,yd)(x_d,y_d),再由内参转换为像素坐标:

u=fx,xd+cx,v=fy,yd+cy u=f_x,x_d+c_x,\qquad v=f_y,y_d+c_y

如果暂时忽略畸变,则:

u=fxXZ+cx,v=fyYZ+cy u=f_x\frac{X}{Z}+c_x,\qquad v=f_y\frac{Y}{Z}+c_y

矩阵形式:

[u v 1]=[fx0cx 0fycy 001][X/Z Y/Z 1] \begin{bmatrix} u\ v\ 1 \end{bmatrix} = \begin{bmatrix} f_x&0&c_x\ 0&f_y&c_y\ 0&0&1 \end{bmatrix} \begin{bmatrix} X/Z\ Y/Z\ 1 \end{bmatrix}

所以内参矩阵 KK 做的事情非常朴素:

  1. fx,fyf_x,f_y 缩放;
  2. cx,cyc_x,c_y 移动坐标原点。

可以这样理解:

fxf_x 虽然是 f/sxf/s_x 的合并系数,但它是数字相机真正可观测、真正用于投影的焦距参数。

回到 D435 的内参

首先只看 Right IR Camera。它在某个具体 stream profile 下有自己的一套:

fx,fy,cx,cy,D f_x,\quad f_y,\quad c_x,\quad c_y,\quad D

Left IR Camera 和 RGB Camera 也分别有自己的内参。改变分辨率、裁剪或缩放模式后,要读取对应 profile 的内参,不能无条件复用另一个 profile 的数值。

最后再次回答:

xsensor=fX/Zx_{\mathrm{sensor}}=fX/Z 不是 fxf_x。左边是一个以 mm 表示的位置;fxf_x 是一个以 pixel 表示的投影尺度参数。正确的像素关系是 ucx=fxX/Zu-c_x=f_xX/Z