%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
辛苦原創所得,轉載請注明出處
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start -- 攝像機標定 ---------------------------------------------->
攝像機標定的數學過程如下
http://blog.csdn.net/ssw_1990/article/details/53216767
標定事先選用棋盤格要注意一些問題,張正友論文中建議棋盤格數大於7*7。opencv標定時候對正方形的棋盤格標定板是不能識別的,需要長方形的標定板。張正友論文中建議每次拍攝標定板占50%以上,但這是對畸變並不是很大的普通相機而言的,對於球面相機是不適用的,相反球面相機標定使用的標定板占比應該較小比較好(對於格子並不是非常密的棋盤格而言),原因是因為棋盤格每個角點之間的距離越大,這段距離之間的可能發生畸變的點越多,如果占比過大就無法將形變體現在棋盤格中。棋盤格的選用應該根據實際需要選用,對於要求精密識別的情況,則需要高精度的棋盤格,相應的價格也會較高;對於精度要求並不是很高的(如抓取)情況並不需要精度很高的標定板,也能夠節省開支。
這里程序的實現是在opencv中,所以就用opencv的程序來說明具體的過程.注意各個版本的opencv之間的程序移植性並不好,以下程序是在opencv2.4.3下編制運行的,每一步的要用到的輸入輸出都做了紅色標記.
立體相機標定分為兩個步驟,一個是單目標定(本文檔第2步),另一個是雙目標定
單目標定獲得相機的x,y軸的焦距;x,y軸的坐標原點位置;世界坐標系和平面坐標之間的旋轉和平移矩陣,5個畸變系數
雙目標定獲得兩個相機成像平面之間的旋轉和平移矩陣
注意
1.程序運行前需要插上攝像頭,否則程序有可能不能正常運行
2.單目標定
(1).獲取棋盤格圖像
for (int i=1; i<=19; i++)//輸入左標定板圖像
{
std::stringstream str;//聲明輸入輸出流
str << "./left" << i << ".jpg";//以名字方式把圖像輸入到流
std::cout << str.str() << std::endl;//.str("")清除內容 .clear()清空標記位
leftFileList.push_back(str.str());//.push_back從容器后向前插入數據
leftBoardImage = cv::imread(str.str(),0);//用來顯示即時輸入的圖像
cv::namedWindow("left chessboard image");
cv::imshow("left chessboard image",leftBoardImage);
cv::waitKey(10);
}
(2).定義棋盤格的角點數目
cv::Size boardSize(14,10)
(3).程序定位提取角點
這里建立的是理想成像平面(三維,第三維為0,單位為格子數)和圖像坐標系(二維,單位是像素)之間的關系
(a)首先聲明兩個坐標容器
std::vector<cv::Point2f> imageCorners;//二位坐標點
std::vector<cv::Point3f> objectCorners;//三維坐標點
(b)初始化棋盤角點,令其位置位於(x,y,z)=(i,j,0),一個棋盤格為一個坐標值
for (int i=0; i<boardSize.height; i++)
{
for (int j=0; j<boardSize.width; j++)
{
objectCorners.push_back(cv::Point3f(i, j, 0.0f));
}
}
(c)直接使用opencv內函數找到二維角點坐標,並建立標定標定格子和實際坐標間的關系(像素級別)
這個函數使用時,當標定板是長方形時可以找到角點,但是標定板是正方形時,就找不到,原因還未知.
cv::findChessboardCorners(image, boardSize, imageCorners);
(d)獲得像素精度往往是不夠的,還需要獲得亞像素的精度
cv::cornerSubPix(image,
imageCorners, //輸入/輸出
cv::Size(5,5),//搜索框的一半,表示在多大窗口定位角點
cv::Size(-1,-1), //死區
cv::TermCriteria(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS,
30, // max number of iterations//迭代最大次數
0.01)); // min accuracy//最小精度
注:
TermCriteria模板類,取代了之前的CvTermCriteria,這個類是作為迭代算法的終止條件的,這個類在參考手冊里介紹的很簡單,我查了些資料,這里介紹一下。該類變量需要3個參數,一個是類型,第二個參數為迭代的最大次數,最后一個是特定的閾值。類型有CV_TERMCRIT_ITER、CV_TERMCRIT_EPS、CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,分別代表着迭代終止條件為達到最大迭代次數終止,迭代到閾值終止,或者兩者都作為迭代終止條件。以上的宏對應的c++的版本分別為TermCriteria::COUNT、TermCriteria::EPS,這里的COUNT也可以寫成MAX_ITER。
(4)將角點圖像顯示出來
cv::drawChessboardCorners(image, boardSize, imageCorners, found);
cv::imshow("Corners on Chessboard", image);
cv::waitKey(10);
(5).相機標定
calibrateCamera(leftObjectPoints, // the 3D points,3d點,整數的物理坐標(角點數目為單位)
leftImagePoints, // the image points,圖像點
imageSize,
//cv::Size(310,240), // image size, 以像素衡量的圖像尺寸
leftCameraMatrix, // output camera matrix,輸出的3*3相機矩陣
leftDistCoeffs, // output distortion matrix.輸出的5*1畸變系數
leftRvecs, leftTvecs, // Rs, Ts.旋轉和平移(每一張圖都有各自的旋轉平移向量)
leftFlag); // set options.額外選項
// ,CV_CALIB_USE_INTRINSIC_GUESS);
其中:
leftObjectPoints為世界坐標系中的點。在使用時,應該輸入一個三維點的vector的vector類型
imagePoints為其對應的二維圖像點。和objectPoints一樣,應該輸入vector類型。
imageSize為圖像的大小
leftCameraMatrix是通過這個標定函數我們可以獲得的內參矩陣,內參矩陣總是3*3的
leftDistCoeffs可以獲得的畸變矩陣,畸變矩陣是一個5*1的矩陣(k1,k2,p1,p2,k3)
leftRvecs, leftTvecs,可以獲得的圖像平面相對於世界坐標的旋轉和平移矩陣
flags為標定是所采用的算法。程序中直接使用0即可,還可以使用如下某個或者某幾個參數:
CV_CALIB_USE_INTRINSIC_GUESS:使用該參數時,在cameraMatrix矩陣中應該有fx,fy,cx,cy的估
計值。否則的話,將初始化(cx,cy)圖像的中心點,使用最小二乘估算出fx,fy。如果內參數矩陣和畸
變居中已知的時候,應該標定模塊中的solvePnP()函數計算外參數矩陣。
CV_CALIB_FIX_PRINCIPAL_POINT:在進行優化時會固定光軸點。當
CV_CALIB_USE_INTRINSIC_GUESS參數被設置,光軸點將保持在中心或者某個輸入的值。
CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只將fy作為可變量,進行優化計算。當
CV_CALIB_USE_INTRINSIC_GUESS沒有被設置,fx和fy將會被忽略。只有fx/fy的比值在計算中會被
用到。
CV_CALIB_ZERO_TANGENT_DIST:設定切向畸變參數(p1,p2)為零。
CV_CALIB_FIX_K1,...,CV_CALIB_FIX_K6:對應的徑向畸變在優化中保持不變。如果設置了
CV_CALIB_USE_INTRINSIC_GUESS參數,
CV_CALIB_RATIONAL_MODEL:計算k4,k5,k6三個畸變參數。如果沒有設置,則只計算其它5個
畸變參數。
3.雙目標定
(1)右相機的標定,過程如上.其實是個重復的過程
(2).雙目標定.立體標定需要有一個相機作為基准,常用的是用左相機作為基准
在這里需要提前明確什么是本征矩陣,基礎矩陣
本征矩陣E:它包含了物理空間中兩個攝像機相關的旋轉(R)和平移信息(T)。T和R描述了一台攝像機相
對於另外一台攝像機在全局坐標系中的相對位置,與基礎矩陣相對應,這建立的相機主軸之間的關
系,聯系的是物理坐標,不是像素坐標。
基礎矩陣F:除了包含本征矩陣E的信息外,還包含了兩個攝像機的內參數。由於F包含了這些內參數,
因此它可以在像素坐標系將兩個攝像機關聯起來。我們觀察攝像機內參數矩陣M,如果圖像沒有
畸變,即cx,cy為0,並且焦距進行了歸一化處理,那么M就成了單位陣,此時本征矩陣F就等於
基礎矩陣E,即F=E。
更加詳細的關於本征矩陣和基礎矩陣的數學知識可以參考如下網址
http://blog.csdn.net/xiaoyinload/article/details/49000855
https://www.zhihu.com/question/27581884
(3)使用函數進行雙目標定
stereoCalibrate(stereoObjectPoints,
leftImagePoints,//輸入左相機圖像點
rightImagePoints,//輸入右相機圖像點
leftCameraMatrix,//輸入/輸出左相機內參矩陣(是輸入還是輸出由最后一項flag決定)
leftDistCoeffs,//輸入/輸出左相機的畸變矩陣(是輸入還是輸出由最后一項flag決定)(k1,k2,p1,p2,k3)
rightCameraMatrix,//輸入/輸出右相機的內參矩陣(是輸入還是輸出由最后一項flag決定)
rightDistCoeffs,//輸入/輸出右相機的畸變矩陣(是輸入還是輸出由最后一項flag決定)
cv::Size(310,240),//圖像的大小
stereoR,//獲得兩相機旋轉矩陣
stereoT,//獲得兩相機平移矩陣
stereoE,//獲得兩相機本征矩陣
stereoF,//獲得兩相機基礎矩陣
cvTermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 100, 1e-5),
CV_CALIB_FIX_INTRINSIC
);
CV_CALIB_FIX_INTRINSIC要確認cameraMatrix? and distCoeffs?所以只有R, T, E , 和F矩陣被估計出來
CV_CALIB_USE_INTRINSIC_GUESS根據指定的FLAG優化一些或全部的內在參數。初始值是由用戶提供。
CV_CALIB_FIX_PRINCIPAL_POINT在優化過程中確定主點。
CV_CALIB_FIX_FOCAL_LENGTH確定和 .
CV_CALIB_FIX_ASPECT_RATIO優化 . 確定的比值.
CV_CALIB_SAME_FOCAL_LENGTH執行以及 .
CV_CALIB_ZERO_TANGENT_DIST設置每個相機切向畸變系數為零並且設為固定值。
CV_CALIB_FIX_K1,...,CV_CALIB_FIX_K6在優化中不改變相應的徑向畸變系數. 如果設置CV_CALIB_USE_INTRINSIC_GUESS , 使用distCoeffs矩陣提供的系數。否則將其置零.
CV_CALIB_RATIONAL_MODEL能夠輸出系數k4,k5,k6。提供向后兼容性,這額外FLAG應該明確指定校正函數使用理性模型和返回8個系數。如果FLAG沒有被設置,該函數計算並只返回5畸變系數。
注:
(k1,k2,p1,p2,k3)中的k3一般都為0,只有對特種相機(如魚眼相機)才不為0
這里旋轉矩陣(M*3)和平移矩陣(M*3)與相機和世界坐標系的旋轉矩陣和平移矩陣不同,每個旋轉向量可以通過調用cvRodrigues2()來轉換為3*3的旋轉矩陣.
(4)相機矯正,得到3*3的旋轉矩陣,得到3*4的投影矩陣
stereoRectify(
leftCameraMatrix, //輸入左相機內參矩陣
leftDistCoeffs, //輸入左相機畸變矩陣
rightCameraMatrix, //輸入右相機內參矩陣
rightDistCoeffs, //輸入右相機畸變矩陣
cv::Size(310,240), //用於校正的圖像大小
stereoR, //輸入第一和第二相機坐標系之間的旋轉矩陣
stereoT, //輸入第一和第二相機坐標系之間的平移矩陣
correctLeftR, //輸出第一個相機的3x3矯正變換(旋轉矩陣)
correctRightR, //輸出第二個相機的3x3矯正變換(旋轉矩陣)
projectLeft, //在第一台相機的新的坐標系統(矯正過的)輸出 3x4 的投影矩陣
projectRight, //在第二台相機的新的坐標系統(矯正過的)輸出 3x4 的投影矩陣
Q,
CV_CALIB_ZERO_DISPARITY,
1
//Size newImageSize=Size(),
//Rect* validPixROI1=0,
//Rect* validPixROI2=0
);
Q: 輸出深度視差映射矩陣,矩陣Q是一個任意提供的矩陣(比如, stereoRectify()所能得出的矩陣).
flags: 操作的 flag可以是零或者是CV_CALIB_ZERO_DISPARITY . 如果設置了CV_CALIB_ZERO_DISPARITY,函數的作用是使每個相機的主點在校正后的圖像上有相同的像素坐標。如果未設置標志,功能還可以改變圖像在水平或垂直方向(取決於極線的方向)來最大化有用的圖像區域。
alpha: 自由縮放參數。如果是-1或沒有,該函數執行默認縮放。否則,該參數應在0和1之間。alpha=0,校正后的圖像進行縮放和偏移,只有有效像素是可見的(校正后沒有黑色區域)。alpha= 1意味着校正圖像的抽取和轉移,所有相機原始圖像素像保留在校正后的圖像(源圖像像素沒有丟失)。顯然,任何中間值產生這兩種極端情況之間的中間結果。
o newImageSize– 校正后新的圖像分辨率。相同的尺寸應傳遞給initUndistortRectifyMap()(見OpenCV樣品目錄stereo_calib.cpp樣品)。當(0,0)傳遞(默認),它設置為原始圖像大小。設置為較大的值能幫助你保存原始圖像的細節,特別是當有一個大的徑向畸變時。
o validPixROI1– 校正后的圖像可選的輸出矩形,里面所有像素都是有效的。如果alpha= 0,ROIs覆蓋整個圖像。否則,他們可能會比較小。
o validPixROI2– 校正后的圖像可選的輸出矩形,里面所有像素都是有效的。如果alpha= 0,ROIs覆蓋整個圖像。否則,他們可能會比較小。
(5)用於計算最后的映射
cv::initUndistortRectifyMap(//用於計算畸變映射
leftCameraMatrix, //輸入3*3相機內參矩陣.computed camera matrix
leftDistCoeffs, //輸入5*1相機畸變矩陣.computed distortion matrix
//cv::Mat(),
//cv::Mat(),
stereoR, //輸入第一和第二相機坐標系之間的旋轉矩陣
projectLeft,//輸入的3*4投影矩陣camera matrix to generate undistorted
或者使用 correctLeftR, //輸入第一個相機的3x3矯正變換(旋轉矩陣)
cv::Size(310,240),
//leftImage.size(), //去畸變圖像尺寸.size of undistorted
CV_32FC1, //輸出映射圖像的類型.type of output map
leftMap1, leftMap2 //輸出x坐標和y坐標映射函數.the x and y mapping functions
);
(6)將矯正后的像素重新投影到圖片上
cv::remap(leftImage, undistorted, leftMap1, leftMap2,
cv::INTER_LINEAR); //插值類型interpolation type
cv::remap(rightImage, undistorted, rightMap1, rightMap2,
cv::INTER_LINEAR); //插值類型interpolation type
(7)最后重投影后的圖片是含有黑邊的,要進行裁剪
<-------------------------------------------------------------------- end----- 攝像機標定
start ---- matlab參數(若使用matlab傳統標定工具箱是不能在matlab2017以上版本運行的)導入opencv ------------------------------------------------------------->
這里使用的導入方式是通過直接賦值的方式來做的,並沒有使用.xml方式導入,.xml方式導入可以參照<學習opencv>書中所述,因為這種導入方式是針對opencv1使用的,不知道是否在opencv2上也適用,懶了下,就使用直接賦值的方式做了,具體如下:
leftCameraMatrix.at<double>(0,0) = 0; leftCameraMatrix.at<double>(0,1) = 991.67704 ; leftCameraMatrix.at<double>(0,2) = 259.63864;
leftCameraMatrix.at<double>(1,0) = 0; leftCameraMatrix.at<double>(1,1) = 991.67704 ; leftCameraMatrix.at<double>(1,2) = 259.63864;
leftCameraMatrix.at<double>(2,0) = 0; leftCameraMatrix.at<double>(2,1) = 0 ; leftCameraMatrix.at<double>(2,2) = 1;
leftDistCoeffs.at<double>(0,0) = -0.38645; leftDistCoeffs.at<double>(0,1) = 0.61461; leftDistCoeffs.at<double>(0,2) = -0.00030;
leftDistCoeffs.at<double>(0,3) = 0.00508;leftDistCoeffs.at<double>(0,4) = 0.00000;
1.先利用Matlab標定得到如下所示的結果:
Intrinsic parameters of left camera:
Focal Length: fc_left = [ 209.35975 209.91146 ] ?[ 1.29389 1.28810 ]
Principal point: cc_left = [ 112.70811 83.34529 ] ?[ 2.94940 2.31088 ]
Skew: alpha_c_left = [ 0.00000 ] ?[ 0.00000 ] => angle of pixel axes = 90.00000 ?0.00000 degrees
Distortion: kc_left = [ 0.08414 -2.06248 -0.00441 0.00295 0.00000 ] ?[ 0.08103 0.71627 0.00294 0.00358 0.00000 ]
Intrinsic parameters of right camera:
Focal Length: fc_right = [ 1208.84080 1208.58242 ] ?[ 3.04212 3.10299 ]
Principal point: cc_right = [ 593.84687 340.22721 ] ?[ 5.56734 4.44145 ]
Skew: alpha_c_right = [ 0.00000 ] ?[ 0.00000 ] => angle of pixel axes = 90.00000 ?0.00000 degrees
Distortion: kc_right = [ -0.17114 0.00931 -0.00018 0.00056 0.00000 ] ?[ 0.01366 0.10070 0.00083 0.00097 0.00000 ]
Extrinsic parameters (position of right camera wrt left camera):
Rotation vector: om = [ -0.02893 -0.02493 0.00643 ] ?[ 0.01112 0.01464 0.00069 ]
Translation vector: T = [ -39.21639 -0.63083 15.74866 ] ?[ 0.47172 0.45645 2.87469 ]
A.內參對應關系
opencv內參矩陣如下
Fx 0 Cx
0 Fy Cy
0 0 1
而Matlab的存儲格式為
Focal Length: fc_left = [ 209.35975 209.91146 ] = [fx fy]
Principal point: cc_left = [ 112.70811 83.34529 ] = [cx cy]
B.畸變矩陣對應關系
opencv對應畸變矩陣為 [k1 k2 p1 p2 k3],
而Matlab的存儲格式為(一般對於非球面相機,k3為0)
Distortion: kc_left = [ 0.08414 -2.06248 -0.00441 0.00295 0.00000 ] = [k1 k2 p1 p2 k3]
C.兩相機旋轉矩陣對應關系
matlab標定的結果為
Rotation vector: om = [ -0.02893 -0.02493 0.00643 ] ?[ 0.01112 0.01464 0.00069 ]
處理三維問題是,通常采用的是旋轉矩陣的方式來描述.一個向量乘以旋轉矩陣等價於向量以某種方式進行旋轉.除了采用旋轉矩陣來描述外,還可以用旋轉向量來描述旋轉,旋轉向量的長度(模)表示繞軸逆時針旋轉的角度(弧度).旋轉向量與旋轉矩陣可以通過羅德里格斯(Rodrigues)變換進行轉換.matlab中使用的是旋轉向量,進行移植時要進行轉換變為旋轉矩陣.變換程序如下
////start ------------使用cvRodrigues2()將 3*1矩陣變為3*3矩陣 --------------------
int i;
double r_vec[3]={-2.100418,-2.167796,0.273330};
double R_matrix[9];
CvMat pr_vec;
CvMat pR_matrix;
cvInitMatHeader(&pr_vec,1,3,CV_64FC1,r_vec,CV_AUTOSTEP);
cvInitMatHeader(&pR_matrix,3,3,CV_64FC1,R_matrix,CV_AUTOSTEP);
cvRodrigues2(&pr_vec, &pR_matrix,0);
for(i=0; i<9; i++)
{
printf("%f\n",R_matrix[i]);
}
////end ------------使用cvRodrigues2()將 3*1矩陣變為3*3矩陣 (matlab使用 rodrigues(),從向量變到矩陣用rotationVectorToMatrix())--------------------
d.兩相機平移矩陣對應關系
matlab參數如下,matlab下是以右相機為基准變到左相機的坐標系.直接使用就可以
Translation vector: T = [ -39.21639 -0.63083 15.74866 ] ?[ 0.47172 0.45645 2.87469 ]
<----------------------------------------------------------------------- end --- matlab參數導入opencv
start --- 新版matlab(2014以后)標定方式----------------------------------------->
單目可以直接使用camera calibrator模塊進行標定(需要選擇app模塊),注意無論使用哪種標定,對於export cameraParams參數操作都是導出到matlab運行環境中,如果再想保存在電腦中,需要通過matlab環境右鍵保存
雙目標定的目的是獲取相機的內參和外參,外參包括旋轉矩陣和平移向量;
在進行雙目標定時,有兩種常見的數據庫:matlab視覺處理庫和opencv開源視覺庫;
通常matlab中標定的結果比較穩定,可以在matlab中標定之后再使用opencv標定;
在matlab標定中,大家比較熟悉的是使用標定工具箱,一幅幅圖像進行手動標定;
這很耗時間;
本經驗主要介紹一下,使用Matlab中的函數進行一鍵自動標定雙目相機參數。
-
打開matlab,輸入命令:“stereoCameraCalibrator”,
彈出標定窗口,
將窗口上方Skew、Tangential Distortion以及3 Coefficients選項選中,
將2 Coefficients選項去掉,
-
點擊“Add Images”按鈕,
彈出添加對話框,輸入左標定圖像所在文件夾和右標定圖像所在文件夾;
並且輸入棋盤格上每個格子所占的真實尺寸大小;
-
選擇好左右相機所在文件之后,
點擊確定按鈕,系統會自動檢測效果好的標定圖像,並剔除效果不好的圖像;
-
點擊“Calibrate”按鈕,進行標定;
窗口界面包含:
標定的效果;或雙目矯正結果;
重投影誤差柱狀圖:對於重投影誤差較大的圖像可以手動刪除掉,然后重新標定;
相機外參結構圖;
-
點擊“Exprot Camera Parameters”按鈕,
保存標定的參數數據;
-
標定結果分析:
對於雙目標定數據,
通常只需要得到:
相機內參:Intrinsic Matrix;
畸變:徑向畸變+切向畸變;[k1,k2,k3,p1,p2]
重投影平均誤差;
相機外參:旋轉矩陣3*3 +平移向量3*1
-
此工具箱優點就是可以自動進行標定;但標定結果對於圖像的要求比較高,如果圖像效果不好,標定所得到的結果用於立體校正的話會出現很大的偏差。 魯棒性不如手動標定的好;
<---------------------------------------- end matlab新版雙目標定
-- matlab雙目矯正-------------------------------------->
~ rectifystereimage()函數使用
令附普通相機和紅外相機關系的處理:
http://blog.csdn.net/aichipmunk/article/details/9264703
注:
~ 內參解釋
其中fx,fy為焦距,u0和v0為主點坐標,表示圖像的中心像素坐標和圖像原點像素坐標之間相差的橫向和縱向像素數