1. 程式人生 > >魚眼相機標定以及OpenCV實現

魚眼相機標定以及OpenCV實現

https://blog.csdn.net/u010784534/article/details/50474371

在另一篇文章中我已經寫過有關普通相機模型及其OpenCV標定實現,這篇文章將主要關注魚眼相機模型及其OpenCV標定實現。 
先看一張魚眼相機拍攝出來的結果:

è¿éåå¾çæè¿°

從圖中可以看出很明顯的畸變。對魚眼相機標定,有時候也可以用普通相機的標定方法對其進行標定,但是卻不能保證去畸變後的效果是最好的。因此對於Gopro等魚眼鏡頭拍攝出來的影象去畸變,最好的方法就是採用魚眼相機標定方法進行標定。

魚眼相機模型
魚眼相機的內參模型依然可以表示為: 
⎧⎩⎨⎪⎪fx000fy0cxcy1⎫⎭⎬⎪⎪
{fx0cx0fycy001}

這與普通鏡頭的成像模型沒有區別。兩者之間的區別主要體現在畸變係數,魚眼相機的畸變係數為{k1,k2,k3,k4k1,k2,k3,k4},畸變係數不同,就導致魚眼相機的投影關係也發生了變化,主要變化發生在考慮畸變情況下的投影關係轉化: 
設(X,Y,Z)為空間中一個三維點,它在成像平面內的成像座標為(u,v),在考慮畸變的情況下, 
⎧⎩⎨⎪⎪xcyczc⎫⎭⎬⎪⎪=R∗⎧⎩⎨⎪⎪XYZ⎫⎭⎬⎪⎪+T
{xcyczc}=R∗{XYZ}+T

a=xc/zc,b=yc/zca=xc/zc,b=yc/zc 
r2=a2+b2r2=a2+b2 
θ=atan(r)θ=atan(r) 
θ′=θ(1+k1θ2+k2θ4+k3θ6+k4θ8)θ′=θ(1+k1θ2+k2θ4+k3θ6+k4θ8) 
x′=(θ′/r)xcx′=(θ′/r)xc 
y′=(θ′/r)ycy′=(θ′/r)yc 
u=fxx′+cxu=fxx′+cx 
v=fyy′+cyv=fyy′+cy
OpenCV實現魚眼相機標定
利用opencv實現魚眼相機的標定和普通相機標定的標定流程基本一致,具體流程如下:

檢測角點 
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)獲取角點更精細的檢測結果
初始化標定板上角點的三維座標
開始標定 
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=fyfx=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));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
標定結果: 

è¿éåå¾çæè¿°