AI老铁 · 2020年12月08日

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

转载自:双目测距系列(二)鱼眼镜头双目标定及测距
作者:ltshan139

前言

这几天把基于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<StereoSGBM> 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<double>(0,0))。
fx则为左摄像头内参矩阵的第一个值m_fisheye_intrinsicsL.val[0]
d则为每个像素在左右摄像头像素坐标系上X方向的差,由前面匹配步骤所得。



推荐阅读



更多海思AI芯片方案学习笔记欢迎关注海思AI芯片方案学习

推荐阅读
关注数
18801
内容数
1347
嵌入式端AI,包括AI算法在推理框架Tengine,MNN,NCNN,PaddlePaddle及相关芯片上的实现。欢迎加入微信交流群,微信号:aijishu20(备注:嵌入式)
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息