1. 程式人生 > >基於opencv的相機標定

基於opencv的相機標定

利用opencv中帶的相機標定camera_calibration.cpp檔案,整理後進行相機矯正。
在opencv中的目錄:/opencv-3.3.0/samples/cpp/tutorial_code/calib3d/camera_calibration
在裡面還有需要的in_VID5.xml、VID5.xml根據自己需要進行修改。
另外需要事先拍好的圖片或視訊或直接用攝像頭進行標定。
一、認識函式
1、bool cv::findChessboardCorners( // 如果找到角點則返回true
cv::InputArray image, // 輸入的棋盤格影象(8UC1或8UC3)
cv::Size patternSize, // 棋盤格內部角點的行、列數
cv::OutputArray corners, // 輸出的棋盤格角點
int flags = cv::CALIB_CB_ADAPTIVE_THRESH
| cv::CALIB_CB_NORMALIZE_IMAGE
);
2、void cv::cornerSubPix(
cv::InputArray image, // 輸入影象
cv::InputOutputArray corners, // 角點(既作為輸入也作為輸出)
cv::Size winSize, // 區域大小為 NXN; N=(winSize*2+1)
cv::Size zeroZone, // 類似於winSize,但是總具有較小的範圍,Size(-1,-1)表示忽略
cv::TermCriteria criteria // 停止優化的標準
);
3、 void cv::drawChessboardCorners(
cv::InputOutputArray image, // 棋盤格影象(8UC3)即是輸入也是輸出
cv::Size patternSize, // 棋盤格內部角點的行、列數
cv::InputArray corners, // findChessboardCorners()輸出的角點
bool patternWasFound // findChessboardCorners()的返回值
);
4、bitwise_not(Mat, Mat);//影象反轉
5、void cv::undistort ( InputArray src,
OutputArray dst,
InputArray cameraMatrix,
InputArray distCoeffs,
InputArray newCameraMatrix = noArray()
)
6、void cv::initUndistortRectifyMap(
InputArray _cameraMatrix,
InputArray _distCoeffs,
InputArray _matR,
InputArray _newCameraMatrix,
Size size, int m1type,
OutputArray _map1,
OutputArray _map2 )
7、void cv::remap ( InputArray src,
OutputArray dst,
InputArray map1,
InputArray map2,
int interpolation,
int borderMode = BORDER_CONSTANT,
const Scalar & borderValue = Scalar()
)
二、程式碼,有註釋
1.cameracalibrate.hpp

#ifndef CAMERACALIBRATE_HPP
#define CAMERACALIBRATE_HPP

#include <iostream>
#include <time.h>
#include <sstream>
#include <stdio.h>

#include <opencv2/core/core.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv; using namespace std; enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 }; class cameraCalibrate; class cameraCalibrate { public: /** * @brief 建構函式,初始化引數inputDetect,flag * @param * @author * @data */ cameraCalibrate(); /** * @brief 向輸出檔案寫入資料 * @param * @author * @data */
void write(FileStorage &fs) const; /** * @brief 從輸入檔案讀取節點資料 * @param * @author * @data */ void read(const FileNode &node); /** * @brief 切換到下一張圖片 * @param * @author * @data */ Mat nextImage(); /** * @brief 從圖片資料檔案中讀取圖片列表 * @param * @author * @data */ static bool readImageList(const string& filename, vector<string>& list); /** * @brief 檢查從資料檔案讀取的引數是否合法,通過inputCheck訪問 * @param * @author * @data */ void chectInput(); public: enum Pattern { NOT_EXISTING, CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID }; //檢測模式 //無檢測,棋盤,圓形網格,非對稱圓形網格 enum InputType {INVALID, CAMERA, VIDEO_FILE, IMAGE_LIST}; //輸入型別:無型別,相機,視訊,影象 int cameraID; // 使用攝像頭為攝像頭標號 vector<string> imageList; // 使用影象為影象名字列表 int atImageList; // 當前第幾張影象 VideoCapture inputCapture; // 使用攝像頭,開啟的攝像頭 InputType inputType; // 輸入型別,包括INVALID, CAMERA, VIDEO_FILE, IMAGE_LIST bool inputCheck; // 檢測輸入資料是否合法 int flag; // 執行動作標誌 Pattern calibrationPattern; // 檢測模式,包括NOT_EXISTING, CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID // 由patternToUse轉化 string patternToUse; // 將要使用的模式,從輸入檔案中讀取轉化為Pattern Size boardSize; // 棋盤格子個數 橫排個數width,縱排個數height float squareSize; // 格子大小 邊長單位 mm int nrFrames; // 圖片張數 float aspectRatio; // 寬高比 int delay; // video延時獲取幀 bool bwritePoints; // 非0在輸出檔案中寫入特徵點 bool bwriteExtrinsics; // 非0在輸出檔案中寫入相機外部引數 bool calibZeroTangentDist; // 非0假設切向畸變為0 bool calibFixPrincipalPoint;// 非0在全域性優化中主角點不變 bool flipVertical; // 非0在水平軸上翻轉輸入影象 string outputFileName; // 輸出檔名稱 bool showUndistorsed; // 非0顯示採集影象 string input; // 影象輸入檔名稱,可以是攝像頭標號 public: /** * @brief 執行校準並儲存 * @param * @author * @data */ bool runCalibrationAndSave(/*cameraCalibrate& s, */Size imageSize, Mat& cameraMatrix, Mat& distCoeffs, vector<vector<Point2f> > imagePoints ); /** * @brief 計算重投影誤差 * @param * @author * @data */ double computeReprojectionErrors( const vector<vector<Point3f> >& objectPoints, const vector<vector<Point2f> >& imagePoints, const vector<Mat>& rvecs, const vector<Mat>& tvecs, const Mat& cameraMatrix , const Mat& distCoeffs, vector<float>& perViewErrors); /** * @brief 計算特徵角位置 * @param * @author * @data */ void calcBoardCornerPositions(/*Size boardSize, float squareSize, */vector<Point3f>& corners/*, cameraCalibrate::Pattern patternType*/); /** * @brief 執行校準 * @param * @author * @data */ bool runCalibration( /*cameraCalibrate& s, */Size& imageSize, Mat& cameraMatrix, Mat& distCoeffs, vector<vector<Point2f> > imagePoints, vector<Mat>& rvecs, vector<Mat>& tvecs, vector<float>& reprojErrs, double& totalAvgErr); /** * @brief 儲存相機引數 * @param * @author * @data */ void saveCameraParams( /*cameraCalibrate& s, */Size& imageSize, Mat& cameraMatrix, Mat& distCoeffs, const vector<Mat>& rvecs, const vector<Mat>& tvecs, const vector<float>& reprojErrs, const vector<vector<Point2f> >& imagePoints, double totalAvgErr ); }; static void read(const FileNode& node, cameraCalibrate& x, const cameraCalibrate& default_value = cameraCalibrate()) { if(node.empty()) { x = default_value; } else { x.read(node); } } #endif // CAMERACALIBRATE_HPP

2.cameracalibrate.cpp

#include "cameracalibrate.hpp"

cameraCalibrate::cameraCalibrate() : inputCheck(false), flag(0)
{
}

void cameraCalibrate::write(FileStorage &fs) const
{
    fs << "{" << "BoardSize_Width"  << boardSize.width
              << "BoardSize_Height" << boardSize.height
              << "Square_Size"         << squareSize
              << "Calibrate_Pattern" << patternToUse
              << "Calibrate_NrOfFrameToUse" << nrFrames
              << "Calibrate_FixAspectRatio" << aspectRatio
              << "Calibrate_AssumeZeroTangentialDistortion" << calibZeroTangentDist
              << "Calibrate_FixPrincipalPointAtTheCenter" << calibFixPrincipalPoint

              << "Write_DetectedFeaturePoints" << bwritePoints
              << "Write_extrinsicParameters"   << bwriteExtrinsics
              << "Write_outputFileName"  << outputFileName

              << "Show_UndistortedImage" << showUndistorsed

              << "Input_FlipAroundHorizontalAxis" << flipVertical
              << "Input_Delay" << delay
              << "Input" << input
       << "}";
}

void cameraCalibrate::read(const FileNode &node)
{
    node["BoardSize_Width" ] >> boardSize.width;
    node["BoardSize_Height"] >> boardSize.height;
    node["Calibrate_Pattern"] >> patternToUse;
    node["Square_Size"]  >> squareSize;
    node["Calibrate_NrOfFrameToUse"] >> nrFrames;
    node["Calibrate_FixAspectRatio"] >> aspectRatio;
    node["Write_DetectedFeaturePoints"] >> bwritePoints;
    node["Write_extrinsicParameters"] >> bwriteExtrinsics;
    node["Write_outputFileName"] >> outputFileName;
    node["Calibrate_AssumeZeroTangentialDistortion"] >> calibZeroTangentDist;
    node["Calibrate_FixPrincipalPointAtTheCenter"] >> calibFixPrincipalPoint;
    node["Input_FlipAroundHorizontalAxis"] >> flipVertical;
    node["Show_UndistortedImage"] >> showUndistorsed;
    node["Input"] >> input;
//    cout<<input;
    node["Input_Delay"] >> delay;
    chectInput();
}

Mat cameraCalibrate::nextImage()
{
    Mat result;
    if( inputCapture.isOpened() )
    {
        Mat view0;
        inputCapture >> view0;
        view0.copyTo(result);
    }
    else if( atImageList < (int)imageList.size() )
        result = imread(imageList[atImageList++], CV_LOAD_IMAGE_COLOR);

//    imshow("aa",result);
//    cout<<"aa";
    return result;
}

bool cameraCalibrate::readImageList(const string &filename, vector<string> &list)
{
    list.clear();
    FileStorage fs(filename, FileStorage::READ);
    if( !fs.isOpened() )
        return false;
    FileNode n = fs.getFirstTopLevelNode();
    if( n.type() != FileNode::SEQ )
        return false;
    FileNodeIterator it = n.begin(), it_end = n.end();
    for( ; it != it_end; ++it )
    {
        list.push_back((string)*it);
//        cout<<(string)*it<<endl;
    }
    return true;
}

void cameraCalibrate::chectInput()
{
    inputCheck = true;
    if (boardSize.width <= 0 || boardSize.height <= 0)
    {
        cerr << "Invalid Board size: " << boardSize.width << " " << boardSize.height << endl;
        inputCheck = false;
    }
    if (squareSize <= 10e-6)
    {
        cerr << "Invalid square size " << squareSize << endl;
        inputCheck = false;
    }
    if (nrFrames <= 0)
    {
        cerr << "Invalid number of frames " << nrFrames << endl;
        inputCheck = false;
    }

    if (input.empty())      // Check for valid input
            inputType = INVALID;
    else
    {
        if (input[0] >= '0' && input[0] <= '9')
        {
            stringstream ss(input);
            ss >> cameraID;
            inputType = CAMERA;
        }
        else
        {
//            cout<<input;
            if (readImageList(input, imageList))
                {
                    inputType = IMAGE_LIST;
                    nrFrames = (nrFrames < (int)imageList.size()) ? nrFrames : (int)imageList.size();
                }
            else
                inputType = VIDEO_FILE;
        }
        if (inputType == CAMERA)
            inputCapture.open(cameraID);
        if (inputType == VIDEO_FILE)
            inputCapture.open(input);
        if (inputType != IMAGE_LIST && !inputCapture.isOpened())
                inputType = INVALID;
    }
    if (inputType == INVALID)
    {
        cerr << " Inexistent input: " << input;
        inputCheck = false;
    }

    flag = 0;
    if(calibFixPrincipalPoint) flag |= CV_CALIB_FIX_PRINCIPAL_POINT;
    if(calibZeroTangentDist)   flag |= CV_CALIB_ZERO_TANGENT_DIST;
    if(aspectRatio)            flag |= CV_CALIB_FIX_ASPECT_RATIO;


    calibrationPattern = NOT_EXISTING;
    if (!patternToUse.compare("CHESSBOARD")) calibrationPattern = CHESSBOARD;
    if (!patternToUse.compare("CIRCLES_GRID")) calibrationPattern = CIRCLES_GRID;
    if (!patternToUse.compare("ASYMMETRIC_CIRCLES_GRID")) calibrationPattern = ASYMMETRIC_CIRCLES_GRID;
    if (calibrationPattern == NOT_EXISTING)
        {
            cerr << " Inexistent camera calibration mode: " << patternToUse << endl;
            inputCheck = false;
        }
    atImageList = 0;
}

bool cameraCalibrate::runCalibrationAndSave(Size imageSize, Mat &cameraMatrix, Mat &distCoeffs, vector<vector<Point2f> > imagePoints)
{
    vector<Mat> rvecs, tvecs;
    vector<float> reprojErrs;
    double totalAvgErr = 0;

    bool ok = runCalibration(imageSize,cameraMatrix,distCoeffs,imagePoints,rvecs,tvecs,reprojErrs,totalAvgErr);

    cout<< (ok ? "Calibration succeeded" : "Calibration failed")
        << ". avg reprojection error = " << totalAvgErr << endl;

    if(ok)
        saveCameraParams(imageSize,cameraMatrix,distCoeffs,rvecs,tvecs,reprojErrs,imagePoints,totalAvgErr);

    return ok;
}

double cameraCalibrate::computeReprojectionErrors(const vector< vector<Point3f> > &objectPoints, const vector<vector<Point2f> > &imagePoints, const vector<Mat> &rvecs, const vector<Mat> &tvecs, const Mat &cameraMatrix, const Mat &distCoeffs, vector<float> &perViewErrors)
{
    vector<Point2f> imagePoints2;
    size_t totalPoints = 0;
    double totalErr = 0, err;
    perViewErrors.resize(objectPoints.size());

    for(size_t i = 0; i < objectPoints.size(); ++i )
    {
        projectPoints(objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);
        err = norm(imagePoints[i], imagePoints2, NORM_L2);

        size_t n = objectPoints[i].size();
        perViewErrors[i] = (float) std::sqrt(err*err/n);
        totalErr        += err*err;
        totalPoints     += n;
    }

    return std::sqrt(totalErr/totalPoints);
}

void cameraCalibrate::calcBoardCornerPositions(/*Size boardSize, float squareSize, */vector<Point3f> &corners/*, cameraCalibrate::Pattern patternType*/)
{
    corners.clear();

    switch (calibrationPattern) {
    case cameraCalibrate::CHESSBOARD:
    case cameraCalibrate::CIRCLES_GRID:
        for(int i = 0; i < boardSize.height; ++i)
            for(int j = 0; j < boardSize.width; ++j)
                corners.push_back(Point3f(j*squareSize,i*squareSize,0));
        break;
    case cameraCalibrate::ASYMMETRIC_CIRCLES_GRID:
        for( int i = 0; i < boardSize.height; i++ )
            for( int j = 0; j < boardSize.width; j++ )
                corners.push_back(Point3f((2*j + i % 2)*squareSize, i*squareSize, 0));
        break;
    default:
        break;
    }
}

bool cameraCalibrate::runCalibration(Size &imageSize, Mat &cameraMatrix, Mat &distCoeffs, vector<vector<Point2f> > imagePoints, vector<Mat> &rvecs, vector<Mat> &tvecs, vector<float> &reprojErrs, double &totalAvgErr)
{
    cameraMatrix = Mat::eye(3,3,CV_64F);
    if(flag & CALIB_FIX_ASPECT_RATIO)
        cameraMatrix.at<double>(0,0) = aspectRatio;
    distCoeffs = Mat::zeros(8,1,CV_64F);

    vector< vector<Point3f> > objectPoints(1);
    calcBoardCornerPositions(objectPoints[0]);
    objectPoints.resize(imagePoints.size(),objectPoints[0]);

    double rms;
    rms = calibrateCamera(objectPoints,imagePoints,imageSize,cameraMatrix,distCoeffs,rvecs,tvecs,
                          flag|CV_CALIB_FIX_K4|CV_CALIB_FIX_K5);
    cout << "Re-projection error reported by calibrateCamera: " << rms << endl;

    bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);

    totalAvgErr = computeReprojectionErrors(objectPoints,imagePoints,rvecs,tvecs,cameraMatrix,
                                            distCoeffs,reprojErrs);

    return ok;
}

void cameraCalibrate::saveCameraParams(Size &imageSize, Mat &cameraMatrix, Mat &distCoeffs, const vector<Mat> &rvecs, const vector<Mat> &tvecs, const vector<float> &reprojErrs, const vector<vector<Point2f> > &imagePoints, double totalAvgErr)
{
    FileStorage fs(outputFileName,FileStorage::WRITE);

    time_t tm;
    time(&tm);
    struct tm *t2 = localtime(&tm);
    char buf[1024];
    strftime(buf,sizeof(buf),"%c",t2);

    fs << "calibration_time" << buf;
    if(!rvecs.empty() || !reprojErrs.empty())
        fs << "nr_of_frames" << (int)max(rvecs.size(),reprojErrs.size());
    fs << "image_width" << imageSize.width;
    fs << "image_height" << imageSize.height;
    fs << "board_width" << boardSize.width;
    fs << "board_height" << boardSize.height;
    fs << "square_size" << squareSize;

    if(flag & CALIB_FIX_ASPECT_RATIO)
        fs << "fix_aspect_ratio" << aspectRatio;

    if(flag)
    {
        stringstream flagsStringStream;
        flagsStringStream << "flags:"
                          << (flag & CALIB_USE_INTRINSIC_GUESS ? " +use_intrinsic_guess" : "")
                          << (flag & CALIB_FIX_ASPECT_RATIO ? " +fix_aspectRatio" : "")
                          << (flag & CALIB_FIX_PRINCIPAL_POINT ? " +fix_principal_point" : "")
                          << (flag & CALIB_ZERO_TANGENT_DIST ? " +zero_tangent_dist" : "");
        fs.writeComment(flagsStringStream.str());
    }

    fs << "flags" << flag;

    fs << "camera_matrix" << cameraMatrix;
    fs << "distortion_coefficients" << distCoeffs;

    fs << "avg_reprojection_error" << totalAvgErr;

    if (!reprojErrs.empty())
        fs << "per_view_reprojection_errors" << Mat(reprojErrs);

    if(!rvecs.empty() && !tvecs.empty() )
    {
        CV_Assert(rvecs[0].type() == tvecs[0].type());
        Mat bigmat((int)rvecs.size(), 6, CV_MAKETYPE(rvecs[0].type(), 1));
        bool needReshapeR = rvecs[0].depth() != 1 ? true : false;
        bool needReshapeT = tvecs[0].depth() != 1 ? true : false;

        for( size_t i = 0; i < rvecs.size(); i++ )
        {
            Mat r = bigmat(Range(int(i), int(i+1)), Range(0,3));
            Mat t = bigmat(Range(int(i), int(i+1)), Range(3,6));

            if(needReshapeR)
                rvecs[i].reshape(1, 1).copyTo(r);
            else
            {
                //*.t() is MatExpr (not Mat) so we can use assignment operator
                CV_Assert(rvecs[i].rows == 3 && rvecs[i].cols == 1);
                r = rvecs[i].t();
            }

            if(needReshapeT)
                tvecs[i].reshape(1, 1).copyTo(t);
            else
            {
                CV_Assert(tvecs[i].rows == 3 && tvecs[i].cols == 1);
                t = tvecs[i].t();
            }
        }
        fs.writeComment("a set of 6-tuples (rotation vector + translation vector) for each view");
        fs << "extrinsic_parameters" << bigmat;
    }

    if(!imagePoints.empty() )
    {
        Mat imagePtMat((int)imagePoints.size(), (int)imagePoints[0].size(), CV_32FC2);
        for( size_t i = 0; i < imagePoints.size(); i++ )
        {
            Mat r = imagePtMat.row(int(i)).reshape(2, imagePtMat.cols);
            Mat imgpti(imagePoints[i]);
            imgpti.copyTo(r);
        }
        fs << "image_points" << imagePtMat;
    }
}

3.main.cpp

#include <iostream>
#include <cameracalibrate.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;
using namespace std;

int main(int argc, char* argv[])
{
    cameraCalibrate camcal;
    //提取配置檔案中引數
    const string inputFile = argc > 1 ? argv[1] : "in_Arlco_Camera_data.xml";
    FileStorage fs(inputFile,FileStorage::READ);
    if(!fs.isOpened())
    {
        cout << "Could not open the configuration file: \"" << inputFile << "\"" << endl;
        return -1;
    }
    fs["Settings"] >> camcal;
    fs.release();

    if(!camcal.inputCheck)
    {
        cout << "Invalid input detected. Application stopping." << endl;
        return -1;
    }
    //相機標定引數
    vector< vector<Point2f> > imagePoints;
    Mat cameraMatrix, distCoeffs;
    Size imageSize;
    int mode = camcal.inputType == cameraCalibrate::IMAGE_LIST ? CAPTURING : DETECTION;
    clock_t prevTimestamp = 0;
    const Scalar RED(0,0,255),GREEN(0,255,0);
    const char ESC_KEY = 27;

    for(;;)
    {
        //讀入圖片
        Mat view;
        bool blinkOutput = false;
        view = camcal.nextImage();

        if(mode == CAPTURING && imagePoints.size() >= (size_t)camcal.nrFrames)
        {
            if(camcal.runCalibrationAndSave(imageSize,cameraMatrix,distCoeffs,imagePoints))
                mode = CALIBRATED;
            else
                mode = DETECTION;
        }

        if(view.empty())
        {
            if(mode != CALIBRATED && !imagePoints.empty())
                camcal.runCalibrationAndSave(imageSize,cameraMatrix,distCoeffs,imagePoints);
            break;
        }
        imageSize = view.size();

        if(camcal.flipVertical) flip(view,view,0);

        vector<Point2f> pointBuf;

        bool found;
        int chessBoardFlags = CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE | CALIB_CB_FAST_CHECK;

        //棋盤格角點檢測
        switch (camcal.calibrationPattern) {
        case cameraCalibrate::CHESSBOARD:
            found = findChessboardCorners(view,camcal.boardSize,pointBuf,chessBoardFlags);
            break;
        case cameraCalibrate::CIRCLES_GRID:
            found = findCirclesGrid( view, camcal.boardSize, pointBuf );
            break;
        case cameraCalibrate::ASYMMETRIC_CIRCLES_GRID:
            found = findCirclesGrid( view, camcal.boardSize, pointBuf, CALIB_CB_ASYMMETRIC_GRID );
            break;
        default:
            found = false;
            break;
        }

        //找到棋盤格角點
        if(found)
        {
            if(camcal.calibrationPattern == cameraCalibrate::CHESSBOARD)
            {
                Mat viewGray;
                cvtColor(view, viewGray, COLOR_BGR2GRAY);
                //亞畫素級角點檢測
                cornerSubPix(viewGray,pointBuf,Size(11,11),Size(-1,-1),
                             TermCriteria(TermCriteria::EPS+TermCriteria::COUNT,30,0.1));
            }

            if(mode == CAPTURING && (!camcal.inputCapture.isOpened()
                                     || clock() - prevTimestamp > camcal.delay*1e-3*CLOCKS_PER_SEC))
            {
                imagePoints.push_back(pointBuf);
                prevTimestamp = clock();
                blinkOutput = camcal.inputCapture.isOpened();
            }
            //棋盤格角點繪製
            drawChessboardCorners( view, camcal.boardSize, Mat(pointBuf), found );
        }

        //提示資訊
        string msg = (mode == CAPTURING) ? "100/100" :
                      mode == CALIBRATED ? "Calibrated" : "Press 'g' to start";
        int baseLine = 0;
        Size textSize = getTextSize(msg, 1, 1, 1, &baseLine);
        Point textOrigin(view.cols - 2*textSize.width - 10, view.rows - 2*baseLine - 10);

        if( mode == CAPTURING )
        {
           if(camcal.showUndistorsed)
               msg = format( "%d/%d Undist", (int)imagePoints.size(), camcal.nrFrames );
           else
               msg = format( "%d/%d", (int)imagePoints.size(), camcal.nrFrames );
        }

        putText( view, msg, textOrigin, 1, 1, mode == CALIBRATED ?  GREEN : RED);

        //影象反轉
        if( blinkOutput )
            bitwise_not(view, view);

        if( mode == CALIBRATED && camcal.showUndistorsed )
        {
            Mat temp = view.clone();
            undistort(temp, view, cameraMatrix, distCoeffs);
        }
        //顯示找到角點的影象
        imshow("Image View", view);
        char key = (char)waitKey(!camcal.inputCapture.isOpened() ? 50 : camcal.delay);

        if( key  == ESC_KEY )
            break;

        if( key == 'u' && mode == CALIBRATED )
            camcal.showUndistorsed = !camcal.showUndistorsed;

        if( camcal.inputCapture.isOpened() && key == 'g' )
        {
            mode = CAPTURING;
            imagePoints.clear();
        }
    }

    //顯示校正後影象
    if( camcal.inputType == cameraCalibrate::IMAGE_LIST && camcal.showUndistorsed )
    {
        Mat view, rview, map1, map2;

        initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(),
                                getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0),
                                imageSize, CV_16SC2, map1, map2);

        for(size_t i = 0; i < camcal.imageList.size(); i++ )
        {
            view = imread(camcal.imageList[i], IMREAD_COLOR);
            if(view.empty())
                continue;
            remap(view, rview, map1, map2, INTER_LINEAR);
            imshow("Image View", rview);
            char c = (char)waitKey();
            if( c  == ESC_KEY || c == 'q' || c == 'Q' )
                break;
        }
    }

    return 0;
}