相机标定原理可查看张正友相机标定的论文,圆点检测原理可查看opencv的相关文档。标定板使用白底黑圆点标定板。(欢迎进Q群交流:874653199)

        头文件circleGridCalibration.h

#pragma once

#include<iostream>
#include<opencv2\core\core.hpp>

namespace circleGridCalibrate{

  //针孔模型-单相机标定
  void monoCalibratePinhole(std::vector<cv::Mat> srcImage, cv::Size boardSize,double centerDistance);
  //针孔模型-双相机标定
  void stereoCalibratePinhole(std::vector<cv::Mat> leftImage, std::vector<cv::Mat> rightImage, cv::Size boardSize, double centerDistance);

}

        cpp文件circleGridCalibration.cpp

#include "circleGridCalibration.h"

#include<opencv2\calib3d.hpp>
#include<opencv2\highgui.hpp>
#include<opencv2\imgproc.hpp>

#include <direct.h>
#include<fstream>

#pragma warning(disable:4996)


namespace circleGridCalibrate{

  void monoCalibratePinhole(std::vector<cv::Mat> srcImage,cv::Size boardSize, double centerDistance);

  void stereoCalibratePinhole(std::vector<cv::Mat> leftImage, std::vector<cv::Mat> rightImage,cv::Size boardSize, double centerDistance);


}
//针孔模型-计算反投影误差
static double computeReprojectionErrors(const std::vector<std::vector<cv::Point3f> >& objectPoints, const std::vector<std::vector<cv::Point2f> >& imagePoints, const std::vector<cv::Mat>& rvecs, const std::vector<cv::Mat>& tvecs,
  const cv::Mat& cameraMatrix, const cv::Mat& distCoeffs, std::vector<float>& perViewErrors)
{
  std::vector<cv::Point2f> imagePoints2;
  int i, totalPoints = 0;
  double totalErr = 0, err;
  perViewErrors.resize(objectPoints.size());

  for (i = 0; i < (int)objectPoints.size(); i++)
  {
    cv::projectPoints(objectPoints[i], rvecs[i], tvecs[i],
      cameraMatrix, distCoeffs, imagePoints2);
    err = norm(cv::Mat(imagePoints[i]), cv::Mat(imagePoints2), cv::NORM_L2);
    int n = (int)objectPoints[i].size();
    perViewErrors[i] = (float)std::sqrt(err*err / n);
    totalErr += err*err;
    totalPoints += n;
  }

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



//小孔成像-单相机标定
void circleGridCalibrate::monoCalibratePinhole(std::vector<cv::Mat> srcImage, cv::Size boardSize, double centerDistance){

  int numbers = srcImage.size();

  std::vector<cv::Mat>grayImage(numbers);

  for (int i = 0; i < numbers; i++) {

    if (srcImage[i].channels() == 1) {

      grayImage[i] = srcImage[i];

    }
    else {
    
      cv::cvtColor(srcImage[i], grayImage[i],cv::COLOR_RGB2GRAY);
    
    }

  
  }


  cv::Size imageSize = srcImage[0].size();

  std::vector<std::vector<cv::Point2f>>totalImagePoint;

  std::vector<cv::Point2f>singleImagePoint;


  for (int i = 0; i < numbers; i++){

    /*
    CALIB_CB_SYMMETRIC_GRID 使用圆形的对称图案。
    CALIB_CB_ASYMMETRIC_GRID 使用非对称圆形图案。
    CALIB_CB_CLUSTERING 使用特殊的算法进行网格检测。它对透视失真更鲁棒,但对背景杂波更加敏感。
    */
    bool patternFound = findCirclesGrid(grayImage[i], boardSize, singleImagePoint, cv::CALIB_CB_SYMMETRIC_GRID || cv::CALIB_CB_CLUSTERING);

    if (patternFound){

      std::cout << i + 1 << ":" << "succeed" << std::endl;

      //角点绘制
      drawChessboardCorners(srcImage[i], boardSize, singleImagePoint, patternFound);

      totalImagePoint.push_back(singleImagePoint);

      singleImagePoint.clear();

      cv::imshow("circle grid", srcImage[i]);

      cv::waitKey(500);
    }
    else{
      std::cout << i + 1 << ":" << "failed" << std::endl;
    }
  }


  cv::Point3f TobjectPoint;

  int numbers1= totalImagePoint.size();

  std::vector<std::vector<cv::Point3f>>totalObjectPoint;

  std::vector<cv::Point3f>singleObjectPoint;


  for (int i = 0; i < numbers1; i++){
    for (int m = 0; m < boardSize.height; m++){//11
      for (int n = 0; n < boardSize.width; n++){//12
        TobjectPoint.x = n*centerDistance;
        TobjectPoint.y = m*centerDistance;
        TobjectPoint.z = 0;
        singleObjectPoint.push_back(TobjectPoint);
      }

    }
    totalObjectPoint.push_back(singleObjectPoint);
    singleObjectPoint.clear();
  }


  cv::Mat cameraMatrix, distCoeffs;
  std::vector<cv::Mat>rvecs, tvecs;

  calibrateCamera(totalObjectPoint, totalImagePoint, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, 0, cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 30, DBL_EPSILON));//
  std::vector<float>perViewErrors;
  double err = computeReprojectionErrors(totalObjectPoint, totalImagePoint, rvecs, tvecs, cameraMatrix, distCoeffs, perViewErrors);


  std::fstream result;
  result.open("monoPinholeResult.txt", std::fstream::out);

  result << "相机内参数:" << cameraMatrix << std::endl;
  result << "畸变系数:" << distCoeffs << std::endl;
  result << "反投影误差均值:" << err << std::endl;

  for (int i = 0; i <numbers1; i++){
    cv::Mat R;
    cv::Rodrigues(rvecs[i], R);
    result<< " 第" << i + 1 << "幅反投影误差:" << perViewErrors[i] << std::endl;
    result << " 第" << i + 1 << "幅旋转矩阵:" << R << std::endl;//旋转矩阵为相机坐标系至世界坐标系
    result << " 第" << i + 1 << "幅平移矩阵:" << tvecs[i]<< std::endl;//平移矩阵为相机坐标系至世界坐标系
  }


}

//小孔成像-双相机标定
void circleGridCalibrate::stereoCalibratePinhole(std::vector<cv::Mat> leftImage, std::vector<cv::Mat> rightImage, cv::Size boardSize, double centerDistance){//这里没调用单目标定函数是由于左右检测出的图像可能不一致

  int numbers = leftImage.size();

  std::vector<cv::Mat>leftGrayImage(numbers);
  std::vector<cv::Mat>rightGrayImage(numbers);

  for (int i = 0; i < numbers; i++){

    if (leftImage[i].channels() == 1) {

      leftGrayImage[i] = leftImage[i];

      rightGrayImage[i] = rightImage[i];

    }
    else {

      cvtColor(leftImage[i], leftGrayImage[i], cv::COLOR_RGB2GRAY);
      cvtColor(rightImage[i], rightGrayImage[i], cv::COLOR_RGB2GRAY);

    }
  }

  cv::Size imageSize = leftImage[0].size();



  std::vector<std::vector<cv::Point2f>>leftTotalImagePoint;
  std::vector<std::vector<cv::Point2f>>rightTotalImagePoint;

  std::vector<cv::Point2f>leftSingleImagePoint;
  std::vector<cv::Point2f>rightSingleImagePoint;

  for (int i = 0; i < numbers; i++)
  {
    /*
    CALIB_CB_SYMMETRIC_GRID 使用圆形的对称图案。
    CALIB_CB_ASYMMETRIC_GRID 使用非对称圆形图案。
    CALIB_CB_CLUSTERING 使用特殊的算法进行网格检测。它对透视失真更鲁棒,但对背景杂波更加敏感。
    */
  
    bool leftPatternFound = findCirclesGrid(leftGrayImage[i], boardSize, leftSingleImagePoint, cv::CALIB_CB_SYMMETRIC_GRID);
    bool rightPatternFound = findCirclesGrid(rightGrayImage[i], boardSize, rightSingleImagePoint,cv::CALIB_CB_SYMMETRIC_GRID);
    if (leftPatternFound && rightPatternFound){
      std::cout << i + 1 << ":" << "succeed" << std::endl;
      
      //角点绘制
      drawChessboardCorners(leftImage[i], boardSize, leftSingleImagePoint, leftPatternFound);
      drawChessboardCorners(rightImage[i], boardSize, rightSingleImagePoint, rightPatternFound);

      leftTotalImagePoint.push_back(leftSingleImagePoint);
      rightTotalImagePoint.push_back(rightSingleImagePoint);

      leftSingleImagePoint.clear();
      rightSingleImagePoint.clear();

      cv::imshow("Left", leftImage[i]);
      cv::imshow("Right", rightImage[i]);

      cv::waitKey(500);
    }
    else{
      std::cout << i + 1 << ":" << "failed" << std::endl;
    }


  } 


      cv::Point3f TobjectPoint;

    int numbers1 = leftTotalImagePoint.size();

    std::vector<std::vector<cv::Point3f>>totalObjectPoint;
    std::vector<cv::Point3f>singleObjectPoint;


    for (int i = 0; i < numbers1; i++){
      for (int m = 0; m < boardSize.height; m++){
        for (int n = 0; n < boardSize.width; n++){
          TobjectPoint.x = n*centerDistance;
          TobjectPoint.y = m*centerDistance;
          TobjectPoint.z = 0;
          singleObjectPoint.push_back(TobjectPoint);
        }

      }
      totalObjectPoint.push_back(singleObjectPoint);
      singleObjectPoint.clear();
    }


    cv::Mat leftCamera, leftDisCoeffs, rightCamera, rightDisCoeffs;
    std::vector<cv::Mat>leftRvecs, leftTvecs, rightRvecs, rightTvecs;
    std::vector<float>leftPerViewErrors, rightPerViewErrors;

    calibrateCamera(totalObjectPoint, leftTotalImagePoint, imageSize, leftCamera, leftDisCoeffs, leftRvecs, leftTvecs, 0, cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 30, DBL_EPSILON));//
    double leftErr = computeReprojectionErrors(totalObjectPoint, leftTotalImagePoint, leftRvecs, leftTvecs, leftCamera, leftDisCoeffs, leftPerViewErrors);


    calibrateCamera(totalObjectPoint, rightTotalImagePoint, imageSize, rightCamera, rightDisCoeffs, rightRvecs, rightTvecs, 0, cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 30, DBL_EPSILON));//
    double rightErr = computeReprojectionErrors(totalObjectPoint, rightTotalImagePoint, rightRvecs, rightTvecs, rightCamera, rightDisCoeffs, rightPerViewErrors);

    cv::Mat R, T, E, F;


    double err = stereoCalibrate(totalObjectPoint, leftTotalImagePoint, rightTotalImagePoint, leftCamera, leftDisCoeffs, rightCamera, rightDisCoeffs, imageSize, R,
      T, E, F, cv::CALIB_FIX_INTRINSIC , cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, 30, 1e-6));//|| CV_CALIB_USE_INTRINSIC_GUESS

    

    std::fstream result;
    result.open("stereoPinholeResult.txt", std::fstream::out);

    result << "左相机内参数:" << leftCamera << std::endl;
    result << "左相机畸变系数:" << leftDisCoeffs << std::endl;
    result << "左相机反投影误差:" << leftErr << std::endl;

    for (int i = 0; i < numbers1; i++){
      cv::Mat LR;
      cv::Rodrigues(leftRvecs[i], LR);
      result << " 第" << i + 1 << "幅反投影误差:" << leftPerViewErrors[i] << std::endl;
      result << " 第" << i + 1 << "幅旋转矩阵:" << LR << std::endl;//旋转矩阵为相机坐标系至世界坐标系
      result << " 第" << i + 1 << "幅平移矩阵:" << leftTvecs[i] << std::endl;//平移矩阵为相机坐标系至世界坐标系
    }


    result << "右相机内参数:" << rightCamera << std::endl;
    result << "右相机畸变系数:" << rightDisCoeffs << std::endl;
    result << "右相机反投影误差:" << rightErr << std::endl;

    for (int i = 0; i < numbers1; i++){
      cv::Mat RR;
      cv::Rodrigues(rightRvecs[i], RR);
      result << " 第" << i + 1 << "幅反投影误差:" << rightPerViewErrors[i] << std::endl;
      result << " 第" << i + 1 << "幅旋转矩阵:" << RR << std::endl;//旋转矩阵为相机坐标系至世界坐标系
      result << " 第" << i + 1 << "幅平移矩阵:" << rightTvecs[i] << std::endl;//平移矩阵为相机坐标系至世界坐标系
    }

    result << "双目标定误差:" << err << std::endl;
    result << "右相机至左相机的旋转矩阵:" << R << std::endl;
    result << "右相机至左相机的平移矩阵:" << T << std::endl;
    result << "本质矩阵:" << E << std::endl;
    result << "基础矩阵:" << F << std::endl;
}

        测试demo main.cpp

#include"circleGridCalibration.h"
#include<opencv2\highgui.hpp>


int main(){

  std::string path = "./Data/data7/%d.bmp";//这里输入图片所在路径

  int numbers = 11;//这里输入图片数量

  char filename1[200];

  char filename2[200];

  std::strcpy(filename1, path.c_str());

  std::vector<cv::Mat>srcImage(numbers);

  for (int i = 1; i <= numbers; i++) {

    sprintf(filename2, filename1, i);

    srcImage[i - 1] = cv::imread(filename2);
    
  }

  cv::Size boardSize(14, 12);//这里输入每行每列圆圈的圆点个数

  double circleDistance = 50;//圆心之间的距离

  circleGridCalibrate::monoCalibratePinhole(srcImage, boardSize, circleDistance);

  return 0;

}

        圆心检测结果及标定结果:

图1 圆心检测结果

图2 标定结果

转载请声明来源,免费内容请勿收费。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐