livox_camera_lidar_calibration学习--相机内参标定

开源代码位于:GitHub - Shelfcol/livox_camera_lidar_calibration_modified: livox_camera_lidar_calibration的改进得

代码为cameraCalib.cpp

运行: roslaunch camera_lidar_calibration cameraCalib.launch

1. 拍摄并读取23张拍摄的棋盘格照片

        相片拍摄:准备20张以上的照片数据,各个角度和位置都要覆盖,拍摄的时候距离不要太近(3米左右),如图所示:

        Mat imageInput = imread(filename);

2. 提取棋盘格的角点

        Size board_size = Size(row_number, col_number); /* 标定板上每行、列的角点数 */

        vector<Point2f> image_points_buf; /* 缓存每幅图像上检测到的角点 */

        0 == findChessboardCorners(imageInput, board_size, image_points_buf)

3. 对角点提取成功的图片进行亚像素角点提取

        Mat view_gray; // 保存对应的灰度图

        cvtColor(imageInput, view_gray, cv::COLOR_RGB2GRAY); // 转灰度图

        /* 亚像素精确化 */

        // image_points_buf 初始的角点坐标向量,同时作为亚像素坐标位置的输出

        // Size(5,5) 搜索窗口大小

        // (-1,-1)表示没有死区

        // TermCriteria 角点的迭代过程的终止条件, 可以为迭代次数和角点精度两者的组合

        cornerSubPix(view_gray, image_points_buf, Size(5, 5), Size(-1, -1), TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 30, 0.1));

        (具体的原理后面补上)

         drawChessboardCorners(view_gray, board_size, image_points_buf, false); // 用于在图片中标记角点

4. 根据图片的宽高设定假设的世界角点坐标。

        这里的boad_size 的height为棋盘格的数值方向的角点数,width为水平方向角点数,这里注意不包含最外一圈黑块。下面图片对应的width = 8, height = 6。其中每个方块的尺寸为Size square_size(w,h),w和h单位为mm。

         这里假定左上角的角点坐标为(0,0),按照格子的尺寸计算其他的角点的坐标,这里假定标定板位于世界坐标系z=0的平面上

/* 初始化标定板上角点的三维坐标,以(0,0)坐标开始,每个角点的长宽由测量确定 */
int i, j, t;
for (t = 0; t<image_count; t++) { // 遍历所有图片数
    vector<Point3f> tempPointSet;
	for (i = 0; i<board_size.height; i++) {
		for (j = 0; j<board_size.width; j++) {
			Point3f realPoint;

			/* 假设标定板放在世界坐标系中z=0的平面上 */
			realPoint.x = i * square_size.width;
			realPoint.y = j * square_size.height; // 右x下y
			realPoint.z = 0;
			tempPointSet.push_back(realPoint);
		}
	}
	object_points.push_back(tempPointSet);
}

5. 根据提取的角点和假定的世界坐标系角点计算相机内参

/* 开始标定 */
// object_points 世界坐标系中的角点的三维坐标
// image_points_seq 每一个内角点对应的图像坐标点
// image_size 图像的像素尺寸大小
// cameraMatrix 输出,内参矩阵
// distCoeffs 输出,畸变系数
// rvecsMat 输出,旋转向量
// tvecsMat 输出,位移向量
// 0 标定时所采用的算法
calibrateCamera(object_points, image_points_seq, image_size, cameraMatrix, distCoeffs, rvecsMat, tvecsMat, 0); // 

6. 根据计算的内参进行三维点重投影,计算标定误差

/* 通过得到的摄像机内外参数,对假定空间的三维点进行重新投影计算,得到新的投影点,保存在image_points2中 */

输入:假定的世界点,是世界点对应的旋转,平移矩阵,相机内参,畸变系数,输出投影到相机平面的点坐标

projectPoints(tempPointSet, rvecsMat[i], tvecsMat[i], cameraMatrix, distCoeffs, image_points2);

这里学习一个小知识

         CV_<bit_depth>(S|U|F)C<number_of_channels>

         CV_32FC2表示32位浮点数双通道

	double total_err = 0.0;         /* 所有图像的平均误差的总和 */
	double err = 0.0;               /* 每幅图像的平均误差 */
	vector<Point2f> image_points2;  /* 保存重新计算得到的投影点 */
	fout << "Average error: \n";

	for (i = 0; i<image_count; i++) {
		vector<Point3f> tempPointSet = object_points[i];

		/* 通过得到的摄像机内外参数,对假定空间的三维点进行重新投影计算,得到新的投影点,保存在image_points2中 */
		projectPoints(tempPointSet, rvecsMat[i], tvecsMat[i], cameraMatrix, distCoeffs, image_points2);

		/* 计算新的投影点和旧的投影点之间的误差*/
		vector<Point2f> tempImagePoint = image_points_seq[i]; // 第i张图片的角点坐标
		Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2); // 双通道数据,每个存放Vec2f
		Mat image_points2Mat  = Mat(1, image_points2.size(),  CV_32FC2);

		for (unsigned int j = 0; j < tempImagePoint.size(); j++) {
			image_points2Mat.at<Vec2f>(0, j) = Vec2f(image_points2[j].x, image_points2[j].y);
			tempImagePointMat.at<Vec2f>(0, j) = Vec2f(tempImagePoint[j].x, tempImagePoint[j].y);
		}
		err = norm(image_points2Mat, tempImagePointMat, NORM_L2); // 计算L2距离
		total_err += err /= point_counts[i];
		fout << "The error of picture " << i + 1 << " is " << err << " pixel" << endl;
	}
	fout << "Overall average error is: " << total_err / image_count << " pixel" << endl << endl;

7. 标定结果

        1个3x3的内参矩阵,5个畸变纠正参数(k1,k2,p1,p2,k3)

8. Matlab标定相机内参

        Matlab中的cameraCalibrator工具可以标定内参,我们只需要结果中的1,,2和11数据

猜你喜欢

转载自blog.csdn.net/qq_38650944/article/details/124120612