OpenCV系列【2】,一个简单定标并储存结果的程序

基于张正友定标法的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");
	}
}
时间: 2024-07-30 13:01:29

OpenCV系列【2】,一个简单定标并储存结果的程序的相关文章

[软件测试学习]考虑到测试的代码编写/int.parse的非法输入—由一个简单的c#闰年检测程序说起

一个简单的C#的闰年检测程序 1.闰年检测的函数编写 当提起检测平年闰年时候,第一反应写出的代码 1 public static bool isLeapYear(int year){ 2 return ((year % 4 == 0 && year % 100 != 0)||(year % 400 == 0)) 3 } 但是这个并不易于测试和出现错后的修改,更改代码如下 1 public static bool isLeapYear(int year){ 2 bool check = ne

Spring学习(二)——使用用Gradle构建一个简单的Spring MVC Web应用程序

1.新建一个Gradle工程(Project) 在新建工程窗口的左侧中选择 [Gradle],右侧保持默认选择,点击next,模块命名为VelocityDemo. 2.在该工程下新建一个 module,在弹出的窗口的左侧中选择 [Gradle],右侧勾选[Spring MVC],如下图所示: 并勾选[Application server],下方选择框中选择Tomcat7.0,如无该选项,则选中右边的 [ New... ] -- [ Tomcat Server ], 配置 Tomcat .配置好后

一个简单的计算分数的小程序

一个简单的计算分数的小程序 代码如下: package Day05; public class ExamGradeDemo { public static void main(String[] args) { char[][] answers = { {'C','B','D','C','A','A','D','C','D','C'}, {'A','C','B','D','C','A','D','C','B','D'}, {'A','C','B','D','B','D','C','A','A','

模仿一个简单登录(SharedPreferences储存)

学习自慕课网,不过感觉老师这个章节讲的不认真... 首先写布局,用户名,编辑框,密码,编辑框,是否保存,登录按钮,取消按钮(没用来着) <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > &

一个简单的人物图片相似对比程序

程序是参考了两部分的别人的代码,一个是找出照片中的人头,另一个是对两个人头照片做对比. # -*- coding: utf-8 -*- # feimengjuan # 利用python实现多种方法来实现图像识别 import cv2 import numpy as np from matplotlib import pyplot as plt, image # 最简单的以灰度直方图作为相似比较的实现 def classify_gray_hist(image1, image2, size=(256

go语言实现一个简单的登录注册web小程序

最近学习golang也有一段时间了,基础差不多学了个大概,因为本人是java程序员,所以对web更感兴趣.根据<go web编程>中的例子改编一个更简单的例子,供新手参考,废话不多说,上菜: 这个例子使用到了beego框架和beedb框架,如果是go新手beego和beedb得自己去google下载安装. 目录结构: index.go package controllers import ( "fmt" "github.com/astaxie/beego"

Socket 通讯,一个简单的 C/S 聊天小程序

Socket,这玩意,当时不会的时候,抄别人的都用不好,简单的一句话形容就是“笨死了”:也是很多人写的太复杂,不容易理解造成的.最近在搞erlang和C的通讯,也想试试erlang是不是可以和C#简单通讯,就简单的做了些测试用例,比较简单,觉得新手也可以接受. 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.

ffmpeg+sdl教程----编写一个简单的播放器4(让程序更模块化)

来源:http://blog.csdn.net/mu399/article/details/5815444 音频和视频之间的同步,再那之前需要先做一些准备工作. 为了让程序更模块化,便于扩展,需要把原来main函数中的各个功能模块代码分离出来放在相应的函数中.该教程和上个教程相比代码量和难度都增加很多,比上个教程使用了更多的线程,一定要理解清楚各个函数和数据结构之间的关联以及线程之间如何协同工作. [c-sharp] view plaincopy // ffmpegExe.cpp: 主项目文件.

一个简单的模拟进程调度的C++程序

这是我们操作系统实验课的一个实验程序,模拟简单的若干个进程在执行态和就绪态之间的变动. 我根据实验指导书上给出的程序进行了一点修改. #include<cstdio> #include<cstdlib> struct PCB { char name[10]; char state; //状态 int super; //优先级 int ntime; //预计运行时间 int rtime; //实际运行时间 PCB *link; }*ready=NULL, *p; void disp(