opencv单目相机标定(源码+讲解)

//闲话先不说,直接上一段完整源码

#include "opencv2/core.hpp"
#include 
#include "opencv2/imgproc.hpp"
#include "opencv2/calib3d.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp"
#include 
#include 
#include 
#include 
#include 

using namespace cv;
using namespace std;

static double computeReprojectionErrors(
	const vector<vector<Point3f> >& objectPoints,
	const vector<vector<Point2f> >& imagePoints,
	const vector<Mat>& rvecs, const vector<Mat>& tvecs,
	const Mat& cameraMatrix, const Mat& distCoeffs
)
{
	vector<Point2f> imagePoints2;
	int i, totalPoints = 0;
	double totalErr = 0, err;


	for (i = 0; i < (int)objectPoints.size(); i++)
	{
		projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i],
			cameraMatrix, distCoeffs, imagePoints2);
		err = norm(Mat(imagePoints[i]), Mat(imagePoints2), NORM_L2);
		int n = (int)objectPoints[i].size();

		totalErr += err*err;
		totalPoints += n;
	}

	return std::sqrt(totalErr / totalPoints);
}
int main(int argc, char** argv)
{
	
	vector<string> files;
	glob("E:\\images", files);

	// 定义变量
	vector<vector<Point2f>> imagePoints;//像点
	vector<vector<Point3f>> objectPoints;//物点
	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 30, 0.001);//进行亚像素精度的调整,获得亚像素级别的角点坐标
	int numCornersHor = 7;//heigh
	int numCornersVer = 9;//width
	int numSquares = 13;//单位mm
	vector<Point3f> obj;
	//存放每张图的角点坐标,并存入obj中(物点)
	for (int i = 0; i < numCornersHor; i++)
		for (int j = 0; j < numCornersVer; j++)
			obj.push_back(Point3f((float)j * numSquares, (float)i * numSquares, 0));



	//发现与绘制棋盘格
	//遍历每张图片
	Size s;
	//像点
	for (int i = 0; i < files.size(); i++) {
		printf("image file : %s \n", files[i].c_str());
		Mat image = imread(files[i]);
		s = image.size();
		Mat gray;
		cvtColor(image, gray, COLOR_BGR2GRAY);//转灰度
		vector<Point2f> corners;
		bool ret = findChessboardCorners(gray, Size(9, 7), corners, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS);//该函数的功能就是判断图像内是否包含完整的棋盘图,若能检测完全,就把他们的角点坐标(从上到下,从左到右)记录,并返回true,否则为false,CALIB_CB_FILTER_QUADS用于去除检测到的错误方块。
		if (ret) {
			cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1), criteria);//用于发现亚像素精度的角点位置
			drawChessboardCorners(image, Size(9, 7), corners, ret);//将每一个角点出做标记,此为物点对应的像点坐标
			imagePoints.push_back(corners);//将角点坐标存入imagePoints中,此为像点坐标
			objectPoints.push_back(obj);//把存放在每张图的所有角点坐标,存在objectPoints中,物点坐标
			//imshow("calibration-demo", image);
			waitKey(500);
		}
	}


	//计算内参与畸变系数
	Mat intrinsic = Mat(3, 3, CV_32FC1);
	Mat distCoeffs;//畸变矩阵
	vector<Mat> rvecs;//旋转向量R
	vector<Mat> tvecs;//平移向量T
	//内参矩阵
	intrinsic.ptr<float>(0)[0] = 1;
	intrinsic.ptr<float>(1)[1] = 1;
	calibrateCamera(objectPoints, imagePoints, s, intrinsic, distCoeffs, rvecs, tvecs);
	FileStorage fs("E:\\images\\out_calibration.yml", FileStorage::WRITE);//存储标定结果,这里可以选择自己的存放路径
	fs << "intrinsic" << intrinsic;//存放内参矩阵
	fs << "distCoeffs" << distCoeffs;//存放畸变矩阵
	fs << "board_width" << 9;//存放标定板长度信息
	fs << "board_height" << 7;//存放标定板宽度信息
	fs << "square_size" << 0.013;//存放标定板格子尺寸信息
	fs << "R" << rvecs;//R
	fs << "T" << tvecs;//T
	printf("Done Calibration\n");
	cout << "Calibration error: " << computeReprojectionErrors(objectPoints, imagePoints, rvecs, tvecs, intrinsic, distCoeffs) << endl;//输出投影的误差
	//Mat intrinsic,distCoeffs= calibration();
	//校正图像
	Mat dst;
	Mat image = imread("E:\\images\\cal 13.jpg");
	undistort(image, dst, intrinsic, distCoeffs);
	imshow("image", image);
	imshow("undistort image", dst);
	waitKey(10000);
}

//相信不少人看晕了,别怕分段讲解
1.首先定义一些容器,存放物点,像点。这两个点极为重要,通过他们的映射关系找到内参矩阵
2.TermCriteria 亚像素级别角点检测与findChessboardCorners()配合使用

vector<string> files;
	glob("E:\\images", files);

	// 定义变量
	vector<vector<Point2f>> imagePoints;//像点
	vector<vector<Point3f>> objectPoints;//物点
	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 30, 0.001);//进行亚像素精度的调整,获得亚像素级别的角点坐标

3.最重要的两个for 循环:找到像点,物点。并用pushback()将其存到容器中。
//找到物点

//存放每张图的角点坐标,并存入obj中(物点)
	for (int i = 0; i < numCornersHor; i++)
		for (int j = 0; j < numCornersVer; j++)
			obj.push_back(Point3f((float)j * numSquares, (float)i * numSquares, 0));

//找到像点

for (int i = 0; i < files.size(); i++) {
		printf("image file : %s \n", files[i].c_str());
		Mat image = imread(files[i]);
		s = image.size();
		Mat gray;
		cvtColor(image, gray, COLOR_BGR2GRAY);//转灰度
		vector<Point2f> corners;
		bool ret = findChessboardCorners(gray, Size(9, 7), corners, CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FILTER_QUADS);//该函数的功能就是判断图像内是否包含完整的棋盘图,若能检测完全,就把他们的角点坐标(从上到下,从左到右)记录,并返回true,否则为false,CALIB_CB_FILTER_QUADS用于去除检测到的错误方块。
		if (ret) {
			cornerSubPix(gray, corners, Size(11, 11), Size(-1, -1), criteria);//用于发现亚像素精度的角点位置
			drawChessboardCorners(image, Size(9, 7), corners, ret);//将每一个角点出做标记,此为物点对应的像点坐标
			imagePoints.push_back(corners);//将角点坐标存入imagePoints中,此为像点坐标
			objectPoints.push_back(obj);//把存放在每张图的所有角点坐标,存在objectPoints中,物点坐标
			//imshow("calibration-demo", image);

4.calibrationCamera()标定相机(R,T,内参,畸变矩阵).
//输入就是像点,物点,图像尺寸

	calibrateCamera(objectPoints, imagePoints, s, intrinsic, distCoeffs, rvecs, tvecs);

5.给大家列入需要自行更改的API:
//glob("E:\\images", files); 输入自己图像路径,注意是\
6.校正图像
//undistort函数,输入内参和畸变矩阵

Mat dst;
	Mat image = imread("E:\\images\\cal 13.jpg");
	undistort(image, dst, intrinsic, distCoeffs);
	imshow("image", image);
	imshow("undistort image", dst);
	waitKey(10000);

你可能感兴趣的:(opencv单目相机标定(源码+讲解))