当前位置:   article > 正文

opencv张正友标定法

opencv张正友标定法

在标定之前我要想一想为什么要标定,什么相机需要标定,标定的输入是啥,标定的输出是啥

标定的目的:为了求出相机的内参和外参,内参和外参就可以对之后相机拍出来的照片进行矫正,得到畸变很小的图片。

标定的输入:用相机拍出来一系列的棋盘格图片。

标定的输出:相机的内参和外参。

 

流程:

1,采集一系列棋盘格图;

2,对每一张图,提取其角点信息;

3,对每一张图,提取其亚像素角点信息;

4,对相机进行标定

5,查看标定结果,并对结果进行评价

6,利用标定结果对棋盘图进行矫正。

 

1,准备标定图片

标定图片需要标定板在不同位置、不同角度、不同姿态下拍摄,最少需要三张,以10-20张为宜。

2,对每一张照片,进行角点提取。

  1. CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize, OutputArray corners,
  2. int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE );

image:传进来的棋盘格图

patternSize:一张图中角点的行列数,比方说棋盘格子大小是12*9,那么角点就有11*9个。vector<Point2f>

3,对每一张图,进一步提取亚像素角点信息。

为了进一步的提高精度,需要在初步提取的角点位置上进一步提取亚像素信息,降低标定的误差

  1. CV_EXPORTS_W void cornerSubPix( InputArray image, InputOutputArray corners,
  2. Size winSize, Size zeroZone,
  3. TermCriteria criteria );

image:标定的棋盘图。

corners:一系列的初始的角点坐标,同时也作为亚像素坐标的输出,一般需要float型,或者double型。

winSize:搜索的半径。

zeroZone:死区的一半尺寸,死区为不对搜索区的中央位置做求和运算的区域。它是用来避免自相关矩阵出现某些可能的奇异性。当值为(-1,-1)是表示没有死区。

criteria:定义求角点的终止条件,可以是迭代次数和角点精度的组合。

 

CV_EXPORTS bool find4QuadCornerSubpix( InputArray img, InputOutputArray corners, Size region_size );

image:标定的棋盘图。

corners:一系列的初始的角点坐标,同时也作为亚像素坐标的输出,一般需要float型,或者double型。

region_size:搜索窗口的半径。

画图展示一下效果:

  1. CV_EXPORTS_W void drawChessboardCorners( InputOutputArray image, Size patternSize,
  2. InputArray corners, bool patternWasFound );

                                                    

4,相机标定

获得一系列的图的角点信息之后就可以对相机进行标定了,然后计算相机的内参和外参。

  1. CV_EXPORTS_W double calibrateCamera( InputArrayOfArrays objectPoints,
  2. InputArrayOfArrays imagePoints, Size imageSize,
  3. InputOutputArray cameraMatrix, InputOutputArray distCoeffs,
  4. OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
  5. int flags = 0, TermCriteria criteria = TermCriteria(
  6. TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON) );

objectPoints:世界坐标系中的三维点,棋盘格之间的真实坐标是知道的,我们认为他是在同一平面上的点                                                                    vector<vector<Point3f>> obj;

imagePoints:每一张图角点对用的坐标点 ;vector<vector<Point2f>> allCorners;

imageSize:图片的像素尺寸大小

cameraMatrix:相机的内参矩阵;Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0));

distCoeffs:畸变矩阵(外参);Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0))

rvecs:旋转向量;vector<Mat>rvecs;

tvecs:位移向量;vector<Mat>tvecs;

flag:标定所采取的方法;

            CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,在cameraMatrix矩阵中应该有fx,fy,u0,v0的估计值。否则的话,将初始化(u0,v0)图像的中心点,使用最小二乘估算出fx,fy。 
             CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,光轴点将保持在中心或者某个输入的值。 
             CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当                                                               CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx和fy将会被忽略。只有fx/fy的比值在计算中会被用到。 
             CV_CALIB_ZERO_TANGENT_DIST:设定切向畸变参数(p1,p2)为零。 
             CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:对应的径向畸变在优化中保持不变。 
             CV_CALIB_RATIONAL_MODEL:计算k4,k5,k6三个畸变参数。如果没有设置,则只计算其它5个畸变参数。

criteria:迭代的终止条件;

5,对标定的结果进行评价

  1. CV_EXPORTS_W void projectPoints( InputArray objectPoints,
  2. InputArray rvec, InputArray tvec,
  3. InputArray cameraMatrix, InputArray distCoeffs,
  4. OutputArray imagePoints,
  5. OutputArray jacobian = noArray(),
  6. double aspectRatio = 0 );

利用相机的内外参,对一幅图中的三维点重新投影计算,得到新的投影点

objectPoints:相机坐标系中的三维坐标
rvec, tvec:旋转矩阵和位移矩阵
cameraMatrix, distCoeffs:相机的内参矩阵和畸变矩阵
imagePoints:每个点对应图像中的坐标点

jacobian是雅可比行列式;

aspectRatio是跟相机传感器的感光单元有关的可选参数,如果设置为非0,则函数默认感光单元的dx/dy是固定的,会依此对雅可比矩阵进行调整;

6,查看标定的结果,并对棋盘图进行矫正

方法一:使用initUndistortRectifyMap和remap两个函数配合实现。

initUndistortRectifyMap用来计算畸变映射,remap把求得的映射应用到图像上。

initUndistortRectifyMap的函数原型:

方法二:使用undistort函数实现

  1. CV_EXPORTS_W void undistort( InputArray src, OutputArray dst,
  2.                              InputArray cameraMatrix,
  3.                              InputArray distCoeffs,
  4.                              InputArray newCameraMatrix = noArray() );

src:棋盘格图

dst:矫正好的棋盘格图

cameraMatrix:相机内参

distCoeffs:相机外参

srcMat和dstMat:

  1. void CameraCalibrationZhang()
  2. {
  3. int imageHeight, imageWidth;
  4. vector<String> leftStrings, rightStrings;
  5. vector<Mat> leftMats, rightMats;
  6. glob("C:\\Users\\Ring\\Desktop\\0420\\left\\", leftStrings);
  7. glob("C:\\Users\\Ring\\Desktop\\0420\\right\\", rightStrings);
  8. for (int i = 0; i < leftStrings.size(); i++)
  9. {
  10. leftMats.push_back(imread(leftStrings[i], 0));
  11. rightMats.push_back(imread(rightStrings[i], 0));
  12. }
  13. vector<vector<Point2f>> allCorners; allCorners.clear();
  14. int boardWidth = 11, boardHeight = 8;
  15. float squaresize = 25; //棋盘格一格的实际大小是25mm
  16. for (int i = 0; i < leftMats.size(); i++)
  17. {
  18. Mat src = leftMats[i];
  19. //1,设置一些参数,比方说:棋盘格总共右多少个角点,棋盘格的真实大小啊,等等
  20. //2,寻找角点
  21. vector<Point2f> corners;
  22. bool isFind = findChessboardCorners(src, Size(boardWidth, boardHeight), corners);
  23. //3,寻找亚像素角点信息
  24. cornerSubPix(src, corners, Size(5, 5), Size(-1, -1), TermCriteria(1 + 2, 20, 0.1));
  25. //find4QuadCornerSubpix(src, corners, Size(5, 5)); //第二种方法
  26. allCorners.push_back(corners);
  27. /*Mat rgbMat;
  28. cvtColor(src, rgbMat, CV_GRAY2RGB);
  29. Mat showMat = rgbMat.clone();
  30. drawChessboardCorners(showMat, Size(boardWidth, boardHeight), corners, isFind);*/
  31. cout << "";
  32. }
  33. //4,相机标定
  34. vector<vector<Point3f>> obj;
  35. vector<Point3f> imgpoint;
  36. for (int rowIndex = 0; rowIndex < boardHeight; rowIndex++)
  37. {
  38. for (int colIndex = 0; colIndex < boardWidth; colIndex++)
  39. {
  40. imgpoint.push_back(Point3f(rowIndex * squaresize, colIndex * squaresize, 0));
  41. }
  42. }
  43. for (int imgIndex = 0; imgIndex < leftMats.size(); imgIndex++)
  44. {
  45. obj.push_back(imgpoint);
  46. }
  47. Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));
  48. Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));
  49. vector<Mat> rvecs, tvecs;
  50. calibrateCamera(obj, allCorners, leftMats[0].size(), cameraMatrix, distCoeffs, rvecs, tvecs);
  51. //5,对标定结果进行评价
  52. vector<Point2f> image_points2; /* 保存重新计算得到的投影点 */
  53. double total_err = 0.0; /* 所有图像的平均误差的总和 */
  54. double err = 0.0; /* 每幅图像的平均误差 */
  55. for (int i = 0; i<leftMats.size(); i++)
  56. {
  57. vector<Point3f> tempPointSet = obj[i];
  58. /* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */
  59. projectPoints(tempPointSet, rvecs[i], tvecs[i], cameraMatrix, distCoeffs, image_points2);
  60. /* 计算新的投影点和旧的投影点之间的误差*/
  61. vector<Point2f> tempImagePoint = allCorners[i];
  62. Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2);
  63. Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2);
  64. for (int j = 0; j < tempImagePoint.size(); j++)
  65. {
  66. image_points2Mat.at<Vec2f>(0, j) = Vec2f(image_points2[j].x, image_points2[j].y);
  67. tempImagePointMat.at<Vec2f>(0, j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);
  68. }
  69. double err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
  70. total_err += err /= 88;
  71. std::cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
  72. cout<< "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
  73. }
  74. //6,查看标定的结果,并对棋盘图进行矫正
  75. Mat srcMat = leftMats[0], dstMat;
  76. undistort(srcMat, dstMat, cameraMatrix, distCoeffs);
  77. cout << "";
  78. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/495641
推荐阅读
相关标签
  

闽ICP备14008679号