1. 程式人生 > >詳細介紹Opencv實現張正友法相機標定

詳細介紹Opencv實現張正友法相機標定

一.本程式基於以下配置:
- Visual Studio 2015
- OpenCV 3.1.0
開發環境配置參考:OpenCV3.1.0+VS2013開發環境配置

二.本程式準備工作:

本程式使用的標定板是10行14列棋盤格圖案,示意圖如下:

在不同視角拍攝這樣的照片14張,依次命名為chess1、chess2、…、chess14,並將其放在當前.cpp檔案目錄下面,如下圖所示:
這裡寫圖片描述

其中calibdata文字文件的內容如下:
這裡寫圖片描述

三.完整源程式如下:

#include"opencv2/core/core.hpp"
#include"opencv2/imgproc/imgproc.hpp"
#include"opencv2/calib3d/calib3d.hpp" #include"opencv2/highgui/highgui.hpp" #include<iostream> #include<fstream> using namespace std; using namespace cv; int main() { ifstream fin("calibdata.txt");//標定所用影象檔案的路徑 ofstream fout("calibration_result.txt");//儲存標定結果 //讀取每一幅影象,從中提取出內角點,然後對角點進行亞畫素精確化
cout<<"開始提取特徵點.........."; int image_count = 0;//影象的數量 Size image_size;//影象的尺寸 Size board_size = Size(13,9);//標定板上每行、列的內角點數,一般地,行列數不要相同 vector<Point2f> image_points_buf;//用於儲存檢測到的內角點影象座標位置 vector<vector<Point2f>> image_points_seq;//儲存檢測到的所有角點 string filename;//影象名
int count = 0;//儲存角點數 while(getline(fin,filename))//從文字文件中依次讀取待標定圖片名 { image_count++; cout<<"image_count = "<<image_count<<endl; Mat imageInput = imread(filename);//依次讀取當前目錄下圖片 if(image_count == 1)//讀入第一張圖片時獲取影象寬高資訊 { image_size.width = imageInput.cols; image_size.height = imageInput.rows; cout<<"image_size.width = "<<image_size.width<<endl; cout<<"image_size.height = "<<image_size.height<<endl; } //提取標定板的內角點,輸入必須是8位的灰度或者彩色影象 if(0 == findChessboardCorners(imageInput,board_size,image_points_buf)) { cout<<"Cannot find chessboard corners!\n"; exit(1); } else { Mat view_gray; cvtColor(imageInput,view_gray,CV_RGB2GRAY);//轉灰度圖 //find4QuadCornerSubpix(view_gray,image_points_buf,Size(5,5)); //亞畫素精確化方法一 //image_points_buf初始化的角點座標向量,同時作為亞畫素座標位置的輸出,浮點型數 cornerSubPix(view_gray,image_points_buf,Size(5,5),Size(-1,-1), //亞畫素精確化方法二 //Size(5,5)是搜尋視窗的大小,Size(-1,-1)表示沒有死區 //第四個引數定義求角點的迭代過程的終止條件,可以為迭代次數和角點精度兩者的組合 count += image_points_buf.size(); image_points_seq.push_back(image_points_buf);//儲存亞畫素角點 drawChessboardCorners(view_gray,board_size,image_points_buf,false); //用於繪製被成功標定的角點,輸入8位灰度或者彩色影象 //第四個引數是標誌位,用來指示定義的棋盤內角點是否被完整的探測到 //false表示有未被探測到的內角點,這時候函式會以圓圈標記出檢測到的內角點 imshow("Camera Calibration",view_gray);//顯示圖片 waitKey(500);//暫停0.5S } cout << "count = " << count<< endl;//顯示角點累加後的值 } int total = image_points_seq.size();//圖片總數 cout << "total = " << total << endl; int CornerNum = board_size.width*board_size.height;//每張圖片上總的內角點數 for(int ii = 0;ii<total;ii++) { cout << "第" << ii + 1 << "張圖片的資料:" << endl; for(int jj = 0;jj<CornerNum;jj++) { if (0 == jj % 3) cout << endl;//每三個角點座標之後換行 else cout.width(10);//輸出格式控制 cout << "(" << image_points_seq[ii][jj].x << "," << image_points_seq[ii][jj].y << ")"; } cout << endl; } cout<<"角點提取完成!\n"; cout<<"開始標定............"; Size square_size = Size(10,10);//設定棋盤格子的實際邊長,單位為mm vector<vector<Point3f>> object_points;//儲存標定板上角點的三維座標 Mat cameraMatrix = Mat(3,3,CV_32FC1,Scalar::all(0));//相機內參數矩陣 vector<int> point_counts;//每幅影象中角點的數量 Mat distCoeffs = Mat(1,5,CV_32FC1,Scalar::all(0));//攝像機的5個畸變係數:k1,k2,k3,p1,p2 vector<Mat> tvecsMat;//每幅影象的平移向量 vector<Mat> rvecsMat;//每幅影象的旋轉向量 //初始化標定板上角點的三維座標 int i,j,t; for(t = 0;t<image_count;t++) { vector<Point3f> temPointSet; for(i = 0;i<board_size.height;i++) { for(j = 0;j<board_size.width;j++) { Point3f realPoint; //假設標定板放在世界座標系中的z = 0平面上 //需要依據棋盤上單個黑白矩陣的大小,計算出(初始化)每一個內角點的世界座標 realPoint.x = i*square_size.width; realPoint.y = j*square_size.height; realPoint.z = 0; temPointSet.push_back(realPoint); } } object_points.push_back(temPointSet); } //初始化每幅影象中的角點數量,假定每幅影象中都可以看到完整的標定板 for(i = 0;i<image_count;i++) { point_counts.push_back(board_size.width*board_size.height); } calibrateCamera(object_points,image_points_seq,image_size, cameraMatrix,distCoeffs,rvecsMat,tvecsMat,0); /* object_points 世界座標系中角點的三維座標,image_points_seq 每個內角點對應的影象座標點 image_size 影象的畫素尺寸大小,cameraMatrix 輸出,內參數矩陣,distCoeffs 輸出,畸變係數 rvecsMat 輸出,旋轉向量,tvecsMat 輸出,位移向量,0標定時採用的演算法 在使用該函式進行標定運算之前,需要對棋盤上每一個內角點的空間座標系的位置座標進行初始化, 標定的結果是生成相機的內參數矩陣cameraMatrix、相機的5個畸變係數distCoeffs,另外每張影象 都會生成屬於自己的平移向量和旋轉向量 */ cout<<"標定完成!"<<endl; cout<<"開始輸出標定結果....."<<endl; double total_err = 0.0; //所有影象的平均誤差的總和,初始化為0.0 double err = 0.0; //每幅影象的平均誤差 vector<Point2f> image_points2; //儲存重新計算得到的投影點 cout<<"每幅影象的標定誤差:\n"; fout<<"每幅影象的標定誤差:\n"; for(i = 0;i<image_count;i++) { vector<Point3f> tempPointSet = object_points[i];//取出每幅影象角點的三維空間座標 projectPoints(tempPointSet,rvecsMat[i],tvecsMat[i], cameraMatrix,distCoeffs,image_points2); //通過得到的攝像機內外引數,對空間的三維點進行重新投影計算,得到新的投影點 vector<Point2f> tempImagePoint = image_points_seq[i];//原來每幅影象中角點影象座標 Mat tempImagePointMat = Mat(1,tempImagePoint.size(),CV_32FC2); //用於將原影象座標點儲存成一行多列的Mat,由兩個通道浮點型資料構成 Mat image_points2Mat = Mat(1,image_points2.size(),CV_32FC2); //用於將重投影座標點儲存成一行多列的Mat,以便於計算重投影誤差 for(int j = 0;j <tempImagePoint.size(); j++) { image_points2Mat.at<Vec2f>(0,j) = Vec2f(image_points2[j].x,image_points2[j].y); tempImagePointMat.at<Vec2f>(0,j) = Vec2f(tempImagePoint[j].x,tempImagePoint[j].y); }//賦值 //Vec2f表示的是2通道float型別的Vector,mat.at<Vec2f>(y, x)是訪問影象的一種方式 err = norm(image_points2Mat,tempImagePointMat,NORM_L2); //計算每張圖片重投影座標和亞畫素角點座標之間的偏差 total_err += err /= point_counts[i];//累加誤差 cout<<"第"<<i+1<<"幅影象的平均誤差:"<<err<<"畫素"<<endl; fout<<"第"<<i+1<<"幅影象的平均誤差:"<<err<<"畫素"<<endl; } cout<<"總體平均誤差:"<<total_err/image_count<<"畫素"<<endl; fout<<"總體平均誤差:"<<total_err/image_count<<"畫素"<<endl<<endl; cout<<"評價完成!"<<endl; cout<<"開始儲存標定結果............"<<endl; Mat rotation_matrix = Mat(3,3,CV_32FC1,Scalar::all(0));//儲存每幅影象的旋轉矩陣 fout<<"相機內參數矩陣:"<<endl; fout<<cameraMatrix<<endl<<endl; fout<<"畸變係數:\n"; fout<<distCoeffs<<endl<<endl<<endl; for(i = 0; i<image_count;i++) { fout<<"第"<<i+1<<"幅影象的旋轉向量:"<< endl; fout<<rvecsMat[i]<<endl; Rodrigues(rvecsMat[i],rotation_matrix);//將旋轉向量轉換為相應的旋轉矩陣 fout<<"第"<<i+1<<"幅影象的旋轉矩陣:"<< endl; fout<<rotation_matrix<< endl << endl; fout<<"第"<<i+1<<"幅影象的平移向量:"<< endl; fout<<tvecsMat[i]<< endl <<endl; } cout<<"完成儲存"<< endl; fout<< endl; //顯示標定結果 Mat mapx = Mat(image_size,CV_32FC1);//輸出的X座標重對映引數 Mat mapy = Mat(image_size,CV_32FC1);//輸出的Y座標重對映引數 Mat R = Mat::eye(3,3,CV_32F); cout<<"儲存矯正影象"<<endl; string imageFileName; std::stringstream StrStm; for(int i = 0;i < image_count;i++) { cout<<"Frame # "<<i+1<<"....."<<endl; initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, image_size, CV_32FC1, mapx, mapy);//用來計算畸變對映 StrStm.clear();//清除快取 imageFileName.clear(); string filePath = "chess"; StrStm<<i+1; StrStm>>imageFileName; filePath += imageFileName; filePath += ".bmp"; //獲取圖片路徑 Mat imageSource = imread(filePath);//讀取影象 Mat newimage = imageSource.clone();//拷貝影象 remap(imageSource,newimage,mapx,mapy,INTER_LINEAR);//把求得的對映應用到影象上 //與initUndistortRectifyMap結合使用,為矯正方法之一 //undistort(imageSource,newimage,cameraMatrix,distCoeffs);//矯正方法二 //第五個引數newCameraMatrix=noArray(),預設跟cameraMatrix保持一致,故可省 imageFileName += "_d.jpg";//矯正後圖片命名 imwrite(imageFileName,newimage);//儲存矯正後的圖片 } cout<<"儲存結束"<<endl; fin.close(); fout.close(); getchar();//等待輸入以退出 return 0; }

四.執行結果:

這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

圖片太多,中間省略幾張。。。

這裡寫圖片描述
這裡寫圖片描述

calibration_result.txt的結果如下:

每幅影象的標定誤差:
第1幅影象的平均誤差:0.0316613畫素
第2幅影象的平均誤差:0.0389023畫素
第3幅影象的平均誤差:0.0382147畫素
第4幅影象的平均誤差:0.0370629畫素
第5幅影象的平均誤差:0.0385986畫素
第6幅影象的平均誤差:0.0411222畫素
第7幅影象的平均誤差:0.0301272畫素
第8幅影象的平均誤差:0.0356303畫素
第9幅影象的平均誤差:0.0376777畫素
第10幅影象的平均誤差:0.0327415畫素
第11幅影象的平均誤差:0.0257617畫素
第12幅影象的平均誤差:0.0254189畫素
第13幅影象的平均誤差:0.0288391畫素
第14幅影象的平均誤差:0.0281152畫素
總體平均誤差:0.0335624畫素

相機內參數矩陣:
[2468.47496640925, 0, 1048.733444731282;
0, 2466.303729383499, 781.2776671184165;
0, 0, 1]

畸變係數:
[-0.337153976094791, 0.5625790549241111, -0.0004451387687732284, 5.767871907399249e-05, -1.615195594149361]

第1幅影象的旋轉向量:
[1.832204013190426;
-1.97952864389898;
-0.4381686980068267]
第1幅影象的旋轉矩陣:
[-0.05552643342962338, -0.8675821507778331, -0.4941842033516397;
-0.9950941432290062, 0.088674995984375, -0.04386788345134665;
0.08188077492253884, 0.4893239793213493, -0.868249723270445]

第1幅影象的平移向量:
[42.96524585005136;
38.32952150925843;
222.9912740841345]

第2幅影象的旋轉向量:
[1.584731236327221;
-2.207688033499483;
-0.6043175791111356]
第2幅影象的旋轉矩陣:
[-0.3091732926321815, -0.798262176337548, -0.5169036399096227;
-0.9502349038328671, 0.2811852708390378, 0.1341211057250835;
0.03828188423676798, 0.5326465444288723, -0.8454715583905021]

第2幅影象的平移向量:
[69.74394948874316;
5.086864923950324;
294.4591241780354]

第3幅影象的旋轉向量:
[-1.473949748410117;
2.31855971511969;
1.035999692169115]
第3幅影象的旋轉矩陣:
[-0.4803079766576785, -0.8563844330649897, -0.1894992093993095;
-0.7124954115441877, 0.2549531410021226, 0.6537194998021941;
-0.5115217845678576, 0.4490040074399208, -0.7326260063739247]

第3幅影象的平移向量:
[61.33952853487049;
-9.017935885808466;
338.6033873028243]

第4幅影象的旋轉向量:
[-0.7465736599001761;
2.506508904972995;
0.600457192725938]
第4幅影象的旋轉矩陣:
[-0.7500133063059861, -0.5919424240016485, 0.2951003338375199;
-0.3939740782147958, 0.7581903860462892, 0.5195495781942097;
-0.5312856727399724, 0.2734072149268043, -0.8018628491002705]

第4幅影象的平移向量:
[72.59290680319951;
-26.81723942274018;
284.8320214867886]

第5幅影象的旋轉向量:
[-0.7283682830866786;
2.544530908621899;
0.6357133676594682]
第5幅影象的旋轉矩陣:
[-0.7762623488644779, -0.5737245311930547, 0.2612602687794558;
-0.3834367786036743, 0.7586505858595253, 0.5267119947267545;
-0.500392848219717, 0.3086898943637603, -0.8088989717932951]

第5幅影象的平移向量:
[100.1209866961551;
-32.29634238993099;
306.584645572055]

第6幅影象的旋轉向量:
[-0.5237302004887391;
2.886335271763946;
-0.3575089401502572]
第6幅影象的旋轉矩陣:
[-0.9204005991990246, -0.3207721518269676, 0.2235351507176743;
-0.365616894748324, 0.9087093140932435, -0.2014241017226591;
-0.1385172309449962, -0.2671190915911554, -0.9536563152618697]

第6幅影象的平移向量:
[73.10631401051033;
-61.20027337080823;
322.5830605558965]

第7幅影象的旋轉向量:
[1.713568861903209;
2.054266570651059;
0.1275839829491686]
第7幅影象的旋轉矩陣:
[-0.1189475689363267, 0.9084901241809632, 0.4006209806160879;
0.9510798800820409, 0.2201192678099205, -0.2167823093380896;
-0.2851289840533027, 0.35523682551853, -0.8902293301437832]

第7幅影象的平移向量:
[-68.77620078523513;
-50.33338630124742;
279.3099139868805]

第8幅影象的旋轉向量:
[1.818225297581961;
2.416158251961891;
0.6448996550355059]
第8幅影象的旋轉矩陣:
[-0.3075469299716437, 0.9081606873844775, 0.284005372756168;
0.9288944014598498, 0.2218260808714645, 0.296560922546706;
0.2063251724864512, 0.35501740201652, -0.9118073082970308]

第8幅影象的平移向量:
[-61.68795969126989;
-48.39125620237197;
259.9247597410303]

第9幅影象的旋轉向量:
[-1.847647793051378;
-2.434372024849804;
-0.5641186320132542]
第9幅影象的旋轉矩陣:
[-0.2927069486806816, 0.9372808759138351, 0.1892807487311871;
0.9250022564756624, 0.2273995446169124, 0.3044014990484665;
0.242267347591411, 0.2641855536440529, -0.9335483520079759]

第9幅影象的平移向量:
[-34.9737442953684;
-41.07623681991357;
262.4502152811082]

第10幅影象的旋轉向量:
[1.69102969291048;
2.015536806640728;
-0.08921956607969511]
第10幅影象的旋轉矩陣:
[-0.1002242520458908, 0.9377989307560487, 0.3323977508567916;
0.9047607130849443, 0.2248971882948798, -0.361703340812717;
-0.4139603258282911, 0.264488979297568, -0.871023781805219]

第10幅影象的平移向量:
[-42.13169665197236;
-41.61090361839386;
288.3911563121005]

第11幅影象的旋轉向量:
[1.724430894665768;
2.33520070621025;
-0.7246472524282326]
第11幅影象的旋轉矩陣:
[-0.3281780053536973, 0.9307450708250076, -0.1612848720029889;
0.8585432544884051, 0.222686147487633, -0.461859675539632;
-0.3939577096269682, -0.2900422259810942, -0.8721655979075336]

第11幅影象的平移向量:
[-50.94693146467624;
-44.76151448070497;
303.7724473124102]

第12幅影象的旋轉向量:
[1.696368490203205;
2.153193672447314;
-0.4174155271926968]
第12幅影象的旋轉矩陣:
[-0.2093204256487086, 0.9725161681105834, 0.1019669660711291;
0.8639645737210686, 0.2327734149230861, -0.446521838950814;
-0.4579849066958599, -0.005370295015354715, -0.8889437469099114]

第12幅影象的平移向量:
[-58.7176317481046;
-45.7774147598785;
250.7908690340958]

第13幅影象的旋轉向量:
[1.746182820818435;
2.38376805814103;
0.8197327892115064]
第13幅影象的旋轉矩陣:
[-0.349578928430417, 0.8640106590061456, 0.3623260326297403;
0.9041163433351445, 0.2096799568728731, 0.3723008909471983;
0.2456994312401408, 0.4577334342273422, -0.8544658522601795]

第13幅影象的平移向量:
[-24.35541784964123;
-38.59140556730291;
171.2705769754649]

第14幅影象的旋轉向量:
[1.644152775376657;
2.007535689228716;
0.5936125402120078]
第14幅影象的旋轉矩陣:
[-0.167199482507626, 0.7761523420058212, 0.6079735808800089;
0.9819782500137466, 0.1862065131653021, 0.03233961896435755;
-0.08810816959352724, 0.6024240005624765, -0.7932983511877354]

第14幅影象的平移向量:
[-41.58825298021998;
-44.96941216039689;
169.3603333851799]

第一次寫部落格~