【opencv】顯微鏡/投影儀 圓陣列標定板標定

BHY_發表於2017-07-26

由於在顯微鏡以及投影儀下,棋盤格角點提取會非常不準確,因此必須採用圓形陣列標定板進行標定,opencv裡本身提供了圓形陣列標定版的介面,然而在顯微鏡以及投影儀下卻提取不到,因為標定板很小(顯微鏡下才6mm*6mm)經過放大,圓變形很嚴重,因此這裡通過另外的方法求取。


在顯微鏡視場中,前景與背景區別很大,直接固定閾值128得到二值圖即可,在二值圖中檢測所有輪廓,對得到的輪廓做一個篩選,首先是輪廓周長(點的個數),當然也可以通過形態學,去除掉小的噪點。對剩下的輪廓求取最小外接圓,進行圓形狀的一個比對,如果輪廓是圓心,則為目標


圓形狀比對的一個依據參考博文:http://www.cnblogs.com/jsxyhelu/p/4503900.html

//根據輪廓點和圓心計算方差
float ComputeVariance(std::vector<cv::Point> theContour, Point2f theCenter)
{
    int a[65535],n;
    float aver,s;
    float sum=0,e=0;
    n = theContour.size();
    for(int i=0;i<n;i++)
    {
        a[i] = sqrt((theContour[i].x - theCenter.x) * (theContour[i].x - theCenter.x) + (theContour[i].y - theCenter.y) * (theContour[i].y - theCenter.y));
        sum+=a[i];
    }
    aver=sum/n;
    for(int i=0;i<n;i++)
        e+=(a[i]-aver)*(a[i]-aver);
    e/=n-1;
    s=sqrt(e);
    return e;
}


經過以上步驟,可以得到視場中的16個圓心,但是其順序是亂的,需要按照一定的順序進行排序,由於連通域檢測時,16個圓輪廓在vector中是連續儲存的並且是以Y座標從大到小排的(如果不是這樣,也可以通過演算法後面的結果),先拿到2個邊上的圓心座標,計算兩點線段的斜率,尋找下一個點的原則是,下一個點與上一個點線段的斜率(角度)與上一個點與上上一個點的線段斜率(角度)夾角要最小,距離要最短。但兩者不能同時兼顧到,因此根據標定板圓的排列分為了2種情況,下一個點不在同一行和在同一行的兩種情況,在同一行時,則主要兼顧距離最短,不在同一行時,則主要兼顧夾角最小。


bool findMicroCircle(Mat image, Size board_size, vector<Point2f> &corners)
{
    Mat imageGray;
    cvtColor(image, imageGray , CV_RGB2GRAY);

    Mat imageThreshold;
    threshold(imageGray, imageThreshold, 128, 255, CV_THRESH_BINARY);

    //提取輪廓  
    vector<vector<Point>>contours; 
    vector<Point2f> corners_temp;
    int count = 0;
    findContours(imageThreshold, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE); 

    for (int i = 0; i < contours.size(); i++)
    {
        if (contours[i].size() > 200)
        {
            Point2f center;
            float radius;

            //drawContours(image, contours, i, Scalar(0,0,255), 2); 
            minEnclosingCircle(contours[i], center, radius);
            float e = ComputeVariance(contours[i], center);//方差

            if (e < 50)
            {
                count++;
                corners_temp.push_back(center);
            }
        }
    }

    //drawContours(image, contours, -1, Scalar(0,0,255), 2); 

    if (count == board_size.height * board_size.width)
    {
        int index;//下標記錄
        float k1,k2;//斜率
        float angle1,angle2,angle,anglemin,dis,dismin;//前後斜率夾角
        corners.push_back(corners_temp[0]);//0,1號點壓入,供計算初始斜率
        corners.push_back(corners_temp[1]);

        //k1 = (corners[0].y - corners[1].y) / (corners[0].x - corners[1].x);//初始斜率
        
       
        for (int j = 2; j < count; j++)
        {
            bool samelineFlag = true;
            anglemin = 90;//最小角度差
            dismin = 9999999;//兩點最小距離

            k1 = (corners[j-1].y - corners[j-2].y) / (corners[j-1].x - corners[j-2].x);//初始斜率
            angle1 = atan(k1) * 180 / CV_PI;

            for (int i = 2; i < corners_temp.size(); i++)
            {
                if (corners_temp[i].x - corners[j-1].x == 0)//斜率為0
                {
                    k2 = 0;
                }
                else
                {
                    k2 = (corners_temp[i].y - corners[j-1].y) / (corners_temp[i].x - corners[j-1].x);//後續斜率
                }

                angle2 = atan(k2) * 180 / CV_PI;

                angle = abs(angle1 - angle2);

                dis = sqrt((corners_temp[i].y - corners[j-1].y) * (corners_temp[i].y - corners[j-1].y) + (corners_temp[i].x - corners[j-1].x) * (corners_temp[i].x - corners[j-1].x));

                int sameline = (atan(1.0 / (board_size.height-1)) * 180 / CV_PI) + 5;

                if (angle < sameline)//如果計算的角度差小於最小角度值與同一條直線判斷閾值的和,則更新下標
                {
                    if (angle < sameline/4 && dis < dismin && j % board_size.width != 0 && (j-1) % board_size.width != 0)//同一條直線上,角度差不超過5度認為是同一條直線上,此時應該比較距離
                    {
                        dismin = dis;
                        index = i;
                    }
                    if (angle < anglemin && j % board_size.width == 0)//換行,只找角度差最小的
                    {
                        anglemin = angle;
                        index = i;
                    }
                    if (dis < dismin && (j-1) % board_size.width == 0)//換行後第一次,只找角度差最小的
                    {
                        dismin = dis;
                        index = i;
                    }
                }
            }
            corners.push_back(corners_temp[index]);
            circle(image, corners_temp[index], 15, Scalar(0,255,0), 3, 8, 0);
            circle(image, corners_temp[0], 15, Scalar(0,0,255), -1, 8, 0);
            circle(image, corners_temp[1], 15, Scalar(0,0,255), -1, 8, 0);

            std::vector<Point2f>::iterator it = corners_temp.begin() + index;
            corners_temp.erase(it);
        }
        return true;
    }
    else
    {
        return false;
    }   
}




其實從哪個點提取無所謂,但是要保證一個原則就是一行一行按順序排列點即可。


標定與校正結果如下:

第1幅影象的平均誤差:0.80577畫素
第2幅影象的平均誤差:0.701184畫素
第3幅影象的平均誤差:0.70272畫素
第4幅影象的平均誤差:0.547063畫素
第5幅影象的平均誤差:1.09536畫素
第6幅影象的平均誤差:0.76506畫素
第7幅影象的平均誤差:0.667793畫素
第8幅影象的平均誤差:0.822369畫素
第9幅影象的平均誤差:0.877443畫素
第10幅影象的平均誤差:0.856679畫素
第11幅影象的平均誤差:0.681006畫素
總體平均誤差:0.774767畫素

相機內引數矩陣:
[39251.0769115962, 0, 932.5315829671392;
  0, 41185.50490132452, 694.5148137172613;
  0, 0, 1]
畸變係數:
[-24.56078184561823, -13.9232076080276, 0.004546530516996267, 0.005352287915247098, -0.007488930052855393]
第1幅影象的旋轉向量:
[-2.400648046930939;
  2.39789006446686;
  -0.5030773963661545]
第1幅影象的旋轉矩陣:
[0.0007116874091214198, -0.9999975463682025, 0.002097798514260035;
  -0.9165202388330997, -0.001491368632978607, -0.3999855342745764;
  0.3999876814482535, -0.001638010126739448, -0.9165190514181771]
第1幅影象的平移向量:
[1.740430650160699;
  1.980388421080897;
  213.9421545468698]
第2幅影象的旋轉向量:
[2.026069655685576;
  -1.95425564704494;
  0.4404653877035846]
第2幅影象的旋轉矩陣:
[0.03229009767692703, -0.9993252200120062, 0.01750583445509979;
  -0.91021254996335, -0.03663704040549276, -0.4125176858748505;
  0.4128806891599921, -0.002613793848253693, -0.9107813703631013]
第2幅影象的平移向量:
[1.434303442340692;
  1.933775134314402;
  215.7635414434346]
第3幅影象的旋轉向量:
[-2.506973189718734;
  2.266959042094225;
  -0.5296331774750954]
第3幅影象的旋轉矩陣:
[0.09190965608196688, -0.9949790263151063, 0.03961505158317322;
  -0.9095342846029065, -0.1000781939290523, -0.4034002233908377;
  0.4053393642995623, 0.00104512819403188, -0.9141656892797304]
第3幅影象的平移向量:
[2.115457782230888;
  2.63554510808202;
  214.3826147104438]
第4幅影象的旋轉向量:
[2.091249202153444;
  -1.904469058116255;
  0.4192461330508303]
第4幅影象的旋轉矩陣:
[0.08818120869555068, -0.9957969531149588, 0.02474878986851795;
  -0.9141364173315228, -0.09076976925361158, -0.3951144891014927;
  0.3957002463245116, 0.01221790311839707, -0.9182984470759549]
第4幅影象的平移向量:
[1.229216205018381;
  2.958858389701409;
  215.2865289017586]
第5幅影象的旋轉向量:
[2.052248883184534;
  -1.782921024377806;
  0.516852253248877]
第5幅影象的旋轉矩陣:
[0.1311653007292452, -0.9908488716301933, 0.03184615320222786;
  -0.8542562667193232, -0.129267016908198, -0.5035238515804592;
  0.5030326974018403, 0.03884008147299184, -0.8633942051089989]
第5幅影象的平移向量:
[1.211696914955099;
  1.858255483709739;
  215.9341707929622]
第6幅影象的旋轉向量:
[2.096455574273039;
  -1.930547560805298;
  0.301475989544044]
第6幅影象的旋轉矩陣:
[0.08784392817508158, -0.9956067885038483, -0.03241245081484015;
  -0.9383224556449042, -0.07177843894483371, -0.3382289534252786;
  0.334416526976894, 0.06012469033347367, -0.9405055066808574]
第6幅影象的平移向量:
[2.702045961192787;
  1.77720171122464;
  220.6739208255806]
第7幅影象的旋轉向量:
[-2.249647067123821;
  1.790500361198483;
  0.4486109104045156]
第7幅影象的旋轉矩陣:
[0.2060376646047496, -0.9740240671687818, -0.09394465040759759;
  -0.90325267668592, -0.2262354126433894, 0.3646260277678985;
  -0.3764081333126875, 0.00972906172558119, -0.926402861898644]
第7幅影象的平移向量:
[1.36362867973697;
  2.669282558564297;
  225.9698060155932]
第8幅影象的旋轉向量:
[-2.15307960749997;
  2.011120796187208;
  0.09171998559648421]
第8幅影象的旋轉矩陣:
[0.07581061381791343, -0.9933676827270825, 0.08644881575809901;
  -0.9813750892821667, -0.05898515769153534, 0.1828214574619851;
  -0.1765097305220621, -0.09869852119222058, -0.9793379993370511]
第8幅影象的平移向量:
[2.760921681914984;
  1.602440113356596;
  222.0324924843436]
第9幅影象的旋轉向量:
[-2.441623710430115;
  1.442733098603825;
  0.01861088167794312]
第9幅影象的旋轉矩陣:
[0.4943342731749869, -0.8575998295617726, 0.1419723871045915;
  -0.8536522578609274, -0.4481113765116447, 0.2654694274107283;
  -0.1640470938892193, -0.2524256852550024, -0.9536088424558804]
第9幅影象的平移向量:
[1.56167374380016;
  2.69886532502509;
  222.8328423002089]
第10幅影象的旋轉向量:
[-0.3020672487178615;
  -0.3177006009417958;
  -1.748049962240801]
第10幅影象的旋轉矩陣:
[-0.1947892568904155, 0.9804375716976252, 0.02827213122040473;
  -0.9077898948900995, -0.1911219165559888, 0.373349594545632;
  0.3714493937745785, 0.04705933505299129, 0.9272598162591047]
第10幅影象的平移向量:
[-0.3428851599804441;
  2.695720889270264;
  218.1581049286656]
第11幅影象的旋轉向量:
[-2.501831188933102;
  1.332345638343028;
  0.3106030443469637]
第11幅影象的旋轉矩陣:
[0.5492422450311522, -0.8339504535735243, -0.05347520226845928;
  -0.771624664883495, -0.5306805918268061, 0.3506757562218761;
  -0.3208244579463564, -0.1513431545953232, -0.934968938917525]
第11幅影象的平移向量:
[0.2722978014067166;
  2.75998728572356;
  218.6845829362973]


以下程式碼從標定圖資料夾下讀取標定圖,進行定標,校正待標定圖資料夾下的所有圖片,並把校正結果儲存到校正圖資料夾下

//opencv2.4.9 vs2012
#include <opencv2\opencv.hpp>
#include <fstream>
#include <direct.h>

using namespace std;
using namespace cv;

//根據輪廓點和圓心計算方差
float ComputeVariance(std::vector<cv::Point> theContour, Point2f theCenter)
{
    int a[65535],n;
    float aver,s;
    float sum=0,e=0;
    n = theContour.size();
    for(int i=0;i<n;i++)
    {
        a[i] = sqrt((theContour[i].x - theCenter.x) * (theContour[i].x - theCenter.x) + (theContour[i].y - theCenter.y) * (theContour[i].y - theCenter.y));
        sum+=a[i];
    }
    aver=sum/n;
    for(int i=0;i<n;i++)
        e+=(a[i]-aver)*(a[i]-aver);
    e/=n-1;
    s=sqrt(e);
    return e;
}

bool findMicroCircle(Mat image, Size board_size, vector<Point2f> &corners)
{
    Mat imageGray;
    cvtColor(image, imageGray , CV_RGB2GRAY);

    Mat imageThreshold;
    threshold(imageGray, imageThreshold, 128, 255, CV_THRESH_BINARY);

    //提取輪廓  
    vector<vector<Point>>contours; 
    vector<Point2f> corners_temp;
    int count = 0;
    findContours(imageThreshold, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE); 

    for (int i = 0; i < contours.size(); i++)
    {
        if (contours[i].size() > 200)
        {
            Point2f center;
            float radius;

            //drawContours(image, contours, i, Scalar(0,0,255), 2); 
            minEnclosingCircle(contours[i], center, radius);
            float e = ComputeVariance(contours[i], center);//方差

            if (e < 50)
            {
                count++;
                corners_temp.push_back(center);
            }
        }
    }

    //drawContours(image, contours, -1, Scalar(0,0,255), 2); 

    if (count == board_size.height * board_size.width)
    {
        int index;//下標記錄
        float k1,k2;//斜率
        float angle1,angle2,angle,anglemin,dis,dismin;//前後斜率夾角
        corners.push_back(corners_temp[0]);//0,1號點壓入,供計算初始斜率
        corners.push_back(corners_temp[1]);

        //k1 = (corners[0].y - corners[1].y) / (corners[0].x - corners[1].x);//初始斜率
        
       
        for (int j = 2; j < count; j++)
        {
            bool samelineFlag = true;
            anglemin = 90;//最小角度差
            dismin = 9999999;//兩點最小距離

            k1 = (corners[j-1].y - corners[j-2].y) / (corners[j-1].x - corners[j-2].x);//初始斜率
            angle1 = atan(k1) * 180 / CV_PI;

            for (int i = 2; i < corners_temp.size(); i++)
            {
                if (corners_temp[i].x - corners[j-1].x == 0)//斜率為0
                {
                    k2 = 0;
                }
                else
                {
                    k2 = (corners_temp[i].y - corners[j-1].y) / (corners_temp[i].x - corners[j-1].x);//後續斜率
                }

                angle2 = atan(k2) * 180 / CV_PI;

                angle = abs(angle1 - angle2);

                dis = sqrt((corners_temp[i].y - corners[j-1].y) * (corners_temp[i].y - corners[j-1].y) + (corners_temp[i].x - corners[j-1].x) * (corners_temp[i].x - corners[j-1].x));

                int sameline = (atan(1.0 / (board_size.height-1)) * 180 / CV_PI) + 5;

                if (angle < sameline)//如果計算的角度差小於最小角度值與同一條直線判斷閾值的和,則更新下標
                {
                    if (angle < sameline/4 && dis < dismin && j % board_size.width != 0 && (j-1) % board_size.width != 0)//同一條直線上,角度差不超過5度認為是同一條直線上,此時應該比較距離
                    {
                        dismin = dis;
                        index = i;
                    }
                    if (angle < anglemin && j % board_size.width == 0)//換行,只找角度差最小的
                    {
                        anglemin = angle;
                        index = i;
                    }
                    if (dis < dismin && (j-1) % board_size.width == 0)//換行後第一次,只找角度差最小的
                    {
                        dismin = dis;
                        index = i;
                    }
                }
            }
            corners.push_back(corners_temp[index]);
            //circle(image, corners_temp[index], 15, Scalar(0,255,0), 3, 8, 0);
            //circle(image, corners_temp[0], 15, Scalar(0,0,255), -1, 8, 0);
            //circle(image, corners_temp[1], 15, Scalar(0,0,255), -1, 8, 0);

            std::vector<Point2f>::iterator it = corners_temp.begin() + index;
            corners_temp.erase(it);
        }
        return true;
    }
    else
    {
        return false;
    }   
}

int main()
{
    _mkdir("校正圖");

    double time0 = static_cast<double>(getTickCount());
    ofstream fout("caliberation_result.txt");  /**    儲存定標結果的檔案     **/

    /************************************************************************  
           讀取每一幅影象,從中提取出角點,然後對角點進行亞畫素精確化  
    *************************************************************************/   
    cout<<"開始提取角點………………"<<endl; 
    int image_count=  11;                    /****    影象數量     ****/   
    Size image_size;                         /****     影象的尺寸      ****/   
    Size board_size = Size(4,4);            /****    定標板上每行、列的角點數       ****/  
    vector<Point2f> corners;                  /****    快取每幅影象上檢測到的角點       ****/
    vector<vector<Point2f>>  corners_Seq;    /****  儲存檢測到的所有角點       ****/   
    vector<Mat>  image_Seq;


    int count = 0;
    for( int i = 0;  i < image_count ; i++)
    {
        cout<<"標定圖【"<< i+1 <<"】..."<<endl;
        string imageFileName;
        std::stringstream StrStm;
        StrStm<<i+1;
        StrStm>>imageFileName;
        imageFileName += ".bmp";
        Mat image = imread("標定圖/" + 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 );
        //圓陣列方式
        //bool patternfound = findCirclesGrid(image, board_size, corners); 
        //圓陣列非對稱方式
        //bool patternfound = findCirclesGrid(image, board_size, corners, CALIB_CB_ASYMMETRIC_GRID ); 
        //自定義顯微鏡下圓陣列方式
        bool patternfound = findMicroCircle(image, board_size, corners); 

        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++)
            {
                if (j + 1 < corners.size())
                {
                    line(imageTemp, corners[j], corners[j + 1], Scalar(0,255,0), 2, 8, 0);
                }              
                circle( imageTemp, corners[j], 10, Scalar(0,0,255), -1, 8, 0);
            }
            string imageFileName;
            std::stringstream StrStm;
            StrStm<<i+1;
            StrStm>>imageFileName;
            imageFileName += "_corner.jpg";
            imwrite(imageFileName, imageTemp);
            cout<<"標定圖【"<<i+1<<"】...end"<<endl;

            count = count + corners.size();
            corners_Seq.push_back(corners);
        }   

        corners.clear();
        image_Seq.push_back(image);
    }   
    cout<<"角點提取完成!\n"; 
    /************************************************************************  
           攝像機定標  
    *************************************************************************/   
    cout<<"開始定標………………"<<endl;   
    Size2d square_size = Size2d(1.2,1.2);                                      /**** 實際測量得到的定標板上每個棋盤格的大小   ****/  
    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<<"標定圖【"<<i+1<<"】..."<<endl;
        Mat newCameraMatrix = Mat(3,3,CV_32FC1,Scalar::all(0));
        initUndistortRectifyMap(intrinsic_matrix,distortion_coeffs,R, getOptimalNewCameraMatrix(intrinsic_matrix, distortion_coeffs, image_size, 1, image_size, 0),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;

    time0 = ((double)getTickCount()-time0)/getTickFrequency();
    cout<<"標定用時:"<<time0<<"秒"<<endl;

    /************************************************************************  
           批量校正圖片  
    *************************************************************************/  
    if (1)
    {
        double time1 = static_cast<double>(getTickCount());

        int image_count = 47; /****    待校正影象數量     ****/   

        for( int i = 0;  i < image_count ; i++)
        {
            cout<<"待校正圖【"<< i+1 <<"】..."<<endl;
            string imageFileName;
            std::stringstream StrStm;
            StrStm<<i+1;
            StrStm>>imageFileName;
            imageFileName += ".bmp";
            Mat image = imread("待校正圖/" + imageFileName);//引號中可加統一檔名字首 
            image_size = image.size();

            cout<<"待校正圖【"<< i+1 <<"】校正完畢"<<endl;

            initUndistortRectifyMap(intrinsic_matrix,distortion_coeffs,R, getOptimalNewCameraMatrix(intrinsic_matrix, distortion_coeffs, image_size, 1, image_size, 0),image_size,CV_32FC1,mapx,mapy);
            Mat t = image.clone();
            cv::remap(image, t, mapx, mapy, INTER_LINEAR);

            imwrite("校正圖/" + imageFileName, t);         
        }
        cout<<"儲存結束"<<endl;

        time1 = ((double)getTickCount()-time1)/getTickFrequency();
        cout<<"校正用時:"<<time1<<"秒"<<endl;
    }

    getchar();
    return 0;
}











相關文章