張正友相機標定Opencv實現以及標定流程&&標定結果評價&&圖像矯正流程解析(附標定程序和棋盤圖)

https://my.oschina.net/abcijkxyz/blog/787659

 

#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <fstream>

using namespace cv;
using namespace std;

void main() 
{
	ifstream fin("calibdata.txt"); /* 標定所用圖像文件的路徑 */
	ofstream fout("caliberation_result.txt");  /* 保存標定結果的文件 */	
	//讀取每一幅圖像,從中提取出角點,然後對角點進行亞像素精確化	
	cout<<"開始提取角點………………";
	int image_count=0;  /* 圖像數量 */
	Size image_size;  /* 圖像的尺寸 */
	Size board_size = Size(4,6);    /* 標定板上每行、列的角點數 */
	vector<Point2f> image_points_buf;  /* 緩存每幅圖像上檢測到的角點 */
	vector<vector<Point2f>> image_points_seq; /* 保存檢測到的所有角點 */
	string filename;
	int count= -1 ;//用於存儲角點個數。
	while (getline(fin,filename))
	{
		image_count++;		
		// 用於觀察檢驗輸出
		cout<<"image_count = "<<image_count<<endl;		
		/* 輸出檢驗*/
		cout<<"-->count = "<<count;		
		Mat imageInput=imread(filename);
		if (image_count == 1)  //讀入第一張圖片時獲取圖像寬高信息
		{
			image_size.width = imageInput.cols;
			image_size.height =imageInput.rows;			
			cout<<"image_size.width = "<<image_size.width<<endl;
			cout<<"image_size.height = "<<image_size.height<<endl;
		}

		/* 提取角點 */
		if (0 == findChessboardCorners(imageInput,board_size,image_points_buf))
		{			
			cout<<"can not find chessboard corners!\n"; //找不到角點
			exit(1);
		} 
		else 
		{
			Mat view_gray;
			cvtColor(imageInput,view_gray,CV_RGB2GRAY);
			/* 亞像素精確化 */
			find4QuadCornerSubpix(view_gray,image_points_buf,Size(5,5)); //對粗提取的角點進行精確化
			//cornerSubPix(view_gray,image_points_buf,Size(5,5),Size(-1,-1),TermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,0.1));
			image_points_seq.push_back(image_points_buf);  //保存亞像素角點
			/* 在圖像上顯示角點位置 */
			drawChessboardCorners(view_gray,board_size,image_points_buf,false); //用於在圖片中標記角點
			imshow("Camera Calibration",view_gray);//顯示圖片
			waitKey(500);//暫停0.5S		
		}
	}
	int total = image_points_seq.size();
	cout<<"total = "<<total<<endl;
	int CornerNum=board_size.width*board_size.height;  //每張圖片上總的角點數
	for (int ii=0 ; ii<total ;ii++)
	{
		if (0 == ii%CornerNum)// 24 是每幅圖片的角點個數。此判斷語句是爲了輸出 圖片號,便於控制檯觀看 
		{	
			int i = -1;
			i = ii/CornerNum;
			int j=i+1;
			cout<<"--> 第 "<<j <<"圖片的數據 --> : "<<endl;
		}
		if (0 == ii%3)	// 此判斷語句,格式化輸出,便於控制檯查看
		{
			cout<<endl;
		}
		else
		{
			cout.width(10);
		}
		//輸出所有的角點
		cout<<" -->"<<image_points_seq[ii][0].x;
		cout<<" -->"<<image_points_seq[ii][0].y;
	}	
	cout<<"角點提取完成!\n";

	//以下是攝像機標定
	cout<<"開始標定………………";
	/*棋盤三維信息*/
	Size square_size = Size(10,10);  /* 實際測量得到的標定板上每個棋盤格的大小 */
	vector<vector<Point3f>> object_points; /* 保存標定板上角點的三維座標 */
	/*內外參數*/
	Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0)); /* 攝像機內參數矩陣 */
	vector<int> point_counts;  // 每幅圖像中角點的數量
	Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0)); /* 攝像機的5個畸變係數:k1,k2,p1,p2,k3 */
	vector<Mat> tvecsMat;  /* 每幅圖像的旋轉向量 */
	vector<Mat> rvecsMat; /* 每幅圖像的平移向量 */
	/* 初始化標定板上角點的三維座標 */
	int i,j,t;
	for (t=0;t<image_count;t++) 
	{
		vector<Point3f> tempPointSet;
		for (i=0;i<board_size.height;i++) 
		{
			for (j=0;j<board_size.width;j++) 
			{
				Point3f realPoint;
				/* 假設標定板放在世界座標系中z=0的平面上 */
				realPoint.x = i*square_size.width;
				realPoint.y = j*square_size.height;
				realPoint.z = 0;
				tempPointSet.push_back(realPoint);
			}
		}
		object_points.push_back(tempPointSet);
	}
	/* 初始化每幅圖像中的角點數量,假定每幅圖像中都可以看到完整的標定板 */
	for (i=0;i<image_count;i++)
	{
		point_counts.push_back(board_size.width*board_size.height);
	}	
	/* 開始標定 */
	calibrateCamera(object_points,image_points_seq,image_size,cameraMatrix,distCoeffs,rvecsMat,tvecsMat,0);
	cout<<"標定完成!\n";
	//對標定結果進行評價
	cout<<"開始評價標定結果………………\n";
	double total_err = 0.0; /* 所有圖像的平均誤差的總和 */
	double err = 0.0; /* 每幅圖像的平均誤差 */
	vector<Point2f> image_points2; /* 保存重新計算得到的投影點 */
	cout<<"\t每幅圖像的標定誤差:\n";
	fout<<"每幅圖像的標定誤差:\n";
	for (i=0;i<image_count;i++)
	{
		vector<Point3f> tempPointSet=object_points[i];
		/* 通過得到的攝像機內外參數,對空間的三維點進行重新投影計算,得到新的投影點 */
		projectPoints(tempPointSet,rvecsMat[i],tvecsMat[i],cameraMatrix,distCoeffs,image_points2);
		/* 計算新的投影點和舊的投影點之間的誤差*/
		vector<Point2f> tempImagePoint = image_points_seq[i];
		Mat tempImagePointMat = Mat(1,tempImagePoint.size(),CV_32FC2);
		Mat image_points2Mat = Mat(1,image_points2.size(), CV_32FC2);
		for (int j = 0 ; j < tempImagePoint.size(); j++)
		{
			image_points2Mat.at<Vec2f>(0,j) = Vec2f(image_points2[j].x, image_points2[j].y);
			tempImagePointMat.at<Vec2f>(0,j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);
		}
		err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
		total_err += err/=  point_counts[i];   
		std::cout<<"第"<<i+1<<"幅圖像的平均誤差:"<<err<<"像素"<<endl;   
		fout<<"第"<<i+1<<"幅圖像的平均誤差:"<<err<<"像素"<<endl;   
	}   
	std::cout<<"總體平均誤差:"<<total_err/image_count<<"像素"<<endl;   
	fout<<"總體平均誤差:"<<total_err/image_count<<"像素"<<endl<<endl;   
	std::cout<<"評價完成!"<<endl;  
	//保存定標結果  	
	std::cout<<"開始保存定標結果………………"<<endl;       
	Mat rotation_matrix = Mat(3,3,CV_32FC1, Scalar::all(0)); /* 保存每幅圖像的旋轉矩陣 */
	fout<<"相機內參數矩陣:"<<endl;   
	fout<<cameraMatrix<<endl<<endl;   
	fout<<"畸變係數:\n";   
	fout<<distCoeffs<<endl<<endl<<endl;   
	for (int i=0; i<image_count; i++) 
	{ 
		fout<<"第"<<i+1<<"幅圖像的旋轉向量:"<<endl;   
		fout<<tvecsMat[i]<<endl;    
		/* 將旋轉向量轉換爲相對應的旋轉矩陣 */   
		Rodrigues(tvecsMat[i],rotation_matrix);   
		fout<<"第"<<i+1<<"幅圖像的旋轉矩陣:"<<endl;   
		fout<<rotation_matrix<<endl;   
		fout<<"第"<<i+1<<"幅圖像的平移向量:"<<endl;   
		fout<<rvecsMat[i]<<endl<<endl;   
	}   
	std::cout<<"完成保存"<<endl; 
	fout<<endl;
	/************************************************************************  
	顯示定標結果  
	*************************************************************************/
	Mat mapx = Mat(image_size,CV_32FC1);
	Mat mapy = Mat(image_size,CV_32FC1);
	Mat R = Mat::eye(3,3,CV_32F);
	std::cout<<"保存矯正圖像"<<endl;
	string imageFileName;
	std::stringstream StrStm;
	for (int i = 0 ; i != image_count ; i++)
	{
		std::cout<<"Frame #"<<i+1<<"..."<<endl;
		initUndistortRectifyMap(cameraMatrix,distCoeffs,R,cameraMatrix,image_size,CV_32FC1,mapx,mapy);		
		StrStm.clear();
		imageFileName.clear();
		string filePath="chess";
		StrStm<<i+1;
		StrStm>>imageFileName;
		filePath+=imageFileName;
		filePath+=".bmp";
		Mat imageSource = imread(filePath);
		Mat newimage = imageSource.clone();
		//另一種不需要轉換矩陣的方式
		//undistort(imageSource,newimage,cameraMatrix,distCoeffs);
		remap(imageSource,newimage,mapx, mapy, INTER_LINEAR);		
		StrStm.clear();
		filePath.clear();
		StrStm<<i+1;
		StrStm>>imageFileName;
		imageFileName += "_d.jpg";
		imwrite(imageFileName,newimage);
	}
	std::cout<<"保存結束"<<endl;	
	return ;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章