鱼眼相机标定以及OpenCV实现


在另一篇文章中我已经写过有关普通相机模型及其OpenCV标定实现,这篇文章将主要关注鱼眼相机模型及其OpenCV标定实现。 
先看一张鱼眼相机拍摄出来的结果:

这里写图片描述

从图中可以看出很明显的畸变。对鱼眼相机标定,有时候也可以用普通相机的标定方法对其进行标定,但是却不能保证去畸变后的效果是最好的。因此对于Gopro等鱼眼镜头拍摄出来的图像去畸变,最好的方法就是采用鱼眼相机标定方法进行标定。

鱼眼相机模型

鱼眼相机的内参模型依然可以表示为: 

fx000fy0cxcy1

这与普通镜头的成像模型没有区别。两者之间的区别主要体现在畸变系数,鱼眼相机的畸变系数为{k1,k2,k3,k4},畸变系数不同,就导致鱼眼相机的投影关系也发生了变化,主要变化发生在考虑畸变情况下的投影关系转化: 
设(X,Y,Z)为空间中一个三维点,它在成像平面内的成像坐标为(u,v),在考虑畸变的情况下, 
xcyczc=RXYZ+T

a=xc/zc,b=yc/zc 
r2=a2+b2 
θ=atan(r) 
θ=θ(1+k1θ2+k2θ4+k3θ6+k4θ8) 
x=(θ/r)xc 
y=(θ/r)yc 
u=fxx+cx 
v=fyy+cy

OpenCV实现鱼眼相机标定

利用opencv实现鱼眼相机的标定和普通相机标定的标定流程基本一致,具体流程如下:

  1. 检测角点 
    cv::findChessboardCorners(InputArray image, Size patternSize, OutputArray corners, int 
    flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE}
     
    获得棋盘标定板的角点位置,使用 
    cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, 
    TermCriteria criteria)
    获取角点更精细的检测结果
  2. 初始化标定板上角点的三维坐标
  3. 开始标定 
    double fisheye::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, 
    const Size& image_size, InputOutputArray K, InputOutputArray D, 
    OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs, int flags=0, 
    TermCriteria criteria=TermCriteria(TermCriteria::COUNT + TermCriteria:: 
    EPS, 100, DBL_EPSILON))
     
    注意:K,D 分别表示内参矩阵和畸变系数向量,在定义时要定义为double型,这里推荐使用Matx33d和Vec4d数据类型,更为方便简单。objectPoints,imagePoints可以是float型,也可以是double型,但是再stereorectify中需要时double型。flags的可选项有很多,其中需要注意的是必须要指定CALIB_FIX_SKEW,代表求解时假设内参中fx=fy
    4.评定误差(可选项)

        for (int i = 0; i != image_count; i++)
        {
            cout << "Frame #" << i + 1 << "..." << endl;
            string image_Name;
            stringstream stream;
            stream << (i + startNum);
            stream >> image_Name;
            image_Name = path_ChessboardImage + image_Name + ".jpg";
            cv::Mat image = imread(image_Name);
            Mat image_gray;
            cvtColor(image, image_gray, CV_RGB2GRAY);
            vector<Point2f> corners;                   
            bool patternFound = findChessboardCorners(image_gray, board_size, corners,
                CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK);
            if (!patternFound || fullcornersNum != corners.size())
            {
                cout << "can not find chessboard corners!\n";
                continue;
            }
            else
            {
                cornerSubPix(image_gray, 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);
                successImageNum = successImageNum + 1;
                image_Seq.push_back(image);
            }
        }
        /************************************************************************
        摄像机定标
        *************************************************************************/
        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));

    标定结果: 
    这里写图片描述


posted on 2017-11-16 09:55 zmj 阅读(2479) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理