雙目測距系列(二)魚眼鏡頭雙目標定及測距

前言

這幾天把基於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方向的差,由前面匹配步驟所得。

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章