基于张正友定标法的Opencv3.1定标程序,先用findchessboard找到棋盘,用cornersubpix做亚像素定位,再用calibrateCamera进行定标,最后将定标结果储存在xml文件里
程序基于vs2013和opencv3.1,要注意的是3.1根之前2XX系列很多地方不同,很多函数不同,具体情况看代码,
新手,难免犯诸如switch语句没有default之类的错误,大神看见请轻拍...
/*********************************************************************************** * @COPYRIGHT NOTICE * @Copyright (c) 2016, LiZichuan * @All rights reserved 文件名 : 0.0,摄像头的打开.cpp 版本 : ver 1.0 作者 : LiZichuan 日期 : 2016/3/22 13:37 简介 : 0.0,棋盘格读取与标定 ***********************************************************************************/ /***************************************************************************** 功 能 :头文件、命名空间包含部分 描 述 : *****************************************************************************/ #include "stdafx.h" #include "windows.h" //#include "stdio.h" #include "string" #include "iostream" #include "opencv2/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/opencv.hpp" #include "opencv2/calib3d.hpp" #include "opencv2/xfeatures2d.hpp" using namespace std; using namespace cv; using namespace cv::xfeatures2d; /***************************************************************************** 功 能 :宏定义部分 描 述 : *****************************************************************************/ /***************************************************************************** 功 能 :全局变量声明 描 述 : *****************************************************************************/ /***************************************************************************** 功 能 :全局函数声明 描 述 : *****************************************************************************/ //向xml文件写入矩阵 void uprintMatrix(CvFileStorage *fs, Mat umatrix, string uname); //向xml文件写入矩阵组 void uprintVectorMat(CvFileStorage *fs, vector<Mat> umatrix, string uname); //显示矩阵 void PrintfMatrix(Mat umatrix); //显示矩阵组 void PrintfVectorMat(vector<Mat> umatrix); /***************************************************************************** 函数名称 :main()函数 函数功能 : 作 者 :LiZichuan 日 期 :2016/3/22 13:40 *****************************************************************************/ int _tmain(int argc, _TCHAR* argv[]) { //1,定标准备 //参数准备 const double RealLengthOfChess = 20.2; //棋盘格单格实际宽度 const int chessBoard_Num = 32; //棋盘数量——定标图像读取次数 const int board_wide_Num = 9; //棋盘格每行格子数量 const int board_heigh_Num = 9; //棋盘格每列格子数量 Mat tempImage, tempImage_gray, tempImage1, tempImage_gray1; //定义原始图像和灰度图 vector<Point2f> corners, corners1; //每次检测得到的角点坐标 vector<vector<Point2f>> imagePoints, imagePoints1; //定义检测到角点在图像中的二重二维坐标组 vector<vector<Point3f>> objectPoints, objectPoints1; //定义检测到角点在实际中的二重三维坐标组 bool flag_chess_find, flag_chess_find_1; //定义棋盘检测成功的标志位 VideoCapture cap(0); //定义视频流 namedWindow("棋盘角点检测", WINDOW_AUTOSIZE); //创建源视频窗口() namedWindow("标定及矫正后", WINDOW_AUTOSIZE); //创建矫正后视频窗口 //TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 1000, 0.00001); cap >> tempImage; //采集视频 Size imageSize = Size(tempImage.cols, tempImage.rows); //定义采集视频窗口尺寸 //2,定标并显示结果,进行矫正并显示图像 while (true) { //2.1,开始定标 cout << "程序准备完成,按下‘c’开始采集图像" << endl; if (waitKey(0) == 'c') { //2.1.1,开始第n次图像读取并检测棋盘图,进行定标 for (int detectTime = 0; detectTime < chessBoard_Num; ++detectTime) { //2.1.1.1,从摄像头采集图像 cout << "按下'v',开始检测棋盘格" << endl; bool continue_flag = 0; //定义一个布尔变量作为棋盘检测成功的标志位 //不断循环采集图像,直到按下V按键进行棋盘格检测 while (1) { cap >> tempImage; //采集视频 cvtColor(tempImage, tempImage_gray, CV_BGR2GRAY); //将采集到视频转化为灰度图 imshow("棋盘角点检测", tempImage); //显示源视频 //按下V,开始采集图像 if (waitKey(1) == 'v') { //检测棋盘格,并生成检测成功标志位 flag_chess_find = findChessboardCorners(tempImage, Size(9, 9), corners, CV_CALIB_CB_ADAPTIVE_THRESH + CV_CALIB_CB_FAST_CHECK); //如果检测成功则按照不同颜色依次序画出角点,如果未成功则用红色画出检测到的角点 drawChessboardCorners(tempImage, Size(9, 9), corners, flag_chess_find); //显示角点 imshow("棋盘角点检测", tempImage); cout << "第" << detectTime << "次棋盘检测结果如下,v按‘c’继续标定,按‘r’重新检测" << endl; //等待按键 switch (waitKey(0)) { case 'c':continue_flag = 1; default:break; } } //根据上一个函数结果,如果按下C则跳出循环执行下一步,否则继续循环采集视频 if (continue_flag) break; } //2.1.1.2,如果角点检测成功,进行亚像素焦点定位并储存角点坐标于坐标组中 if (corners.size() > 2) { //亚像素角点检测 cornerSubPix(tempImage_gray, corners, Size(5, 5), Size(-1, -1), TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 1000, 0.00001)); //绘制图像并显示 for (int i = 0; i < corners.size(); i++) { circle(tempImage, corners[i], 8, Scalar(40, 25, 255), 2, 8); } imshow("棋盘角点检测", tempImage); //显示文字提示并循环显示检测到的棋盘图角点坐标 cout << "棋盘格亚像素精确定位结果如下" << endl; cout << " 棋盘检测角点数目:" << corners.size() << endl; for (int i = 0; i < corners.size(); i++ ) { if (i%2 == 0) //如果是偶数则下一次继续写(角点标号从0开始) { cout << "角点" << i << "精确坐标为:" << corners[i].x << "," << corners[i].y; } else //如果是奇数则下一次回车 { cout << " 角点" << i << "精确坐标为:" << corners[i].x << "," << corners[i].y << endl; } } cout << endl; cout << "第" << detectTime + 1 << "次棋盘检测结束" << endl; cout << "按‘r’重新进行本次检测,按‘c’进行下次检测" << endl; //等待按键,如果按下C则储存角点坐标,按下R则重新检测棋盘格获取角点坐标 switch (waitKey(0)) { case 'r': detectTime = detectTime - 1; //本次检测结果作废,重新检测 case 'c': //将角点坐标存入二重坐标组 { vector<Point2f> imagePoints_temp; //定义临时的角点二重二维坐标组 vector<Point3f> objectPoints_temp; //定义临时的角点二重三维坐标组 for (int j = 0; j < corners.size(); ++j) //利用循环依次将检测到角点坐标存入临时角点坐标组 { Point2f imgPoint_temp2; Point3f objPoint_temp2; imgPoint_temp2.x = corners[j].x; imgPoint_temp2.y = corners[j].y; objPoint_temp2.x = j % board_wide_Num * ((float)RealLengthOfChess); objPoint_temp2.y = j / board_wide_Num * ((float)RealLengthOfChess); objPoint_temp2.z = 0; imagePoints_temp.push_back(imgPoint_temp2); objectPoints_temp.push_back(objPoint_temp2); } imagePoints.push_back(imagePoints_temp); //将临时角点坐标组存入图像角点的二重二维坐标组 objectPoints.push_back(objectPoints_temp); //将临时角点坐标组存入实际角点的二重三维坐标组 } } } //2.1.1.3,如果检测失败,等待进行下一次棋盘检测或者退出 else { cout << "棋盘格检测失败,按‘r’重新检测,按‘q’退出" << "检测数量为" << corners.size() << endl; switch (waitKey(0)) { case 'r':detectTime = detectTime - 1; default: return false; } } } //2.1.2,根据之前角点坐标亚像素检测结果,进行相机标定 Mat intrinsic_Matrix(3, 3, CV_64F), distortion_coeffs(8, 1, CV_64F); vector<Mat> rvecs, tvecs; //定义旋转向量组和平移向量组 double time0 = getTickCount(); //2.1.3,进行定标 calibrateCamera(objectPoints, //角点的实际坐标 imagePoints, //角点的图像坐标 imageSize, //定标图像尺寸 intrinsic_Matrix, //相机内参数矩阵 distortion_coeffs, //畸变参数矩阵 rvecs, //旋转向量组 tvecs //平移向量组 ); //2.1.4,标定 cout << "相机定标结束,用时:" << (getTickCount()-time0)/1000 << "毫秒,定标结果如下:" << endl; //2.1.5,计算定标误差: double total_error_value = 0.0, error_value[chessBoard_Num]; for (int i = 0; i < chessBoard_Num; i++) { vector<Point2f> imagePoints2; //定义一个临时图像角点二重二维坐标组 //根据旋转向量、平移向量、内参矩阵和相机畸变矩阵计算实际角点在图像坐标席上投影 projectPoints(objectPoints[i], //角点实际坐标 rvecs[i], //旋转向量 tvecs[i], //平移向量 intrinsic_Matrix, //内参矩阵 distortion_coeffs, //畸变矩阵 imagePoints2 //储存计算得到的图像角点坐标 ); Mat tempImagePointMat = Mat(1, imagePoints[i].size(), CV_32FC2); //创建用于储存角点的图像坐标的矩阵 Mat imagePoints2Mat = Mat(1, imagePoints2.size(), CV_32FC2); //创建用于储存角点的图像坐标理论值的矩阵 for (int j = 0; j < imagePoints[i].size(); j++) { imagePoints2Mat.at<Vec2f>(0, j) = Vec2f(imagePoints2[j].x, imagePoints2[j].y); //将角点图像坐标存入矩阵 tempImagePointMat.at<Vec2f>(0, j) = Vec2f(imagePoints[i][j].x, imagePoints[i][j].y); //将角点图像坐标理论值存入矩阵 } error_value[i] = norm(imagePoints2Mat, tempImagePointMat, NORM_L2); //对图像坐标和图像坐标理论值进行归一化 total_error_value += error_value[i] /= imagePoints[i].size(); //结果累加然后除以角点数 cout << "第" << i + 1 << "幅图像的平均误差:" << error_value[i] << "像素" << endl; } cout << "总体平均误差:" << total_error_value / chessBoard_Num << "像素" << endl; //2.1.6,显示定标结果 //在命令台显示结果 printf("相机内参矩阵:\n"); PrintfMatrix(intrinsic_Matrix); printf("相机畸变矩阵:\n"); PrintfMatrix(distortion_coeffs); printf("旋转向量组:\n"); PrintfVectorMat(rvecs); printf("平移向量组:\n"); PrintfVectorMat(tvecs); //2.1.7,储存标定结果 //定义文件储存指针 CvFileStorage *fs = cvOpenFileStorage("123.xml", //文件名称 0, CV_STORAGE_WRITE, //模式为写入模式 "GB2312" //文本编码 ); cvWriteComment(fs, "这个文件用于记录定标结果。", 0); //向文件写入短语 //储存标定结果 cvWriteComment(fs, "定标参数如下:", 0); //向文件写入短语 uprintMatrix(fs, intrinsic_Matrix, "相机内参矩阵"); //写入相机内参 uprintMatrix(fs, distortion_coeffs, "相机畸变矩阵"); //写入畸变矩阵 uprintVectorMat(fs, rvecs, "旋转向量组"); //写入旋转向量组 uprintVectorMat(fs, tvecs, "平移向量组"); //写入平移向量组 cvWriteComment(fs, "定标参数写入结束", 0); //向文件写入短语 //储存定标误差 cvWriteComment(fs, "定标误差如下:", 0); //向文件写入短语 cvWriteReal(fs, "total_average_error", total_error_value / chessBoard_Num); //写入所有图片的平均误差 cvWriteComment(fs, "每幅图片定标误差如下:", 0); //向文件写入短语 for (int i = 0; i < chessBoard_Num; i++) { cvWriteComment(fs, "Photo_Counter", 0); cvWriteReal(fs, "error_value", error_value[i]); //写入每幅图片误差 } cvWriteComment(fs, "误差写入结束", 0); //向文件写入短语 //储存角点坐标 cvWriteComment(fs, "角点图像坐标为:", 0); //向文件写入短语 for (int i = 0; i < chessBoard_Num; i++) { cvWriteInt(fs, "Photes_Counter", i); for (int j = 0; j < corners.size(); j++) { cvWriteInt(fs, "Corners_Counter", j); //写入角点序号 float temp_coordinate[] = { imagePoints[i][j].x, imagePoints[i][j].y}; //定义用于输出坐标的临时变量 cvStartWriteStruct(fs, "coordinates", CV_NODE_SEQ); //开始写入结构体 cvWriteRawData(fs, temp_coordinate, 2, "f"); //写坐标 cvEndWriteStruct(fs); //结构体写入结束 } } cvWriteComment(fs, "角点图像坐标写入结束", 0); //向文件写入短语 cvWriteComment(fs, "角点实际坐标为:", 0); //向文件写入短语 for (int i = 0; i < chessBoard_Num; i++) { cvWriteInt(fs, "Photos_Counter", i); for (int j = 0; j < corners.size(); j++) { cvWriteInt(fs, "Corners_Counter", j); //写入角点序号 float temp_coordinate[] = { objectPoints[i][j].x, objectPoints[i][j].y, objectPoints[i][j].z }; //定义用于输出坐标的临时变量 cvStartWriteStruct(fs, "coordinates", CV_NODE_SEQ); //开始写入结构体 cvWriteRawData(fs, temp_coordinate, 3, "f"); //写坐标 cvEndWriteStruct(fs); //结构体写入结束 } } cvWriteComment(fs, "角点实际坐标写入结束", 0); //向文件写入短语 cvReleaseFileStorage(&fs); //文件写入结束,释放文件指针 //2.1.8,使用定标结果矫正采集到图像,并显示出来 cout << "显示矫正结果,按'w'键继续" << endl; //不断循环显示矫正后图像 while (true) { Mat srcImage, srcImage1; //定义临时矩阵变量 cap >> srcImage; //从相机采集图像 //进行矫正 undistort(srcImage, //输入源图像 srcImage1, //存放矫正后图像 intrinsic_Matrix, //内参矩阵 distortion_coeffs //畸变矩阵 ); imshow("棋盘角点检测", srcImage); //显示结果 imshow("标定及矫正后", srcImage1); if (waitKey(5) == 'q') break; } cout << "本轮定标结束,按‘q’键退出,‘r’键进行下一轮定标" << endl; } //2.2,如果按下q,则退出循环,按下r,重新定标 if (waitKey(0) == 'q') { break; } else { while (waitKey(0) != 'r'); } } return 0; } /***************************************************************************** 函数名称 :uprintVectorMat()函数 函数功能 : 作 者 :LiZichuan 日 期 :2016/3/26 20:06 *****************************************************************************/ void uprintVectorMat(CvFileStorage *fs, vector<Mat> umatrix, string uname) { cvWriteComment(fs, uname.c_str(), 0); //逐次按矩阵、按行、按列写入矩阵组值 for (int i = 0; i < umatrix.size(); ++ i) { cvWriteInt(fs, "Mat_Number", i); for (int j = 0; j < umatrix[i].rows; ++ j) { for (int k = 0; k < umatrix[i].cols; ++k) { cvWriteReal(fs, "Value", umatrix[i].at<double>(j, k)); } } } cvWriteComment(fs, "写入结束", 0); } /***************************************************************************** 函数名称 :uprintMatrix()函数 函数功能 : 作 者 :LiZichuan 日 期 :2016/3/26 20:21 *****************************************************************************/ void uprintMatrix(CvFileStorage *fs, Mat umatrix, string uname) { cvWriteComment(fs, uname.c_str(), 0); //逐次按行按列写入矩阵值 for (int i = 0; i < umatrix.rows; ++ i) { for (int j = 0; j < umatrix.cols; ++ j) { cvWriteReal(fs, "Value", umatrix.at<double>(i, j)); } } cvWriteComment(fs, "写入结束", 0); } /***************************************************************************** 函数名称 :PrintfMatrix()函数 函数功能 : 作 者 :LiZichuan 日 期 :2016/3/26 20:51 *****************************************************************************/ void PrintfMatrix(Mat umatrix) { for (int i = 0; i < umatrix.rows; ++ i) { for (int j = 0; j < umatrix.cols; ++ j) { printf(" %lf", umatrix.at<double>(i, j)); } printf("\n"); } printf("\n"); } /***************************************************************************** 函数名称 :PrintfVectorMat()函数 函数功能 : 作 者 :LiZichuan 日 期 :2016/3/26 20:51 *****************************************************************************/ void PrintfVectorMat(vector<Mat> umatrix) { for (int i = 0; i < umatrix.size(); ++ i) { for (int j = 0; j < umatrix[i].rows; ++ j) { for (int k = 0; k < umatrix[i].cols; ++ k) { printf(" %lf", umatrix[i].at<double>(j, k)); } printf("\n"); } printf("\n"); } }