三維重建代碼實(shí)現(xiàn)(三)

寫在開頭

最近在學(xué)習(xí)三維重建的相關(guān)知識(shí)埋泵,打算將三維重建SFM的整個(gè)過程用代碼的形式梳理一下砂竖,本章節(jié)主要實(shí)現(xiàn)相機(jī)標(biāo)定代碼遂蛀。
這里我們假定你有一定的三維重建相關(guān)的基本知識(shí)谭跨,作者在這里推薦高翔博士的《視覺SLAM十四講:從理論到實(shí)踐》,在B站上有高翔博士的講解視頻。

程序流程

  1. 使用相機(jī)拍攝一系列的棋盤格的圖片螃宙。
  2. 對每一個(gè)圖片提取角點(diǎn)信息蛮瞄,對于角點(diǎn)信息不夠精確,進(jìn)一步提取亞像素角點(diǎn)信息谆扎;
  3. 在圖片中提取出角點(diǎn)挂捅。
  4. 相機(jī)標(biāo)定。
  5. 對標(biāo)定結(jié)果評價(jià)堂湖,計(jì)算誤差闲先。
  6. 使用標(biāo)定結(jié)果對原圖片進(jìn)行矯正。

步驟一:拍攝圖片

標(biāo)定圖片一般來說需要我們在不同位置无蜂、不同角度伺糠、不同姿態(tài)下拍攝。在之前我們的數(shù)學(xué)推到中我們得知酱讶,一般來說我們需要至少3張圖片才可以確定為一解退盯。但是,通常我們使用10~20張圖片來標(biāo)定泻肯。

步驟二:提取角點(diǎn)信息和提取亞像素角點(diǎn)信息

OPENCV中提取棋盤格中內(nèi)角點(diǎn)的函數(shù):findChessboardCorners()

函數(shù)原型

CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize, OutputArray corners,
                                         int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE );

函數(shù)說明:

這個(gè)函數(shù)的功能是確定輸入圖像中是否有棋盤格圖案,并檢測棋盤格的內(nèi)角點(diǎn)慰照。如果所有的內(nèi)角點(diǎn)都找到了灶挟,那么函數(shù)返回一個(gè)非0值;如果沒有找到所有的內(nèi)角點(diǎn)毒租,就會(huì)返回0稚铣。

參數(shù)說明:

  • image:輸入的棋盤格圖像,必須是8位的灰度或彩色圖像墅垮。
  • patternSize:每一個(gè)圖片中惕医,每行和每列角點(diǎn)的個(gè)數(shù)。注意算色,每行和每列的角點(diǎn)個(gè)數(shù)不能相同抬伺,因?yàn)檫@樣不易辨別方向。
  • corners:輸入角點(diǎn)的坐標(biāo)灾梦。通常用 cv:Point2f 向量來保存峡钓。
  • flags:默認(rèn)為0∪艉樱可選參數(shù)如下:
    • CALIB_CB_ADAPTIVE_THRESH 使用自適應(yīng)閾值(通過平均圖像亮度計(jì)算得到)將圖像轉(zhuǎn)換為黑白圖能岩,而不是一個(gè)固定的閾值。
    • CALIB_CB_NORMALIZE_IMAGE 在使用固定閾值或者自適應(yīng)閾值進(jìn)行二值化之前萧福,先使用equalizeHist()來均衡化圖像亮度拉鹃。
    • CALIB_CB_FILTER_QUADS 使用其他的準(zhǔn)則(如輪廓面積,周長,方形形狀)來去除在輪廓檢測階段檢測到的錯(cuò)誤方塊膏燕。

有兩個(gè)函數(shù)可以實(shí)現(xiàn)提取亞像素角點(diǎn)信息:cornerSubPix钥屈、find4QuadCornerSubpix。在提取棋盤格角點(diǎn)時(shí)兩者的效果差不多煌寇,隨便使用哪一個(gè)都行焕蹄。

函數(shù)原型

void cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria)

參數(shù)說明

  • image:輸入圖像。
  • corners:初始的角點(diǎn)坐標(biāo)阀溶,同時(shí)也會(huì)作為亞像素角點(diǎn)坐標(biāo)的輸出腻脏;通常用cv::Point2f/Point2d 向量來保存,vector<cv::Point2f/Point2d> points银锻。
  • winSize:大小為搜索窗口的一半永品。
  • zeroZone:死區(qū)的一般尺寸,死區(qū)為不對搜索區(qū)的中央位置做求和運(yùn)算的區(qū)域击纬。
  • criteria:迭代的終止條件鼎姐。

函數(shù)原型

CV_EXPORTS bool find4QuadCornerSubpix( InputArray img, InputOutputArray corners, Size region_size )

參數(shù)說明

  • img:輸入圖像,最好是8位灰度圖像更振,檢測效率更高炕桨;
  • corners:初始的角點(diǎn)坐標(biāo),同時(shí)也會(huì)作為亞像素角點(diǎn)坐標(biāo)的輸出肯腕;通常用cv::Point2f/Point2d 向量來保存献宫,vector<cv::Point2f/Point2d> points
  • region_size:角點(diǎn)搜索窗口的大小实撒。

步驟三:畫出角點(diǎn)

函數(shù)原型

CV_EXPORTS_W void drawChessboardCorners( InputOutputArray image, Size patternSize, InputArray corners, bool patternWasFound )

函數(shù)說明

畫出檢測的角點(diǎn)姊途。

參數(shù)說明

  • image:圖像,8位灰度或彩色圖像知态。
  • patternSize:每一幅棋盤格圖片中捷兰,每行和每列角點(diǎn)的個(gè)數(shù)。
  • corners:初始的角點(diǎn)坐標(biāo)负敏,同時(shí)也會(huì)作為亞像素角點(diǎn)坐標(biāo)的輸出贡茅;通常用cv::Point2f/Point2d 向量來保存,vector<cv::Point2f/Point2d> points原在。
  • patternWasFound:標(biāo)志位友扰,用來只是是否檢測倒所有的棋盤內(nèi)角點(diǎn)。true表示完整地檢測到了所有內(nèi)角點(diǎn)庶柿,函數(shù)會(huì)用直線將角點(diǎn)依次連接起來村怪;false表示沒有完整檢測到所有內(nèi)角點(diǎn),函數(shù)會(huì)用紅色圓圈標(biāo)出檢測到的內(nèi)角點(diǎn)浮庐。

步驟四:相機(jī)標(biāo)定

函數(shù)原型

CV_EXPORTS_W double calibrateCamera( InputArrayOfArrays objectPoints,
                                     InputArrayOfArrays imagePoints, Size imageSize,
                                     InputOutputArray cameraMatrix, InputOutputArray distCoeffs,
                                     OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
                                     int flags = 0, TermCriteria criteria = TermCriteria(
                                        TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON) );

參數(shù)說明

  • objectPoints:一系列點(diǎn)的三維坐標(biāo)甚负,即若干張圖片中對應(yīng)的若干個(gè)點(diǎn)的三維坐標(biāo)柬焕。在使用時(shí)應(yīng)該建立一個(gè)二維的vector,即vector<vector<Point3f>> objectPoints梭域。我們需要根據(jù)棋盤格每個(gè)黑白格子的長寬斑举,計(jì)算出各個(gè)內(nèi)角點(diǎn)的三維坐標(biāo)。通常我們會(huì)取z=0病涨,而只計(jì)算x和y 坐標(biāo)富玷。
  • imagePoints:若干張圖片對應(yīng)的若干的內(nèi)角點(diǎn)的坐標(biāo),通常采用vector<vector<Point2f>> image_points表示既穆。
  • imageSize:圖像的像素尺寸大小赎懦。
  • cameraMatrix:相機(jī)的內(nèi)參矩陣,對應(yīng)推導(dǎo)時(shí)的內(nèi)參矩陣A幻工,大小為3×3励两。
  • distCoeffs:相機(jī)的畸變參數(shù)矩陣,有5個(gè)畸變參數(shù):k_1,k_2,p_1,p_2,k_3囊颅,矩陣大小為1×5当悔。
  • rvecs:旋轉(zhuǎn)向量,羅德里格旋轉(zhuǎn)向量踢代,是相機(jī)外參盲憎;因?yàn)橛腥舾蓮垐D片,所以通常使用Mat類型的vector 表示胳挎,vector<Mat> rvecs焙畔。
  • tvecs:位移向量,與旋轉(zhuǎn)向量一樣串远,也是相機(jī)外參,通常使用Mat類型的vector 表示儿惫,vector<Mat> tvecs澡罚。
  • flags:表示標(biāo)定時(shí)采用的算法。默認(rèn)為0肾请,其他有:
    • CV_CALIB_USE_INTRINSIC_GUESS:使用該參數(shù)時(shí)留搔,在cameraMatrix矩陣中應(yīng)該有fx,fy,u0,v0的估計(jì)值。否則的話铛铁,將初始化(u0,v0)圖像的中心點(diǎn)隔显,使用最小二乘估算出fx,fy饵逐。
    • CV_CALIB_FIX_PRINCIPAL_POINT:在進(jìn)行優(yōu)化時(shí)會(huì)固定光軸點(diǎn)括眠。當(dāng)CV_CALIB_USE_INTRINSIC_GUESS參數(shù)被設(shè)置,光軸點(diǎn)將保持在中心或者某個(gè)輸入的值倍权。
    • CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值掷豺,只將fy作為可變量,進(jìn)行優(yōu)化計(jì)算。當(dāng)CV_CALIB_USE_INTRINSIC_GUESS沒有被設(shè)置当船,fx和fy將會(huì)被忽略题画。只有fx/fy的比值在計(jì)算中會(huì)被用到。
    • CV_CALIB_ZERO_TANGENT_DIST:設(shè)定切向畸變參數(shù)(p1,p2)為零德频。
    • CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:對應(yīng)的徑向畸變在優(yōu)化中保持不變苍息。
    • CV_CALIB_RATIONAL_MODEL:計(jì)算k4,k5壹置,k6三個(gè)畸變參數(shù)竞思。如果沒有設(shè)置,則只計(jì)算其它5個(gè)畸變參數(shù)蒸绩。
  • criteria:迭代的終止條件衙四。
    這個(gè)函數(shù)解決的就是我們以前推導(dǎo)的極大似然優(yōu)化問題:
    \sum_{i=1}^{n} \sum_{j=1}^{m}\left\|m_{i j}-\hat{m}\left(A, k_{1}, k_{2}, R_{i}, t_{i}, M_{j}\right)\right\|
    其中:\hat{m}\left(A, k_{i}, k_{j}, R_{i}, t_{i}, M_{j}\right)表示的是點(diǎn)M_j在第i個(gè)圖像上的投影。

步驟五:評價(jià)標(biāo)定結(jié)果

我們在進(jìn)行相機(jī)標(biāo)定時(shí)患亿,本身要解決的是一個(gè)優(yōu)化問題传蹈,而優(yōu)化的對象就是角點(diǎn)與三維點(diǎn)投影到圖像點(diǎn)坐標(biāo)之間的差值,通過不斷迭代步藕,盡可能地最小化這個(gè)差值惦界。我們對標(biāo)定結(jié)果評價(jià)時(shí),就是計(jì)算投影點(diǎn)與檢測到的亞像素角點(diǎn)坐標(biāo)的差值咙冗。由于是二維的沾歪,所以分別對x和y坐標(biāo)求差值,再求平方根雾消,即求L2范數(shù)灾搏。

函數(shù)原型

CV_EXPORTS_W void projectPoints( InputArray objectPoints,
                                 InputArray rvec, InputArray tvec,
                                 InputArray cameraMatrix, InputArray distCoeffs,
                                 OutputArray imagePoints,
                                 OutputArray jacobian = noArray(),
                                 double aspectRatio = 0 );

參數(shù)說明

  • objectPoints:一系列點(diǎn)的三維坐標(biāo),即若干張圖片中對應(yīng)的若干個(gè)點(diǎn)的三維坐標(biāo)立润。在使用時(shí)應(yīng)該建立一個(gè)二維的vector狂窑,即vector<vector<Point3f>> objectPoints。我們需要根據(jù)棋盤格每個(gè)黑白格子的長寬桑腮,計(jì)算出各個(gè)內(nèi)角點(diǎn)的三維坐標(biāo)泉哈。通常我們會(huì)取z=0,而只計(jì)算x和y 坐標(biāo)破讨。
  • rvecs:旋轉(zhuǎn)向量丛晦,羅德里格旋轉(zhuǎn)向量,是相機(jī)外參提陶;因?yàn)橛腥舾蓮垐D片烫沙,所以通常使用Mat類型的vector 表示,vector<Mat> rvecs搁骑。
  • tvecs:位移向量斧吐,與旋轉(zhuǎn)向量一樣又固,也是相機(jī)外參,通常使用Mat類型的vector 表示煤率,vector<Mat> tvecs仰冠。
  • cameraMatrix:相機(jī)的內(nèi)參矩陣,對應(yīng)推導(dǎo)時(shí)的內(nèi)參矩陣A蝶糯,大小為3×3洋只。
  • distCoeffs:相機(jī)的畸變參數(shù)矩陣,有5個(gè)畸變參數(shù):k_1,k_2,p_1,p_2,k_3
    昼捍,矩陣大小為1×5识虚。
  • imagePoints:若干張圖片對應(yīng)的若干的內(nèi)角點(diǎn)的坐標(biāo),通常采用vector<vector<Point2f>> image_points表示妒茬。

步驟六:矯正圖像

使用前面求得的內(nèi)參和外參以及畸變參數(shù)數(shù)據(jù)担锤,可以對圖像進(jìn)行畸變矯正。使用initUndistortRectifyMapremap兩個(gè)函數(shù)來實(shí)現(xiàn)乍钻。initUndistortRectifyMap用來計(jì)算畸變映射肛循,remap把求得的映射應(yīng)用到圖像上。

函數(shù)原型

CV_EXPORTS_W void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs,
                           InputArray R, InputArray newCameraMatrix,
                           Size size, int m1type, OutputArray map1, OutputArray map2 );

參數(shù)說明

  • cameraMatrix:相機(jī)的內(nèi)參矩陣银择;
  • distCoeffs:相機(jī)的畸變參數(shù)構(gòu)成的矩陣多糠;
  • R:可選的輸入,是第一和第二相機(jī)坐標(biāo)之間的旋轉(zhuǎn)矩陣浩考;
  • newCameraMatrix:校正后的內(nèi)參矩陣夹孔;
  • size:攝像機(jī)采集的無失真的圖像尺寸;
  • m1type:定義map1的數(shù)據(jù)類型析孽,可以是CV_32FC1或者CV_16SC2搭伤;
  • map1map2:分別對應(yīng)X和Y坐標(biāo)的重映射參數(shù)。

函數(shù)原型

CV_EXPORTS_W void remap( InputArray src, OutputArray dst,
                         InputArray map1, InputArray map2,
                         int interpolation, int borderMode = BORDER_CONSTANT,
                         const Scalar& borderValue = Scalar());

參數(shù)說明

  • src:輸入圖像袜瞬,原始有畸變的圖像闷畸;
  • dst:輸出圖像,校正后的圖像吞滞;
  • map1:X坐標(biāo)的映射;
  • map2:Y坐標(biāo)的映射盾沫;
  • interpolation:圖像的插值方式裁赠;
  • borderMode:邊界填充方式;

Code

#include <iostream>
#include <vector>
#include <fstream>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/calib3d/calib3d.hpp>

using namespace std;
using namespace cv;

int main()
{
    ifstream inImgPath("calibdata.txt");    //標(biāo)定所用圖像文件的路徑
    vector<string> imgList;
    vector<string>::iterator p;
    string temp;
    if (!inImgPath.is_open())
    {
        cout << "沒有找到文件" << endl;
    }
    //讀取文件中保存的圖片文件路徑赴精,并存放在數(shù)組中
    while (getline(inImgPath, temp))
    {
        imgList.push_back(temp);
    }

    ofstream fout("caliberation_result.txt");   //保存標(biāo)定結(jié)果的文件

    cout << "開始提取角點(diǎn)......" << endl;
    cv::Size image_size;//保存圖片大小
    cv::Size pattern_size = cv::Size(4, 6);//標(biāo)定板上每行佩捞、每列的角點(diǎn)數(shù);測試圖片中的標(biāo)定板上內(nèi)角點(diǎn)數(shù)為4*6
    vector<cv::Point2f> corner_points_buf;//建一個(gè)數(shù)組緩存檢測到的角點(diǎn)蕾哟,通常采用Point2f形式
    vector<cv::Point2f>::iterator corner_points_buf_ptr;
    vector<vector<cv::Point2f>> corner_points_of_all_imgs;
    int image_num = 0;
    string filename;
    while(image_num < imgList.size())
    {
        filename = imgList[image_num++];
        cout << "image_num = " << image_num << endl;
        cout << filename.c_str() << endl;
        cv::Mat imageInput = cv::imread(filename.c_str());
        if (image_num == 1)
        {
            image_size.width = imageInput.cols;
            image_size.height = imageInput.rows;
            cout << "image_size.width = " << image_size.width << endl;
            cout << "image_size.height = " << image_size.height << endl;
        }

        if (findChessboardCorners(imageInput, pattern_size, corner_points_buf) == 0)
        {
            cout << "can not find chessboard corners!\n";   //找不到角點(diǎn)
            exit(1);
        }
        else
        {
            cv::Mat gray;
            cv::cvtColor(imageInput, gray, CV_RGB2GRAY);
            cv::find4QuadCornerSubpix(gray, corner_points_buf, cv::Size(5, 5));
            corner_points_of_all_imgs.push_back(corner_points_buf);
            cv::drawChessboardCorners(gray, pattern_size, corner_points_buf, true);
            cv::imshow("camera calibration", gray);
            cv::waitKey(100);
        }
    }

    int total = corner_points_of_all_imgs.size();
    cout << "total=" << total << endl;
    int cornerNum = pattern_size.width * pattern_size.height;//每張圖片上的總的角點(diǎn)數(shù)
    for (int i = 0; i < total;i++)
    {
        cout << "--> 第" << i + 1 << "幅圖片的數(shù)據(jù) -->:" << endl;
        for (int j = 0;j < cornerNum;j++)
        {
            cout << "-->" << corner_points_of_all_imgs[i][j].x;
            cout << "-->" << corner_points_of_all_imgs[i][j].y;
            if ((j + 1) % 3 == 0)
            {
                cout << endl;
            }
            else
            {
                cout.width(10);
            }
        }
        cout << endl;
    }

    cout << endl << "角點(diǎn)提取完成" << endl;

    //攝像機(jī)標(biāo)定
    cout << "開始標(biāo)定………………" << endl;
    cv::Mat cameraMatrix = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0));//內(nèi)外參矩陣一忱,H——單應(yīng)性矩陣
    cv::Mat distCoefficients = cv::Mat(1, 5, CV_32FC1, cv::Scalar::all(0));//攝像機(jī)的5個(gè)畸變系數(shù):k1,k2,p1,p2,k3
    vector<cv::Mat> tvecsMat;//每幅圖像的平移向量莲蜘,t
    vector<cv::Mat> rvecsMat;//每幅圖像的旋轉(zhuǎn)向量(羅德里格旋轉(zhuǎn)向量)
    vector<vector<cv::Point3f>> objectPoints;//保存所有圖片的角點(diǎn)的三維坐標(biāo)
                                             //初始化每一張圖片中標(biāo)定板上角點(diǎn)的三維坐標(biāo)
    int i, j, k;
    for (k = 0;k < image_num;k++)//遍歷每一張圖片
    {
        vector<cv::Point3f> tempCornerPoints;//每一幅圖片對應(yīng)的角點(diǎn)數(shù)組
        //遍歷所有的角點(diǎn)
        for (i = 0;i < pattern_size.height;i++)
        {
            for (j = 0;j < pattern_size.width;j++)
            {
                cv::Point3f singleRealPoint;//一個(gè)角點(diǎn)的坐標(biāo)
                singleRealPoint.x = i * 10;
                singleRealPoint.y = j * 10;
                singleRealPoint.z = 0;//假設(shè)z=0
                tempCornerPoints.push_back(singleRealPoint);
            }
        }
        objectPoints.push_back(tempCornerPoints);
    }

    cv::calibrateCamera(objectPoints, corner_points_of_all_imgs, image_size, cameraMatrix, distCoefficients, rvecsMat, tvecsMat, 0);
    cout << "標(biāo)定完成" << endl;

    //開始保存標(biāo)定結(jié)果
    cout << "開始保存標(biāo)定結(jié)果" << endl;

    cout << endl << "相機(jī)相關(guān)參數(shù):" << endl;
    fout << "相機(jī)相關(guān)參數(shù):" << endl;
    cout << "1.內(nèi)外參數(shù)矩陣:" << endl;
    fout << "1.內(nèi)外參數(shù)矩陣:" << endl;
    cout << "大小:" << cameraMatrix.size() << endl;
    fout << "大辛庇:" << cameraMatrix.size() << endl;
    cout << cameraMatrix << endl;
    fout << cameraMatrix << endl;

    cout << "2.畸變系數(shù):" << endl;
    fout << "2.畸變系數(shù):" << endl;
    cout << "大衅鼻:" << distCoefficients.size() << endl;
    fout << "大小:" << distCoefficients.size() << endl;
    cout << distCoefficients << endl;
    fout << distCoefficients << endl;

    cout << endl << "圖像相關(guān)參數(shù):" << endl;
    fout << endl << "圖像相關(guān)參數(shù):" << endl;
    cv::Mat rotation_Matrix = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0));//旋轉(zhuǎn)矩陣
    for (i = 0;i < image_num;i++)
    {
        cout << "第" << i + 1 << "幅圖像的旋轉(zhuǎn)向量:" << endl;
        fout << "第" << i + 1 << "幅圖像的旋轉(zhuǎn)向量:" << endl;
        cout << rvecsMat[i] << endl;
        fout << rvecsMat[i] << endl;
        cout << "第" << i + 1 << "幅圖像的旋轉(zhuǎn)矩陣:" << endl;
        fout << "第" << i + 1 << "幅圖像的旋轉(zhuǎn)矩陣:" << endl;
        cv::Rodrigues(rvecsMat[i], rotation_Matrix);//將旋轉(zhuǎn)向量轉(zhuǎn)換為相對應(yīng)的旋轉(zhuǎn)矩陣
        cout << rotation_Matrix << endl;
        fout << rotation_Matrix << endl;
        cout << "第" << i + 1 << "幅圖像的平移向量:" << endl;
        fout << "第" << i + 1 << "幅圖像的平移向量:" << endl;
        cout << tvecsMat[i] << endl;
        fout << tvecsMat[i] << endl;
    }

    cout << "結(jié)果保存完畢" << endl;

    //對標(biāo)定結(jié)果進(jìn)行評價(jià)
    cout << "開始評價(jià)標(biāo)定結(jié)果......" << endl;

    //計(jì)算每幅圖像中的角點(diǎn)數(shù)量芬迄,假設(shè)全部角點(diǎn)都檢測到了
    int corner_points_counts;
    corner_points_counts = pattern_size.width * pattern_size.height;

    cout << "每幅圖像的標(biāo)定誤差:" << endl;
    fout << "每幅圖像的標(biāo)定誤差:" << endl;
    double err = 0;//單張圖像的誤差
    double total_err = 0;//所有圖像的平均誤差
    for (i = 0;i < image_num;i++)
    {
        vector<cv::Point2f> image_points_calculated;//存放新計(jì)算出的投影點(diǎn)的坐標(biāo)
        vector<cv::Point3f> tempPointSet = objectPoints[i];
        cv::projectPoints(tempPointSet, rvecsMat[i], tvecsMat[i], cameraMatrix, distCoefficients, image_points_calculated);

        //計(jì)算新的投影點(diǎn)與舊的投影點(diǎn)之間的誤差
        vector<cv::Point2f> image_points_old = corner_points_of_all_imgs[i];
        //將兩組數(shù)據(jù)換成Mat格式
        cv::Mat image_points_calculated_mat = cv::Mat(1, image_points_calculated.size(), CV_32FC2);
        cv::Mat image_points_old_mat = cv::Mat(1, image_points_old.size(), CV_32FC2);
        for (j = 0;j < tempPointSet.size();j++)
        {
            image_points_calculated_mat.at<cv::Vec2f>(0, j) = cv::Vec2f(image_points_calculated[j].x, image_points_calculated[j].y);
            image_points_old_mat.at<cv::Vec2f>(0, j) = cv::Vec2f(image_points_old[j].x, image_points_old[j].y);
        }
        err = cv::norm(image_points_calculated_mat, image_points_old_mat, cv::NORM_L2);
        err /= corner_points_counts;
        total_err += err;
        cout << "第" << i + 1 << "幅圖像的平均誤差:" << err << "像素" << endl;
        fout << "第" << i + 1 << "幅圖像的平均誤差:" << err << "像素" << endl;
    }
    cout << "總體平均誤差:" << total_err / image_num << "像素" << endl;
    fout << "總體平均誤差:" << total_err / image_num << "像素" << endl;
    cout << "評價(jià)完成" << endl;

    fout.close();

    cv::Mat mapx = cv::Mat(image_size, CV_32FC1);
    cv::Mat mapy = cv::Mat(image_size, CV_32FC1);
    cv::Mat R = cv::Mat::eye(3, 3, CV_32F);
    cout << "保存矯正圖像" << endl;
    string imageFileName;
    std::stringstream StrStm;
    for (int i = 0;i < image_num;i++)
    {
        cout << "Frame #" << i + 1 << endl;
        cv::initUndistortRectifyMap(cameraMatrix, distCoefficients, R, cameraMatrix, image_size, CV_32FC1, mapx, mapy);
        cv::Mat src_image = cv::imread(imgList[i].c_str(), 1);
        cv::Mat new_image = src_image.clone();
        cv::remap(src_image, new_image, mapx, mapy, cv::INTER_LINEAR);
        imshow("原始圖像", src_image);
        imshow("矯正后圖像", new_image);

        StrStm.clear();
        imageFileName.clear();
        StrStm << i + 1;
        StrStm >> imageFileName;
        imageFileName += "_d.jpg";
        cv::imwrite(imageFileName, new_image);

        cv::waitKey(200);
    }
    cout << "保存結(jié)束" << endl;

    cv::waitKey(0);

    return 0;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末问顷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子禀梳,更是在濱河造成了極大的恐慌杜窄,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件算途,死亡現(xiàn)場離奇詭異塞耕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嘴瓤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門扫外,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人纱注,你說我怎么就攤上這事畏浆。” “怎么了狞贱?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵刻获,是天一觀的道長。 經(jīng)常有香客問我瞎嬉,道長蝎毡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任氧枣,我火速辦了婚禮沐兵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘便监。我一直安慰自己扎谎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布烧董。 她就那樣靜靜地躺著毁靶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪逊移。 梳的紋絲不亂的頭發(fā)上预吆,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機(jī)與錄音胳泉,去河邊找鬼拐叉。 笑死岩遗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凤瘦。 我是一名探鬼主播宿礁,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼廷粒!你這毒婦竟也來了窘拯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤坝茎,失蹤者是張志新(化名)和其女友劉穎涤姊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗤放,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡思喊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了次酌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恨课。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖岳服,靈堂內(nèi)的尸體忽然破棺而出剂公,到底是詐尸還是另有隱情,我是刑警寧澤吊宋,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布纲辽,位于F島的核電站,受9級特大地震影響璃搜,放射性物質(zhì)發(fā)生泄漏拖吼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一这吻、第九天 我趴在偏房一處隱蔽的房頂上張望吊档。 院中可真熱鬧,春花似錦唾糯、人聲如沸怠硼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拒名。三九已至,卻和暖如春芋酌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背雁佳。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工脐帝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留同云,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓堵腹,卻偏偏與公主長得像炸站,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子疚顷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容