机器视觉学习笔记(8)——基于OpenCV的Bouguet立体校正

机器视觉学习笔记(8)——基于OpenCV的Bouguet立体校正

标签: 机器视觉


1.什么是立体校正

在机器视觉学习笔记(7)——基于OpenCV的双目摄像机标定中,我们已经计算出描述两个{camera}坐标系关系的矩阵R和T,立体校正主要就是这两个参数在发挥作用。双目摄像机系统主要的任务就是测距,而视差求距离公式是在双目系统处于理想情况下推导的,所以就要将实际的双目系统校正为理想的双目系统。

理想双目系统:两摄像机图像平面平行,光轴和图像平面垂直,极点处于无线远处,此时点 ( x 0 , y 0 ) (x_0,y_0) (x0,y0)对应的级线就是 y = y 0 y=y_0 y=y0

2.Bouguet校正原理

  • 校正过程如下图所示:
  • 校正过程中两个图像平面均旋转一半的R,这样可以使重投影畸变最小,此时两个摄像机图像平面共面(畸变校正后光轴也平行),但是行不对准
  • 极点是两个{camera}坐标系原点的连线和图像平面的交点,要想使得极点处于无穷远处(即行对准),就必须两个摄像机的图像平面和两个{camera}坐标系原点的连线平行
  • 可以计算 R r e c t R_{rect} Rrect矩阵使得极点处于无穷远处:

    (1) R r e c t = [ e 1 T e 2 T e 3 T ] R_{rect}= \begin{bmatrix} e_1^T\\e_2^T\\e_3^T \end{bmatrix}\tag{1} Rrect=e1Te2Te3T(1)
    由于图像平面最终和{camera}坐标系连线平行,所以
    (2) e 1 = T ∣ ∣ T ∣ ∣ e_1=\frac{T}{||T||}\tag{2} e1=TT(2)
    其中 T = [ T x , T y , T z ] T T=[T_x,T_y,T_z]^T T=[Tx,Ty,Tz]T e 2 e_2 e2 e 1 e_1 e1正交即可,选择主光轴方向 ( 0 , 0 , 1 ) (0,0,1) (0,0,1) e 1 e_1 e1叉积
    (3) e 2 = [ − T y T x 0 ] T T x 2 + T y 2 e_2=\frac {\begin{bmatrix}-T_y&T_x&0\end{bmatrix}^T} {\sqrt{T_x^2+T_y^2}}\tag{3} e2=Tx2+Ty2 [TyTx0]T(3)
    e 3 e_3 e3只要与 e 1 e_1 e1 e 2 e_2 e2正交即可,使用叉积
    (4) e 3 = e 1 × e 2 e_3=e_1 \times e_2\tag{4} e3=e1×e2(4)
    R r e c t R_{rect} Rrect左乘到 R R R分解后作用于左右{camera}坐标系的矩阵即可得到最终的立体校正矩阵。

3.重投影矩阵Q

重投影矩阵Q实现了世界坐标系{world}和像素坐标系{pixel}之间的转换。具体如下:
(5) Q = [ 1 0 0 − c x 0 1 0 − c y 0 0 0 f 0 0 − 1 / T x ( c x − c x ′ ) / T x ] Q=\begin{bmatrix}1&0&0&-c_x\\ 0&1&0&-c_y\\ 0&0&0&f\\ 0&0&-1/T_x&(c_x-c_x^{'})/T_x\\ \end{bmatrix}\tag{5} Q=100001000001/Txcxcyf(cxcx)/Tx(5)

(6) Q [ x y d 1 ] = [ X Y Z W ] Q\begin{bmatrix}x\\y\\d\\1\end{bmatrix}= \begin{bmatrix}X\\Y\\Z\\W\end{bmatrix}\tag{6} Qxyd1=XYZW(6)
其中d表示视差,三维坐标为 ( X / W , Y / W , Z / W ) (X/W,Y/W,Z/W) (X/W,Y/W,Z/W) c x ′ c_x{'} cx表示右图像的主点,当校正正确时, c x = c x ′ c_x=c_x{'} cx=cx,在立体校正正确的前提下展开(6)式,可得
(7) ( X / W , Y / W , Z / W ) = ( − x − c x d T x , − y − c y d T x , − f d T x ) (X/W,Y/W,Z/W)=(-\frac{x-c_x}{d}T_x, -\frac{y-c_y}{d}T_x, -\frac{f}{d}T_x)\tag{7} (X/W,Y/W,Z/W)=(dxcxTx,dycyTx,dfTx)(7)
之所以有负号是因为 T x T_x Tx是负的,之所以要减去 c x c_x cx c y c_y cy是因为{pixel}坐标系的原点在左上角,而{camera}坐标系的原点在光轴上。

其实三维坐标根据图形相似的原理可以直接写出来,如果能做到此步骤,证明是真的理解了双目测距的原理。

4.源码解析

一共有3个过程,分别是计算旋转矩阵和投影矩阵,计算校正查找映射表,校正两幅图像检验效果。

4.1计算旋转矩阵和投影矩阵

使用OpenCV中stereoRectify函数即可

void stereoRectify(
    InputArray cameraMatrix1, 
    InputArray distCoeffs1, 
    InputArray cameraMatrix2, 
    InputArray distCoeffs2, 
    Size imageSize, 
    InputArray R, 
    InputArray T, 
    OutputArray R1,//左摄像机旋转矩阵
    OutputArray R2,//右摄像机旋转矩阵
    OutputArray P1,//左摄像机投影矩阵 
    OutputArray P2,//右摄像机投影矩阵 
    OutputArray Q,//重投影矩阵
    int flags=CALIB_ZERO_DISPARITY,//主点坐标相同 
    double alpha,//裁减系数
    Size newImageSize=Size(), 
    Rect* roi1=0, Rect* roi2=0
)
    //应用
	//Mat R1, R2, P1, P2, Q;
	//Rect roi1, roi2;
	//stereoRectify(M1, D1, M2, D2, imageSize, R, T, R1, R2, P1, P2, Q,
	//	CALIB_ZERO_DISPARITY, 0, imageSize, &roi1, &roi2);

4.2计算校正查找映射表

校正查找映射表可以将原始图像和校正后的图像上的点一一对应起来

使用OpenCV中initUndistortRectifyMap函数即可,需要注意的是要针对左右相机分别计算校正查找映射表。

void initUndistortRectifyMap(
    InputArray cameraMatrix, 
    InputArray distCoeffs, 
    InputArray R, 
    InputArray newCameraMatrix,
    Size size, 
    int m1type,
    OutputArray map1, 
    OutputArray map2
)
    //应用
    //Mat rmap[2][2];
    //initUndistortRectifyMap(M1, D1, R1, P1, imageSize, CV_16SC2, rmap[0][0], rmap[0][1]);
    //initUndistortRectifyMap(M2, D2, R2, P2, imageSize, CV_16SC2, rmap[1][0], rmap[1][1]);

4.3检验效果

载入两张图片,校正后合并到一张大图上,每隔32个像素画一条横线,可以明显的看到实现了行对准。

	Mat img1 = imread("left01.jpg"), img1r;
	Mat img2 = imread("right01.jpg"), img2r;

	Mat img(imageSize.height, imageSize.width * 2, CV_8UC3);//高度一样,宽度双倍
	imshow("rectified", img);
	remap(img1, img1r, rmap[0][0], rmap[0][1], CV_INTER_AREA);//左校正
	remap(img2, img2r, rmap[1][0], rmap[1][1], CV_INTER_AREA);//右校正

	Mat imgPart1 = img( Rect(0, 0, imageSize.width, imageSize.height) );//浅拷贝
	Mat imgPart2 = img( Rect(imageSize.width, 0, imageSize.width, imageSize.height) );//浅拷贝
	resize(img1r, imgPart1, imgPart1.size(), 0, 0, CV_INTER_AREA);
	resize(img2r, imgPart2, imgPart2.size(), 0, 0, CV_INTER_AREA);

	//画横线
	for( int i = 0; i < img.rows; i += 32 )
		line(img, Point(0, i), Point(img.cols, i), Scalar(0, 255, 0), 1, 8);
	
	//显示行对准的图形
	Mat smallImg;//由于我的分辨率1:1显示太大,所以缩小显示
	resize(img, smallImg, Size(), 0.5, 0.5, CV_INTER_AREA);
	imshow("rectified", smallImg);

效果如下:

5.总结

  • 可以将标定的结果保存在本地的文本文件中,这样只需要标定一次,以后直接载入标定的参数即可
  • 截止本篇博文为止,双目系统系统的标定就结束了。其实标定这一块可以做很多文章,张正友标定法应用有近20年了,相信其他的标定方法也有不少。由于博主的主要任务还是识别与匹配,所以就不打算在这里多做停留了
  • 标定部分主要就是线性代数、矩阵分析和机器人学的知识,要是不理解标定的推导过程,可以学习一下这几个方面的知识,以后也会用到
  • 实现起来还是蛮简单的,但是最好能推导一下公式,如果一直调用API但是不理解原理,很快就会遇到天花板

你可能感兴趣的:(机器视觉学习笔记)