1. 程式人生 > >Opencv—張正友標定流程及標定結果評價

Opencv—張正友標定流程及標定結果評價

##1.相機標定的目的 :

**相機標定的輸入:**標定影象上所有內點(inliers)的影象座標,標定板上所有inliers的空間三維座標(一般情況下假設Z=0平面上)。

**相機標定的輸出:**獲取攝像機的內參和外參矩陣,同時也會得到每一副標定影象的旋轉和平移矩陣,內參和外參可以對之後相機拍攝的影象就進行矯正,得到畸變相對很小的影象。

使用平臺: Ubuntu14.04 、Xtion 、13x8棋盤格標定板(尺寸20x20mm)

2.標定需要用到的函式詳解:

(1)使用findChessboardCorners函式提取角點,這裡的角點提取的是標定影象上的內點(inliers),函式原型為:

CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize,  
                                         OutputArray corners,  
                                  int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE );

第一個引數image:傳入拍攝的棋盤圖Mat影象,必須為8位或者rgb影象;
第二個引數patternSize:棋盤圖的內角點的行列數,行列一般不同。
第三個引數corners:儲存檢測到的角點二維座標位置,型別Point2f,vector<Point2f>image_corners
第四個引數:用於查詢棋盤格角點的方式,有預設值。

這裡函式是布林型別的,一般可以用來判斷該標定版是否檢測到角點,如果是,可進行find4QuadCornerSubpix 函式,輸出亞畫素。

(2)為了提高標定精度,需要在提取的角點資訊基礎上得到其精確位置,這裡我們用find4QuadCornerSubpix,有時候我們也用cornerSubPix。

CV_EXPORTS bool find4QuadCornerSubpix(InputArray img, InputOutputArray corners, Size region_size);

第一個引數img:輸入的Mat矩陣,最好是8位灰度影象,檢測效率更高;
第二個引數corners:初始的角點座標向量,既是輸入也是輸出,因為是亞畫素座標位置的輸出,所以需要用浮點型資料,一般用Point2f/Point3d,即 vector<Point2f/Point2d> image_corners
第三個引數region_size,角點搜尋視窗的尺寸;

** 在一般情況下,其實我們用得較多的是cornerSubPix,但是我們這裡用的是棋盤格,而
find4QuadCornerSubpix是專門用來獲取棋盤圖上內角點的精確位置的。

(3)在棋盤圖上繪製找到的內角點drawChessboardCorners

CV_EXPORTS_W void drawChessboardCorners( InputOutputArray image, Size patternSize,
                                         InputArray corners, bool patternWasFound );

第一個引數image:8位灰度或者彩色影象;
第二個引數patterSize:與findChessboardCorners()函式的patternSize是一樣的,指的是棋盤圖的內角點的行列數,行列一般不同。
第三個引數corners:與上面倆個corners是一致的,vector<Point2f/Point2d> image_corners
第四個引數patternWasFound:用來指示定義的棋盤內角點是否被完全的探測到,true表示被完整探測到,依次連線各個角點,大概示意圖如下:

這裡寫圖片描述

(4)獲取到棋盤標定圖的內角影象座標後,使用calibrateCamera()函式進行標定,計算相機內參和外參係數。

CV_EXPORTS_W double calibrateCamera( InputArrayOfArrays objectPoints,
                                     InputArrayOfArrays imagePoints,
                                     Size imageSize,
                                     CV_OUT InputOutputArray cameraMatrix,
                                     CV_OUT InputOutputArray distCoeffs,
                                     OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
                                     int flags=0, TermCriteria criteria = TermCriteria(
                                     TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON) );

第一個引數obejectPoints:為世界座標系中的三維點,在使用時,應該輸入一個三維座標點的向量的向量,即vector<vector<Point3f>> object_points。需要根據棋盤上單個黑白矩陣大小,計算出每個內角點的世界座標。
第二個引數imagePoints,為每一個內角點對應的影象座標點。和obejectPoints一樣,應該輸入vector<vector<Point3f>> image_points的變數。
第三個引數imageSize,為影象的畫素尺寸大小,在計算內參和畸變矩陣時需要使用到。
第四個引數cameraMatrix:為相機內參矩陣。輸入一個Mat cameraMatix即可,如Mat cameraMatix=Mat(3,3,CV_32FC1,Scalar::all(0));
第五個引數distCoeffs為畸變矩陣,輸入Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0));
第六個引數rvecs為旋轉向量:輸出一個Mat型別的vector,即vector<Mat> rvecs
第七個引數rvecs為平移向量:輸出一個Mat型別的vector,即vector<Mat> tvecs
第八個引數flag為標定時採用的演算法(這裡不展開,一般使用時預設為0)
第九個引數criteria是最終優化迭代終止條件設定。

在使用該函式進行標定運算之前,需要對棋盤上每個角點的空間座標系位置座標進行初始化(就是對其進行賦值),算出相機內參矩陣、相機畸變、另外每張圖片會生成屬於自己的平移向量和旋轉向量。

(5)對標定結果進行評價的方法是通過得到攝像機內外參對,利用projectPoints()函式對空間的三維點進行重新投影計算,得到空間三維點在影象上新的投影點的座標,計算投影座標和亞畫素角點座標之間的偏差,偏差越小,標定結果越好。

CV_EXPORTS_W void projectPoints( InputArray objectPoints,
                                 InputArray rvec, InputArray tvec,
                                 InputArray cameraMatrix, InputArray distCoeffs,
                                 OutputArray imagePoints,
                                 OutputArray jacobian=noArray(),
                                 double aspectRatio=0 );

第一個引數obejectPoints:為相機座標系中的三維座標點;
第二個引數revc:影象對應的旋轉向量;
第三個引數tvec:影象對應的位移向量;
第四個引數cameraMatrix:相機的內參;
第五個引數distCoeffs:相機的畸變;
第六個引數imagePoints:投影完後的影象上的座標點,即obejectPoints對應的畫素點。
第七個引數是雅克比矩陣;
第八個引數aspectRatio跟相機感測器感光單元有關,如果設定為非0,則函式預設感光單元dx/dy是固定的,會對雅克比矩陣進行調整。

(6)檢視標定效果,有倆種方法:1.使用initUndistortRectifyMap()和reamap()倆個函式配合使用,initUndistortRectifyMap( )用來計算畸變對映,remap把求得的對映應用到影象上。

initUndistortRectifyMap( )函式原型

CV_EXPORTS_W void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs,
                           InputArray R, InputArray newCameraMatrix,
                           Size size, int m1type, OutputArray map1, OutputArray map2 );

第一個引數cameraMatrix:為之前求得的相機的內參;
第二個引數distcoeffs:為之前求得的相機畸變矩陣;
第三個引數R:可選的輸入,是相機第一和第二相機座標系之間的旋轉矩陣;
第四個引數newCameraMatrix:,輸入校正後的攝像機矩陣;
第五個引數size:攝像機採集的無失真的影象尺寸;
第六個引數m1type,定義map1的資料型別,可以是CV_32FC1或者CV_16SC2;
第七個引數map1和第八個引數map2,輸出的X/Y座標重對映引數;

remap函式原型:

CV_EXPORTS_W void remap( InputArray src, OutputArray dst,
                         InputArray map1, InputArray map2,
                         int interpolation, int borderMode=BORDER_CONSTANT,
                         const Scalar& borderValue=Scalar());

第一個引數src,輸入引數,代表畸變的原始影象;
第二個引數dst,矯正後的輸出影象,跟輸入影象具有相同的型別和大小;
第三個引數cameraMatrix為之前求得的相機的內參矩陣;
第四個引數distCoeffs為之前求得的相機畸變矩陣;
第五個引數newCameraMatrix,預設跟cameraMatrix保持一致;

為了實現上面的方式,還可以使用undistort( )函式實現,但是效率較慢,這裡我們就不展開了。

未完待續。。。。