opencv相机标定(2)-单目相机标定流程

常用的标定函数和流程,网上一大堆,这里就不想详细写了
这里说一下标定后常见的问题和我自己的一些做法。

1.标定后丢失部分像素信息

畸变校正后,边缘处出现一些黑色像素区域,其实也算是正常的,图片去畸变后补充的像素

可以用initUndistortRectifyMap,传递新的相机参数矩阵得到新的mapx,mapy来解决。代码如下

Mat NewCameraMatrix = cameraMatrix.clone();
double scale = 0.9;//比例系数
NewCameraMatrix.at<double>(0, 0) = NewCameraMatrix.at<double>(0, 0) / scale;
NewCameraMatrix.at<double>(1, 1) = NewCameraMatrix.at<double>(1, 1) / scale;
initUndistortRectifyMap(cameraMatrix, distCoeffs, Matx33d::eye(), NewCameraMatrix, PicSize, CV_32FC1, mapx, mapy);
for (int i = 0; i < files.size(); i++)
{
	img = imread(files.at(i));
	remap(img, showimg, mapx, mapy, INTER_LINEAR, BORDER_CONSTANT);
	imshow("img", showimg);
	waitKey(0);
}

个人理解,修改fx,fy相当于修改了相机成像模型中最后一步,相机物理坐标系->相机像素坐标系这一步,因此看起来的效果实际只是比例缩放。效果图(图像分辨率不变)如下

2.调整世界坐标系方向与图像方向一致

这种需求相当于相机安装不正的情形吧,效果上相当于使棋盘图片方向与图像方向一致。这部分还没办法完全理解,暂时先放下了

3.调整世界坐标原点

这个主要是一些工业应用的方便性,例如直接在图像作矢量图,直接切割使切的位置就是矢量图在图像上显示的位置。这部分应该用仿射变换就行了。暂时没做。
相机标定流程和常用的算子代码如下:

#include "LearnBasic.h"
#include "stdio.h"
#include 
#include 
#include 

void listFiles(string path, vector<string>& files)
{
	//文件句柄
	intptr_t hFile = 0;
	//文件信息
	struct _finddata_t fileinfo;
	string p;
	if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
	{
		do
		{
			//如果是目录,迭代之
			//如果不是,加入列表
			if ((fileinfo.attrib &  _A_SUBDIR))
			{
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
					listFiles(p.assign(path).append("\\").append(fileinfo.name), files);
			}
			else
			{
				//cout << fileinfo.name << endl;
				files.push_back(p.assign(path).append("\\").append(fileinfo.name));
			}
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}
}

void Calibration()
{
	//读取文件夹下的图片
	String path = "C:\\zyk\\calibration";
	vector<string> files;
	listFiles(path, files);

	//基本参数
	int Rownum = 7;
	int Colnum = 9;
	double space = 15.0;
	Size PicSize(2592, 1944);
	vector<vector<Point2f>> ImgPoints;
	vector<vector<Point3f>> WorldPoints;
	vector<cv::Point2f> ImgPoint;
	vector<Point3f> WorldPoint;
	Mat	   cameraMatrix;//相机内参,最好不要指定类型,否则某些算子类型不符合的话报错
	Mat    distCoeffs;//相机畸变系数
	vector<Mat> rvecs;//旋转矢量
	vector<Mat> tvecs;//偏移矢量

	//构建棋盘坐标
	int NPoints = Rownum * Colnum;
	for (int j = 0; j < NPoints; j++)
	{
		WorldPoint.push_back(Point3f((j % Colnum) * space, (j / Colnum) * space, 0));//x,y,z
	}
	//检测所有图片棋盘角点
	Mat img, img_Gray, showimg;
	namedWindow("img", WINDOW_NORMAL);
	for (int i = 0; i < files.size(); i++)
	{
		img = imread(files.at(i));
		cvtColor(img, img_Gray, COLOR_BGR2GRAY);
		if (cv::findChessboardCorners(img_Gray, cv::Size(Colnum, Rownum), ImgPoint, 0) == 0)
		{
			cout << "drop out:" << files.at(i) << endl;
			continue;
		}
		WorldPoints.push_back(WorldPoint);
		cornerSubPix(img_Gray, ImgPoint, cv::Size(3, 3), cv::Size(-1, -1),
			cv::TermCriteria(cv::TermCriteria::MAX_ITER + TermCriteria::EPS, 30, 0.1));
		ImgPoints.push_back(ImgPoint);
		drawChessboardCorners(img, cv::Size(Colnum, Rownum), ImgPoint, true);
		// 		imshow("img", img);
		// 		waitKey(0);
	}
	double err = cv::calibrateCamera(WorldPoints, ImgPoints, PicSize,
		cameraMatrix, distCoeffs, rvecs, tvecs
	/*,CALIB_THIN_PRISM_MODEL*/);//flag不设置,只计算五个畸变系数。其他相机模型要详细研究flag参数

	//通过得到的摄像机内外参数,对角点的空间三维坐标进行重新投影计算
	cout << "cameraMatrix:" << cameraMatrix << endl;
	cout << "distCoeffs:" << distCoeffs << endl;
	cout << "标定误差:" << err << endl;

	vector<Point2f> prj_points, undistort_points;
	for (int i = 0; i < files.size(); i++)
	{
		img = imread(files.at(i));
		showimg = img.clone();
		projectPoints(WorldPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, prj_points);//世界坐标->图像坐标点(畸变)
		drawChessboardCorners(showimg, cv::Size(Colnum, Rownum), prj_points, true);
		imshow("img", showimg);
		waitKey(0);

		undistort(img, showimg, cameraMatrix, distCoeffs, cameraMatrix);//图像畸变校正
		undistortPoints(ImgPoints[i], undistort_points, cameraMatrix, distCoeffs, noArray(), cameraMatrix);//畸变坐标点->无畸变坐标点
		drawChessboardCorners(showimg, cv::Size(Colnum, Rownum), undistort_points, true);
		imshow("img", showimg);
		waitKey(0);
	}
	//另一种畸变校正图像方式,undistort内部好像每次也是调用initUndistortRectifyMap和remap,效率不高
	Mat mapx, mapy;
	initUndistortRectifyMap(cameraMatrix, distCoeffs, Matx33d::eye(), cameraMatrix, PicSize, CV_32FC1, mapx, mapy);

	for (int i = 0; i < files.size(); i++)
	{
		img = imread(files.at(i));
		remap(img, showimg, mapx, mapy, INTER_LINEAR, BORDER_CONSTANT);
		imshow("img", showimg);
		waitKey(0);
	}
	//显示优化、调整
	//图像畸变校正后,边缘容易一些黑色像素,不美观
	//可以虚拟修改相机fx,fy参数,即相当于使用了一个更小或更大的靶面尺寸
	Mat NewCameraMatrix = cameraMatrix.clone();
	double scale = 0.95;//比例系数
	NewCameraMatrix.at<double>(0, 0) = NewCameraMatrix.at<double>(0, 0) / scale;
	NewCameraMatrix.at<double>(1, 1) = NewCameraMatrix.at<double>(1, 1) / scale;
	initUndistortRectifyMap(cameraMatrix, distCoeffs, Matx33d::eye(), NewCameraMatrix, PicSize, CV_32FC1, mapx, mapy);
	for (int i = 0; i < files.size(); i++)
	{
		img = imread(files.at(i));
		remap(img, showimg, mapx, mapy, INTER_LINEAR, BORDER_CONSTANT);
		imshow("img", showimg);
		waitKey(0);
	}
	//若想最终得到的图像方向跟世界坐标系方向一致,可以加入旋转矩阵
	Mat RotateMtx;
	NewCameraMatrix = cameraMatrix.clone();
	for (int i = 0; i < files.size(); i++)
	{
		//这部分可能理解有问题,旋转矢量这个还不太明白,为什么要负方向
		Rodrigues(-rvecs[i], RotateMtx);
		initUndistortRectifyMap(cameraMatrix, distCoeffs, RotateMtx, cameraMatrix, PicSize, CV_32FC1, mapx, mapy);
		img = imread(files.at(i));
		remap(img, showimg, mapx, mapy, INTER_LINEAR, BORDER_CONSTANT);
		imshow("img", showimg);
		waitKey(0);  
	}
	cv::destroyWindow("img");
}

你可能感兴趣的:(opencv)