opencv计算单目相机内外参矩阵与畸变校正

代码见
https://learnopencv.com/camera-calibration-using-opencv
相关知识点请参考《视觉SLAM十四讲从理论到实践》,附链接如下
https://pan.baidu.com/s/154vSodYtNG_mGOxvFJDiQQ
提取码: qkw8
文末的代码在上面源码的基础上进行了部分修改,改正几个错误并添加了使用相机内参进行畸变校正的代码。我用的是双目相机所以代码中有裁剪图像的部分,此处自行修改。遇到什么问题可评论或私信我。

源码修改与解读

首先在一个文件夹中保存若干张带有棋盘格的图片,越多越好,然后将std::string path设置为图片保存的路径。
int CHECKERBOARD修改为你的棋盘格的角点的数量,比如我的棋盘格有7行9列,角点就应该是6,8。
源码大致流程如下:

  • 首先定义2个二维数组objpointsimgpoints,他们的每个元素分别存放棋盘格的三维世界坐标和他们对应的二维像素坐标,比如有12张图片,角点以6,8为例,那尺寸就是12x48。
  • 循环读取一张图片并用findChessboardCorners函数尝试定位图片中棋盘格的角点;
  • 使用calibrateCamera函数,用所有图片计算出的角点坐标计算内外参矩阵和畸变参数;
  • 我在后面加上根据内参矩阵使用undistort函数对原图进行畸变校正。

用到的几个函数分析

代码中用到的函数主要在calib3d.hpp文件夹中,opencv文档中对应Main modules->calib3d。

findChessboardCorners

找到棋盘格中的角点。
参数:
image输入 带棋盘格的图像,应为灰度图
patternSize输入 棋盘格的角点的数量
corners输出 每个角点的像素坐标
flags输入 一些设置

cornerSubPix

亚像素级上精确定位坐标。没看懂,以后再理解。文档中该函数位置:Main modules->imgproc->Feature Detection
参数:
image输入 图片
corners输入 角点坐标
winSize输入 搜索窗口边长的一半。比如如果设置窗口为(5,5),那么将使用 ( 5 ∗ 2 + 1 ) × ( 5 ∗ 2 + 1 ) = 11 × 11 (5*2+1)\times(5*2+1)=11\times 11 (52+1)×(52+1)=11×11的搜索窗口
zeroZone设置死区范围以避免极点。一般设为(-1,-1)即可
criteria

drawChessboardCorners

将标出角点的数据写到一个Mat里,后面可以用imshow显示。

calibrateCamera

根据输入的图片和角点坐标计算内外参矩阵。
参数:
objectPoints输入 所有角点的三维世界坐标
imagePoints输入 所有角点的二维像素坐标
imageSize输入 图像尺寸
cameraMatrix输出 3x3的相机内参矩阵
distCoeffs输出 相机畸变参数,5个参数
rvecs输出 包含3x3的旋转矩阵的向量,向量长度等于棋盘格图片数量(假设所有准备好的图片中均有棋盘格)
tvecs输出 包含3x1的平移矩阵的向量,向量长度等于棋盘格图片数量
return返回重映射的均方根误差。
算法步骤如下:
(1) 计算初始内参;
(2) 计算初始相机姿态;
(3) 使用Levenberg-Marquardt全局优化算法最小化重映射误差。

undistort

畸变校正。该函数是initUndistortRectifyMapremap两个函数的组合。推荐分别使用两个函数,只需要预先使用initUndistortRectifyMap函数计算好map1map2然后对每张图片使用remap函数校正畸变。
参数:
src输入 畸变图像
dst输出 校正后图像
cameraMatrix输入 相机内参矩阵
distCoeffs输入 相机畸变参数

源码

#include 
int CHECKERBOARD[2]{6,8};
int main()
{
  using namespace std;
  using namespace cv;
  vector<vector<Point3f>> objpoints;
  vector<vector<Point2f>> imgpoints;
  vector<Point3f> objp;
  for(int i{0}; i<CHECKERBOARD[1]; i++)
    for(int j{0}; j<CHECKERBOARD[0]; j++)
      objp.push_back(Point3f(j,i,0));
  vector<String> images;
  string path = "/home/pi/Desktop/myfile/cpp/camera/build/*.png";
  glob(path, images);
  Mat origin, frame, gray;
  vector<Point2f> corner_pts;
  bool success;
  for(int i{0}; i<images.size(); i++) {
    origin = imread(images[i]);
    frame = origin(Rect(0,0,640,480));
    cvtColor(frame,gray,COLOR_BGR2GRAY);
    success = findChessboardCorners(gray, Size(CHECKERBOARD[0], CHECKERBOARD[1]), corner_pts, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE);
    if(success) {
      TermCriteria criteria(TermCriteria::EPS | TermCriteria::MAX_ITER, 30, 0.001);
      cornerSubPix(gray,corner_pts,Size(11,11), Size(-1,-1),criteria);
      drawChessboardCorners(frame, Size(CHECKERBOARD[0], CHECKERBOARD[1]), corner_pts, success);
      objpoints.push_back(objp);
      imgpoints.push_back(corner_pts);
    }
    imshow("Image",frame);
    waitKey(0);
  }
  destroyAllWindows();
  Mat cameraMatrix,distCoeffs,R,T;
  Size imageSize = Size(gray.cols, gray.rows);
  calibrateCamera(objpoints, imgpoints, imageSize, cameraMatrix, distCoeffs, R, T);
  Mat newCameraMatrix = getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1);
  cout << "NewCameraMatrix : " << newCameraMatrix << endl;
  cout << "cameraMatrix : " << cameraMatrix << endl;
  cout << "distCoeffs : " << distCoeffs << endl;
  cout << "Rotation vector : " << R << endl;
  cout << "Translation vector : " << T << endl;
  cout << "finished." << endl;
  Mat map1, map2;
  initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(),
                          getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0),
                          imageSize, CV_16SC2, map1, map2);
  for (int i=images.size()-1; i>=0; --i){
    origin = imread(images[i]);
    frame = origin(Rect(0,0,640,480));
    // undistort(frame, origin, cameraMatrix, distCoeffs);
    remap(frame, origin, map1, map2, INTER_LINEAR);
    imshow("Image", origin);
    waitKey(0);
  }
  return 0;
}

你可能感兴趣的:(硕士毕设,自动驾驶,opencv,计算机视觉,图像处理)