本系列文章是记录学习高翔所著《视觉slam14讲》的内容总结,文中的主要文字和代码、图片都是引用自课本和高翔博士的博客。代码运行效果是在自己电脑上实际运行得出。手动记录主要是为了深入理解
相机模型是用来描述将三维世界坐标系中的坐标点映射到二维图像平面的过程
使用针孔和畸变两个模型来描述整个投影过程。
(1) 相机坐标系内中物理坐标点 P c ~ = [ X , Y , Z ] T \tilde{P_c}=[X,Y,Z]^T Pc~=[X,Y,Z]T,
(2) 成像平面内成像坐标点 P ′ = [ X ′ , Y ′ , Z ′ ] T P^{'}=[X^{'},Y^{'},Z^{'}]^T P′=[X′,Y′,Z′]T, 焦距为 f f f,
通过对称成像平面和归一化成像平面得到二者关系为
X ′ = f X Z Y ′ = f Y Z X^{'}=f\frac{X}{Z} \\ Y^{'}=f\frac{Y}{Z} X′=fZXY′=fZY
(3) 像素坐标系内像素坐标为 [ u , v ] T [u,v]^T [u,v]T,像素坐标系和成像平面相差了一个缩放( u 轴 缩 放 α 倍 , v 轴 缩 放 β 倍 u轴缩放\alpha 倍,v轴缩放\beta 倍 u轴缩放α倍,v轴缩放β倍)和一个原点的平移 [ c x , c y ] T [c_x,c_y]^T [cx,cy]T
p ′ p^{'} p′和像素坐标 [ u , v ] T [u,v]^T [u,v]T关系为
u = α X ′ + c x v = β Y ′ + c y u=\alpha X^{'}+c_x \\ v=\beta Y^{'}+c_y u=αX′+cxv=βY′+cy
代入上式得到像素坐标和相机坐标系内点的关系,
u = f x X Z + c x v = f y Y Z + c y u=f_x\frac{X}{Z}+c_x \\ v=f_y\frac{Y}{Z}+c_y u=fxZX+cxv=fyZY+cy
其中, f x = α f , f y = β f f_x=\alpha f, f_y=\beta f fx=αf,fy=βf
齐次化写成矩阵形式,
[ u v 1 ] = 1 Z [ f x 0 c x 0 f y c y 0 0 1 ] [ X Y Z ] = 1 Z K P c ~ \begin{bmatrix} u\\ v\\ 1 \end{bmatrix}=\frac{1}{Z} \begin{bmatrix} f_x & 0 & c_x\\ 0 & f_y & c_y\\ 0&0&1 \end{bmatrix}\begin{bmatrix} X\\ Y\\ Z \end{bmatrix}=\frac{1}{Z}K\tilde{P_c} ⎣⎡uv1⎦⎤=Z1⎣⎡fx000fy0cxcy1⎦⎤⎣⎡XYZ⎦⎤=Z1KPc~按照传统习惯将 Z Z Z挪到左侧,表达的就是相机坐标系中点和像素坐标系中像素的关系
Z [ u v 1 ] = [ f x 0 c x 0 f y c y 0 0 1 ] [ X Y Z ] = K P c ~ Z\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\\ Y\\ Z \end{bmatrix}=K\tilde{P_c} Z⎣⎡uv1⎦⎤=⎣⎡fx000fy0cxcy1⎦⎤⎣⎡XYZ⎦⎤=KPc~
把中间量 K K K称作相机内参。 K = [ f x 0 c x 0 f y c y 0 0 1 ] K=\begin{bmatrix} f_x & 0 & c_x\\ 0 & f_y & c_y\\ 0&0&1 \end{bmatrix} K=⎣⎡fx000fy0cxcy1⎦⎤
相机坐标 P P P是由它的世界坐标 P w P_w Pw变换而来,相机位姿由 R , t R,t R,t决定。那么有下式成立,
Z P u v = Z [ u v 1 ] = K ( R P w + t ) = K ( T P w ) ( 1 : 3 ) ZP_{uv}=Z\begin{bmatrix} u\\ v\\ 1 \end{bmatrix}=K(RP_w+t)=K(TP_{w})_{(1:3)} ZPuv=Z⎣⎡uv1⎦⎤=K(RPw+t)=K(TPw)(1:3)
最后一个式子里面隐含着一次齐次坐标到非齐次坐标的转换( T P w TP_w TPw的最后一维是1),也就是 P c ~ = K ( R P w + t ) = ( T P w ) ( 1 : 3 ) = [ X Y Z ] \tilde{P_c}=K(RP_w+t)=(TP_{w})_{(1:3)}=\begin{bmatrix} X\\ Y\\ Z \end{bmatrix} Pc~=K(RPw+t)=(TPw)(1:3)=⎣⎡XYZ⎦⎤
两边除以 Z Z Z, 所以, P u v = 1 Z K ( T P w ) ( 1 : 3 ) = 1 Z K P c ~ = K [ X Z Y Z 1 ] = K P c P_{uv}=\frac{1}{Z}K(TP_{w})_{(1:3)}=\frac{1}{Z}K\tilde{P_c}=K\begin{bmatrix} \frac{X}{Z}\\ \frac{Y}{Z}\\ 1 \end{bmatrix}=KP_c Puv=Z1K(TPw)(1:3)=Z1KPc~=K⎣⎡ZXZY1⎦⎤=KPc
其中 P c = [ X Z Y Z 1 ] P_c=\begin{bmatrix} \frac{X}{Z}\\ \frac{Y}{Z}\\ 1 \end{bmatrix} Pc=⎣⎡ZXZY1⎦⎤称为归一化坐标,二维的齐次坐标,它位于相机前方 z = 1 z=1 z=1的平面上,叫做归一化平面。
其实到现在才理解了什么是归一化平面,这个平面其实是相机坐标系下的一个平面,利用3d点除以z得到的平面。
变换过程总结:
世界坐标系下点 P w → R , t 或 者 T P c ~ = R P w + t = [ X Y Z ] → 投 影 到 归 一 化 平 面 P c = [ X Z Y Z 1 ] → 经 过 相 机 内 参 P u v = K P c P_w\xrightarrow{R,t或者T}\tilde{P_c}=RP_w+t=\begin{bmatrix} X\\ Y\\ Z \end{bmatrix}\xrightarrow{投影到归一化平面}P_c=\begin{bmatrix} \frac{X}{Z}\\ \frac{Y}{Z}\\ 1 \end{bmatrix}\xrightarrow{经过相机内参}P_{uv}=KP_c PwR,t或者TPc~=RPw+t=⎣⎡XYZ⎦⎤投影到归一化平面Pc=⎣⎡ZXZY1⎦⎤经过相机内参Puv=KPc
一般使用的去畸变处理方法:先对整张图像去畸变,得到去畸变后的图像,然后讨论图像上点的空间位置。
根据三角形相似得到
z − f z = b − u L + u R b \frac{z-f}{z}=\frac{b-u_L+u_R}{b} zz−f=bb−uL+uR
整理得到
z = f b d , d = u L − u R z=\frac{fb}{d}, d=u_L-u_R z=dfb,d=uL−uR
原理
例如一张宽度为640像素,高度为480像素的灰度图(0~255)表示为
//unsigned char image[h][w];
unsigned char image[480][640];
像素坐标原点位于图像的左上角,X轴向右。Y轴向下,Z轴向前(里)。
unsigned char pixel = image[y][x]
先定位到某一行,在到某一列,再到某一个通道,最后是某一个像素。
cv::Mat image = cv::read("lena.png");
for (size_t y=0; y(y);
unsigned char data_ptr = &row_ptr[x*image.channels()];
for(int c=0; c!=image.channels();c++)
{
unsigned char data = data_ptr[c];
}
}
}