相機畸變校訂、求出參數、具體應用

1、原理

前提:攝像頭固定由於攝像頭一動,內參不變(畸變係數),可是外參(座標變換)會變。
經過拍攝幾張標定板的照片,而後獲得畸變係數和相機內外參係數,而後每次讀取攝像機圖片時,將這些係數帶進去,計算以後就能夠獲得矯正後的圖片了。
效果以下:ios

畸變校訂前
這裏寫圖片描述緩存

畸變校訂後
這裏寫圖片描述函數

顯然上面圖片四周直線都是彎曲的,被矯正後,變得效果不錯了。ui

2、具體步驟

標定圖:在這裏插入圖片描述
程序在第三部分,具體步驟以下:
一、將第三步的代碼複製到工程裏
二、 在工程目錄下(主函數.cpp相同目錄下)創建一個caliberation文件夾,採集10——20張照片(不一樣角度,方向,可是要把角點所有顯示出來),將照片放入該文件夾下。spa

效果以下:
這裏寫圖片描述
三、新建一個calibdata.txt文件,將步驟2的圖片路徑寫進去格式以下:3d

./caliberation/1.jpg
./caliberation/2.jpg
./caliberation/3.jpg
./caliberation/4.jpg
./caliberation/5.jpg
./caliberation/6.jpg
./caliberation/7.jpg
./caliberation/8.jpg
./caliberation/9.jpg
./caliberation/10.jpg
./caliberation/11.jpg
./caliberation/12.jpg

四、新建一個chess文件夾(名字隨便,記得在程序裏改),用於保存畸變校訂後的圖片。
五、運行程序,會生成一個caliberation_result.txt文件,裏面保存了內外參等一些參數。好比畸變係數,旋轉矩陣,平移矩陣等。code

3、參數獲取程序代碼

//2018.6.19:畸變校訂



#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 (imageInput.empty())
		{
			cout << "can not open pic!\n";
			exit(-1);
		}
		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); //用於在圖片中標記角點    
			namedWindow("Camera Calibration", 0);//建立窗口
			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 += ".jpg";
		Mat imageSource = imread("1.jpg"); //讀取畸變圖片
		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;
}

4、使用程序

這個部分就是將獲得的參數,應用到具體的程序中,不用每次進行標定,只要攝像頭位置不變,就能夠將畸變參數帶進去就能夠矯正。orm

void InitMat(Mat& m, float* num)
{
	for (int i = 0; i<m.rows; i++)
	for (int j = 0; j<m.cols; j++)
		m.at<float>(i, j) = *(num + i * m.rows + j);
}




int main()
{
	int i = 1000;
	int n = 1;
	Mat edges;
	Mat frame = imread("2.jpg"); //讀取畸變圖片

	Mat R = Mat::eye(3, 3, CV_32F);

	Size image_size;  /* 圖像的尺寸 */
	//獲取圖像大小
	image_size.width = 1920;
	image_size.height = 1080;

	//cameraMatrix爲 "相機內參數矩陣:" << endl;
	Mat mapx = Mat(image_size, CV_32FC1);
	Mat mapy = Mat(image_size, CV_32FC1);

	//參數矩陣
	float neican_data[] = { 9558.649257742036, 0, 959.3165310990756, 0, 9435.752651759443, 532.7507141910969, 0, 0, 1 };
	Mat cameraMatrix(3, 3, CV_32FC1);
	InitMat(cameraMatrix, neican_data);

	cout << "cameraMatrix= " << endl << " " << cameraMatrix << endl << endl;
	//測得的畸變係數
	float jibian_data[] = { -6.956561513881647, -68.83902522804168, -0.004834538444671919, 0.01471273691928269, -0.4916103704308509 };
	Mat distCoeffs(1, 5, CV_32FC1); /* 攝像機的5個畸變係數:k1,k2,p1,p2,k3 */
	InitMat(distCoeffs, jibian_data);
	cout << "distCoeffs= " << endl << " " << distCoeffs << endl << endl;









	i = 0;
	namedWindow("【原始圖】", 0);//參數爲零,則能夠自由拖動
	imshow("【原始圖】", frame);
	/********相機矯正*******************************************************************************/

	initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, image_size, CV_32FC1, mapx, mapy);


	Mat imageSource = frame; //讀取畸變圖片
	Mat newimage = imageSource.clone(); //校訂後輸出圖片

	remap(imageSource, newimage, mapx, mapy, INTER_LINEAR);

	namedWindow("畸變校訂後的圖片", 0);//參數爲零,則能夠自由拖動
	imshow("畸變校訂後的圖片", newimage);
	}

上面只是矯正部分的代碼圖片

相關文章
相關標籤/搜索