双目测距系列(二)鱼眼镜头双目标定及测距

前言

这几天把基于opencv C++ api将鱼眼镜头的双目标定以及测距功能实现完毕,效果还可以,至少对齐得非常棒。 这里把其流程及其关键函数在这里总结一下。

对于双目标定而言,opencv一共支持两种模型:普通针孔相机模型和鱼眼相机模型fisheye。后者是opencv3.0后才开始支持的。从使用角度讲,它俩主要差别就在于畸变系数不一样。

双目测距流程一共分为四大步:标定,对齐,匹配以及测距。这点对于普通摄像头模型和鱼眼模型都适用。下面就基于鱼眼摄像头模型来讲解各个步骤具体内容。

标定

标定Calibration包括单目标定和双目标定,前者的输出结果主要是内参(3x3矩阵,包括fx,fy以及cx和cy)和畸变系数(1x4矩阵 K1,K2,K3,K4);后者输出的主要是是外参,即右摄像头基于左摄像头的姿态,包括R和T两个矩阵。

标定一个主要工作就是对着标定板拍图,标定板最好遍布整个图像区域,一般20~30张就足够了。 opencv目前可以对三种pattern的标定板:棋盘格,圆以及非对称圆来找角点,其API如下所示:

        case Settings::CHESSBOARD:
            found = findChessboardCorners( view, s.boardSize, pointBuf, chessBoardFlags);
            break;
        case Settings::CIRCLES_GRID:
            found = findCirclesGrid( view, s.boardSize, pointBuf );
            break;
        case Settings::ASYMMETRIC_CIRCLES_GRID:
            found = findCirclesGrid( view, s.boardSize, pointBuf, CALIB_CB_ASYMMETRIC_GRID );

角点正确找到后,就可以开始单目标定,其对应API为:

    CV_EXPORTS_W double calibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, const Size& image_size,
        InputOutputArray K, InputOutputArray D, OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, int flags = 0,
            TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 100, DBL_EPSILON));

单目标定结束后,接下来就是双目标定:

    CV_EXPORTS_W double stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2,
                                  InputOutputArray K1, InputOutputArray D1, InputOutputArray K2, InputOutputArray D2, Size imageSize,
                                  OutputArray R, OutputArray T, int flags = fisheye::CALIB_FIX_INTRINSIC,
                                  TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 100, DBL_EPSILON));

这里需要注意的是,双目标定可以基于前面单目标定出来的内参来直接算R和T,也可以将单目内参作为一个初始值来重新迭代计算出新的内参和R以及T。

对齐

摄像头内参和外参都有了之后,就可以开始调用下面的API来分别获得左、右摄像头新的旋转矩阵R和内参投影矩阵P。

    CV_EXPORTS_W void stereoRectify(InputArray K1, InputArray D1, InputArray K2, InputArray D2, const Size &imageSize, InputArray R, InputArray tvec,
        OutputArray R1, OutputArray R2, OutputArray P1, OutputArray P2, OutputArray Q, int flags, const Size &newImageSize = Size(),
        double balance = 0.0, double fov_scale = 1.0);

 紧接着是基于新的矩阵来生成左右摄像头的映射表left_mapx, left_mapy, right_mapx以及right_mapy。

    CV_EXPORTS_W void initUndistortRectifyMap(InputArray K, InputArray D, InputArray R, InputArray P,
        const cv::Size& size, int m1type, OutputArray map1, OutputArray map2);

有了映射表mapx和mapy,在后面测距的时候就可以调用remap()来对新的测试图片进行校正。

匹配 

匹配是相对最耗时的步骤,即使前面左右图像对齐后,只需要在行上来匹配。常用的匹配算法有SGBM,BM等等。相对来讲,SGBM兼顾了速度和准确度,因而用的比较多。

	Ptr sgbm = StereoSGBM::create(0, 16, 3);
	sgbm->setPreFilterCap(63);
	sgbm->setBlockSize(pParas->sgbmWindowSize);
	int channel_cnt = left_rectify_img.channels();
	sgbm->setP1(8 * channel_cnt * pParas->sgbmWindowSize * pParas->sgbmWindowSize);
	sgbm->setP2(32 * channel_cnt * pParas->sgbmWindowSize * pParas->sgbmWindowSize);
	sgbm->setMinDisparity(0);
	sgbm->setNumDisparities(pParas->NumDisparities);
	sgbm->setUniquenessRatio(pParas->UniquenessRatio);
	sgbm->setSpeckleWindowSize(101);
	sgbm->setSpeckleRange(10);
	sgbm->setDisp12MaxDiff(-1);
	sgbm->setMode(StereoSGBM::MODE_SGBM);

opencv已经将匹配算法 封装的很好了,唯一需要注意的就是参数值得调节会带来不一样得匹配效果。常见的需要调节的参数有:

		paras.sgbmWindowSize = 7;
		paras.NumDisparities = 16 * 20;
		paras.UniquenessRatio = 12;

测距 

匹配完成就能得到视差图disparity map。 有了视差图,每个点的Z方向上深度值获取就变得简单了。通过下面公式:

Z = B * fx / d

B是两个摄像头之间的距离,其值等于外参平移矩阵X方向上的绝对值,即abs(T.at(0,0))。

fx则为左摄像头内参矩阵的第一个值m_fisheye_intrinsicsL.val[0]

d则为每个像素在左右摄像头像素坐标系上X方向的差,由前面匹配步骤所得。

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(深度学习)