张正友相机标定Opencv实现以及标定流程标定结果评价图像矫正流程解析(附标定程序和棋盘图)

from:https://blog.csdn.net/dcrmg/article/details/52939318

使用Opencv实现张正友法相机标定之前,有几个问题事先要确认一下,那就是相机为什么需要标定,标定需要的输入和输出分别是哪些?


相机标定的目的:获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的选择和平移矩阵),内参和外参系数可以对之后相机拍摄的图像就进行矫正,得到畸变相对很小的图像。

相机标定的输入:标定图像上所有内角点的图像坐标,标定板图像上所有内角点的空间三维坐标(一般情况下假定图像位于Z=0平面上)。

相机标定的输出:摄像机的内参、外参系数。


这三个基础的问题就决定了使用Opencv实现张正友法标定相机的标定流程、标定结果评价以及使用标定结果矫正原始图像的完整流程:


1. 准备标定图片

2. 对每一张标定图片,提取角点信息

3. 对每一张标定图片,进一步提取亚像素角点信息

4. 在棋盘标定图上绘制找到的内角点(非必须,仅为了显示)

5. 相机标定

6. 对标定结果进行评价

7. 查看标定效果——利用标定结果对棋盘图进行矫正


1. 准备标定图片


标定图片需要使用标定板在不同位置、不同角度、不同姿态下拍摄,最少需要3张,以10~20张为宜。标定板需要是黑白相间的矩形构成的棋盘图,制作精度要求较高,如下图所示:





2.对每一张标定图片,提取角点信息


需要使用findChessboardCorners函数提取角点,这里的角点专指的是标定板上的内角点,这些角点与标定板的边缘不接触。

 findChessboardCorners函数原型:

  1. //! finds checkerboard pattern of the specified size in the image
  2. CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize,
  3. OutputArray corners,
  4. int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE );


第一个参数Image,传入拍摄的棋盘图Mat图像,必须是8位的灰度或者彩色图像;

第二个参数patternSize,每个棋盘图上内角点的行列数,一般情况下,行列数不要相同,便于后续标定程序识别标定板的方向;

第三个参数corners,用于存储检测到的内角点图像坐标位置,一般用元素是Point2f的向量来表示:vector<Point2f> image_points_buf;

第四个参数flage:用于定义棋盘图上内角点查找的不同处理方式,有默认值。



3. 对每一张标定图片,进一步提取亚像素角点信息


为了提高标定精度,需要在初步提取的角点信息上进一步提取亚像素信息,降低相机标定偏差,常用的方法是cornerSubPix,另一个方法是使用find4QuadCornerSubpix函数,这个方法是专门用来获取棋盘图上内角点的精确位置的,或许在相机标定的这个特殊场合下它的检测精度会比cornerSubPix更高?

cornerSubPix函数原型:

  1. //! adjusts the corner locations with sub-pixel accuracy to maximize the certain cornerness criteria
  2. CV_EXPORTS_W void cornerSubPix( InputArray image, InputOutputArray corners,
  3. Size winSize, Size zeroZone,
  4. TermCriteria criteria );

第一个参数image,输入的Mat矩阵,最好是8位灰度图像,检测效率更高;

第二个参数corners,初始的角点坐标向量,同时作为亚像素坐标位置的输出,所以需要是浮点型数据,一般用元素是Pointf2f/Point2d的向量来表示:vector<Point2f/Point2d> iamgePointsBuf;

第三个参数winSize,大小为搜索窗口的一半;

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

第五个参数criteria,定义求角点的迭代过程的终止条件,可以为迭代次数和角点精度两者的组合;

find4QuadCornerSubpix函数原型:

  1. //! finds subpixel-accurate positions of the chessboard corners
  2. CV_EXPORTS bool find4QuadCornerSubpix(InputArray img, InputOutputArray corners, Size region_size);

第一个参数img,输入的Mat矩阵,最好是8位灰度图像,检测效率更高;

第二个参数corners,初始的角点坐标向量,同时作为亚像素坐标位置的输出,所以需要是浮点型数据,一般用元素是Pointf2f/Point2d的向量来表示:vector<Point2f/Point2d> iamgePointsBuf;

第三个参数region_size,角点搜索窗口的尺寸;

在其中一个标定的棋盘图上分别运行cornerSubPix和find4QuadCornerSubpix寻找亚像素角点,两者定位到的亚像素角点坐标分别为:


   cornerSubPix:                                                 find4QuadCornerSubpix:

                       


虽然有一定差距,但偏差基本都控制在0.5个像素之内。



4. 在棋盘标定图上绘制找到的内角点(非必须,仅为了显示)



drawChessboardCorners函数用于绘制被成功标定的角点,函数原型:


  1. //! draws the checkerboard pattern (found or partly found) in the image
  2. CV_EXPORTS_W void drawChessboardCorners( InputOutputArray image, Size patternSize,
  3. InputArray corners, bool patternWasFound );

第一个参数image,8位灰度或者彩色图像;

第二个参数patternSize,每张标定棋盘上内角点的行列数;

第三个参数corners,初始的角点坐标向量,同时作为亚像素坐标位置的输出,所以需要是浮点型数据,一般用元素是Pointf2f/Point2d的向量来表示:vector<Point2f/Point2d> iamgePointsBuf;

第四个参数patternWasFound,标志位,用来指示定义的棋盘内角点是否被完整的探测到,true表示别完整的探测到,函数会用直线依次连接所有的内角点,作为一个整体,false表示有未被探测到的内角点,这时候函数会以(红色)圆圈标记处检测到的内角点;

以下是drawChessboardCorners函数中第四个参数patternWasFound设置为true和false时内角点的绘制效果:

patternWasFound=ture时,依次连接各个内角点:




patternWasFound=false时,以(红色)圆圈标记处角点位置:




5. 相机标定



获取到棋盘标定图的内角点图像坐标之后,就可以使用calibrateCamera函数进行标定,计算相机内参和外参系数,

calibrateCamera函数原型:

  1. //! finds intrinsic and extrinsic camera parameters from several fews of a known calibration pattern.
  2. CV_EXPORTS_W double calibrateCamera( InputArrayOfArrays objectPoints,
  3. InputArrayOfArrays imagePoints,
  4. Size imageSize,
  5. CV_OUT InputOutputArray cameraMatrix,
  6. CV_OUT InputOutputArray distCoeffs,
  7. OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
  8. int flags=0, TermCriteria criteria = TermCriteria(
  9. TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON) );

第一个参数objectPoints,为世界坐标系中的三维点。在使用时,应该输入一个三维坐标点的向量的向量,即vector<vector<Point3f>> object_points。需要依据棋盘上单个黑白矩阵的大小,计算出(初始化)每一个内角点的世界坐标。

第二个参数imagePoints,为每一个内角点对应的图像坐标点。和objectPoints一样,应该输入vector<vector<Point2f>> image_points_seq形式的变量;

第三个参数imageSize,为图像的像素尺寸大小,在计算相机的内参和畸变矩阵时需要使用到该参数;

第四个参数cameraMatrix为相机的内参矩阵。输入一个Mat cameraMatrix即可,如Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0));

第五个参数distCoeffs为畸变矩阵。输入一个Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0))即可;

第六个参数rvecs为旋转向量;应该输入一个Mat类型的vector,即vector<Mat>rvecs;

第七个参数tvecs为位移向量,和rvecs一样,应该为vector<Mat> tvecs;

第八个参数flags为标定时所采用的算法。有如下几个参数:

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是最优迭代终止条件设定。

在使用该函数进行标定运算之前,需要对棋盘上每一个内角点的空间坐标系的位置坐标进行初始化,标定的结果是生成相机的内参矩阵cameraMatrix、相机的5个畸变系数distCoeffs,另外每张图像都会生成属于自己的平移向量和旋转向量。



6. 对标定结果进行评价



对标定结果进行评价的方法是通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到空间三维点在图像上新的投影点的坐标,计算投影坐标和亚像素角点坐标之间的偏差,偏差越小,标定结果越好。

对空间三维坐标点进行反向投影的函数是projectPoints,函数原型是:

  1. //! projects points from the model coordinate space to the image coordinates. Also computes derivatives of the image coordinates w.r.t the intrinsic and extrinsic camera parameters
  2. CV_EXPORTS_W void projectPoints( InputArray objectPoints,
  3. InputArray rvec, InputArray tvec,
  4. InputArray cameraMatrix, InputArray distCoeffs,
  5. OutputArray imagePoints,
  6. OutputArray jacobian=noArray(),
  7. double aspectRatio=0 );


第一个参数objectPoints,为相机坐标系中的三维点坐标;

第二个参数rvec为旋转向量,每一张图像都有自己的选择向量;

第三个参数tvec为位移向量,每一张图像都有自己的平移向量;

第四个参数cameraMatrix为求得的相机的内参数矩阵;

第五个参数distCoeffs为相机的畸变矩阵;

第六个参数iamgePoints为每一个内角点对应的图像上的坐标点;

第七个参数jacobian是雅可比行列式;

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

下边显示了某一张标定图片上的亚像素角点坐标和根据标定结果把空间三维坐标点映射回图像坐标点的对比:


find4QuadCornerSubpix查找到的亚像素点坐标:                           projectPoints映射的坐标:

                                                     


以下是每一幅图像上24个内角点的平均误差统计数据:




7. 查看标定效果——利用标定结果对棋盘图进行矫正



利用求得的相机的内参和外参数据,可以对图像进行畸变的矫正,这里有两种方法可以达到矫正的目的,分别说明一下。

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

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

initUndistortRectifyMap的函数原型:

  1. //! initializes maps for cv::remap() to correct lens distortion and optionally rectify the image
  2. CV_EXPORTS_W void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs,
  3. InputArray R, InputArray newCameraMatrix,
  4. Size size, int m1type, OutputArray map1, OutputArray map2 );

第一个参数cameraMatrix为之前求得的相机的内参矩阵;

第二个参数distCoeffs为之前求得的相机畸变矩阵;

第三个参数R,可选的输入,是第一和第二相机坐标之间的旋转矩阵;

第四个参数newCameraMatrix,输入的校正后的3X3摄像机矩阵;

第五个参数size,摄像机采集的无失真的图像尺寸;

第六个参数m1type,定义map1的数据类型,可以是CV_32FC1或者CV_16SC2;

第七个参数map1和第八个参数map2,输出的X/Y坐标重映射参数;


remap函数原型:

  1. //! warps the image using the precomputed maps. The maps are stored in either floating-point or integer fixed-point format
  2. CV_EXPORTS_W void remap( InputArray src, OutputArray dst,
  3. InputArray map1, InputArray map2,
  4. int interpolation, int borderMode=BORDER_CONSTANT,
  5. const Scalar& borderValue=Scalar());

第一个参数src,输入参数,代表畸变的原始图像;

第二个参数dst,矫正后的输出图像,跟输入图像具有相同的类型和大小;

第三个参数map1和第四个参数map2,X坐标和Y坐标的映射;

第五个参数interpolation,定义图像的插值方式;

第六个参数borderMode,定义边界填充方式;


方法二:使用undistort函数实现

undistort函数原型:

  1. //! corrects lens distortion for the given camera matrix and distortion coefficients
  2. CV_EXPORTS_W void undistort( InputArray src, OutputArray dst,
  3. InputArray cameraMatrix,
  4. InputArray distCoeffs,
  5. InputArray newCameraMatrix=noArray() );

第一个参数src,输入参数,代表畸变的原始图像;

第二个参数dst,矫正后的输出图像,跟输入图像具有相同的类型和大小;

第三个参数cameraMatrix为之前求得的相机的内参矩阵;

第四个参数distCoeffs为之前求得的相机畸变矩阵;

第五个参数newCameraMatrix,默认跟cameraMatrix保持一致;

方法一相比方法二执行效率更高一些,推荐使用。


以下是使用某一张标定图使用方法一和方法二进行矫正的效果图对比。

原始标定图像:



方法一,使用initUndistortRectifyMap和remap实现矫正效果:



方法二,使用undistort函数实现矫正效果:



两个方法从矫正效果上看,结果是一致的。


以下是完整的工程代码:


  1. #include "opencv2/core/core.hpp"
  2. #include "opencv2/imgproc/imgproc.hpp"
  3. #include "opencv2/calib3d/calib3d.hpp"
  4. #include "opencv2/highgui/highgui.hpp"
  5. #include <iostream>
  6. #include <fstream>
  7. using namespace cv;
  8. using namespace std;
  9. void main()
  10. {
  11. ifstream fin("calibdata.txt"); /* 标定所用图像文件的路径 */
  12. ofstream fout("caliberation_result.txt"); /* 保存标定结果的文件 */
  13. //读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化
  14. cout<<"开始提取角点………………";
  15. int image_count=0; /* 图像数量 */
  16. Size image_size; /* 图像的尺寸 */
  17. Size board_size = Size(4,6); /* 标定板上每行、列的角点数 */
  18. vector<Point2f> image_points_buf; /* 缓存每幅图像上检测到的角点 */
  19. vector<vector<Point2f>> image_points_seq; /* 保存检测到的所有角点 */
  20. string filename;
  21. int count= -1 ;//用于存储角点个数。
  22. while (getline(fin,filename))
  23. {
  24. image_count++;
  25. // 用于观察检验输出
  26. cout<<"image_count = "<<image_count<<endl;
  27. /* 输出检验*/
  28. cout<<"-->count = "<<count;
  29. Mat imageInput=imread(filename);
  30. if (image_count == 1) //读入第一张图片时获取图像宽高信息
  31. {
  32. image_size.width = imageInput.cols;
  33. image_size.height =imageInput.rows;
  34. cout<<"image_size.width = "<<image_size.width<<endl;
  35. cout<<"image_size.height = "<<image_size.height<<endl;
  36. }
  37. /* 提取角点 */
  38. if (0 == findChessboardCorners(imageInput,board_size,image_points_buf))
  39. {
  40. cout<<"can not find chessboard corners!\n"; //找不到角点
  41. exit(1);
  42. }
  43. else
  44. {
  45. Mat view_gray;
  46. cvtColor(imageInput,view_gray,CV_RGB2GRAY);
  47. /* 亚像素精确化 */
  48. find4QuadCornerSubpix(view_gray,image_points_buf,Size(5,5)); //对粗提取的角点进行精确化
  49. //cornerSubPix(view_gray,image_points_buf,Size(5,5),Size(-1,-1),TermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,0.1));
  50. image_points_seq.push_back(image_points_buf); //保存亚像素角点
  51. /* 在图像上显示角点位置 */
  52. drawChessboardCorners(view_gray,board_size,image_points_buf,false); //用于在图片中标记角点
  53. imshow("Camera Calibration",view_gray);//显示图片
  54. waitKey(500);//暂停0.5S
  55. }
  56. }
  57. int total = image_points_seq.size();
  58. cout<<"total = "<<total<<endl;
  59. int CornerNum=board_size.width*board_size.height; //每张图片上总的角点数
  60. for (int ii=0 ; ii<total ;ii++)
  61. {
  62. if (0 == ii%CornerNum)// 24 是每幅图片的角点个数。此判断语句是为了输出 图片号,便于控制台观看
  63. {
  64. int i = -1;
  65. i = ii/CornerNum;
  66. int j=i+1;
  67. cout<<"--> 第 "<<j <<"图片的数据 --> : "<<endl;
  68. }
  69. if (0 == ii%3) // 此判断语句,格式化输出,便于控制台查看
  70. {
  71. cout<<endl;
  72. }
  73. else
  74. {
  75. cout.width(10);
  76. }
  77. //输出所有的角点
  78. cout<<" -->"<<image_points_seq[ii][0].x;
  79. cout<<" -->"<<image_points_seq[ii][0].y;
  80. }
  81. cout<<"角点提取完成!\n";
  82. //以下是摄像机标定
  83. cout<<"开始标定………………";
  84. /*棋盘三维信息*/
  85. Size square_size = Size(10,10); /* 实际测量得到的标定板上每个棋盘格的大小 */
  86. vector<vector<Point3f>> object_points; /* 保存标定板上角点的三维坐标 */
  87. /*内外参数*/
  88. Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0)); /* 摄像机内参数矩阵 */
  89. vector<int> point_counts; // 每幅图像中角点的数量
  90. Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0)); /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */
  91. vector<Mat> tvecsMat; /* 每幅图像的旋转向量 */
  92. vector<Mat> rvecsMat; /* 每幅图像的平移向量 */
  93. /* 初始化标定板上角点的三维坐标 */
  94. int i,j,t;
  95. for (t=0;t<image_count;t++)
  96. {
  97. vector<Point3f> tempPointSet;
  98. for (i=0;i<board_size.height;i++)
  99. {
  100. for (j=0;j<board_size.width;j++)
  101. {
  102. Point3f realPoint;
  103. /* 假设标定板放在世界坐标系中z=0的平面上 */
  104. realPoint.x = i*square_size.width;
  105. realPoint.y = j*square_size.height;
  106. realPoint.z = 0;
  107. tempPointSet.push_back(realPoint);
  108. }
  109. }
  110. object_points.push_back(tempPointSet);
  111. }
  112. /* 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 */
  113. for (i=0;i<image_count;i++)
  114. {
  115. point_counts.push_back(board_size.width*board_size.height);
  116. }
  117. /* 开始标定 */
  118. calibrateCamera(object_points,image_points_seq,image_size,cameraMatrix,distCoeffs,rvecsMat,tvecsMat,0);
  119. cout<<"标定完成!\n";
  120. //对标定结果进行评价
  121. cout<<"开始评价标定结果………………\n";
  122. double total_err = 0.0; /* 所有图像的平均误差的总和 */
  123. double err = 0.0; /* 每幅图像的平均误差 */
  124. vector<Point2f> image_points2; /* 保存重新计算得到的投影点 */
  125. cout<<"\t每幅图像的标定误差:\n";
  126. fout<<"每幅图像的标定误差:\n";
  127. for (i=0;i<image_count;i++)
  128. {
  129. vector<Point3f> tempPointSet=object_points[i];
  130. /* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */
  131. projectPoints(tempPointSet,rvecsMat[i],tvecsMat[i],cameraMatrix,distCoeffs,image_points2);
  132. /* 计算新的投影点和旧的投影点之间的误差*/
  133. vector<Point2f> tempImagePoint = image_points_seq[i];
  134. Mat tempImagePointMat = Mat(1,tempImagePoint.size(),CV_32FC2);
  135. Mat image_points2Mat = Mat(1,image_points2.size(), CV_32FC2);
  136. for (int j = 0 ; j < tempImagePoint.size(); j++)
  137. {
  138. image_points2Mat.at<Vec2f>(0,j) = Vec2f(image_points2[j].x, image_points2[j].y);
  139. tempImagePointMat.at<Vec2f>(0,j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);
  140. }
  141. err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
  142. total_err += err/= point_counts[i];
  143. std::cout<<"第"<<i+1<<"幅图像的平均误差:"<<err<<"像素"<<endl;
  144. fout<<"第"<<i+1<<"幅图像的平均误差:"<<err<<"像素"<<endl;
  145. }
  146. std::cout<<"总体平均误差:"<<total_err/image_count<<"像素"<<endl;
  147. fout<<"总体平均误差:"<<total_err/image_count<<"像素"<<endl<<endl;
  148. std::cout<<"评价完成!"<<endl;
  149. //保存定标结果
  150. std::cout<<"开始保存定标结果………………"<<endl;
  151. Mat rotation_matrix = Mat(3,3,CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */
  152. fout<<"相机内参数矩阵:"<<endl;
  153. fout<<cameraMatrix<<endl<<endl;
  154. fout<<"畸变系数:\n";
  155. fout<<distCoeffs<<endl<<endl<<endl;
  156. for (int i=0; i<image_count; i++)
  157. {
  158. fout<<"第"<<i+1<<"幅图像的旋转向量:"<<endl;
  159. fout<<tvecsMat[i]<<endl;
  160. /* 将旋转向量转换为相对应的旋转矩阵 */
  161. Rodrigues(tvecsMat[i],rotation_matrix);
  162. fout<<"第"<<i+1<<"幅图像的旋转矩阵:"<<endl;
  163. fout<<rotation_matrix<<endl;
  164. fout<<"第"<<i+1<<"幅图像的平移向量:"<<endl;
  165. fout<<rvecsMat[i]<<endl<<endl;
  166. }
  167. std::cout<<"完成保存"<<endl;
  168. fout<<endl;
  169. /************************************************************************
  170. 显示定标结果
  171. *************************************************************************/
  172. Mat mapx = Mat(image_size,CV_32FC1);
  173. Mat mapy = Mat(image_size,CV_32FC1);
  174. Mat R = Mat::eye(3,3,CV_32F);
  175. std::cout<<"保存矫正图像"<<endl;
  176. string imageFileName;
  177. std::stringstream StrStm;
  178. for (int i = 0 ; i != image_count ; i++)
  179. {
  180. std::cout<<"Frame #"<<i+1<<"..."<<endl;
  181. initUndistortRectifyMap(cameraMatrix,distCoeffs,R,cameraMatrix,image_size,CV_32FC1,mapx,mapy);
  182. StrStm.clear();
  183. imageFileName.clear();
  184. string filePath="chess";
  185. StrStm<<i+1;
  186. StrStm>>imageFileName;
  187. filePath+=imageFileName;
  188. filePath+=".bmp";
  189. Mat imageSource = imread(filePath);
  190. Mat newimage = imageSource.clone();
  191. //另一种不需要转换矩阵的方式
  192. //undistort(imageSource,newimage,cameraMatrix,distCoeffs);
  193. remap(imageSource,newimage,mapx, mapy, INTER_LINEAR);
  194. StrStm.clear();
  195. filePath.clear();
  196. StrStm<<i+1;
  197. StrStm>>imageFileName;
  198. imageFileName += "_d.jpg";
  199. imwrite(imageFileName,newimage);
  200. }
  201. std::cout<<"保存结束"<<endl;
  202. return ;
  203. }


标定图例1:



标定图例2:



标定结果1:



标定结果2:



矫正效果1:



矫正效果2:



以上程序已经是完整程序,需要棋盘标定图或者整个项目包的可以到这里下载:张正友相机标定Opencv实现(完整程序+棋盘图)。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

opencv双目视觉标定、匹配和测量 (附代码)

from&#xff1a;https://blog.csdn.net/bcj296050240/article/details/52778741双目视觉原理方面参照《学习Opencv》和大牛博客 http://blog.csdn.net/chenyusiyuan/article/details/5970799中16-19系列博客。本文主要记录我自己在双目视觉标定&#xff0c;立体匹配&#xff0…

工业相机的选择方法

信号  工业相机的信号类型有模拟信号和数字信号两种。模拟相机必须有图像采集卡&#xff0c;标准的模拟相机分辨率很低&#xff0c;采集到的是模拟信号&#xff0c;经数字采集卡转换为数字信号进行传输存储。工业数字相机采集到的是数字信号&#xff0c;数字信号不受电噪声影…

socket编程初级

什么是socket定义socket通常也称作套接字&#xff0c;用于描述IP地址和端口&#xff0c;是一个通信链的句柄&#xff0c;应用程序通常通过套接字向网络发出请求或者应答网络请求。socket起源于Unix&#xff0c;而Unix/Linux基本哲学之一就是“一切皆文件”&#xff0c;对于文件…

OpenCV—基本数据结构与示例

OpenCV的基本数据结构及示例OpenCV中强大的Mat类型大家已经比较熟悉了。这里梳理一些在工程中其他经常用到的几种基本数据类型。包括&#xff1a;VecScalarPointSizeRectRotatedRect1. Vec类1.1 基本概念Vec是一个模板类&#xff0c;主要用于存储数值向量。1.2 用法&#xff08…

转载-程序员编程技术迅速提高的终极攻略

2019独角兽企业重金招聘Python工程师标准>>> 前言 你是否觉得自己从学校毕业的时候只做过小玩具一样的程序&#xff1f;走入职场后哪怕没有什么经验也可以把以下这些课外练习走一遍&#xff08;朋友的抱怨&#xff1a;学校课程总是从理论出发&#xff0c;作业项目都…

SVM

from&#xff1a;https://blog.csdn.net/liugan528/article/details/79448379 SVM 1. 基本概念 支持向量机&#xff08;Support Vector Machine, SVM&#xff09;的基本模型是在特征空间上找到最佳的分离超平面使得训练集上正负样本间隔最大。SVM是用来解决二分类问题的有监督学…

LoadRunner常用术语

1.场景 2.负载发生器 3.虚拟用户 4.虚拟用户脚本 5.事务 6.思考时间 7.集合点 8.事务响应时间 转载于:https://www.cnblogs.com/Andy-Lv/p/5263707.html

跟我学Shiro目录贴

2019独角兽企业重金招聘Python工程师标准>>> http://jinnianshilongnian.iteye.com/blog/2018398 转载于:https://my.oschina.net/zhanggongming/blog/714844

【WinForm-无边框窗体】实现Panel移动窗体,没有边框的窗体

没有边框的窗体怎么移动&#xff1f;其实方法有很多&#xff0c;下面介绍一种用控件来移动窗体&#xff0c;Panel或PictureBox都可。主要设置控件的MouseDowm和MouseLeave事件。 第一步&#xff1a;窗体设计 窗体最上面是一个panel1 窗体最下面是一个Panel3&#xff0c;只显示最…

\Grokking Algorithms\简介与作者采访

\关键点\这本书目的是告诉读者解决问题的新方法。 \这本书试图通过插图来让大家更容易掌握主题&#xff0c;避免部分读者觉得太费解。 \这本书不仅适合没有接触过算法的人&#xff0c;也适合刚从计算机专业毕业的学生。 \这本书提供了非常多的例子和简单练习。 \这并不是一本参…

ZedGraph使用经验

开源的统计图控件中基本常用的是OpenFlashChar和ZedGraph&#xff0c;今天就先来讲讲ZedGraph的使用。 ZedGraph资源 ZedGraph来源&#xff1a;http://sourceforge.net/project/showfiles.php?group_id114675ZedGraph相关例子资源&#xff1a;http://zedgraph.org/wiki/index.…

机器学习实战之SVM

一引言&#xff1a;支持向量机这部分确实很多&#xff0c;想要真正的去理解它&#xff0c;不仅仅知道理论&#xff0c;还要进行相关的代码编写和测试&#xff0c;二者想和结合&#xff0c;才能更好的帮助我们理解SVM这一非常优秀的分类算法支持向量机是一种二类分类算法&#x…

mysql 时间类型 datetime,timestamp

2019独角兽企业重金招聘Python工程师标准>>> CURRENT_TIMESTAMP 的 timestamp 意思是 根据 mysql 的当前时间&#xff0c;使用mysql 的当前时间。 参考地址&#xff1a;http://blog.sina.com.cn/s/blog_67cc6e7d0100nrwk.html datetime 和 timestamp 都是保存时间格…

机器学习中的交叉验证(cross-validation)

from:https://blog.csdn.net/lhx878619717/article/details/49079785 交叉验证&#xff08;Cross validation)&#xff0c;交叉验证用于防止模型过于复杂而引起的过拟合. 有时亦称循环估计&#xff0c; 是一种统计学上将数据样本切割成较小子集的实用方法。于是可以先在一个子…

轻松谈话:谈话的力量

如何与她人搭话&#xff1f;&#xff1f; 第一&#xff1a;给别人一个好印象 1、环境&#xff1a;通过共同环境来激发兴趣&#xff0c;比如&#xff1a;在球场&#xff0c;你觉得谁会赢。 2、对方:多数人喜欢谈论自己。 3、自己&#xff1a;主动表明意图&#xff0c;要真诚。 第…

libSVM介绍(二)

from&#xff1a;https://blog.csdn.net/carson2005/article/details/6539192 鉴于libSVM中的readme文件有点长&#xff0c;而且&#xff0c;都是采用英文书写&#xff0c;这里&#xff0c;我把其中重要的内容提炼出来&#xff0c;并给出相应的例子来说明其用法&#xff0c;大家…

四则运算题2

本题新学知识点&#xff1a; itoa函数 char *itoa( int value, char *string,int radix);[1]原型说明&#xff1a;value&#xff1a;欲转换的数据。string&#xff1a;目标字符串的地址。radix&#xff1a;转换后的进制数&#xff0c;可以是10进制、16进制等。程序实例:#includ…

c++调用Libsvm

libSVM中的readme中文版&#xff1a;http://blog.csdn.net/carson2005/article/details/6539192 LibSVM的package中的Readme文件中介绍了怎样具体的使用LibSvm&#xff0c;可以在Dos下以命令形式进行调用&#xff0c;也可以用程序包中提供的GUI程序Svm-toy进行图形化的操作。sv…

STL -set

转载自&#xff1a;http://blog.csdn.net/LYHVOYAGE/article/details/22989659 set集合容器实现了红黑树&#xff08;Red-Black Tree&#xff09;的平衡二叉检索树的的数据结构&#xff0c; 在插入元素时&#xff0c;它会自动调整二叉树的排列&#xff0c;把该元素放到适当的位…

【机器学习实战之一】:C++实现K-近邻算法KNN

本文不对KNN算法做过多的理论上的解释&#xff0c;主要是针对问题&#xff0c;进行算法的设计和代码的注解。 KNN算法&#xff1a; 优点&#xff1a;精度高、对异常值不敏感、无数据输入假定。 缺点&#xff1a;计算复杂度高、空间复杂度高。 适用数据范围&#xff1a;数值…