zoukankan      html  css  js  c++  java
  • 基于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)组成。
    大概对应的是这个样子:
    或者文档中的表示方法:

    scdot egin{bmatrix} u \ v \ 1 end{bmatrix} = egin{bmatrix} fx &amp; 0 &amp; cx \ 0 &amp; fy &amp; cy \ 0 &amp; 0 &amp; 1 end{bmatrix} cdot egin{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 egin{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,算是比较靠近图像中心。





    附件列表

    • 相关阅读:
      网站测试
      shell102输出数组
      shell101变量
      shell100for无参数形式
      shell99函数中传数组
      shell98函数的参数
      将php中session存入redis中
      windows下安装redis客户端
      window下phpstudy开启redis扩展
      *ginx下开启phpredis扩展
    • 原文地址:https://www.cnblogs.com/jsxyhelu/p/10631282.html
    Copyright © 2011-2022 走看看