转载:https://blog.csdn.net/dcrmg/article/details/52939318

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

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


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

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

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


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


1. 准备标定图片

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

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

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

5. 相机标定

6. 对标定结果进行评价

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


1. 准备标定图片


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





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


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

 findChessboardCorners函数原型:

[cpp] view plaincopy
  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函数原型:

[cpp] view plaincopy
  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函数原型:

[cpp] view plaincopy
  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函数用于绘制被成功标定的角点,函数原型:


[cpp] view plaincopy
  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函数原型:

[cpp] view plaincopy
  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,函数原型是:

[cpp] view plaincopy
  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的函数原型:

[cpp] view plaincopy
  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函数原型:

[cpp] view plaincopy
  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函数原型:

[cpp] view plaincopy
  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函数实现矫正效果:



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


以下是完整的工程代码:


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


标定图例1:



标定图例2:



标定结果1:



标定结果2:



矫正效果1:



矫正效果2:



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

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

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

相关文章

Redis学习笔记--Redis数据过期策略详解==转

本文对Redis的过期机制简单的讲解一下  讲解之前我们先抛出一个问题&#xff0c;我们知道很多时候服务器经常会用到redis作为缓存&#xff0c;有很多数据都是临时缓存一下&#xff0c;可能用过之后很久都不会再用到了&#xff08;比如暂存session&#xff0c;又或者只存放日行…

会员连锁配置以及金额走向

PS&#xff1a;所有电子支付方式的资金走向都是同样的&#xff0c;配置的是什么支付方式就走什么支付方式;下面以支付宝为例说明 一、连锁非总机模式 资金走向&#xff1a; 支付宝&#xff1a;收到的钱在主账号配置的支付宝&#xff0c;会员卡的金额在主账号 微信&#xff1a;收…

C# 多线程及同步简介示例

60年代&#xff0c;在OS中能拥有资源和独立运行的基本单位是进程&#xff0c;然而随着计算机技术的发展&#xff0c;进程出现了很多弊端&#xff0c;一是由于进程是资源拥有者&#xff0c;创建、撤消与切换存在较大的时空开销&#xff0c;因此需要引入轻型进程&#xff1b;二是…

Spring Framework 5.1.6、5.0.13 与 4.3.23 发布

开发四年只会写业务代码&#xff0c;分布式高并发都不会还做程序员&#xff1f; Spring Framework 5.1.6、5.0.13 与 4.3.23 发布了。 v5.1.6 包含 49 个 bug 修复和功能改进&#xff1b;v5.0.13 是 5.0.x 系列的最后一个版本&#xff0c;包含了 25 个 bug 修复和功能改进&am…

单目视觉标定:世界坐标系、相机坐标系、图像坐标系、像素坐标系——简单粗暴,粗暴

转&#xff1a;https://blog.csdn.net/chentravelling/article/details/53558096 1.正文 图像处理、立体视觉等等方向常常涉及到四个坐标系&#xff1a;世界坐标系、相机坐标系、图像坐标系、像素坐标系。例如下图&#xff1a; 构建世界坐标系只是为了更好的描述相机的位置在哪…

VSFTP的使用

一、基本安装 1.安装服务 yum -y install vsftpd //centos Redhat apt-get install vsftpd //debian ubuntu 2.开启服务 service vsftpd status //查看状态&#xff0c;默认是关闭的 service vsftpd start 3.开机随操作系统启动 chkconfig vsftpd on4.添加账号 useradd -d /…

OC Swift混编-Swift.h File not found

https://www.jianshu.com/p/f860fe1718ca 2016.09.13 11:53* 字数 266 阅读 1935评论 1喜欢 1今天碰到个神坑,本人项目是OC项目,最近开始使用Swift开始替代.但是,在替代的已开始就出现问题了:新建swift文件,然后提示创建bridging-Hearder文件,然后swift可以使用OC代码了.这些都…

视觉标定,再来一波!!更简单粗暴!!!!!!

继续&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 一、四个坐标系简介和转换 相机模型为以后一切标定算法的关键&#xff0c;只有这边有相当透彻的理解&#xff0c;对以后的标定算法才能有更好的理解。本人研究了好长时间&#xff0c;几乎每天…

深入浅出NIO之Selector实现原理

2019独角兽企业重金招聘Python工程师标准>>> 前言 Java NIO 由以下几个核心部分组成&#xff1a; 1、Buffer 2、Channel 3、Selector Buffer和Channel在深入浅出NIO之Channel、Buffer一文中已经介绍过&#xff0c;本文主要讲解NIO的Selector实现原理。 之前进行sock…

介绍一下画图小能手matplotlib。

我们在做完数据分析的时候需要把分析出来的结果&#xff0c;做一个图形化的形象表达&#xff0c;这里我们就需要用到画图小能手matplotlib&#xff0c;下面就演示一下常用的条形图和折线图 散点图 散点图的做大的作用是研究两个变量的相关性&#xff08;正相关&#xff0c;负相…

立体视觉标定源代码C++,简单粗暴!粗暴·······

疑点解答&#xff1a; 摄像机矩阵由内参矩阵和外参矩阵组成&#xff0c;对摄像机矩阵进行QR分解可以得到内参矩阵和外参矩阵。 内参包括焦距、主点、倾斜系数、畸变系数 &#xff08;1&#xff09; 其中&#xff0c;fx&#xff0c;fy为焦距&#xff0c;一般情况下&#xff…

MongoDB负载信息一目了然 阿里云HDM重磅发布MongoDB监控和诊断功

2019独角兽企业重金招聘Python工程师标准>>> 混合云数据库管理&#xff08;HDM&#xff09;的统一监控、告警、诊断功能新增了对MongoDB的支持。 通过直观的方式将MongoDB多个维度的负载信息统一整合&#xff0c;不仅可以清晰的查看实时负载信息&#xff0c;也可以方…

在iview的Table中添加Select(render)

首先对Render进行分析&#xff0c;在iview官方的文档中&#xff0c;找到了table插入Button的例子&#xff1a; [javascript] view plaincopy { title: Action, key: action, width: 150, align: center, render: (h, params) > { return h(div, [ h(Butt…

工业机械人运动学正逆解,简单粗暴!!!!!!

ur机械臂是六自由度机械臂&#xff0c;由D-H参数法确定它的运动学模型&#xff0c;连杆坐标系的建立如上图所示。 转动关节θi是关节变量&#xff0c;连杆偏移di是常数。 关节编号 α&#xff08;绕x轴&#xff09; a&#xff08;沿x轴&#xff09; θ&#xff08;绕z轴&am…

python opencv立体测距 立体匹配BM算法

立体标定应用标定数据转换成深度图标定 在开始之前&#xff0c;需要准备的当然是两个摄相头&#xff0c;根据你的需求将两个摄像头进行相对位置的固定&#xff0c;我是按平行来进行固定的&#xff08;如果为了追求两个双目图像更高的生命度&#xff0c;也可以将其按一定钝角固…

Vue基础学习(一)------内部指令

一.v-if v-else v-show 指令 1.v-if v-if:是vue 的一个内部指令&#xff0c;指令用在我们的html中,用来判断是否加载html的DOM 现在举个栗子&#xff0c;判断用户的登录操作&#xff0c;用isLogin作为一个判断字段&#xff0c;登录成功&#xff0c;就显示用户的名称 代码&…

StereoRectify()函数定义及用法畸变矫正与立体校正

畸变矫正是上一篇博文的遗留问题&#xff0c;当畸变系数和内外参数矩阵标定完成后&#xff0c;就应该进行畸变的矫正&#xff0c;以达到消除畸变的目的&#xff0c;此其一。 在该系列第一部分的博文中介绍的立体成像原理中提到&#xff0c;要通过两幅图像估计物点的深度信息&a…

死磕 java集合之TreeMap源码分析(三)- 内含红黑树分析全过程

2019独角兽企业重金招聘Python工程师标准>>> 欢迎关注我的公众号“彤哥读源码”&#xff0c;查看更多源码系列文章, 与彤哥一起畅游源码的海洋。 删除元素 删除元素本身比较简单&#xff0c;就是采用二叉树的删除规则。 &#xff08;1&#xff09;如果删除的位置有两…

四元素理解

旋转变换_四元数 2017年03月29日 11:59:38 csxiaoshui 阅读数&#xff1a;5686 1.简介 四元数是另一种描述三维旋转的方式&#xff0c;四元数使用4个分量来描述旋转&#xff0c;四元数的描述方式如下&#xff1a; qsxiyjzk,(s,x,y,z∈ℝ&#xff09;i2j2k2ijk−1 四元数的由…

31、SAM文件中flag含义解释工具--转载

转载&#xff1a;http://www.cnblogs.com/nkwy2012/p/6362996.html SAM是Sequence Alignment/Map 的缩写。像bwa等软件序列比对结果都会输出这样的文件。samtools网站上有专门的文档介绍SAM文件。具体地址&#xff1a;http://samtools.sourceforge.net/SAM1.pdf很多人困惑SAM文…