opencv张正友标定法

在标定之前我要想一想为什么要标定,什么相机需要标定,标定的输入是啥,标定的输出是啥

标定的目的:为了求出相机的内参和外参,内参和外参就可以对之后相机拍出来的照片进行矫正,得到畸变很小的图片。

标定的输入:用相机拍出来一系列的棋盘格图片。

标定的输出:相机的内参和外参。

 

流程:

1,采集一系列棋盘格图;

2,对每一张图,提取其角点信息;

3,对每一张图,提取其亚像素角点信息;

4,对相机进行标定

5,查看标定结果,并对结果进行评价

6,利用标定结果对棋盘图进行矫正。

 

1,准备标定图片

标定图片需要标定板在不同位置、不同角度、不同姿态下拍摄,最少需要三张,以10-20张为宜。

2,对每一张照片,进行角点提取。

CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize, OutputArray corners,
                                         int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE );

image:传进来的棋盘格图

patternSize:一张图中角点的行列数,比方说棋盘格子大小是12*9,那么角点就有11*9个。vector<Point2f>

3,对每一张图,进一步提取亚像素角点信息。

为了进一步的提高精度,需要在初步提取的角点位置上进一步提取亚像素信息,降低标定的误差

CV_EXPORTS_W void cornerSubPix( InputArray image, InputOutputArray corners,
                                Size winSize, Size zeroZone,
                                TermCriteria criteria );

image:标定的棋盘图。

corners:一系列的初始的角点坐标,同时也作为亚像素坐标的输出,一般需要float型,或者double型。

winSize:搜索的半径。

zeroZone:死区的一半尺寸,死区为不对搜索区的中央位置做求和运算的区域。它是用来避免自相关矩阵出现某些可能的奇异性。当值为(-1,-1)是表示没有死区。

criteria:定义求角点的终止条件,可以是迭代次数和角点精度的组合。

 

CV_EXPORTS bool find4QuadCornerSubpix( InputArray img, InputOutputArray corners, Size region_size );

image:标定的棋盘图。

corners:一系列的初始的角点坐标,同时也作为亚像素坐标的输出,一般需要float型,或者double型。

region_size:搜索窗口的半径。

画图展示一下效果:

CV_EXPORTS_W void drawChessboardCorners( InputOutputArray image, Size patternSize,
                                         InputArray corners, bool patternWasFound );

                                                    

4,相机标定

获得一系列的图的角点信息之后就可以对相机进行标定了,然后计算相机的内参和外参。

CV_EXPORTS_W double calibrateCamera( InputArrayOfArrays objectPoints,
                                     InputArrayOfArrays imagePoints, Size imageSize,
                                     InputOutputArray cameraMatrix, InputOutputArray distCoeffs,
                                     OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
                                     int flags = 0, TermCriteria criteria = TermCriteria(
                                        TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON) );

objectPoints:世界坐标系中的三维点,棋盘格之间的真实坐标是知道的,我们认为他是在同一平面上的点                                                                    vector<vector<Point3f>> obj;

imagePoints:每一张图角点对用的坐标点 ;vector<vector<Point2f>> allCorners;

imageSize:图片的像素尺寸大小

cameraMatrix:相机的内参矩阵;Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0));

distCoeffs:畸变矩阵(外参);Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0))

rvecs:旋转向量;vector<Mat>rvecs;

tvecs:位移向量;vector<Mat>tvecs;

flag:标定所采取的方法;

            CV_CALIB_USE_INTRINSIC_GUESS:使用该参数时,在cameraMatrix矩阵中应该有fx,fy,u0,v0的估计值。否则的话,将初始化(u0,v0)图像的中心点,使用最小二乘估算出fx,fy。 
             CV_CALIB_FIX_PRINCIPAL_POINT:在进行优化时会固定光轴点。当CV_CALIB_USE_INTRINSIC_GUESS参数被设置,光轴点将保持在中心或者某个输入的值。 
             CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只将fy作为可变量,进行优化计算。当                                                               CV_CALIB_USE_INTRINSIC_GUESS没有被设置,fx和fy将会被忽略。只有fx/fy的比值在计算中会被用到。 
             CV_CALIB_ZERO_TANGENT_DIST:设定切向畸变参数(p1,p2)为零。 
             CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:对应的径向畸变在优化中保持不变。 
             CV_CALIB_RATIONAL_MODEL:计算k4,k5,k6三个畸变参数。如果没有设置,则只计算其它5个畸变参数。

criteria:迭代的终止条件;

5,对标定的结果进行评价

CV_EXPORTS_W void projectPoints( InputArray objectPoints,
                                 InputArray rvec, InputArray tvec,
                                 InputArray cameraMatrix, InputArray distCoeffs,
                                 OutputArray imagePoints,
                                 OutputArray jacobian = noArray(),
                                 double aspectRatio = 0 );

利用相机的内外参,对一幅图中的三维点重新投影计算,得到新的投影点

objectPoints:相机坐标系中的三维坐标
rvec, tvec:旋转矩阵和位移矩阵
cameraMatrix, distCoeffs:相机的内参矩阵和畸变矩阵
imagePoints:每个点对应图像中的坐标点

jacobian是雅可比行列式;

aspectRatio是跟相机传感器的感光单元有关的可选参数,如果设置为非0,则函数默认感光单元的dx/dy是固定的,会依此对雅可比矩阵进行调整;

6,查看标定的结果,并对棋盘图进行矫正

方法一:使用initUndistortRectifyMap和remap两个函数配合实现。

initUndistortRectifyMap用来计算畸变映射,remap把求得的映射应用到图像上。

initUndistortRectifyMap的函数原型:

方法二:使用undistort函数实现

CV_EXPORTS_W void undistort( InputArray src, OutputArray dst,
                             InputArray cameraMatrix,
                             InputArray distCoeffs,
                             InputArray newCameraMatrix = noArray() );

src:棋盘格图

dst:矫正好的棋盘格图

cameraMatrix:相机内参

distCoeffs:相机外参

srcMat和dstMat:

void CameraCalibrationZhang()
{
	int imageHeight, imageWidth;
	vector<String> leftStrings, rightStrings;
	vector<Mat> leftMats, rightMats;

	glob("C:\\Users\\Ring\\Desktop\\0420\\left\\", leftStrings);
	glob("C:\\Users\\Ring\\Desktop\\0420\\right\\", rightStrings);

	for (int i = 0; i < leftStrings.size(); i++)
	{
		leftMats.push_back(imread(leftStrings[i], 0));
		rightMats.push_back(imread(rightStrings[i], 0));
	}

	vector<vector<Point2f>> allCorners; allCorners.clear();
	int boardWidth = 11, boardHeight = 8;
	float squaresize = 25;										//棋盘格一格的实际大小是25mm

	for (int i = 0; i < leftMats.size(); i++)
	{
		Mat src = leftMats[i];
		//1,设置一些参数,比方说:棋盘格总共右多少个角点,棋盘格的真实大小啊,等等
		
		//2,寻找角点
		vector<Point2f> corners;
		bool isFind = findChessboardCorners(src, Size(boardWidth, boardHeight), corners);

		//3,寻找亚像素角点信息
		cornerSubPix(src, corners, Size(5, 5), Size(-1, -1), TermCriteria(1 + 2, 20, 0.1));
		//find4QuadCornerSubpix(src, corners, Size(5, 5));													//第二种方法
		allCorners.push_back(corners);

		/*Mat rgbMat;
		cvtColor(src, rgbMat, CV_GRAY2RGB);
		Mat showMat = rgbMat.clone();
		drawChessboardCorners(showMat, Size(boardWidth, boardHeight), corners, isFind);*/

		cout << "";
	}
	
	//4,相机标定
	vector<vector<Point3f>> obj;
	vector<Point3f> imgpoint;
	for (int rowIndex = 0; rowIndex < boardHeight; rowIndex++)
	{
		for (int colIndex = 0; colIndex < boardWidth; colIndex++)
		{
			imgpoint.push_back(Point3f(rowIndex * squaresize, colIndex * squaresize, 0));
		}
	}
	for (int imgIndex = 0; imgIndex < leftMats.size(); imgIndex++)
	{
		obj.push_back(imgpoint);
	}
	Mat cameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));
	Mat distCoeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));
	vector<Mat> rvecs, tvecs;
	calibrateCamera(obj, allCorners, leftMats[0].size(), cameraMatrix, distCoeffs, rvecs, tvecs);

	//5,对标定结果进行评价
	vector<Point2f> image_points2; /* 保存重新计算得到的投影点 */
	double total_err = 0.0; /* 所有图像的平均误差的总和 */
	double err = 0.0; /* 每幅图像的平均误差 */
	for (int i = 0; i<leftMats.size(); i++)
	{
		vector<Point3f> tempPointSet = obj[i];
		/* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */
		projectPoints(tempPointSet, rvecs[i], tvecs[i], cameraMatrix, distCoeffs, image_points2);
		/* 计算新的投影点和旧的投影点之间的误差*/
		vector<Point2f> tempImagePoint = allCorners[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);
		}
		double err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
		total_err += err /= 88;
		std::cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
		cout<< "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
	}


	//6,查看标定的结果,并对棋盘图进行矫正
	Mat srcMat = leftMats[0], dstMat;
	undistort(srcMat, dstMat, cameraMatrix, distCoeffs);

	cout << "";
}

 

  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
张正友标定(Zhang's camera calibration)是一种常用的相机标定方,用于确定相机的内部参数(内参)和外部参数(外参)。该方是由张正友教授在1999年提出的,被广泛应用于计算机视觉和机器人领域。 张正友标定使用特殊的标定板(一般是棋盘格)作为标定物体,在不同位置和姿态下拍摄多张图片。通过对这些图片进行处理,可以计算出相机的内参(如焦距、畸变参数等)和外参(如相机的旋转矩阵和平移向量)。这些参数可以帮助我们进行相机畸变校正、三维重建等任务。 具体实施该标定方时,需要先对标定板进行角点检测,然后通过求解相机的投影变换关系,从而得到相机的内外参数。标定板上的角点在不同位置和姿态下的映射关系提供了用于求解的约束条件。通常,至少需要拍摄10-20张不同姿态的图片才能进行有效的标定。 在OpenCV中,可以使用函数`cv2.calibrateCamera()`来实现张正友标定。该函数可以根据提供的标定板图片和角点信息,计算出相机的内外参数。通过使用这些参数,可以对相机图像进行畸变校正,使得图像中的直线保持直线,提高图像处理的准确性。 总结来说,张正友标定是一种常用的相机标定方,通过拍摄多张标定板图片,可以计算出相机的内外参数,从而帮助我们进行相机畸变校正和三维重建等任务。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值