【opencv 学习笔记】使用cv库API进行相机标定 棋盘格 计算内外参、重投影误差

4 篇文章 0 订阅

参考文章:

https://blog.csdn.net/Yep_Ying/article/details/105372287

https://blog.csdn.net/LuohenYJ/article/details/104697062

https://blog.csdn.net/u010801994/article/details/84563208

标定板图片:

https://gitee.com/chengwen12333/opencv-camera-calibration_retest/tree/master/

 

 

//calibration.cpp
#include <iostream>
#include <sstream>
#include <time.h>
#include <stdio.h>
#include <fstream>

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

using namespace cv;
using namespace std;
#define calibration
int CHECKERBOARD[2]{ 15,19 };

int main()
{
	ifstream fin("right_img.txt");             /* 标定所用图像文件的路径 */
	ofstream fout("caliberation_result_right.txt");  /* 保存标定结果的文件 */

													 // 读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化
	Size image_size;      /* 图像的尺寸 */
	Size board_size = Size(19, 15);             /* 标定板上每行、列的角点数 */
	vector<Point2f> image_points_buf;         /* 缓存每幅图像上检测到的角点 */
	vector<vector<Point2f>> image_points_seq; /* 保存检测到的所有角点 */
	string filename;      // 图片名
	vector<string> filenames;
	std::string path = "./image/*.bmp";
	std::vector<cv::String> images;
	cv::glob(path, images);
	cv::Mat frame, gray;
	double Kernelsize_cali[2]{3.0, 3.0};//标定板的宽度例如我们使用的是3.0mm*3.0mm

	std::vector<std::vector<cv::Point3f> > objpoints;  //真实的坐标点,通过横轴第一个点相对应起来的

	std::vector<cv::Point3f> objp;
	for (int i{ 0 }; i < CHECKERBOARD[1]; i++)
	{
		for (int j{ 0 }; j < CHECKERBOARD[0]; j++)
		{
			objp.push_back(cv::Point3f(j * Kernelsize_cali[0], i * Kernelsize_cali[1], 0));
			cout << "(x,y,z):(" << j << "," << i << "," << 0 << ")" << endl;
		}
		cout << "one success!" << endl;

	}

	for (int i{ 0 }; i < images.size(); i++)
	{
		bool success;
		frame = cv::imread(images[i]);
		image_size = cv::Size(frame.rows,frame.cols);
		if (frame.empty())
		{
			continue;
		}
		if (i == 40)
		{
			int b = 1;
		}
		cout << "the current image is " << i << "th" << endl;
		cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);
		//交点提取
		success = cv::findChessboardCorners(gray, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), image_points_buf, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE);
		if (success)
		{
			// 如果是OpenCV4以下版本,第一个参数为CV_TERMCRIT_EPS | CV_TERMCRIT_ITER
			cv::TermCriteria criteria(TermCriteria::EPS | TermCriteria::Type::MAX_ITER, 30, 0.001);

			// refining pixel coordinates for given 2d points.
			// 为给定的二维点细化像素坐标
			cv::cornerSubPix(gray, image_points_buf, cv::Size(11, 11), cv::Size(-1, -1), criteria);

			// Displaying the detected corner points on the checker board
			// 在棋盘上显示检测到的角点
			cv::drawChessboardCorners(frame, cv::Size(CHECKERBOARD[0], CHECKERBOARD[1]), image_points_buf, success);

			objpoints.push_back(objp);
			image_points_seq.push_back(image_points_buf);

		}
		else
		{
			cout << "some errors happened in cornerSubPix" << endl;
		}

	}
	int CornerNum = board_size.width * board_size.height;  // 每张图片上总的角点数

														   //-------------以下是摄像机标定------------------

														   /*棋盘三维信息*/
											 /*内外参数*/
	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;      /* 每幅图像的平移向量 */

	/* 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 */
	for (int16_t i = 0; i<images.size(); i++)
	{
		point_counts.push_back(board_size.width * board_size.height);
	}

	/* 开始标定 */
	// object_points 世界坐标系中的角点的三维坐标
	// image_points_seq 每一个内角点对应的图像坐标点
	// image_size 图像的像素尺寸大小
	// cameraMatrix 输出,内参矩阵
	// distCoeffs 输出,畸变系数
	// rvecsMat 输出,旋转向量
	// tvecsMat 输出,位移向量
	// 0 标定时所采用的算法
	
	double error = calibrateCamera(objpoints, image_points_seq, image_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat);

	//------------------------标定完成------------------------------------

	// -------------------对标定结果进行评价------------------------------

	double total_err = 0.0;         /* 所有图像的平均误差的总和 */
	double err = 0.0;               /* 每幅图像的平均误差 */
	vector<Point2f> image_points2;  /* 保存重新计算得到的投影点 */
	fout << "每幅图像的标定误差:\n";

	for (int i = 0; i<images.size(); i++)
	{
		vector<Point3f> tempPointSet = objpoints[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);
		}
		//求真实角点像素和重投影像素坐标两者之间的L2范数
		err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
		total_err += err /= point_counts[i];
		fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
	}
	fout << "总体平均误差:" << total_err / images.size() << "像素" << endl << 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<images.size(); 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;
	}
	fout << endl;

	//--------------------标定结果保存结束-------------------------------

	//----------------------显示定标结果--------------------------------

	Mat mapx = Mat(image_size, CV_32FC1);
	Mat mapy = Mat(image_size, CV_32FC1);
	Mat R = Mat::eye(3, 3, CV_32F);
	string imageFileName;
	std::stringstream StrStm;
	for (int i = 0; i <images.size(); i++)
	{
		initUndistortRectifyMap(cameraMatrix, distCoeffs, R, cameraMatrix, image_size, CV_32FC1, mapx, mapy);
		Mat imageSource = imread(images[i]);
		Mat newimage = imageSource.clone();
		remap(imageSource, newimage, mapx, mapy, INTER_LINEAR);
		StrStm.clear();
		imageFileName.clear();
		StrStm << i + 1;
		StrStm >> imageFileName;
		imageFileName += "_d.jpg";
		imwrite(imageFileName, newimage);
	}

	fin.close();
	fout.close();
}

结果:

每幅图像的标定误差:
第1幅图像的平均误差:0.00901338像素
第2幅图像的平均误差:0.00533954像素
第3幅图像的平均误差:0.00476242像素
第4幅图像的平均误差:0.00374578像素
第5幅图像的平均误差:0.00879981像素
第6幅图像的平均误差:0.00560517像素
第7幅图像的平均误差:0.0048039像素
第8幅图像的平均误差:0.00378456像素
第9幅图像的平均误差:0.0072627像素
第10幅图像的平均误差:0.0209396像素
第11幅图像的平均误差:0.00830864像素
第12幅图像的平均误差:0.0147205像素
第13幅图像的平均误差:0.00571387像素
第14幅图像的平均误差:0.00401392像素
第15幅图像的平均误差:0.0033231像素
第16幅图像的平均误差:0.00789049像素
第17幅图像的平均误差:0.00564339像素
第18幅图像的平均误差:0.0322107像素
第19幅图像的平均误差:0.0158545像素
总体平均误差:0.00903874像素

相机内参数矩阵:
[7790.06882421712, 0, 1895.040592745665;
 0, 7788.143662816939, 1323.443925848088;
 0, 0, 1]

畸变系数:
[-0.2092971729778142, 0.336140706232359, 0.001085659642471452, 9.149787693839918e-05, -1.611656058352493]


第1幅图像的旋转向量:
[40.8918494376741;
 -53.12622768474755;
 367.5214753662501]
第1幅图像的旋转矩阵:
[-0.9419550181705912, -0.2867565577707702, 0.1746179266819421;
 0.2255682887967951, -0.9257561113539244, -0.3034708707275642;
 0.2486758750512941, -0.246467642635538, 0.936703800729533]
第1幅图像的平移向量:
[0.03433162173006149;
 0.02104546720082031;
 -0.006740858378163532]

第2幅图像的旋转向量:
[49.38790343037014;
 58.79629337775193;
 373.380359556444]
第2幅图像的旋转矩阵:
[-0.462552768099424, 0.8849393913807501, 0.05410369956138322;
 -0.8254867236753823, -0.4521331944980534, 0.337856838718219;
 0.323444903745976, 0.1116147302807895, 0.9396411795068925]
第2幅图像的平移向量:
[0.06478033897060728;
 -0.03537319776132106;
 3.135868025933476]

第3幅图像的旋转向量:
[-40.72924223441478;
 4.219756463606606;
 372.4960595356939]
第3幅图像的旋转矩阵:
[-0.6098774883460968, 0.770658466513109, -0.184756534932651;
 -0.7746461202946803, -0.6289154071185618, -0.06624801130066736;
 -0.1672508221834984, 0.1027177622446918, 0.9805489400322588]
第3幅图像的平移向量:
[0.04960036043663845;
 0.0380938754767744;
 0.01236001521062122]

第4幅图像的旋转向量:
[-81.3082068969731;
 5.047258616446007;
 373.0192910876365]
第4幅图像的旋转矩阵:
[0.1480818586780773, 0.9687872873034982, -0.1988038105509432;
 -0.9738115890528618, 0.1077686090391296, -0.2001921974805996;
 -0.1725188458018025, 0.223242287358154, 0.9593748636368963]
第4幅图像的平移向量:
[0.03589759585524415;
 -0.009008555690961151;
 0.03899724009689464]

第5幅图像的旋转向量:
[4.186823721461959;
 21.21229449626194;
 356.6987145589036]
第5幅图像的旋转矩阵:
[0.7052011835105583, 0.707953526543744, -0.03864058785246931;
 -0.7075434246271584, 0.7061995896621888, 0.0257767691503226;
 0.04553672190859445, 0.009162085746856759, 0.9989206490720806]
第5幅图像的平移向量:
[-0.05449702750792344;
 0.438658356217181;
 3.02101358594354]

第6幅图像的旋转向量:
[-28.77422347503968;
 9.457474027420234;
 365.3001293971984]
第6幅图像的旋转矩阵:
[-0.520161777522312, -0.8484525650055816, -0.09777509979681961;
 0.8422566147263324, -0.528569082209244, 0.1059175164088352;
 -0.1415468832386192, -0.0272574809534302, 0.9895562185027722]
第6幅图像的平移向量:
[-0.06780459578860312;
 0.1727267622396768;
 3.115861816364419]

第7幅图像的旋转向量:
[73.21306420941193;
 9.894926498059107;
 372.8154105260532]
第7幅图像的旋转矩阵:
[-0.9235775645189886, -0.05636482692167775, 0.3792459473788254;
 0.07640250160168259, -0.9963533807298017, 0.03798155417172588;
 0.3757221580718636, 0.06405425040061097, 0.9245160425538531]
第7幅图像的平移向量:
[-0.01785152736152136;
 -0.1034868066811129;
 3.087003907106642]

第8幅图像的旋转向量:
[78.69464425820962;
 37.79976832018529;
 372.3706951835734]
第8幅图像的旋转矩阵:
[0.7050520522746134, 0.7090899243324834, -0.009647942449488911;
 -0.6965640974065993, 0.6950217022820915, 0.1781664714839282;
 0.1330415791678876, -0.1188962260421109, 0.983953060692154]
第8幅图像的平移向量:
[-0.0653629413630966;
 -0.2084500992684071;
 3.052201756483122]

第9幅图像的旋转向量:
[41.880408908027;
 21.99217504844562;
 361.4664557735976]
第9幅图像的旋转矩阵:
[0.9924510244240694, -0.1223591258812917, 0.008319160605586932;
 0.1224651626426396, 0.9923779007396059, -0.01372538023389374;
 -0.006576325609880401, 0.01464057503035115, 0.9998711944566927]
第9幅图像的平移向量:
[0.003609429338698677;
 0.237128680259337;
 2.963687627172932]

第10幅图像的旋转向量:
[40.99623783961949;
 23.01326647613388;
 372.5362487629638]
第10幅图像的旋转矩阵:
[0.0817862111431345, 0.9958657757830904, 0.03952685531121414;
 -0.9834290847235396, 0.07419941356458071, 0.1654136703736819;
 0.161796943687671, -0.05240041651184994, 0.9854318572903521]
第10幅图像的平移向量:
[-0.2564790587356458;
 -0.1943438525491485;
 2.992207613804558]

第11幅图像的旋转向量:
[1.543402240315645;
 -23.28828645378324;
 357.6316661977772]
第11幅图像的旋转矩阵:
[0.9686976144290699, -0.2477277636046903, -0.01599646644880712;
 0.2477102443959335, 0.9688292069436177, -0.003098805303290942;
 0.01626550401112861, -0.0009606833086272723, 0.9998672464216659]
第11幅图像的平移向量:
[0.2624364670677614;
 0.09361841325521592;
 0.01666918497552899]

第12幅图像的旋转向量:
[7.764363810557446;
 -54.5231805402692;
 369.6353968062355]
第12幅图像的旋转矩阵:
[-0.9901929530292285, -0.1380513398579433, 0.02144162621357531;
 0.1259810648712392, -0.9486722372298325, -0.2900857762856584;
 0.06038780559984505, -0.2845396525491149, 0.9567604188416574]
第12幅图像的平移向量:
[0.02339711249059918;
 0.01758921411642734;
 0.01668696521933025]

第13幅图像的旋转向量:
[-23.14943860211223;
 -55.79609570733787;
 374.8883623442727]
第13幅图像的旋转矩阵:
[-0.9105426680900612, -0.3751052385965438, -0.1738048030546408;
 0.4094619677060464, -0.8762655873686597, -0.2539675518560402;
 -0.05703460870142898, -0.3024147489308149, 0.9514685349707512]
第13幅图像的平移向量:
[0.01672892216971765;
 0.05051227020869364;
 0.01639301274470696]

第14幅图像的旋转向量:
[-65.93936763239849;
 -55.3727394136339;
 374.7237937527545]
第14幅图像的旋转矩阵:
[0.367459244831695, -0.8974815827567413, -0.2439272678487892;
 0.9296741516128715, 0.3618082433210063, 0.0692875666180813;
 0.02607058132470423, -0.2522332326982747, 0.9673152129021698]
第14幅图像的平移向量:
[-0.001139151875071493;
 0.06435273518035893;
 0.03260463159706262]

第15幅图像的旋转向量:
[-79.07956895548273;
 -56.42907586094473;
 375.1819088729843]
第15幅图像的旋转矩阵:
[-0.3588975990209751, 0.9215988169397343, -0.1478111431261583;
 -0.8373458417000329, -0.3878732153291962, -0.3852353958527887;
 -0.4123644684070672, -0.0144910125758437, 0.9109037027863507]
第15幅图像的平移向量:
[0.0008842283217561599;
 0.05175967201470351;
 0.05350211095324433]

第16幅图像的旋转向量:
[-29.32468181664178;
 -15.73852789835761;
 371.149509602206]
第16幅图像的旋转矩阵:
[-0.3438673702225552, -0.927674466896225, -0.145517405024379;
 0.9366633515190242, -0.3498294398829387, 0.01676689930603002;
 -0.06646049666812134, -0.1305352307233351, 0.9892136048006182]
第16幅图像的平移向量:
[0.07041492869789512;
 -0.02847961794634448;
 1.608262252099988]

第17幅图像的旋转向量:
[18.04923159125307;
 -9.675169259273686;
 370.7762376837953]
第17幅图像的旋转矩阵:
[0.8064649824927742, -0.591251339522583, -0.006007122912088088;
 0.5907599935091675, 0.8061383656992961, -0.03381664407826974;
 0.0248367083563409, 0.02372317138199226, 0.999410000479084]
第17幅图像的平移向量:
[0.0635535276346698;
 -0.02364556431953348;
 1.572249817738989]

第18幅图像的旋转向量:
[84.10349533298945;
 -14.09704306246227;
 370.4070104255048]
第18幅图像的旋转矩阵:
[-0.9014626833866448, -0.05152301504180909, 0.4297794892527461;
 0.01870797877284083, -0.996600407802577, -0.08023489700854643;
 0.4324523580599194, -0.06428845999663259, 0.8993619693536511]
第18幅图像的平移向量:
[0.07736738691408115;
 0.02528168231666154;
 1.565945065085318]

第19幅图像的旋转向量:
[83.79642626826502;
 57.62548513800356;
 366.3211104174334]
第19幅图像的旋转矩阵:
[-0.9019485081745429, 0.1083629795875823, 0.4180267374901011;
 0.02521310525661043, -0.9531396321958451, 0.3014782593503028;
 0.4311069332834207, 0.2824576184004788, 0.8569507021308136]
第19幅图像的平移向量:
[0.1180513026060211;
 0.02745343745277672;
 3.12331841573161]

 

 

 

 

 

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值