• 说在前面

操作系统:win10

vs 版本:2017

opencv版本:4.0.1

opencv-contrb版本:4.0.1

接上篇:【OpenCV/aruco】创建board Demo

下一篇:【OpenCV/aruco】第一个AR Demo-二维图片

  • 运行结果

【OpenCV/aruco】校准相机(Camera Calibration) Demo

  • 主要函数

https://docs.opencv.org/4.1.0/d9/d6a/group__aruco.html

calibrateCameraAruco(相机校准)

estimatePoseSingleMarkers (单标记姿态估计)

drawAxis(绘制坐标系轴

  •  代码-校准相机并保存参数
#include <opencv2\highgui.hpp>
#include <opencv2\videoio.hpp>
#include <opencv2\aruco.hpp>
#include <opencv2\aruco\dictionary.hpp>
#include <opencv2\aruco\charuco.hpp>
#include <opencv2\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\opencv.hpp>
#include <vector>
#include <iostream>

using namespace std;
using namespace cv;

//保存相机参数
static bool saveCameraParams(const string &filename, Size imageSize, float aspectRatio, int flags,
	const Mat &cameraMatrix, const Mat &distCoeffs, double totalAvgErr) 
{
	FileStorage fs(filename, FileStorage::WRITE);
	if (!fs.isOpened())
		return false;
	//char buf[1024];

	fs << "image_width" << imageSize.width;
	fs << "image_height" << imageSize.height;

	if (flags & CALIB_FIX_ASPECT_RATIO) fs << "aspectRatio" << aspectRatio;

	//这一段暂时没管
	/*if (flags != 0) {
		sprintf(buf, "flags: %s%s%s%s",
			flags & CALIB_USE_INTRINSIC_GUESS ? "+use_intrinsic_guess" : "",
			flags & CALIB_FIX_ASPECT_RATIO ? "+fix_aspectRatio" : "",
			flags & CALIB_FIX_PRINCIPAL_POINT ? "+fix_principal_point" : "",
			flags & CALIB_ZERO_TANGENT_DIST ? "+zero_tangent_dist" : "");
	}*/

	fs << "flags" << flags;

	fs << "camera_matrix" << cameraMatrix;
	fs << "distortion_coefficients" << distCoeffs;

	fs << "avg_reprojection_error" << totalAvgErr;

	return true;
}

int main()
{
	float aspectRatio = 1;//  fx/fy,长宽比,应该是board的长宽比
	VideoCapture inputVideo(1);//外置摄像头,自带摄像头请使用0

	Ptr<aruco::Dictionary> dictionary =
		aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50);//创建字典

	//创建棋盘对象,参数参考上一篇
	Ptr<aruco::GridBoard> gridboard =
		aruco::GridBoard::create(5, 5, 100, 20, dictionary);
	Ptr<aruco::Board> board = gridboard.staticCast<aruco::Board>();

	// 收集信息
	vector< vector< vector< Point2f > > > allCorners;//所有marker的四个角
	vector< vector< int > > allIds;//所有marker的ID
	Size imgSize;

	while (inputVideo.grab()) {
		Mat image, imageCopy;
		inputVideo.retrieve(image);

		vector< int > ids;
		vector< vector< Point2f > > corners;

		// 检测标记
		aruco::detectMarkers(image, dictionary, corners, ids);

		// draw results
		image.copyTo(imageCopy);
		if (ids.size() > 0) aruco::drawDetectedMarkers(imageCopy, corners, ids);

		putText(imageCopy, "Press 'c' to add current frame. 'ESC' to finish and calibrate",
			Point(10, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 0), 2);

		imshow("out", imageCopy);
		char key = (char)waitKey(30);
		if (key == 27) break;
		if (key == 'c' && ids.size() > 0) {
			cout << "Frame captured" << endl;
			allCorners.push_back(corners);//按下c后,将该帧中所有的标记的角以及id保存,尽量捕捉多一点的图像,20左右
			allIds.push_back(ids);
			imgSize = image.size();
		}
	}

	if (allIds.size() < 1) {
		cerr << "Not enough captures for calibration" << endl;
		return 0;
	}

	Mat cameraMatrix, distCoeffs;
	vector< Mat > rvecs, tvecs;
	double repError;

	if (CALIB_FIX_ASPECT_RATIO) {
		cameraMatrix = Mat::eye(3, 3, CV_64F);
		cameraMatrix.at< double >(0, 0) = aspectRatio;
	}

	// 开始处理收集到的信息
	vector< vector< Point2f > > allCornersConcatenated;
	vector< int > allIdsConcatenated;
	vector< int > markerCounterPerFrame;
	markerCounterPerFrame.reserve(allCorners.size());
	for (unsigned int i = 0; i < allCorners.size(); i++) {
		markerCounterPerFrame.push_back((int)allCorners[i].size());
		for (unsigned int j = 0; j < allCorners[i].size(); j++) {
			allCornersConcatenated.push_back(allCorners[i][j]);
			allIdsConcatenated.push_back(allIds[i][j]);
		}
	}
	// 校准摄像头
	repError = aruco::calibrateCameraAruco(allCornersConcatenated, allIdsConcatenated,
		markerCounterPerFrame, board, imgSize, cameraMatrix,
		distCoeffs, rvecs, tvecs, 1);

	//保存参数,参数保存在.cpp文件同一目录下
	bool saveOk = saveCameraParams("camera.yml", imgSize, aspectRatio, 1, cameraMatrix,
		distCoeffs, repError);

	if (!saveOk) {
		cerr << "Cannot save output file" << endl;
		return 0;
	}

	cout << "Rep Error: " << repError << endl;
	cout << "Calibration saved to " << "camera.yml" << endl;

	return 0;
}
  • 代码-验证校准结果
#include <opencv2\highgui.hpp>
#include <opencv2\videoio.hpp>
#include <opencv2\aruco.hpp>
#include <opencv2\aruco\dictionary.hpp>
#include <opencv2\aruco\charuco.hpp>
#include <opencv2\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\opencv.hpp>
#include <vector>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
	cv::VideoCapture inputVideo(1);//外置摄像头,自带摄像头请使用0
	//VideoWriter writer("VideoTest.avi", VideoWriter::fourcc('M', 'J', 'P', 'G'), 25.0, Size(640, 480));
	cv::Mat cameraMatrix, distCoeffs; // 相机参数

	FileStorage fs("camera.yml", FileStorage::READ);//读取相机参数
	if (!fs.isOpened())
	{
		cout << "Could not open the configuration file!" << endl;
		exit(1);
	}
	fs["camera_matrix"] >> cameraMatrix;
	fs["distortion_coefficients"] >> distCoeffs;
	fs.release();
	cout << cameraMatrix << endl;
	cout << distCoeffs << endl;

	Ptr<aruco::Dictionary> dictionary =
		aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50);//创建字典

	while (inputVideo.grab()) {
		unsigned int i = 0;
		cv::Mat image, imageCopy;

		inputVideo.retrieve(image);
		image.copyTo(imageCopy);

		std::vector<int> ids;
		std::vector<std::vector<cv::Point2f> > corners;

		cv::aruco::detectMarkers(image, dictionary, corners, ids);// 检测markers

		if (ids.size() > 0) {
			cv::aruco::drawDetectedMarkers(imageCopy, corners, ids);
			vector< Vec3d > rvecs, tvecs;//得到旋转矢量以及平移矢量
			cv::aruco::estimatePoseSingleMarkers(corners, 100, cameraMatrix, distCoeffs, rvecs, tvecs); 
			
			for (i = 0; i < ids.size(); i++)
				cv::aruco::drawAxis(imageCopy, cameraMatrix, distCoeffs, rvecs[i], tvecs[i], 50);// 画方向轴
			//writer << imageCopy;
		}

		cv::imshow("out", imageCopy);

		char key = (char)cv::waitKey(20);
		if (key == 'b') break;
	}

	return 0;
}

 

相关文章: