OpenCV双目测距——手工选择匹配点测试

        和上一篇一样,首先简单介绍一下使用OpenCV的范例代码来进行双目标定。我所使用的版本是3.x,4.x差别不大。

一、stereo_calib.cpp的使用

        需要说明的是各个参数,标定板棋盘格上黑白方块交点的横向个数为w,纵向个数为h。s为每个格子的宽度,这个宽度很容易在ps或者word等软件中看到,推荐ps可以切换单位为mm。为了减少标定图像提高标定准确度,3D打印了一个相机固定架和L形的标定板“固定架”,标定板设计时为w=13,h=8。修改代码大约349行左右设置默认参数行为:

cv::CommandLineParser parser(argc, argv, "{w|13|}{h|8|}{s|4.233333333|}{nr||}{help||}{@input|stereo_calib.xml|}");

并在stereo_calib.xml中输入图像对名称——不一定图像越多越准确,很多标定结果问题都是因为某些“不够好的”图片引起的,所以“够用”就好。并且,为了便于观察识别角点位置的准确程度和顺序的正确性,稍微修改一下程序第370行左右:

StereoCalib(imagelist, boardSize, squareSize, true, true, showRectified);

即displayCorners参数为true来显示角点识别顺序和位置。如果观察时间太短,可以修改124行左右:

char c = (char)waitKey(1000);

单位为毫秒,代码中默认500。

二、标定结果

OpenCV双目测距——手工选择匹配点测试_第1张图片可以看到我只用了4组图片,效果么,马马虎虎吧——用一把直尺测量的结果和鼠标选定点的结果一致,精度也就在1mm左右。

现在,我们需要intrinsics.yml和extrinsics.yml来进行双目测距了:

三:stereo_match.cpp的代码

        羊头挂好了,上狗肉:

       Dim img1 As Mat = ImRead(My.Application.Info.DirectoryPath & "\image\left00.jpg", ImreadModes.Color)
        Dim img2 As Mat = ImRead(My.Application.Info.DirectoryPath & "\image\right00.jpg", ImreadModes.Color)

        Dim img_size As Size = img1.Size()
        Dim roi1, roi2 As New Rect
        Dim Q As New Mat

        Dim fs As FileStorage = New FileStorage("intrinsics.yml", FileStorage.Mode.Read)
        Dim M1, D1, M2, D2 As Mat
        M1 = fs.Item("M1")
        D1 = fs.Item("D1")
        M2 = fs.Item("M2")
        D2 = fs.Item("D2")

        fs.Open("extrinsics.yml", FileStorage.Mode.Read)

        Dim R, T, R1, P1, R2, P2 As New Mat
        R = fs.Item("R")
        T = fs.Item("T")
        StereoRectify(M1, D1, M2, D2, img_size, R, T, R1, R2, P1, P2, Q, StereoRectificationFlags.ZeroDisparity, -1, img_size, roi1, roi2)

        Dim map11, map12, map21, map22 As New Mat
        InitUndistortRectifyMap(M1, D1, R1, P1, img_size, CV_16SC2, map11, map12)
        InitUndistortRectifyMap(M2, D2, R2, P2, img_size, CV_16SC2, map21, map22)

        Dim img1r, img2r As New Mat
        Remap(img1, img1r, map11, map12, InterpolationFlags.Linear)
        Remap(img2, img2r, map21, map22, InterpolationFlags.Linear)

        img1 = img1r
        img2 = img2r

        pnlLeft.BackgroundImage = ToBitmap(img1)
        pnlRight.BackgroundImage = ToBitmap(img2)

OpenCV双目测距——手工选择匹配点测试_第2张图片

分别选择粉红色版右上角,可以看到输出值倒数第二个为290mm。在代码中首先读取两个yml文件,而后矫正图像并显示,之后计算两点对应的空间点坐标时,使用TriangulatePoints函数即可,得到一个points4D值,其中第三个与Z坐标对应。如果你的需求是得到点云并且希望使用OPENCV的图像匹配算法,可以参照stereo_match.cpp的代码,使用BM,SGBM等算法来得到它,其参数设置方法与之前一致,参数意义大约如下(有错误的地方欢迎指正):

   img1_filename = samples::findFile(parser.get(0));		//输入的左侧图像
    img2_filename = samples::findFile(parser.get(1));		//输入的右侧图像
    if (parser.has("algorithm"))
    {
        std::string _alg = parser.get("algorithm");		//使用的算法
        alg = _alg == "bm" ? STEREO_BM :
            _alg == "sgbm" ? STEREO_SGBM :
            _alg == "hh" ? STEREO_HH :
            _alg == "var" ? STEREO_VAR :
            _alg == "sgbm3way" ? STEREO_3WAY : -1;
    }
    numberOfDisparities = parser.get("max-disparity");		//最大视差,数值要整除16。STEREO_VAR归一化([0,1])
    SADWindowSize = parser.get("blocksize");				//SAD窗口大小,数值为奇数。匹配窗口大小
    scale = parser.get("scale");							//缩放比例
    no_display = parser.has("no-display");						//显示结果
    if( parser.has("i") )
        intrinsic_filename = parser.get("i");		//输入内部矩阵
    if( parser.has("e") )
        extrinsic_filename = parser.get("e");		//输入外部矩阵
    if( parser.has("o") )
        disparity_filename = parser.get("o");		//输出差异图像
    if( parser.has("p") )
        point_cloud_filename = parser.get("p");	//输出点云文件
    cv::CommandLineParser parser(argc, argv,
        "{@arg1|left00.jpg|}{@arg2|right00.jpg|}{help h||}{algorithm|sgbm|}{max-disparity|64|}{blocksize|5|}{no-display||}{scale|1|}{i|intrinsics.yml|}{e|extrinsics.yml|}{o||}{p||}");

其中max-disparity和blocksize需要仔细调试,各个算法之间运算速度和效果也有差异可以自行百度一下。

剩下的就是左右图像特征匹配的问题了,可以看到俩相机拍摄的时候曝光、白平衡、角度不同引起的反光@#$&%#^&#$OOXX问题很多……后面还得一点一点克服。

你可能感兴趣的:(其他)