前兩天發表的時候沒註意,代碼出了點錯誤,所以修改了一下,重新發上來。
參考:
http://docs.opencv.org/3.0.0/db/d58/group__calib3d__fisheye.html#gga91b6a47d784dd47ea2c76ef656d7c3dca0899eaa2f96d6eed9927c4b4f4464e05
http://docs.opencv.org/master/modules/calib3d/doc/calib3d.html
opencv3.0 fisheye model reference
http://stackoverflow.com/questions/31089265/what-are-the-main-references-to-the-fish-eye-camera-model-in-opencv3-0-0dev/34388476#34388476
Kannala J, Brandt S S. A generic camera model and calibration method for conventional, wide-angle, and fish-eye lenses[J]. Pattern Analysis and Machine Intelligence, IEEE Transactions on, 2006, 28(8): 1335-1340.
文獻地址:http://download.csdn.net/detail/qq_15947787/9583006
opencv也是參考matlab的Calib_gui_fisheye處理,但是不清楚為什麽,Calib_gui_fisheye不能準確的提取角點
魚眼鏡頭模型
魚眼鏡頭的內參模型可以表示為 ,與普通鏡頭的內參一樣,但畸變參數不同,為
,含義如下:
設(X,Y,Z)為一個三維坐標點,投影在圖像上的二維坐標為(u,v),如果不考慮畸變,投影關系如下:
R和t分別代表相機外參中的旋轉矩陣和平移向量。
標定流程
首先調用OpenCV的FindChessboardCorners()來尋找圖像上的標定板的角點,再根據標定板的尺寸指定這些角點對應的三維點的三維坐標,再調用fisheye::calibrate()來進行標定,利用標定結果中的內參和畸變參數調用fisheye::undistortImage()對圖像做去畸變操作。最後調用一張待測試的畸變圖片利用標定結果進行畸變校正。
//運行環境 VS2012+opencv3.0 #include <opencv2\opencv.hpp> #include <fstream> using namespace std; using namespace cv; int main() { ofstream fout("caliberation_result.txt"); /** 保存定標結果的文件 **/ /************************************************************************ 讀取每一幅圖像,從中提取出角點,然後對角點進行亞像素精確化 *************************************************************************/ cout<<"開始提取角點………………"<<endl; int image_count= 25; /**** 圖像數量 ****/ Size board_size = Size(9,6); /**** 定標板上每行、列的角點數 ****/ vector<Point2f> corners; /**** 緩存每幅圖像上檢測到的角點 ****/ vector<vector<Point2f>> corners_Seq; /**** 保存檢測到的所有角點 ****/ vector<Mat> image_Seq; int successImageNum = 0; /**** 成功提取角點的棋盤圖數量 ****/ int count = 0; for( int i = 0; i != image_count ; i++) { cout<<"Frame #"<<i+1<<"..."<<endl; string imageFileName; std::stringstream StrStm; StrStm<<i+1; StrStm>>imageFileName; imageFileName += ".jpg"; cv::Mat image = imread("img"+imageFileName); /* 提取角點 */ Mat imageGray; cvtColor(image, imageGray , CV_RGB2GRAY); bool patternfound = findChessboardCorners(image, board_size, corners,CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE+ CALIB_CB_FAST_CHECK ); if (!patternfound) { cout<<"can not find chessboard corners!\n"; continue; exit(1); } else { /* 亞像素精確化 */ cornerSubPix(imageGray, corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1)); /* 繪制檢測到的角點並保存 */ Mat imageTemp = image.clone(); for (int j = 0; j < corners.size(); j++) { circle( imageTemp, corners[j], 10, Scalar(0,0,255), 2, 8, 0); } string imageFileName; std::stringstream StrStm; StrStm<<i+1; StrStm>>imageFileName; imageFileName += "_corner.jpg"; imwrite(imageFileName,imageTemp); cout<<"Frame corner#"<<i+1<<"...end"<<endl; count = count + corners.size(); successImageNum = successImageNum + 1; corners_Seq.push_back(corners); } image_Seq.push_back(image); } cout<<"角點提取完成!\n"; /************************************************************************ 攝像機定標 *************************************************************************/ cout<<"開始定標………………"<<endl; Size square_size = Size(20,20); vector<vector<Point3f>> object_Points; /**** 保存定標板上角點的三維坐標 ****/ Mat image_points = Mat(1, count, CV_32FC2, Scalar::all(0)); /***** 保存提取的所有角點 *****/ vector<int> point_counts; /* 初始化定標板上角點的三維坐標 */ for (int t = 0; t<successImageNum; t++) { vector<Point3f> tempPointSet; for (int i = 0; i<board_size.height; i++) { for (int j = 0; j<board_size.width; j++) { /* 假設定標板放在世界坐標系中z=0的平面上 */ Point3f tempPoint; tempPoint.x = i*square_size.width; tempPoint.y = j*square_size.height; tempPoint.z = 0; tempPointSet.push_back(tempPoint); } } object_Points.push_back(tempPointSet); } for (int i = 0; i< successImageNum; i++) { point_counts.push_back(board_size.width*board_size.height); } /* 開始定標 */ Size image_size = image_Seq[0].size(); cv::Matx33d intrinsic_matrix; /***** 攝像機內參數矩陣 ****/ cv::Vec4d distortion_coeffs; /* 攝像機的4個畸變系數:k1,k2,k3,k4*/ std::vector<cv::Vec3d> rotation_vectors; /* 每幅圖像的旋轉向量 */ std::vector<cv::Vec3d> translation_vectors; /* 每幅圖像的平移向量 */ int flags = 0; flags |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC; flags |= cv::fisheye::CALIB_CHECK_COND; flags |= cv::fisheye::CALIB_FIX_SKEW; fisheye::calibrate(object_Points, corners_Seq, image_size, intrinsic_matrix, distortion_coeffs, rotation_vectors, translation_vectors, flags, cv::TermCriteria(3, 20, 1e-6)); cout<<"定標完成!\n"; /************************************************************************ 對定標結果進行評價 *************************************************************************/ cout<<"開始評價定標結果………………"<<endl; double total_err = 0.0; /* 所有圖像的平均誤差的總和 */ double err = 0.0; /* 每幅圖像的平均誤差 */ vector<Point2f> image_points2; /**** 保存重新計算得到的投影點 ****/ cout<<"每幅圖像的定標誤差:"<<endl; cout<<"每幅圖像的定標誤差:"<<endl<<endl; for (int i=0; i<image_count; i++) { vector<Point3f> tempPointSet = object_Points[i]; /**** 通過得到的攝像機內外參數,對空間的三維點進行重新投影計算,得到新的投影點 ****/ fisheye::projectPoints(tempPointSet, image_points2, rotation_vectors[i], translation_vectors[i], intrinsic_matrix, distortion_coeffs); /* 計算新的投影點和舊的投影點之間的誤差*/ vector<Point2f> tempImagePoint = corners_Seq[i]; Mat tempImagePointMat = Mat(1,tempImagePoint.size(),CV_32FC2); Mat image_points2Mat = Mat(1,image_points2.size(), CV_32FC2); for (size_t i = 0 ; i != tempImagePoint.size(); i++) { image_points2Mat.at<Vec2f>(0,i) = Vec2f(image_points2[i].x, image_points2[i].y); tempImagePointMat.at<Vec2f>(0,i) = Vec2f(tempImagePoint[i].x, tempImagePoint[i].y); } 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<<intrinsic_matrix<<endl; fout<<"畸變系數:\n"; fout<<distortion_coeffs<<endl; for (int i=0; i<image_count; i++) { fout<<"第"<<i+1<<"幅圖像的旋轉向量:"<<endl; fout<<rotation_vectors[i]<<endl; /* 將旋轉向量轉換為相對應的旋轉矩陣 */ Rodrigues(rotation_vectors[i],rotation_matrix); fout<<"第"<<i+1<<"幅圖像的旋轉矩陣:"<<endl; fout<<rotation_matrix<<endl; fout<<"第"<<i+1<<"幅圖像的平移向量:"<<endl; fout<<translation_vectors[i]<<endl; } cout<<"完成保存"<<endl; fout<<endl; /************************************************************************ 顯示定標結果 *************************************************************************/ Mat mapx = Mat(image_size,CV_32FC1); Mat mapy = Mat(image_size,CV_32FC1); Mat R = Mat::eye(3,3,CV_32F); cout<<"保存矯正圖像"<<endl; for (int i = 0 ; i != image_count ; i++) { cout<<"Frame #"<<i+1<<"..."<<endl; Mat newCameraMatrix = Mat(3,3,CV_32FC1,Scalar::all(0)); fisheye::initUndistortRectifyMap(intrinsic_matrix,distortion_coeffs,R,intrinsic_matrix,image_size,CV_32FC1,mapx,mapy); Mat t = image_Seq[i].clone(); cv::remap(image_Seq[i],t,mapx, mapy, INTER_LINEAR); string imageFileName; std::stringstream StrStm; StrStm<<i+1; StrStm>>imageFileName; imageFileName += "_d.jpg"; imwrite(imageFileName,t); } cout<<"保存結束"<<endl; /************************************************************************ 測試一張圖片 *************************************************************************/ if (1) { cout<<"TestImage ..."<<endl; Mat newCameraMatrix = Mat(3,3,CV_32FC1,Scalar::all(0)); Mat testImage = imread("a.jpg",1); fisheye::initUndistortRectifyMap(intrinsic_matrix,distortion_coeffs,R,intrinsic_matrix,image_size,CV_32FC1,mapx,mapy); Mat t = testImage.clone(); cv::remap(testImage,t,mapx, mapy, INTER_LINEAR); imwrite("TestOutput.jpg",t); cout<<"保存結束"<<endl; } return 0; }
實驗結果:
第1幅圖像的平均誤差:0.0255382像素
第2幅圖像的平均誤差:0.0325026像素
第3幅圖像的平均誤差:0.0244082像素
第4幅圖像的平均誤差:0.0311312像素
第5幅圖像的平均誤差:0.0205482像素
第6幅圖像的平均誤差:0.0385998像素
第7幅圖像的平均誤差:0.0423178像素
第8幅圖像的平均誤差:0.0442407像素
第9幅圖像的平均誤差:0.0396359像素
第10幅圖像的平均誤差:0.0337944像素
第11幅圖像的平均誤差:0.0294888像素
第12幅圖像的平均誤差:0.0391749像素
第13幅圖像的平均誤差:0.0589933像素
第14幅圖像的平均誤差:0.0239711像素
第15幅圖像的平均誤差:0.0267404像素
第16幅圖像的平均誤差:0.0298319像素
第17幅圖像的平均誤差:0.0346551像素
第18幅圖像的平均誤差:0.0538333像素
第19幅圖像的平均誤差:0.0385998像素
第20幅圖像的平均誤差:0.0485061像素
第21幅圖像的平均誤差:0.0441557像素
第22幅圖像的平均誤差:0.0541894像素
第23幅圖像的平均誤差:0.0299604像素
第24幅圖像的平均誤差:0.0385502像素
第25幅圖像的平均誤差:0.0226273像素
總體平均誤差:0.0362398像素
相機內參數矩陣:
[322.4122272795701, 0, 626.3428871332725;
0, 322.6954347937962, 502.2060247826215;
0, 0, 1]
畸變系數:
[-0.0314732, 0.00545013, -0.00336236, 0.000262814]
第1幅圖像的旋轉向量:
[-3.03815, 0.388065, -0.0138772]
第1幅圖像的旋轉矩陣:
[0.9679029375838276, -0.2506097001682914, 0.01893889115435969;
-0.2513223502463919, -0.9648466048558239, 0.07686420080128105;
-0.0009897894864664397, -0.07915685238656738, -0.9968616819985727]
第1幅圖像的平移向量:
[-36.6125, 83.2967, 169.266]
第2幅圖像的旋轉向量:
[-2.32548, 1.54006, 0.40201]
第2幅圖像的旋轉矩陣:
[0.378521586564022, -0.9239226330487051, -0.05557316479564789;
-0.8332059579221207, -0.3662733126966977, 0.4142604157885093;
-0.4030995412899122, -0.1105026178266161, -0.908460197953288]
第2幅圖像的平移向量:
[63.1312, -57.5069, 165.236]
第3幅圖像的旋轉向量:
[2.44823, -1.67407, 0.725759]
第3幅圖像的旋轉矩陣:
[0.2871968675132958, -0.8984517932453987, 0.3321179526985517;
-0.8565648483398981, -0.3960822205235546, -0.3307801916281831;
0.4287360725189769, -0.1894815289131304, -0.8833357970340301]
第3幅圖像的平移向量:
[9.19866, 173.995, 164.369]
第4幅圖像的旋轉向量:
[-2.92706, 0.182327, -0.805427]
第4幅圖像的旋轉矩陣:
[0.8529147476243522, -0.08859570292138545, 0.5144776328556494;
-0.1416154555912262, -0.9878071991931738, 0.06466838454629012;
0.502475368573004, -0.1280146032529221, -0.8550618488340014]
第4幅圖像的平移向量:
[-114.612, 29.7974, 148.561]
第5幅圖像的旋轉向量:
[-2.94241, 0.292916, 0.443054]
第5幅圖像的旋轉矩陣:
[0.9372522606978974, -0.2140932338616103, -0.2751768286599686;
-0.1693276205911718, -0.969441300775839, 0.1775154113168249;
-0.3047726311885831, -0.1197816829265942, -0.9448629486405216]
第5幅圖像的平移向量:
[83.013, 73.6934, 241.275]
第6幅圖像的旋轉向量:
[-2.75668, -1.001, -0.612898]
第6幅圖像的旋轉矩陣:
[0.6946906160764714, 0.6411847485001515, 0.326016972297594;
0.5818913059604812, -0.7673820777792599, 0.2693088464027711;
0.4228563065426579, 0.002620113353144188, -0.9061929590452126]
第6幅圖像的平移向量:
[-156.079, -54.4814, 152.871]
第7幅圖像的旋轉向量:
[-2.83598, -0.88453, -0.738399]
第7幅圖像的旋轉矩陣:
[0.7170926483253193, 0.5539372902422747, 0.4230030876918346;
0.5151514661855453, -0.8300409149112951, 0.213661055085159;
0.4694646958022115, 0.06469588899046283, -0.8805778451352673]
第7幅圖像的平移向量:
[-123.099, -60.5503, 92.6496]
第8幅圖像的旋轉向量:
[-2.94589, 0.176748, -0.583872]
第8幅圖像的旋轉矩陣:
[0.9181256495730359, -0.08877720787035021, 0.3862174244630025;
-0.1403284549278882, -0.9842687480550225, 0.1073450340712148;
0.3706119484496446, -0.1527535235761953, -0.9161403520757148]
第8幅圖像的平移向量:
[-65.3965, 65.5516, 93.5776]
第9幅圖像的旋轉向量:
[-2.98668, 0.512548, 0.0276313]
第9幅圖像的旋轉矩陣:
[0.9427996228264289, -0.3333589160928895, 0.0008392018329849635;
-0.3313366658414531, -0.9367974551508381, 0.1123678863910088;
-0.03667267466945741, -0.1062184592445554, -0.9936663191677078]
第9幅圖像的平移向量:
[25.637, 36.055, 149.153]
第10幅圖像的旋轉向量:
[-2.64713, 0.908772, 0.463857]
第10幅圖像的旋轉矩陣:
[0.7472614296825292, -0.6330773665259889, -0.202023275147545;
-0.5349933296317385, -0.7534557166822899, 0.3822127944592156;
-0.3941858609315764, -0.1775317745962544, -0.9017205642827143]
第10幅圖像的平移向量:
[57.5034, -40.0235, 123.005]
第11幅圖像的旋轉向量:
[2.97099, 0.81535, -0.234947]
第11幅圖像的旋轉矩陣:
[0.8492655838870934, 0.5110771946433385, -0.1324691252345593;
0.503201513260689, -0.8594801895012334, -0.08990017189681285;
-0.1598005165106387, 0.009690457699146879, -0.9871017627137103]
第11幅圖像的平移向量:
[3.82139, 101.52, 152.074]
第12幅圖像的旋轉向量:
[-2.86077, 0.432068, -0.546188]
第12幅圖像的旋轉矩陣:
[0.8891914447529974, -0.2460402134947138, 0.3857496440977657;
-0.3187574034884554, -0.9379532461778478, 0.1365189573126591;
0.3282259774901665, -0.2443520438382157, -0.9124471416869645]
第12幅圖像的平移向量:
[-68.367, 122.421, 113.478]
第13幅圖像的旋轉向量:
[-2.90951, -0.523456, -0.663067]
第13幅圖像的旋轉矩陣:
[0.8449852726290712, 0.3552543160840582, 0.39974274220256;
0.3063658779533411, -0.9342262591823131, 0.1826506104016627;
0.4383375843637559, -0.03186953965575178, -0.8982452307562103]
第13幅圖像的平移向量:
[-94.5581, 9.47137, 65.9367]
第14幅圖像的旋轉向量:
[-2.76209, 0.958866, -0.22678]
第14幅圖像的旋轉矩陣:
[0.7766776415625997, -0.5931753322324745, 0.2119312773699317;
-0.6252672489575734, -0.7667435817622675, 0.1454137105206963;
0.07624112064880313, -0.2454532645072427, -0.9664057049008724]
第14幅圖像的平移向量:
[24.2611, 88.5081, 199.387]
第15幅圖像的旋轉向量:
[-2.84249, 0.0876038, -0.180569]
第15幅圖像的旋轉矩陣:
[0.9902889632803219, -0.04179158984697147, 0.1325942390273699;
-0.07827787950080752, -0.9558115385084162, 0.2833670348417959;
0.1148927447058423, -0.2909944430263867, -0.9498009745950575]
第15幅圖像的平移向量:
[-6.92544, 32.624, 260.476]
第16幅圖像的旋轉向量:
[-2.17961, -1.94061, -0.409744]
第16幅圖像的旋轉矩陣:
[0.1026106120077555, 0.9917855891805973, 0.07637019966642694;
0.9380038094599215, -0.1220286791600962, 0.3244346696965635;
0.3310889846281055, 0.03834509820222493, -0.9428200982699678]
第16幅圖像的平移向量:
[-55.2808, -83.903, 214.285]
第17幅圖像的旋轉向量:
[-2.21555, -0.438635, -0.150732]
第17幅圖像的旋轉矩陣:
[0.9312004456805553, 0.3620443217263547, -0.04230412592218191;
0.2595653905931794, -0.5771436506885823, 0.7742938812079467;
0.2559131453648801, -0.7320034942372609, -0.6314106005238542]
第17幅圖像的平移向量:
[-64.4695, -103.74, 235.816]
第18幅圖像的旋轉向量:
[-2.97812, 0.0864579, -0.700192]
第18幅圖像的旋轉矩陣:
[0.8938975219841372, -0.03636454892238611, 0.4467939567320607;
-0.07340853688611149, -0.9951239589453907, 0.06587482861528146;
0.4422198626281497, -0.09168383671358701, -0.8922083092992448]
第18幅圖像的平移向量:
[-86.0809, 61.0628, 69.4594]
第19幅圖像的旋轉向量:
[-3.00008, 0.239955, 0.155099]
第19幅圖像的旋轉矩陣:
[0.9820966811162741, -0.1644457693799922, -0.09188959611088617;
-0.1513134378357853, -0.9792000888366407, 0.1351718519242486;
-0.1122067398631442, -0.118847696466265, -0.9865520120976629]
第19幅圖像的平移向量:
[-15.7077, 32.6659, 127.149]
第20幅圖像的旋轉向量:
[-2.95971, 0.562952, 0.361615]
第20幅圖像的旋轉矩陣:
[0.9030375888807793, -0.3736248978488473, -0.2119611963917617;
-0.3481253492941344, -0.9256202897130543, 0.1484446713437644;
-0.2516582091790878, -0.06026205256178804, -0.9659382127102203]
第20幅圖像的平移向量:
[41.3163, 10.388, 118.481]
第21幅圖像的旋轉向量:
[-2.79374, 0.881471, 0.164568]
第21幅圖像的旋轉矩陣:
[0.8152064293359687, -0.5775133587591865, -0.04378125196911152;
-0.554407144845817, -0.7999860287028848, 0.22946692926018;
-0.1675446069356131, -0.1627902771509103, -0.9723312863175394]
第21幅圖像的平移向量:
[-31.4756, 3.75149, 134.514]
第22幅圖像的旋轉向量:
[-2.98309, 0.637638, 0.0981236]
第22幅圖像的旋轉矩陣:
[0.9108160328030114, -0.4104565740919268, -0.04404038117117651;
-0.4047071445053953, -0.9088735260558214, 0.1008019881805276;
-0.08140197525412385, -0.07398861006652813, -0.9939312873660584]
第22幅圖像的平移向量:
[85.0147, 158.892, 162.763]
第23幅圖像的旋轉向量:
[2.84253, -1.05597, -0.348542]
第23幅圖像的旋轉矩陣:
[0.7350751148451006, -0.6328998562959699, -0.2431097436056436;
-0.6532645704372982, -0.7571181193324142, -0.004189795928232765;
-0.1814110706292024, 0.1618947969487858, -0.9699897412725961]
第23幅圖像的平移向量:
[99.5097, 185.972, 139.866]
第24幅圖像的旋轉向量:
[2.69949, -1.26373, -0.371771]
第24幅圖像的旋轉矩陣:
[0.6171775811411872, -0.735612817963387, -0.2792232357535401;
-0.7696284157724025, -0.6381835525130317, -0.01984577883350193;
-0.1635968672449875, 0.227146506356165, -0.9600211089751176]
第24幅圖像的平移向量:
[122.42, 183.516, 123.981]
第25幅圖像的旋轉向量:
[2.73412, 0.903944, -0.920859]
第25幅圖像的旋轉矩陣:
[0.6369387495798474, 0.5748277850836829, -0.5136945072506112;
0.5029505052567296, -0.8148489726280646, -0.2882046860635478;
-0.5842515028184876, -0.07479417960493308, -0.8081188106655877]
第25幅圖像的平移向量:
[38.5595, 196.165, 166.877]
畸變圖像角點檢測與校正結果:(只列出一部分)
對了200°視場的魚眼圖像效果還是很好的。
後面的校正圖出現拉伸很嚴重的原因是因為標定板拍攝的時候是傾斜的,並不與光軸垂直。所以會出現近大遠小的情況,也就是拉伸的情況。可以這麽理解,把這些校正圖中的棋盤位置看成無數連續的垂直於光軸的標定板,由於透視效果,遠處的格子自然小,近處的會大,連續後就會成為這種現象,雖然人眼感覺怪怪的,但是符合空間分布情況。可以想象一下隧道裏的情況,下圖還是一個小視場的圖,墻上格子變化很明顯,如果視場更大一些,就更明顯了。
代碼下載:
附件中刪除了部分圖像(有臉的圖都刪了……^-^)
http://download.csdn.net/detail/qq_15947787/9527601
Tags: reference 旋轉矩陣 Brandt camera matlab
文章來源: