码迷,mamicode.com
首页 > 其他好文 > 详细

【opencv】鱼眼图像畸变校正——标定校正

时间:2016-05-18 10:51:16      阅读:444      评论:0      收藏:0      [点我收藏+]

标签:

参考:
http://docs.opencv.org/3.0.0/db/d58/group__calib3d__fisheye.html#gga91b6a47d784dd47ea2c76ef656d7c3dca0899eaa2f96d6eed9927c4b4f4464e05
http://docs.opencv.org/master/modules/calib3d/doc/calib3d.html
opencv2.4.9 Fisheye camera model reference
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.

鱼眼镜头模型
  鱼眼镜头的内参模型可以表示为 技术分享,与普通镜头的内参一样,但畸变参数不同,为技术分享,含义如下:

  设(X,Y,Z)为一个三维坐标点,投影在图像上的二维坐标为(u,v),如果不考虑畸变,投影关系如下:
  技术分享
  R和t分别代表相机外参中的旋转矩阵和平移向量。
技术分享
  
标定流程
  首先调用OpenCV的FindChessboardCorners()来寻找图像上的标定板的角点,再根据标定板的尺寸指定这些角点对应的三维点的三维坐标,再调用fisheye::calibrate()来进行标定,利用标定结果中的内参和畸变参数调用fisheye::undistortImage()对图像做去畸变操作。最后调用一张待测试的畸变图片利用标定结果进行畸变校正。

//运行环境 VS2012+opencv2.4.9
#include <opencv2\opencv.hpp>
#include <fstream>
using namespace std;
using namespace cv;

int main()
{
    ofstream fout("caliberation_result.txt");  /**    保存定标结果的文件     **/

    /************************************************************************  
           读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化  
    *************************************************************************/   
    cout<<"开始提取角点………………"<<endl; 
    int image_count=  4;                    /****    图像数量     ****/   
    Size image_size;                         /****     图像的尺寸      ****/   
    Size board_size = Size(6,9);            /****    定标板上每行、列的角点数       ****/  
    vector<Point2f> corners;                  /****    缓存每幅图像上检测到的角点       ****/
    vector<vector<Point2f>>  corners_Seq;    /****  保存检测到的所有角点       ****/   
    vector<Mat>  image_Seq;


    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); 
        image_size = image.size();
        //image_size = Size(image.cols , image.rows);
        /* 提取角点 */   
        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";   
            exit(1);   
        } 
        else
        {   
            /* 亚像素精确化 */
            cornerSubPix(imageGray, corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
            count = count + corners.size();
            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;                                          /*****    每幅图像中角点的数量    ****/   
    Mat intrinsic_matrix = Mat(3,3, CV_32FC1, Scalar::all(0));                /*****    摄像机内参数矩阵    ****/   
    Mat distortion_coeffs = Mat(1,4, CV_32FC1, Scalar::all(0));            /* 摄像机的4个畸变系数:k1,k2,p1,p2 */ 
    vector<cv::Mat> rotation_vectors;                                      /* 每幅图像的旋转向量 */  
    vector<cv::Mat> translation_vectors;                                  /* 每幅图像的平移向量 */  

    /* 初始化定标板上角点的三维坐标 */     
    for (int t=0;t<image_count;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< image_count; i++)   
    {
        point_counts.push_back(board_size.width*board_size.height);   
    }

    /* 开始定标 */   
    calibrateCamera(object_Points, corners_Seq, image_size,  intrinsic_matrix  ,distortion_coeffs, rotation_vectors, translation_vectors, 0);   
    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];
        /****    通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点     ****/
        projectPoints(tempPointSet, rotation_vectors[i], translation_vectors[i], intrinsic_matrix, distortion_coeffs, image_points2);
        /* 计算新的投影点和旧的投影点之间的误差*/  
        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));
        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;


    /************************************************************************  
           测试一张图片  
    *************************************************************************/
    cout<<"TestImage ..."<<endl;
    Mat newCameraMatrix = Mat(3,3,CV_32FC1,Scalar::all(0));
    Mat testImage = imread("test.jpg",1);
    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.234066像素
第2幅图像的平均误差:0.174135像素
第3幅图像的平均误差:0.230404像素
第4幅图像的平均误差:0.385148像素
总体平均误差:0.255938像素

相机内参数矩阵:
[1526.985633757815, 0, 950.2799160336148;
0, 1524.888027372896, 1626.73459851929;
0, 0, 1]
畸变系数:
[-0.2939854304014771, -0.03809991415256448, -0.0006251660855326396, 0.002477996377628106, 0.08710997961828348]
第1幅图像的旋转向量:
[-2.044082716917989;
2.112604062430628;
-0.4512837261687678]
第1幅图像的旋转矩阵:
[-0.04783928023483552, -0.9443057645861673, 0.3255733807238344;
-0.9949113651213172, 0.01611256042205056, -0.0994573323042737;
0.08867231145656364, -0.3286746238546258, -0.9402713506296841]
第1幅图像的平移向量:
[64.01021865713052;
98.56104619426411;
147.9806640258283]
第2幅图像的旋转向量:
[-1.162650885638092;
2.737696507332989;
0.1448079633779595]
第2幅图像的旋转矩阵:
[-0.6837940150923046, -0.7210082406540599, 0.1121287734387665;
-0.7051562408233225, 0.6924689388697722, 0.1524514503981244;
-0.1875644447966232, 0.02517708500816826, -0.9819295766187237]
第2幅图像的平移向量:
[92.91837801221574;
58.91364372752962;
181.052631025211]
第3幅图像的旋转向量:
[1.684131027388537;
-2.175687329421208;
-0.2403921029056955]
第3幅图像的旋转矩阵:
[-0.2115618190438919, -0.894254676073194, -0.3943984927000438;
-0.9587869203326895, 0.2681969799312525, -0.09379776838876375;
0.1896555776184723, 0.3583000896971452, -0.9141399278016584]
第3幅图像的平移向量:
[35.54570544850407;
64.77270398447031;
122.9118368955376]
第4幅图像的旋转向量:
[2.717206221016452;
1.219573939678138;
0.807283561760737]
第4幅图像的旋转矩阵:
[0.5510728665512401, 0.6808924688738628, 0.4823941765629592;
0.7100594861195663, -0.6862919236796685, 0.1575402223677423;
0.4383311783601724, 0.2557124191800546, -0.8616710142243772]
第4幅图像的平移向量:
[-45.13197759830062;
-19.42296234432086;
113.2028514971537]

畸变图像:
img1.jpg
技术分享
img2.jpg
技术分享
img3.jpg
技术分享
img4.jpg
技术分享
test.jpg
技术分享
校正结果:
技术分享
技术分享
技术分享
技术分享
TestOutput.jpg
技术分享

代码下载:
http://download.csdn.net/detail/qq_15947787/9523484

【opencv】鱼眼图像畸变校正——标定校正

标签:

原文地址:http://blog.csdn.net/qq_15947787/article/details/51441031

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!