注: 程序所用的OpenCV版本是 2.4.10 ,3.0以上的版本可能会有不同

先贴一下完整的工程代码:

  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(11,11)); //对粗提取的角点进行精确化
  49. image_points_seq.push_back(image_points_buf); //保存亚像素角点
  50. /* 在图像上显示角点位置 */
  51. drawChessboardCorners(view_gray,board_size,image_points_buf,true); //用于在图片中标记角点
  52. imshow("Camera Calibration",view_gray);//显示图片
  53. waitKey(500);//暂停0.5S
  54. }
  55. }
  56. int total = image_points_seq.size();
  57. cout<<"total = "<<total<<endl;
  58. int CornerNum=board_size.width*board_size.height; //每张图片上总的角点数
  59. for (int ii=0 ; ii<total ;ii++)
  60. {
  61. if (0 == ii%CornerNum)// 24 是每幅图片的角点个数。此判断语句是为了输出 图片号,便于控制台观看
  62. {
  63. int i = -1;
  64. i = ii/CornerNum;
  65. int j=i+1;
  66. cout<<"--> 第 "<<j <<"图片的数据 --> : "<<endl;
  67. }
  68. if (0 == ii%3) // 此判断语句,格式化输出,便于控制台查看
  69. {
  70. cout<<endl;
  71. }
  72. else
  73. {
  74. cout.width(10);
  75. }
  76. //输出所有的角点
  77. cout<<" -->"<<image_points_seq[ii][0].x;
  78. cout<<" -->"<<image_points_seq[ii][0].y;
  79. }
  80. cout<<"角点提取完成!\n";
  81. //以下是摄像机标定
  82. cout<<"开始标定………………";
  83. /*棋盘三维信息*/
  84. Size square_size = Size(10,10); /* 实际测量得到的标定板上每个棋盘格的大小 */
  85. vector<vector<Point3f>> object_points; /* 保存标定板上角点的三维坐标 */
  86. /*内外参数*/
  87. Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0)); /* 摄像机内参数矩阵 */
  88. vector<int> point_counts; // 每幅图像中角点的数量
  89. Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0)); /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */
  90. vector<Mat> tvecsMat; /* 每幅图像的旋转向量 */
  91. vector<Mat> rvecsMat; /* 每幅图像的平移向量 */
  92. /* 初始化标定板上角点的三维坐标 */
  93. int i,j,t;
  94. for (t=0;t<image_count;t++)
  95. {
  96. vector<Point3f> tempPointSet;
  97. for (i=0;i<board_size.height;i++)
  98. {
  99. for (j=0;j<board_size.width;j++)
  100. {
  101. Point3f realPoint;
  102. /* 假设标定板放在世界坐标系中z=0的平面上 */
  103. realPoint.x = i*square_size.width;
  104. realPoint.y = j*square_size.height;
  105. realPoint.z = 0;
  106. tempPointSet.push_back(realPoint);
  107. }
  108. }
  109. object_points.push_back(tempPointSet);
  110. }
  111. /* 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 */
  112. for (i=0;i<image_count;i++)
  113. {
  114. point_counts.push_back(board_size.width*board_size.height);
  115. }
  116. /* 开始标定 */
  117. calibrateCamera(object_points,image_points_seq,image_size,cameraMatrix,distCoeffs,rvecsMat,tvecsMat,0);
  118. cout<<"标定完成!\n";
  119. //对标定结果进行评价
  120. cout<<"开始评价标定结果………………\n";
  121. double total_err = 0.0; /* 所有图像的平均误差的总和 */
  122. double err = 0.0; /* 每幅图像的平均误差 */
  123. vector<Point2f> image_points2; /* 保存重新计算得到的投影点 */
  124. cout<<"\t每幅图像的标定误差:\n";
  125. fout<<"每幅图像的标定误差:\n";
  126. for (i=0;i<image_count;i++)
  127. {
  128. vector<Point3f> tempPointSet=object_points[i];
  129. /* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */
  130. projectPoints(tempPointSet,rvecsMat[i],tvecsMat[i],cameraMatrix,distCoeffs,image_points2);
  131. /* 计算新的投影点和旧的投影点之间的误差*/
  132. vector<Point2f> tempImagePoint = image_points_seq[i];
  133. Mat tempImagePointMat = Mat(1,tempImagePoint.size(),CV_32FC2);
  134. Mat image_points2Mat = Mat(1,image_points2.size(), CV_32FC2);
  135. for (int j = 0 ; j < tempImagePoint.size(); j++)
  136. {
  137. image_points2Mat.at<Vec2f>(0,j) = Vec2f(image_points2[j].x, image_points2[j].y);
  138. tempImagePointMat.at<Vec2f>(0,j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);
  139. }
  140. err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
  141. total_err += err/= point_counts[i];
  142. std::cout<<"第"<<i+1<<"幅图像的平均误差:"<<err<<"像素"<<endl;
  143. fout<<"第"<<i+1<<"幅图像的平均误差:"<<err<<"像素"<<endl;
  144. }
  145. std::cout<<"总体平均误差:"<<total_err/image_count<<"像素"<<endl;
  146. fout<<"总体平均误差:"<<total_err/image_count<<"像素"<<endl<<endl;
  147. std::cout<<"评价完成!"<<endl;
  148. //保存定标结果
  149. std::cout<<"开始保存定标结果………………"<<endl;
  150. Mat rotation_matrix = Mat(3,3,CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */
  151. fout<<"相机内参数矩阵:"<<endl;
  152. fout<<cameraMatrix<<endl<<endl;
  153. fout<<"畸变系数:\n";
  154. fout<<distCoeffs<<endl<<endl<<endl;
  155. for (int i=0; i<image_count; i++)
  156. {
  157. fout<<"第"<<i+1<<"幅图像的旋转向量:"<<endl;
  158. fout<<tvecsMat[i]<<endl;
  159. /* 将旋转向量转换为相对应的旋转矩阵 */
  160. Rodrigues(tvecsMat[i],rotation_matrix);
  161. fout<<"第"<<i+1<<"幅图像的旋转矩阵:"<<endl;
  162. fout<<rotation_matrix<<endl;
  163. fout<<"第"<<i+1<<"幅图像的平移向量:"<<endl;
  164. fout<<rvecsMat[i]<<endl<<endl;
  165. }
  166. std::cout<<"完成保存"<<endl;
  167. fout<<endl;
  168. system("pause");
  169. return ;
  170. }

运行前需要先准备标定图片和记录标定图片列表的文本文件,并放入程序所在目录下,如下图所示:

文本文件的内容如下:

运行效果图1:

图2:

图3:

图4:

最后在程序所在目录下生成“caliberation_result.txt”文件,记录了标定的误差、相机内外参数信息:

感谢无名前辈提供的测试图例!

Opencv 张正友相机标定傻瓜教程的更多相关文章

  1. OpenCV实现张正友相机标定源代码

    本源代码基于VC++和opencv Opencv2.4.13.6版本开发,实现张正友相机标定源代码,资源包括完整源代码和12张棋盘图片,完美运行.Opencv2.4.13.6安装包下载地址:http: ...

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

    使用Opencv实现张正友法相机标定之前,有几个问题事先要确认一下,那就是相机为什么需要标定,标定需要的输入和输出分别是哪些? 相机标定的目的:获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的 ...

  3. SLAM入门之视觉里程计(6):相机标定 张正友经典标定法详解

    想要从二维图像中获取到场景的三维信息,相机的内参数是必须的,在SLAM中,相机通常是提前标定好的.张正友于1998年在论文:"A Flexible New Technique fro Cam ...

  4. 树莓派Opencv张正友棋盘标定法

    make.Makefile cc = gcc #最简易的makefile文件,这个可以用来进行文件之间的简易构建和链接,生成我们所需要的执行文件: prom = calc deps = $(shell ...

  5. opencv 角点检测+相机标定+去畸变+重投影误差计算

    https://blog.csdn.net/u010128736/article/details/52875137 https://blog.csdn.net/h532600610/article/d ...

  6. OpenCV相机标定和姿态更新

    原帖地址: http://blog.csdn.net/aptx704610875/article/details/48914043 http://blog.csdn.net/aptx704610875 ...

  7. 【视频开发】【计算机视觉】相机标定(Camera calibration)原理、步骤

    相机标定(Camera calibration)原理.步骤 author@jason_ql(lql0716)  http://blog.csdn.net/lql0716 在图像测量过程以及机器视觉应用 ...

  8. 相机标定:PNP基于单应面解决多点透视问题

              利用二维视野内的图像,求出三维图像在场景中的位姿,这是一个三维透视投影的反向求解问题.常用方法是PNP方法,需要已知三维点集的原始模型. 本文做了大量修改,如有不适,请移步原文:  ...

  9. 相机标定简介与MatLab相机标定工具箱的使用(未涉及原理公式推导)

    相机标定 一.相机标定的目的 确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,建立摄像机成像的几何模型,这些几何模型参数就是摄像机参数. 二.通用摄像机模型 世界坐标系.摄像机坐标 ...

随机推荐

  1. [WPF自定义控件库]使用TextBlockHighlightSource强化高亮的功能,以及使用TypeConverter简化调用

    1. 强化高亮的功能 上一篇文章介绍了使用附加属性实现TextBlock的高亮功能,但也留下了问题:不能定义高亮(或者低亮)的颜色.为了解决这个问题,我创建了TextBlockHighlightSou ...

  2. 关于Altium Designer中的搜索图纸上的元件

    一开始以为Altium Designer搜索完成的pcb上的元件用ctrl+f 但是错了,应该是j,c

  3. 高级Java工程师必备 ----- 深入分析 Java IO (三)

    概述 Java IO即Java 输入输出系统.不管我们编写何种应用,都难免和各种输入输出相关的媒介打交道,其实和媒介进行IO的过程是十分复杂的,这要考虑的因素特别多,比如我们要考虑和哪种媒介进行IO( ...

  4. (转)Vim练级攻略

    (转)Vim练级攻略 原文链接:http://coolshell.cn/articles/5426.html vim的学习曲线相当的大(参看各种文本编辑器的学习曲线),所以,如果你一开始看到的是一大堆 ...

  5. [React] Setup 'beforeunload' listener

    In this lesson we'll show how to take a beforeUnload call and convert it to a declarative React Comp ...

  6. 在Linux上安装及配置MariaDB

    安装MariaDB 1.切换到root用户,首先执行rpm -qa | grep -i mysql检查一下是否有已安装的与MySQL相关的东西,如果有,使用rpm -e --nodeps mysql* ...

  7. 用IBM WebSphere DataStage进行数据整合: 第 1 部分 分类: H2_ORACLE 2013-08-23 11:20 688人阅读 评论(0) 收藏

    转自:http://www.ibm.com/developerworks/cn/data/library/techarticles/dm-0602zhoudp/ 引言 传统的数据整合方式需要大量的手工 ...

  8. ajax实现注册用户名时动态显示用户名是否已经被注册(1、ajax可以实现我们常见的注册用户名动态判断)(2、jquery里面的ajax也是类似我们这样封装了的函数)

    ajax实现注册用户名时动态显示用户名是否已经被注册(1.ajax可以实现我们常见的注册用户名动态判断)(2.jquery里面的ajax也是类似我们这样封装了的函数) 一.总结 1.ajax可以实现我 ...

  9. 第二篇:_UICascadingTextStorage attributesAtIndex:effectiveRange:]: Range or index out of bounds

    注意下文标红字段: #pragma mark- 输入改变时 - (void) textFieldDidChanged:(UITextField *) TextField{ //搜索关键字一旦改变,将重 ...

  10. 简单sql部分强化练习题

    简单查询部分sql练习题 -- 选择部门30中的全部职工 select * from emp where deptno = 30; -- 列出全部业务员(CLERK)的姓名,编号,和部门编号 sele ...