您的位置:首页 > 运维架构

【opencv3.0】鱼眼图像畸变校正——标定校正

2016-05-18 08:53 232 查看
前两天发表的时候没注意,代码出了点错误,所以修改了一下,重新发上来。

参考:

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.

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: