机器视觉学习笔记(5)——基于OpenCV的单目摄像机标定

机器视觉学习笔记(5)——基于OpenCV的单目摄像机标定

标签(空格分隔): 机器视觉


本文CameraCalibrator类源代码来自于OpenCV2 计算机视觉编程手册(Robert Laganiere 著 张静 译)

强烈建议阅读机器视觉学习笔记(4)——单目摄像机标定参数说明之后再阅读本文

1.单目摄像机标定目的

单目摄像机标定的目的就是使摄像机实际状态无限接近理论推导的理想状态。单目摄像机标定最终将确定9个参数摄像机内参数有4个透镜畸变参数5个

2.单目摄像机标定流程

  • 制作标定板
  • 使用摄像机拍摄不同角度的标定板
  • 将照片放置于预设的文件夹中
  • 编写程序计算摄像机内参数和透镜畸变参数
  • 保存9个参数

3.关键源代码说明

3.1bool findChessboardCorners((InputArray image, Size patternSize, OutputArray corners)

Finds the positions of internal corners of the chessboard.
(寻找棋盘格标定板的角点)

  • 三个参数依次代表输入图像,角点数目,存储角点的变量
  • 检测到角点以后,常常需要用void drawChessboardCorners()函数将其画出来
  • 如果找到的角点数目和输入的角点数目相同,就会用彩色圆圈画出角点,否则只用红色圆圈画出角点

示例程序如下:

void test()
{
    vector<Point2f> imageCorners;
    Size boardSize(9, 6);
    Mat image = imread("left01.jpg");
    bool found = findChessboardCorners(image, boardSize, imageCorners);
    //绘制角点
    drawChessboardCorners(image, boardSize, imageCorners, found);
    namedWindow("test");
    imshow("test", image);//角点如未全部检测出来只是红色圆圈画出角点
    waitKey();
}

3.2Class CameraCalibrator

class CameraCalibrator{

	//输入点
    std::vector<std::vector<cv::Point3f>> objectPoints;//世界坐标系下的点
    std::vector<std::vector<cv::Point2f>> imagePoints;//像素坐标系下的点
    //输出矩阵
    cv::Mat cameraMatrix;//摄像机内参数矩阵
    cv::Mat distCoeffs;//透镜畸变系数矩阵
	//标定方式
	int flag;
	//用于图像去畸变 
    cv::Mat map1,map2; 
	bool mustInitUndistort;

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

	//导入标定图片提取角点
	int addChessboardPoints(const std::vector<std::string>& filelist, cv::Size & boardSize);
	//添加场景点与对应的图像点
    void addPoints(const std::vector<cv::Point2f>& imageCorners, const std::vector<cv::Point3f>& objectCorners);
	//标定相机
	double calibrate(cv::Size &imageSize);
    //设置标定方式
    void setCalibrationFlag(bool radial8CoeffEnabled=false, bool tangentialParamEnabled=false);
	//消除透镜畸变(标定之后调用有效)
	cv::Mat CameraCalibrator::remap(const cv::Mat &image);

    // 获取矩阵
    cv::Mat getCameraMatrix() { return cameraMatrix; }
    cv::Mat getDistCoeffs()   { return distCoeffs; }
};

4.单目标定实例

  • 源代码:点击下载
  • 实例标定图片:点击下载
  • main函数,说明详见注释
int main()  
{  
	cv::namedWindow("Image");
	cv::Mat image;
	std::vector<std::string> filelist;//存放标定图片路径

	//生成路径,此处表示图片放在工程根目录下的chessboards文件夹
	for (int i=1; i<=20; i++)
	{
		std::stringstream str;
		str << "chessboards/chessboard" << std::setw(2) << std::setfill('0') << i << ".jpg";//图片的相对路径
		std::cout << str.str() << std::endl;

		filelist.push_back(str.str());
		image= cv::imread(str.str());
		cv::imshow("Image",image);
		cv::waitKey(100);
	}

	CameraCalibrator cameraCalibrator;
	//从棋盘格添加角点
	cv::Size boardSize(6, 4);
	cameraCalibrator.addChessboardPoints(
		filelist,	//图片路径
		boardSize);	//角点数目
    //标定相机
	cameraCalibrator.calibrate(image.size());
	//选取某张图片,消除透镜畸变
	image = cv::imread(filelist[6]);
	cv::Mat uImage= cameraCalibrator.remap(image);
	imshow("Original Image", image);
	imshow("Undistorted Image", uImage);

	//打印相机内参数矩阵(3*3矩阵)
	Mat cameraMatrix = cameraCalibrator.getCameraMatrix();
	std::cout << " 相机内参数矩阵:" << cameraMatrix.rows << "x" << cameraMatrix.cols << std::endl;
	for (int i=0; i<cameraMatrix.rows; i++)
		for (int j=0; j<cameraMatrix.cols; j++)
			{
				cout<<setw(10)<<cameraMatrix.at<double>(i, j);
				if (j==2)
					cout<<endl;
			}
	//打印畸变系数矩阵(1*5矩阵)
	Mat distCoeffs = cameraCalibrator.getDistCoeffs();
	std::cout << "畸变系数矩阵:" << distCoeffs.rows << "x" << distCoeffs.cols << std::endl;
	for (int i=0; i<distCoeffs.rows; i++)
		for (int j=0; j<distCoeffs.cols; j++)
			cout<<distCoeffs.at<double>(i, j)<<"\t";
	waitKey(0);
}  
  • 畸变校正之前
  • 畸变校正之后
  • 相机内参数矩阵为
    [ 167.156 0 155.89 0 178.097 119.372 0 0 1 ] \begin{bmatrix} 167.156&0&155.89\\0&178.097&119.372\\0&0&1 \end{bmatrix} 167.156000178.0970155.89119.3721
  • 透镜畸变系数矩阵(分别表示 k 1 , k 2 , p 1 , p 2 , k 3 k_1,k_2,p_1,p_2,k_3 k1,k2,p1,p2,k3)为
    [ − 0.3456 0.1319 − 0.0004 − 0.0034 − 0.0227 ] \begin{bmatrix} -0.3456&0.1319&-0.0004&-0.0034&-0.0227 \end{bmatrix} [0.34560.13190.00040.00340.0227]

5.总结

  • 尽管核心函数都是OpenCV库函数,但是通过面向对象思想把相关函数和变量整合起来定义一个类是非常棒的方式,这样就可以专注于逻辑思考而不是一些变量和语法
  • 标定结果是否准确可以通过相机内参数矩阵大致推算出来。笔者自己的1280*720分辨率相机标定的 x 0 , y 0 x_0,y_0 x0,y0分别是622pix,370pix,恰好是分辨率的一半左右,符合其物理意义,可以断定标定正确(精度另说)
  • 本文实例中的 x 0 , y 0 x_0,y_0 x0,y0分别是156pix,119pix,由此我们可以推断作者相机的分辨率是312*234左右,由于视频分辨率常见的也就那几种,所以可以断定实例程序相机的分辨率是320*240

你可能感兴趣的:(机器视觉学习笔记)