Opencv相机校准之棋盘格标定

1.图像畸变

相机成像可以分为四个步骤:刚体变换、透视投影、畸变校正和数字化图像。
在这里插入图片描述
相机的图像有时候会出现畸变严重现象,畸变指真实成像点与理想成像点间的偏移,产生原因是镜头工艺的不完美,从而导致了不规则的折射。
在这里插入图片描述
修正图像需要相机两种参数:

  • 相机的内部参数。例如镜头的焦距,光学中心和径向畸变系数。
  • 外部参数:这是指摄像机相对于某些世界坐标系的方向(旋转和平移)

通过校准可以改善畸变,图像不失真,接近真实图像,另外,还可以确定相机的自然单位(像素)与实际单位之间的关系(例如毫米),这样标定后就知道图像内物体的大小尺寸。

2.相机校准的流程步骤

  • 步骤1:采集棋盘格图像(10张以上),并预处理
  • 步骤2:找出棋盘格角点坐标
  • 步骤3:进一步提取亚像素角点信息
  • 步骤4:计算出相机内参数矩阵和畸变系数
  • 步骤5:畸变图像校准

步骤1:采集棋盘格图像(10张以上),并预处理

对于采集图像,方法是手里拿着A4纸打印的棋盘格,对着相机变换不同的方角度,采集十张以上;或者棋盘格放到桌上,拿着相机从不同角度一通拍摄。
为了演示这里用手机拍了十五张:
在这里插入图片描述
每张照片是3968x2976(宽x高),先进行下预处理把尺寸压缩四倍:

#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;

Mat image;
int main()
{
    
    
	vector<String> images_path;//创建容器存放读取图像路径
	string image_path = "D:/work_c++/image/chessboardimage/*.jpg";//待处理图路径
	
	glob(image_path, images_path);//读取指定文件夹下图像
	char saveimage[100];
	for (int i = 0; i < images_path.size(); i++)
	{
    
    
		image = imread(images_path[i]);
		resize(image, image, Size(992, 744));//调整大小
		sprintf_s(saveimage, "D:/work_c++/image/temp/%d.jpg", i + 1);//数据格式化输出到字符串,图像存储路径
		imwrite(saveimage, image);//保存
	}

    return 0;
}

结果:
在这里插入图片描述

步骤2:找出棋盘格角点坐标

这里棋盘格每行每列的内角数是(5,7)
在这里插入图片描述
主要函数:

  • findChessboardCorners函数
//! finds checkerboard pattern of the specified size in the image  
CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize,  
                                         OutputArray corners,  
                                         int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE );

/*
第一个参数Image,传入拍摄的棋盘图Mat图像,必须是8位的灰度或者彩色图像;

第二个参数patternSize,每个棋盘图上内角点的行列数,一般情况下,行列数不要相同,便于后续标定程序识别标定板的方向;

第三个参数corners,用于存储检测到的内角点图像坐标位置,一般用元素是Point2f的向量来表示:vector<Point2f> image_points_buf;

第四个参数flage:用于定义棋盘图上内角点查找的不同处理方式,有默认值。

*/

步骤3:进一步提取亚像素角点信息

主要函数:

  • cornerSubPix函数
//! adjusts the corner locations with sub-pixel accuracy to maximize the certain cornerness criteria
CV_EXPORTS_W void cornerSubPix( InputArray image, InputOutputArray corners,
                                Size winSize, Size zeroZone,
                                TermCriteria criteria );

/*
第一个参数image,输入的Mat矩阵,最好是8位灰度图像,检测效率更高;

第二个参数corners,初始的角点坐标向量,同时作为亚像素坐标位置的输出,所以需要是浮点型数据,一般用元素是Pointf2f/Point2d的向量来表示:vector<Point2f/Point2d> iamgePointsBuf;

第三个参数winSize,大小为搜索窗口的一半;

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

第五个参数criteria,定义求角点的迭代过程的终止条件,可以为迭代次数和角点精度两者的组合;

*/

步骤4:计算出相机内参数矩阵和畸变系数

主要函数:

  • calibrateCamera函数
//! finds intrinsic and extrinsic camera parameters from several fews of a known calibration pattern.  
CV_EXPORTS_W double calibrateCamera( InputArrayOfArrays objectPoints,  
                                     InputArrayOfArrays imagePoints,  
                                     Size imageSize,  
                                     CV_OUT InputOutputArray cameraMatrix,  
                                     CV_OUT InputOutputArray distCoeffs,  
                                     OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,  
                                     int flags=0, TermCriteria criteria = TermCriteria(  
                                         TermCriteria::COUNT+TermCriteria::EPS, 30, DBL_EPSILON) 
 
/*
第一个参数objectPoints,为世界坐标系中的三维点。在使用时,应该输入一个三维坐标点的向量的向量,即vector<vector<Point3f>> object_points。需要依据棋盘上单个黑白矩阵的大小,计算出(初始化)每一个内角点的世界坐标。

第二个参数imagePoints,为每一个内角点对应的图像坐标点。和objectPoints一样,应该输入vector<vector<Point2f>> image_points_seq形式的变量;

第三个参数imageSize,为图像的像素尺寸大小,在计算相机的内参和畸变矩阵时需要使用到该参数;

第四个参数cameraMatrix为相机的内参矩阵。输入一个Mat cameraMatrix即可,如Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0));

第五个参数distCoeffs为畸变矩阵。输入一个Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0))即可;

第六个参数rvecs为旋转向量;应该输入一个Mat类型的vector,即vector<Mat>rvecs;

第七个参数tvecs为位移向量,和rvecs一样,应该为vector<Mat> tvecs;

第八个参数flags为标定时所采用的算法。有如下几个参数:

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是最优迭代终止条件设定。

在使用该函数进行标定运算之前,需要对棋盘上每一个内角点的空间坐标系的位置坐标进行初始化,标定的结果是生成相机的内参矩阵cameraMatrix、相机的5个畸变系数distCoeffs,另外每张图像都会生成属于自己的平移向量和旋转向量。
*/                                        

步骤5:畸变图像校准

主要函数:

  • undistort函数
//! corrects lens distortion for the given camera matrix and distortion coefficients  
CV_EXPORTS_W void undistort( InputArray src, OutputArray dst,  
                             InputArray cameraMatrix,  
                             InputArray distCoeffs,  
                             InputArray newCameraMatrix=noArray() ); 

/*
第一个参数src,输入参数,代表畸变的原始图像;

第二个参数dst,矫正后的输出图像,跟输入图像具有相同的类型和大小;

第三个参数cameraMatrix为之前求得的相机的内参矩阵;

第四个参数distCoeffs为之前求得的相机畸变矩阵;

第五个参数newCameraMatrix,默认跟cameraMatrix保持一致;

方法一相比方法二执行效率更高一些,推荐使用
*/

3.代码实现

环境:

  • OpenCV3.4.1
  • vs2015
// CheckerBoardDemo.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;

Mat image,img_gray;
int BOARDSIZE[2]{
    
     5,7 };//棋盘格每行每列角点个数
int main()
{
    
    
	vector<vector<Point3f>> objpoints_img;//保存棋盘格上角点的三维坐标
	vector<Point3f> obj_world_pts;//三维世界坐标
	vector<vector<Point2f>> images_points;//保存所有角点
	vector<Point2f> img_corner_points;//保存每张图检测到的角点
	vector<String> images_path;//创建容器存放读取图像路径

	string image_path = "D:/work_c++/image/temp/*.jpg";//待处理图路径	
	glob(image_path, images_path);//读取指定文件夹下图像

	//转世界坐标系
	for (int i = 0; i < BOARDSIZE[1]; i++)
	{
    
    
		for (int j = 0; j < BOARDSIZE[0]; j++)
		{
    
    
			obj_world_pts.push_back(Point3f(j, i, 0));
		}
	}

	for (int i = 0; i < images_path.size(); i++)
	{
    
    
		image = imread(images_path[i]);
		cvtColor(image, img_gray, COLOR_BGR2GRAY);
		//检测角点
		bool found_success = findChessboardCorners(img_gray, Size(BOARDSIZE[0], BOARDSIZE[1]), 
			img_corner_points,
			CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FAST_CHECK | CV_CALIB_CB_NORMALIZE_IMAGE);
		
		//显示角点
		if (found_success)
		{
    
    
			 //迭代终止条件
			 TermCriteria criteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 30, 0.001);

			 //进一步提取亚像素角点
			 cornerSubPix(img_gray, img_corner_points, Size(11, 11),
				 Size(-1, -1), criteria);

			 //绘制角点
			 drawChessboardCorners(image, Size(BOARDSIZE[0], BOARDSIZE[1]),img_corner_points,
				 found_success);

			 objpoints_img.push_back(obj_world_pts);//从世界坐标系到相机坐标系
			 images_points.push_back(img_corner_points);
		}
		char *output = "image";
		imshow(output, image);
		waitKey(200);
	}
	
	/*
	计算内参和畸变系数等
	*/

	Mat cameraMatrix, distCoeffs, R, T;//内参矩阵,畸变系数,旋转量,偏移量
	calibrateCamera(objpoints_img, images_points, img_gray.size(), 
		cameraMatrix, distCoeffs, R, T);

	cout << "cameraMatrix:" << endl;
	cout << cameraMatrix << endl;

	cout << "*****************************" << endl;
	cout << "distCoeffs:" << endl;
	cout <<  distCoeffs << endl;
	cout << "*****************************" << endl;

	cout << "Rotation vector:" << endl;
	cout << R << endl;

	cout << "*****************************" << endl;
	cout << "Translation vector:" << endl;
	cout << T << endl;


	/*
	畸变图像校准
	*/
	Mat src, dst;
	src = imread("D:/work_c++/image/test/test.jpg");
	undistort(src, dst, cameraMatrix, distCoeffs);
	
	char *dst_output = "image_dst";
	imshow(dst_output, dst);
	waitKey(100);
	imwrite("D:/work_c++/image/test/result.jpg", dst);
	
	destroyAllWindows();//销毁显示窗口
	system("pause");
    return 0;
}


4. 结果

4.1 检测角点效果

在这里插入图片描述

4.2 内参,畸变系数结果

在这里插入图片描述

4.3 畸变图校准测试结果

测试原图:
在这里插入图片描述
测试结果图:
在这里插入图片描述
注:用手机拍的棋盘格图,在打印棋盘格时,勾选了适应A4纸张,没按实际大小打印,导致棋盘格不是方正的格子,一边3.5cm,一边3.7cm,没有重新制作打印,仅用来测试一下。
另外,这里以一个方格为单位,没有考虑单位长度,若要计算实际的参数,需要乘单位长度。
如程序中改变:

	//转世界坐标系
	realsize = Size(x,y);//测得实际真实尺寸
	for (int i = 0; i < BOARDSIZE[1]; i++)
	{
    
    
		for (int j = 0; j < BOARDSIZE[0]; j++)
		{
    
    
			obj_world_pts.push_back(Point3f(j*realsie.with, i*realsize.height, 0));
		}
	}
//与计算内参矩阵无关,与计算外参旋转、平移量有关

目前工业相机基本上畸变都非常小,厂家已经做了很多畸变影响消除,用户不用特地去做畸变矫正,一般是便宜的网络摄像头畸变特别大,需要做畸变矫正处理。

Reference:
https://yq.aliyun.com/articles/62472
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/calib3d/camera_calibration_square_chess/camera_calibration_square_chess.html
https://blog.csdn.net/Crystal_YS/article/details/86582414

猜你喜欢

转载自blog.csdn.net/y459541195/article/details/104544956