Opencv——单目标定

相机标定就是设置相机各种参数的过程。当然相机厂家也会提供一些技术参数,但是对于某些任务来说,所提供的技术参数还不够精确。利用相机标定可以得到更精确的参数。
用这些参数可以更加精确的确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系。

标定步骤

1.一张棋盘格标定板
2.用 cv::findChessboardCorners(…) 函数获取棋盘角点
3.用 cv::drawChessboardCorners(…) 函数在图片上标出检测到的角点
4.用 cv::cornerSubPix(…)取得角点的亚像素级精度,提高标定参数的精确度
5.用 cv::calibrateCamera(…) 进行标定,取得相机矩阵、畸变矩阵以及旋转和平移矩阵

涉及的函数

//找到棋盘内部角点的位置
bool  findChessboardCorners( InputArray image, //输入标定棋盘,必须是一个8位的灰度或彩色图像。
						Size patternSize,//输入棋盘的角点数量
						OutputArray corners,//检测到的角点列表
						int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE //
						);
找到并排列角点,函数返回True,否则为False
//画出角点
void drawChessboardCorners( InputOutputArray image, //输入的棋盘图片
							Size patternSize,//输入棋盘的角点数量
							InputArray corners,// 输入检测到的角点列表
							bool patternWasFound //是否找到完整板的参数
							);
//取得角点的亚像素级精度,提高标定参数的精确度
void cornerSubPix( InputArray image, //输入单通道,8位或浮点图像。
					InputOutputArray corners,//输入/输出检测到的角点列表
					Size winSize, //搜索窗口半径
					Size zeroZone,//
					TermCriteria criteria //终止标志位
					);
//相机标定
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)//终止标志位
                        );

opencv画标签 opencv标定_#include

代码

#include <iostream>
#include <iomanip>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/features2d.hpp>

#include "CameraCalibrator.h"

int main()
{
	cv::Mat image;
	std::vector<std::string> filelist;

	// generate list of chessboard image filename
	// named chessboard01 to chessboard27 in chessboard sub-dir
	for (int i = 1; i <= 28; i++) {

		std::stringstream str;
		str << "video/video" << std::setw(2) << std::setfill('0') << i << ".jpg";
		std::cout << str.str() << std::endl;

		filelist.push_back(str.str());
		image = cv::imread(str.str(), 0);

		// cv::imshow("Board Image",image);	
		// cv::waitKey(100);
	}

	// Create calibrator object
	CameraCalibrator cameraCalibrator;
	// add the corners from the chessboard
	cv::Size boardSize(8, 6);
	cameraCalibrator.addChessboardPoints(
		filelist,	// filenames of chessboard image
		boardSize, "Detected points");	// size of chessboard

	// calibrate the camera
	//cameraCalibrator.setCalibrationFlag(true, true);
	cameraCalibrator.calibrate(image.size());

	// Exampple of Image Undistortion
	image = cv::imread(filelist[14], 0);
	cv::Size newSize(static_cast<int>(image.cols*1.5), static_cast<int>(image.rows*1.5));
	cv::Mat uImage = cameraCalibrator.remap(image, newSize);

	// display camera matrix
	cv::Mat cameraMatrix = cameraCalibrator.getCameraMatrix();
	std::cout << " Camera intrinsic: " << cameraMatrix.rows << "x" << cameraMatrix.cols << std::endl;
	std::cout << cameraMatrix.at<double>(0, 0) << " " << cameraMatrix.at<double>(0, 1) << " " << cameraMatrix.at<double>(0, 2) << std::endl;
	std::cout << cameraMatrix.at<double>(1, 0) << " " << cameraMatrix.at<double>(1, 1) << " " << cameraMatrix.at<double>(1, 2) << std::endl;
	std::cout << cameraMatrix.at<double>(2, 0) << " " << cameraMatrix.at<double>(2, 1) << " " << cameraMatrix.at<double>(2, 2) << std::endl;

	cv::Mat getDistCoeffs = cameraCalibrator.getDistCoeffs();
	std::cout << " getDistCoeffs: " << getDistCoeffs.rows << "x" << getDistCoeffs.cols << std::endl;
	std::cout << getDistCoeffs.at<double>(0, 0) << " " << getDistCoeffs.at<double>(0, 1) << " " << getDistCoeffs.at<double>(0, 2) << " " << getDistCoeffs.at<double>(0, 3) << " " << getDistCoeffs.at<double>(0, 4) << " " << getDistCoeffs.at<double>(0, 5) << " " << getDistCoeffs.at<double>(0, 6) << " " << getDistCoeffs.at<double>(0, 7) << " " << getDistCoeffs.at<double>(0, 8) << " " << getDistCoeffs.at<double>(0, 9) << " " << getDistCoeffs.at<double>(0, 10) << " " << getDistCoeffs.at<double>(0, 11) << " " << getDistCoeffs.at<double>(0, 12) << " " << getDistCoeffs.at<double>(0, 13) << std::endl;

	cv::namedWindow("Original Image");
	cv::imshow("Original Image", image);
	cv::namedWindow("Undistorted Image");
	cv::imshow("Undistorted Image", uImage);

	// Store everything in a xml file
	cv::FileStorage fs("calib.xml", cv::FileStorage::WRITE);
	fs << "Intrinsic" << cameraMatrix;
	fs << "Distortion" << cameraCalibrator.getDistCoeffs();

	cv::waitKey();
	return 0;
}
#ifndef CAMERACALIBRATOR_H
#define CAMERACALIBRATOR_H

#include <vector>
#include <iostream>

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

class CameraCalibrator {

	// input points:
	// the points in world coordinates
	// (each square is one unit)
	std::vector<std::vector<cv::Point3f> > objectPoints;
	// the image point positions in pixels
	std::vector<std::vector<cv::Point2f> > imagePoints;
	// output Matrices
	cv::Mat cameraMatrix = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0));
	cv::Mat distCoeffs = cv::Mat(1, 14, CV_32FC1, cv::Scalar::all(0));
	// flag to specify how calibration is done
	int flag;
	// used in image undistortion 
	cv::Mat map1, map2;
	bool mustInitUndistort;

public:
	CameraCalibrator() : flag(0), mustInitUndistort(true) {}

	// Open the chessboard images and extract corner points
	int addChessboardPoints(const std::vector<std::string>& filelist, cv::Size & boardSize, std::string windowName = "");
	// Add scene points and corresponding image points
	void addPoints(const std::vector<cv::Point2f>& imageCorners, const std::vector<cv::Point3f>& objectCorners);
	// Calibrate the camera
	double calibrate(const cv::Size imageSize);
	// Set the calibration flag
	//void setCalibrationFlag(bool radial8CoeffEnabled = false, bool tangentialParamEnabled = false);
	// Remove distortion in an image (after calibration)
	cv::Mat remap(const cv::Mat &image, cv::Size &outputSize);

	// Getters
	cv::Mat getCameraMatrix() { return cameraMatrix; }
	cv::Mat getDistCoeffs() { return distCoeffs; }
};

#endif // CAMERACALIBRATOR_H
#include "CameraCalibrator.h"

// Open chessboard images and extract corner points
int CameraCalibrator::addChessboardPoints(
	const std::vector<std::string>& filelist, // list of filenames containing board images
	cv::Size & boardSize,                     // size of the board
	std::string windowName) {                 // name of window to display results
											  // if null, no display shown
// the points on the chessboard
	std::vector<cv::Point2f> imageCorners;
	std::vector<cv::Point3f> objectCorners;

	// 3D Scene Points:
	// Initialize the chessboard corners 
	// in the chessboard reference frame
	// The corners are at 3D location (X,Y,Z)= (i,j,0)
	for (int i = 0; i < boardSize.height; i++) {
		for (int j = 0; j < boardSize.width; j++) {

			objectCorners.push_back(cv::Point3f(i, j, 0.0f));
		}
	}

	// 2D Image points:
	cv::Mat image; // to contain chessboard image
	int successes = 0;
	// for all viewpoints
	for (int i = 0; i < filelist.size(); i++) {

		// Open the image
		image = cv::imread(filelist[i], 1);

		// Get the chessboard corners
		bool found = cv::findChessboardCorners(image,         // image of chessboard pattern 
			boardSize,     // size of pattern
			imageCorners); // list of detected corners

// Get subpixel accuracy on the corners
		if (found) {
			cv::cornerSubPix(image, imageCorners,
				cv::Size(5, 5), // half size of serach window
				cv::Size(-1, -1),
				cv::TermCriteria(cv::TermCriteria::MAX_ITER +
					cv::TermCriteria::EPS,
					30,		// max number of iterations 
					0.1));  // min accuracy

			// If we have a good board, add it to our data
			if (imageCorners.size() == boardSize.area()) {

				// Add image and scene points from one view
				addPoints(imageCorners, objectCorners);
				successes++;
			}
		}

		if (windowName.length() > 0 && imageCorners.size() == boardSize.area()) {

			//Draw the corners
			cv::drawChessboardCorners(image, boardSize, imageCorners, found);
			cv::imshow(windowName, image);
			cv::waitKey(100);
		}
	}

	return successes;
}

// Add scene points and corresponding image points
void CameraCalibrator::addPoints(const std::vector<cv::Point2f>& imageCorners, const std::vector<cv::Point3f>& objectCorners) {

	// 2D image points from one view
	imagePoints.push_back(imageCorners);
	// corresponding 3D scene points
	objectPoints.push_back(objectCorners);
}

// Calibrate the camera
// returns the re-projection error
double CameraCalibrator::calibrate(const cv::Size imageSize)
{
	// undistorter must be reinitialized
	mustInitUndistort = true;

	//Output rotations and translations
	std::vector<cv::Mat> rvecs, tvecs;

	// start calibration
	return
		calibrateCamera(objectPoints, // the 3D points
			imagePoints,  // the image points
			imageSize,    // image size
			cameraMatrix, // output camera matrix
			distCoeffs,   // output distortion matrix
			rvecs, tvecs, // Rs, Ts 
			cv::CALIB_RATIONAL_MODEL);        // set options
//					,CV_CALIB_USE_INTRINSIC_GUESS);

}

// remove distortion in an image (after calibration)
cv::Mat CameraCalibrator::remap(const cv::Mat &image, cv::Size &outputSize) {

	cv::Mat undistorted;

	if (outputSize.height == -1)
		outputSize = image.size();

	if (mustInitUndistort) { // called once per calibration

		cv::initUndistortRectifyMap(
			cameraMatrix,  // computed camera matrix
			distCoeffs,    // computed distortion matrix
			cv::Mat(),     // optional rectification (none) 
			cv::Mat(),     // camera matrix to generate undistorted
			outputSize,    // size of undistorted
			CV_32FC1,      // type of output map
			map1, map2);   // the x and y mapping functions

		mustInitUndistort = false;
	}

	// Apply mapping functions
	cv::remap(image, undistorted, map1, map2,
		cv::INTER_LINEAR); // interpolation type

	return undistorted;
}

矫正

用已有的参数矫正图像,默认使用五个系数,也可以采用八个系数的模型,得到系数后就可以计算两个cv::Mat类型的映射函数(分别用x坐标和用y坐标),把有畸变的图像上的像素映射到未畸变的新位置

//计算映射值
void initUndistortRectifyMap(InputArray cameraMatrix, //计算得到的相机矩阵
							 InputArray distCoeffs,//计算得到的畸变矩阵
                             InputArray R, //可选矫正项
                             InputArray newCameraMatrix,//生成无畸变的相机矩阵
                             Size size,//无畸变的图像尺寸
                              int m1type, //输出图片类型
                              OutputArray map1, //映射的X坐标
                              OutputArray map2//映射的Y坐标
                              );
//把输入图片上的点映射到未畸变的新位置
void remap( InputArray src, //输入的图片
			OutputArray dst,//输出矫正的图片
            InputArray map1, //输入映射的X坐标
            InputArray map2,//输入映射的Y坐标
            int interpolation,//插值法
             int borderMode = BORDER_CONSTANT,
            const Scalar& borderValue = Scalar()
            );