基於OpenCV做“三維重建”(2)--封裝標定過程


     既然已經能夠找到了標定點,那么下邊的工作就是使用標定結果了。【這本書在這里的內容組織讓人莫名其妙】但是通過閱讀代碼能夠很方便地串起來。
/*------------------------------------------------------------------------------------------*\
This file contains material supporting chapter 11 of the book:
OpenCV3 Computer Vision Application Programming Cookbook
Third Edition
by Robert Laganiere, Packt Publishing, 2016.
This program is free software; permission is hereby granted to use, copy, modify,
and distribute this source code, or portions thereof, for any purpose, without fee,
subject to the restriction that the copyright notice may not be removed
or altered from any source or altered source distribution.
The software is released on an as-is basis and without any warranties of any kind.
In particular, the software is not guaranteed to be fault-tolerant or free from failure.
The author disclaims all warranties with regard to this software, any use,
and any consequent failure, is purely the responsibility of the user.
Copyright (C) 2016 Robert Laganiere, www.laganiere.name
\*------------------------------------------------------------------------------------------*/
#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/features2d.hpp>
#include "CameraCalibrator.h"
int main()
{
    cv::Mat image;
    std::vector<std::stringfilelist;
    // 按照規則讀入27張圖片
    // 名稱為 chessboard01 to chessboard27 
    for (int i=1; i<=27; i++) {
        std::stringstream str;
        str << "E:/template/calibrateImages/calibrate" << std::setw(2) << std::setfill('0') << i << ".bmp";
        std::cout << str.str() << std::endl;
        filelist.push_back(str.str());
        imagecv::imread(str.str(),0);
    }
    // 創建Calibrator類
    CameraCalibrator cameraCalibrator;
    // 根據圖片實際情況定下boardSize
    cv::Size boardSize(8,6);
    cameraCalibrator.addChessboardPoints(
        filelist,    // filenames of chessboard image
        boardSize"Detected points");    // size of chessboard
    // calibrate the camera
    cameraCalibrator.setCalibrationFlag(true,true);
    cameraCalibrator.calibrate(image.size());
    // 測試標定獲得的結果
    image = cv::imread(filelist[1],0);
    cv::Size newSize(static_cast<int>(image.cols*1.5), static_cast<int>(image.rows*1.5));
    cv::Mat uImagecameraCalibrator.remap(imagenewSize);
    // display camera matrix
    cv::Mat cameraMatrixcameraCalibrator.getCameraMatrix();
    std::cout << " Camera intrinsic: " << cameraMatrix.rows << "x" << cameraMatrix.cols << std::endl;
    std::cout << cameraMatrix.at<double>(0,0) << " " << cameraMatrix.at<double>(0,1) << " " << cameraMatrix.at<double>(0,2) << std::endl;
    std::cout << cameraMatrix.at<double>(1,0) << " " << cameraMatrix.at<double>(1,1) << " " << cameraMatrix.at<double>(1,2) << std::endl;
    std::cout << cameraMatrix.at<double>(2,0) << " " << cameraMatrix.at<double>(2,1) << " " << cameraMatrix.at<double>(2,2) << std::endl;
    cv::namedWindow("Original Image");
    cv::imshow("Original Image"image);
    cv::namedWindow("Undistorted Image");
    cv::imshow("Undistorted Image"uImage);
    // Store everything in a xml file
    cv::FileStorage fs("calib.xml"cv::FileStorage::WRITE);
    fs << "Intrinsic" << cameraMatrix;
    fs << "Distortion" << cameraCalibrator.getDistCoeffs();
    cv::waitKey();
    return 0;
}
下面是類庫:
/*------------------------------------------------------------------------------------------*\
This file contains material supporting chapter 11 of the book:
OpenCV3 Computer Vision Application Programming Cookbook
Third Edition
by Robert Laganiere, Packt Publishing, 2016.
This program is free software; permission is hereby granted to use, copy, modify,
and distribute this source code, or portions thereof, for any purpose, without fee,
subject to the restriction that the copyright notice may not be removed
or altered from any source or altered source distribution.
The software is released on an as-is basis and without any warranties of any kind.
In particular, the software is not guaranteed to be fault-tolerant or free from failure.
The author disclaims all warranties with regard to this software, any use,
and any consequent failure, is purely the responsibility of the user.
Copyright (C) 2016 Robert Laganiere, www.laganiere.name
\*------------------------------------------------------------------------------------------*/
#ifndef CAMERACALIBRATOR_H
#define CAMERACALIBRATOR_H
#include <vector>
#include <iostream>
#include <opencv2/core.hpp>
#include "opencv2/imgproc.hpp"
#include "opencv2/calib3d.hpp"
#include <opencv2/highgui.hpp>
class CameraCalibrator {
    // 輸入點:  
    // 世界坐標系中的點 
    //(每個正方形為一個單位) 
    std::vector<std::vector<cv::Point3f> > objectPoints;
    // 點在圖像中的位置(以像素為單位) 
    std::vector<std::vector<cv::Point2f> > imagePoints;
    // 輸出矩陣 
    cv::Mat cameraMatrix;
    cv::Mat distCoeffs;
    // 指定標定方式的標志
    int flag;
    // used in image undistortion 
    cv::Mat map1,map2
    bool mustInitUndistort;
  public:
    CameraCalibrator() : flag(0), mustInitUndistort(true) {}
    // Open the chessboard images and extract corner points
    int addChessboardPoints(const std::vector<std::string>& filelistcv::Size & boardSizestd::string windowName="");
    // Add scene points and corresponding image points
    void addPoints(const std::vector<cv::Point2f>& imageCornersconst std::vector<cv::Point3f>& objectCorners);
    // Calibrate the camera
    double calibrate(const cv::Size imageSize);
    // Set the calibration flag
    void setCalibrationFlag(bool radial8CoeffEnabled=falsebool tangentialParamEnabled=false);
    // Remove distortion in an image (after calibration)
    cv::Mat remap(const cv::Mat &imagecv::Size &outputSize = cv::Size(-1, -1));
    // Getters
    cv::Mat getCameraMatrix() { return cameraMatrix; }
    cv::Mat getDistCoeffs()   { return distCoeffs; }
};
#endif // CAMERACALIBRATOR_H

/*------------------------------------------------------------------------------------------*\
This file contains material supporting chapter 11 of the book:
OpenCV3 Computer Vision Application Programming Cookbook
Third Edition
by Robert Laganiere, Packt Publishing, 2016.
This program is free software; permission is hereby granted to use, copy, modify,
and distribute this source code, or portions thereof, for any purpose, without fee,
subject to the restriction that the copyright notice may not be removed
or altered from any source or altered source distribution.
The software is released on an as-is basis and without any warranties of any kind.
In particular, the software is not guaranteed to be fault-tolerant or free from failure.
The author disclaims all warranties with regard to this software, any use,
and any consequent failure, is purely the responsibility of the user.
Copyright (C) 2016 Robert Laganiere, www.laganiere.name
\*------------------------------------------------------------------------------------------*/
#include "stdafx.h"
#include "CameraCalibrator.h"
// 打開棋盤圖像,提取角點 
int CameraCalibrator::addChessboardPoints(
         const std::vector<std::string>& filelist// 文件名列表
         cv::Size & boardSize,                     // 標定面板的大小 
         std::string windowName) {                 // name of window to display results
                                                   // if null, no display shown
    // 棋盤上的角點 
    std::vector<cv::Point2fimageCorners;
    std::vector<cv::Point3fobjectCorners;
    // 場景中的三維點: 
    // 在棋盤坐標系中,初始化棋盤中的角點 
    // 角點的三維坐標(X,Y,Z)= (i,j,0) 
    for (int i=0; i<boardSize.heighti++) {
        for (int j=0; j<boardSize.widthj++) {
            objectCorners.push_back(cv::Point3f(ij, 0.0f));
        }
    }
    // 圖像上的二維點:
    cv::Mat image// 用於存儲棋盤圖像 
    int successes = 0;
    // 處理所有視角 
    for (int i=0; i<filelist.size(); i++) {
        // 打開圖像
        image = cv::imread(filelist[i],0);
        // 取得棋盤中的角點
        bool found = cv::findChessboardCorners(image,         // 包含棋盤圖案的圖像
                                               boardSize,     // 圖案的大小
                                               imageCorners); // 檢測到角點的列表
        // 取得角點上的亞像素級精度
        if (found) {
            cv::cornerSubPix(imageimageCorners,
                cv::Size(5, 5), // 搜索窗口的半徑 
                cv::Size(-1, -1),
                cv::TermCriteria(cv::TermCriteria::MAX_ITER +
                    cv::TermCriteria::EPS,
                    30,        // 最大迭代次數 
                    0.1));  // 最小精度 
            // 如果棋盤是完好的,就把它加入結果  
            if (imageCorners.size() == boardSize.area()) {
                // 加入從同一個視角得到的圖像和場景點 
                addPoints(imageCornersobjectCorners);
                successes++;
            }
        }
        if (windowName.length()>0 && imageCorners.size() == boardSize.area()) {
        
            //Draw the corners
            cv::drawChessboardCorners(imageboardSizeimageCornersfound);
            cv::imshow(windowNameimage);
            cv::waitKey(100);
        }
    }
    return successes;
}
// Add scene points and corresponding image points
void CameraCalibrator::addPoints(const std::vector<cv::Point2f>& imageCornersconst std::vector<cv::Point3f>& objectCorners) {
    // 2D image points from one view
    imagePoints.push_back(imageCorners);          
    // corresponding 3D scene points
    objectPoints.push_back(objectCorners);
}
// 標定相機 
// 返回重投影誤差 
double CameraCalibrator::calibrate(const cv::Size imageSize)
{
    mustInitUndistorttrue;
    // 輸出旋轉量和平移量 
    std::vector<cv::Matrvecstvecs;
    // 開始標定
    return 
     calibrateCamera(objectPoints// 三維點 
                    imagePoints,   // 圖像點 
                    imageSize,     // 圖像尺寸 
                    cameraMatrix,  // 輸出相機矩陣 
                    distCoeffs,    // 輸出畸變矩陣 
                    rvecstvecs,  // Rs、Ts 
                    flag);         // 設置選項 
//                    ,CV_CALIB_USE_INTRINSIC_GUESS);
}
// 去除圖像中的畸變(標定后) 
cv::Mat CameraCalibrator::remap(const cv::Mat &imagecv::Size &outputSize) {
    cv::Mat undistorted;
    if (outputSize.height == -1)
        outputSize = image.size();
    if (mustInitUndistort) { // 每個標定過程調用一次  
    
        cv::initUndistortRectifyMap(
            cameraMatrix,  // 計算得到的相機矩陣 
            distCoeffs,    // 計算得到的畸變矩陣 
            cv::Mat(),     // 可選矯正項(無) 
            cv::Mat(),     // 生成無畸變的相機矩陣
            outputSize,    // 無畸變圖像的尺寸
            CV_32FC1,      // 輸出圖片的類型 
            map1map2);   // x 和 y 映射功能 
        mustInitUndistortfalse;
    }
    // Apply mapping functions
    cv::remap(imageundistortedmap1map2
        cv::INTER_LINEAR); // interpolation type
    return undistorted;
}
// Set the calibration options
// 8radialCoeffEnabled should be true if 8 radial coefficients are required (5 is default)
// tangentialParamEnabled should be true if tangeantial distortion is present
void CameraCalibrator::setCalibrationFlag(bool radial8CoeffEnabledbool tangentialParamEnabled) {
    // Set the flag used in cv::calibrateCamera()
    flag = 0;
    if (!tangentialParamEnabledflag += CV_CALIB_ZERO_TANGENT_DIST;
    if (radial8CoeffEnabledflag += CV_CALIB_RATIONAL_MODEL;
}
一發3連之后,倒是的確能夠獲得標定的結果,看上去也不錯,問題就是缺乏量化的東西。

效果似乎是更好一些:
從邊界上能夠看出被拉動了好多,但是實際上不認真分析的話還真看不出來。
這系列操作的目的,是獲得關於相機的參數,比如我這里這個相機,獲得的是:
< ?xml version = "1.0" ? >
<opencv_storage >
<Intrinsic type_id = "opencv-matrix" >
  <rows > 3 < /rows >
  <cols > 3 < /cols >
  <dt >d < /dt >
  <data >
    1. 3589305122261344e + 003 0. 5. 7505355544729957e + 002 
    01. 3565816672769690e + 003 6. 0423226535731465e + 002 
    0. 0. 1.
   < /data >
< /Intrinsic >

<
Distortion type_id = "opencv-matrix" >
  <rows > 1 < /rows >
  <cols > 14 < /cols >
  <dt >d < /dt >
  <data >
    9. 5113243912423840e + 001 1. 4262144540955842e + 003
    5. 2119492051277685e - 003 2. 8847713358900241e - 003
    1. 2859720255043484e + 002 9. 5182218776001392e + 001
    1. 4741397414456521e + 003 6. 8332022963370434e + 002 0. 0. 0. 0. 0. 0. < /data > < /Distortion >
< /opencv_storage >
我重新計算了一次(去掉了幾張圖片),差距好像有些大。
< ?xml version = "1.0" ? >
<opencv_storage >
<Intrinsic type_id = "opencv-matrix" >
  <rows > 3 < /rows >
  <cols > 3 < /cols >
  <dt >d < /dt >
  <data >
    1. 3505709175120112e + 003 0. 5. 8070986392008831e + 002 
    01. 3474004248060101e + 003 5. 9253033568240335e + 002 
    0. 0. 1.
  < /data >
< /Intrinsic >
<Distortion type_id = "opencv-matrix" >
  <rows > 1 < /rows >
  <cols > 14 < /cols >
  <dt >d < /dt >
  <data >
    4. 7269890458124472e + 001 1. 4478281139927145e + 002
    5. 0572001753860724e - 003 2. 7283262006574539e - 003
    1. 3271367851070463e + 002 4. 7547115235593054e + 001
    1. 6768734113490575e + 002 1. 8621811777918711e + 002 0. 0. 0. 0. 0. 0. < /data > < /Distortion >
< /opencv_storage >
第一個矩陣,顯然是3X3的內參矩陣;第二個矩陣的內容是輸入的點,以相機為坐標系中心。它有一個旋轉向量(3X3)和一個平移向量(3X1)組成。
大概對應的是這個樣子:
或者文檔中的表示方法:

s\cdot \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = \begin{bmatrix} fx &amp; 0 &amp; cx \\ 0 &amp; fy &amp; cy \\ 0 &amp; 0 &amp; 1 \end{bmatrix} \cdot \begin{bmatrix} r_{11} &amp; r_{12} &amp; r_{13} &amp; t_{1} \\ r_{21} &amp; r_{22} &amp; r_{23} &amp; t_{2} \\ r_{31} &amp; r_{32} &amp; r_{33} &amp; t_{3} \end{bmatrix} \cdot \begin{bmatrix} X \\ Y \\ Z \\ 1 \end{bmatrix}

這樣,這里(X, Y, Z)是一個點的世界坐標,(u, v)是點投影在圖像平面的坐標,以像素為單位。A被稱作攝像機矩陣,或者內參數矩陣。(cx, cy)是基准點(通常在圖像的中心),fx, fy是以像素為單位的焦距。所以如果因為某些因素對來自於攝像機的一幅圖像升采樣或者降采樣,所有這些參數(fx, fy, cx和cy)都將被縮放(乘或者除)同樣的尺度。內參數矩陣不依賴場景的視圖,一旦計算出,可以被重復使用(只要焦距固定)。旋轉-平移矩陣[R|t]被稱作外參數矩陣,它用來描述相機相對於一個固定場景的運動,或者相反,物體圍繞相機的的剛性運動。也就是[R|t]將點(X, Y, Z)的坐標變換到某個坐標系,這個坐標系相對於攝像機來說是固定不變的。上面的變換等價與下面的形式(z≠0):

這些圖像為1280*1024,所以這里得到cx,cy = 580,590,算是比較靠近圖像中心。





附件列表

     


    免責聲明!

    本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



     
    粵ICP備18138465號   © 2018-2025 CODEPRJ.COM