一、棋盘格标定

原理如下

Halcon 像机标定原理推导_halcon棋盘格标定-

opencv apriltag 标定_opencv apriltag 标定

void  CameraCalibration(std::string  imagepath, int Chessboard_Width, Size& board_size, std::string  imagetype, bool flag, int image_count)
{
	/************************************************************************
			   从摄像机中读取多幅图像,从中提取出角点,然后对角点进行亚像素精确化
		*************************************************************************/

	cv::Mat image;
	Size image_size;                         //    图像的尺寸      
	vector<Point2f> corners;                  //   缓存每幅图像上检测到的角点     
	vector<vector<Point2f>>  corners_Seq;    //  保存检测到的所有角点    
	ofstream fout(imagepath + "\\carlbration\\calibration_result.txt");  //   保存定标结果的文件   
	int count = 0, n = 0;
	stringstream tempname;
	string filename;
	int key;
	string msg;
	int baseLine;
	Size textSize;

	while (n < image_count)
	{
		std::string  path = imagepath + to_string(n + 1) + "." + imagetype;
		const char* pathc = path.c_str();
		//从文件中读入图像
		image = cv::imread(path, -1);
		//如果读入图像失败
		if (image.empty())
		{
			std::cout << "Can not load image" << "\n";
			return;
		}
		//显示图像
		image_size = image.size();
		/* 提取角点 */
		Mat imageGray;
		cv::cvtColor(image, imageGray, CV_RGB2GRAY);
		bool found;
		if (flag == true)
		{

		  found = findChessboardCornersSB(image, board_size, corners, CALIB_CB_EXHAUSTIVE | CALIB_CB_ACCURACY);
			if (found)
			{
				corners_Seq.push_back(corners);
				drawChessboardCorners(image, board_size, Mat(corners), found);
				std::string  filename = imagepath + "\\carlbration\\" + to_string(n + 1) + "." + imagetype;
				imwrite(filename, image);
				n++;
			}
		}
		else
		{
			found = findChessboardCorners(image, board_size, corners, CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK);
			if (found)
			{
				n++;
				tempname << n;
				tempname >> filename;
				std::string  filename = "\\carlbration\\" + to_string(n + 1) + "." + imagetype;
				/* 亚像素精确化 */
				cornerSubPix(imageGray, corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
				count += corners.size();
				corners_Seq.push_back(corners);
				drawChessboardCorners(image, board_size, Mat(corners), found);
				imwrite(filename, image);
				tempname.clear();
				filename.clear();
			}
		}


	}

	std::cout << "角点提取完成!\n";

	/************************************************************************
		   摄像机定标
	*************************************************************************/
	std::cout << "   摄像机定标 ---------------------------" << endl;
	Size square_size = Size(Chessboard_Width, Chessboard_Width);              // 实际测量得到的定标板上每个棋盘格的高  宽25mm  
	vector<vector<Point3f>>  world_object_Points;                                      // 保存定标板上物理角点的三维坐标 

	Mat image_points = Mat(1, count, CV_32FC2, Scalar::all(0));          //  保存提取的所有角点  
	vector<int>  point_counts;                                          //    每幅图像中角点的数量    
	Mat CameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));                //   摄像机内参数矩阵   
	Mat distortion_coeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));            //摄像机的5个畸变系数:k1,k2,p1,p2,k3 
	vector<Mat> rotation_vectors;                                      // 每幅图像的旋转向量 
	vector<Mat> translation_vectors;                                  // 每幅图像的平移向量 

	/* 初始化定标板上角点的三维坐标 */
	for (int t = 0; t < image_count; t++)
	{
		vector<Point3f> tempPointSet;
		for (int i = 0; i < board_size.height; i++)
		{
			for (int j = 0; j < board_size.width; j++)
			{
				/* 假设定标板放在世界坐标系中z=0的平面上 */
				Point3f tempPoint;
				tempPoint.x = i * square_size.width;
				tempPoint.y = j * square_size.height;
				tempPoint.z = 0;
				tempPointSet.push_back(tempPoint);
			}
		}
		world_object_Points.push_back(tempPointSet);
	}

	/* 初始化每幅图像中的角点数,这里我们假设每幅图像中都可以看到完整的定标板 */
	for (int i = 0; i < image_count; i++)
	{
		point_counts.push_back(board_size.width * board_size.height);
	}

	/* 开始定标 */
	calibrateCamera(world_object_Points, corners_Seq, image_size, CameraMatrix, distortion_coeffs, rotation_vectors, translation_vectors);
	std::cout << "打印出标定参数\n";

	cout << "CameraMatrix:" << endl;
	cout << CameraMatrix << endl;

	cout << "*****************************" << endl;
	cout << "distCoeffs:" << endl;
	cout << distortion_coeffs << endl;
	cout << "*****************************" << endl;


	std::cout << "=========================================================\n" << endl;

	std::cout << "定标完成!\n";

	/************************************************************************
		   对定标结果进行评价
	*************************************************************************/
	std::cout << "开始评价定标结果………………" << endl;
	double total_err = 0.0;                   // 所有图像的平均误差的总和 
	double err = 0.0;                           // 每幅图像的平均误差 
	vector<Point2f>  new_image_points;             //   保存重新计算得到的投影点   

	std::cout << "每幅图像的定标误差:" << endl;
	fout << "每幅图像的定标误差:" << endl << endl;
	for (int i = 0; i < image_count; i++)
	{
		vector<Point3f> tempPointSet = world_object_Points[i];
		//  通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点    
		projectPoints(tempPointSet, rotation_vectors[i], translation_vectors[i], CameraMatrix, distortion_coeffs, new_image_points);

		// 计算新的投影点和旧的投影点之间的误差
		vector<Point2f> singleImagePoint = corners_Seq[i];
		Mat tempImagePointMat = Mat(1, singleImagePoint.size(), CV_32FC2);
		Mat image_points2Mat = Mat(1, new_image_points.size(), CV_32FC2);
		for (int j = 0; j < singleImagePoint.size(); j++)
		{
			image_points2Mat.at<Vec2f>(0, j) = Vec2f(new_image_points[j].x, new_image_points[j].y);
			tempImagePointMat.at<Vec2f>(0, j) = Vec2f(singleImagePoint[j].x, singleImagePoint[j].y);
		}

		err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
		total_err += err /= point_counts[i];


		std::cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
		fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
	}
	std::cout << "总体平均误差:" << total_err / image_count << "像素" << endl;
	fout << "总体平均误差:" << total_err / image_count << "像素" << endl << endl;
	std::cout << "评价完成!" << endl;
	  
	/************************************************************************
		   保存定标结果
	*************************************************************************/
	std::cout << "开始保存定标结果………………" << endl;
	Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); // 保存每幅图像的旋转矩阵 

	fout << "相机内参数矩阵:" << endl;
	fout << CameraMatrix << endl << endl;
	fout << "畸变系数:\n";
	fout << distortion_coeffs << endl << endl << endl;
	for (int i = 0; i < image_count; i++)
	{
		fout << "第" << i + 1 << " 幅图像的旋转向量:" << endl;
		fout << rotation_vectors[i] << endl;

		// 将旋转向量转换为相对应的旋转矩阵 
		Rodrigues(rotation_vectors[i], rotation_matrix);
		fout << "第" << i + 1 << " 幅图像的旋转矩阵:" << endl;
		fout << rotation_matrix << endl;
		fout << "第" << i + 1 << " 幅图像的平移向量:" << endl;
		fout << translation_vectors[i] << endl << endl;
	}
	std::cout << "完成保存" << endl;
	fout << endl;





	// 矫正图像
	std::string   origin_image_path= "D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\30.png";
	Mat  origion_image = imread(origin_image_path);
	Mat image_rectify, image_rectify_2;
	// 矫正方法1    undistort 
	undistort(origion_image, image_rectify, CameraMatrix, distortion_coeffs);
	imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify.png", image_rectify);

	// 矫正方法2    initUndistortRectifyMap  和 remap 方法
	Mat map1, map2;
	Size image_Size= origion_image.size();


	initUndistortRectifyMap(CameraMatrix, distortion_coeffs, Mat(), CameraMatrix, image_Size, CV_16SC2, map1, map2);
	remap(image, image_rectify_2, map1, map2, INTER_LINEAR);
	imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify_2.png", image_rectify_2);
}

opencv apriltag 标定_计算机视觉_02

 matlab 标定结果与opencv标定

opencv apriltag 标定_人工智能_03

 矫正图像

// 矫正图像
	std::string   origion_image_path= "D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\30.png";
	Mat  origion_image = imread(origion_image_path);
	Mat image_rectify, image_rectify_2;
	// 矫正方法1    undistort 
	undistort(origion_image, image_rectify, intrinsic_matrix, distortion_coeffs);
	imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify.png", image_rectify);

	// 矫正方法2    initUndistortRectifyMap  和 remap 方法
	Mat map1, map2;
	Size image_Size= origion_image.size();


	initUndistortRectifyMap(intrinsic_matrix, distortion_coeffs, Mat(), intrinsic_matrix, image_Size, CV_16SC2, map1, map2);
	remap(frame, image_rectify_2, map1, map2, INTER_LINEAR);
	imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify_2.png", image_rectify_2);

方法 undistort 

opencv apriltag 标定_角点_04

 initUndistortRectifyMap  和 remap 方法

opencv apriltag 标定_人工智能_05

同一视角看

opencv apriltag 标定_人工智能_06

二、圆标定

opencv apriltag 标定_人工智能_07

我用的标定的图像是halcon的图像

void CameraCalibrationCircle(std::string  imagepath, float  circle_diameter, float  distance_two_circles, int  circleType, Size& board_size, std::string  imagetype, int  image_count)
{
	cv::Mat image;
	Size image_size;                         //    图像的尺寸      
	vector<Point2f> corners;                  //   缓存每幅图像上检测到的角点     
	vector<vector<Point2f>>  corners_Seq;    //  保存检测到的所有角点    
	ofstream fout(imagepath + "\\carlbration\\calibration_result.txt");  //    保存定标结果的文件    
	int count = 0, n = 0;
	stringstream tempname;
	string filename;
	int key;
	string msg;
	int baseLine;
	while (n < image_count)
	{
		std::string  path = imagepath + "calib-3d-coord-0" + to_string(n + 1) + "." + imagetype;
		//	cout << path << endl;
		const char* pathc = path.c_str();
		//从文件中读入图像
		image = cv::imread(path, -1);
		//如果读入图像失败
		if (image.empty())
		{
			std::cout << "Can not load image" << "\n";
			return;
		}
		//显示图像
		image_size = image.size();
		/* 提取角点 */
		//Mat imageGray;
		//cv::cvtColor(image, imageGray, CV_RGB2GRAY);
		bool found;
		//	cout << "+++++++++++++++++++++" << endl;
		if (circleType == 1)
		{
			// 图像   标定板的点
			found = findCirclesGrid(image, board_size, corners, CALIB_CB_SYMMETRIC_GRID);
		}
		else
		{
			found = findCirclesGrid(image, board_size, corners, CALIB_CB_ASYMMETRIC_GRID);
		}
		if (found)
		{
			drawChessboardCorners(image, board_size, Mat(corners), found);
			std::string  filename2 = imagepath + "carlib_images\\calib-3d-coord-0" + to_string(n + 1) + "_calib." + imagetype;
			imwrite(filename2, image);
			count += corners.size();
			corners_Seq.push_back(corners);

		}
		n++;
	}


	std::cout << "角点提取完成!\n";
	// 开始 计算

	cout << "------------开始标定-------------------       " << count << endl;
	vector<vector<Point3f>>  word_calib_Points;// 标定板中在世界坐标系下的点坐标

	Mat image_points = Mat(1, count, CV_32FC2, Scalar::all(0));           //      保存提取的所有角点  
	vector<int>  point_counts;                                           //       每幅图像中角点的数量    
	Mat CamearMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0));                 //      摄像机内参数矩阵    
	Mat distortion_coeffs = Mat(1, 5, CV_32FC1, Scalar::all(0));             //    摄像机的5个畸变系数:k1,k2,p1,p2,k3 
	vector<Mat> rotation_vectors;                                     //    每幅图像的旋转向量 
	vector<Mat> translation_vectors;                                  //    每幅图像的平移向量 

	// 遍历每个图像
	for (size_t num = 0; num < image_count; num++)
	{
		vector<Point3f>  calib_Points;
		// 每张图像  
		for (size_t i = 0; i < board_size.height; i++)
		{
			for (size_t j = 0; j < board_size.width; j++)
			{
				// 
				Point3f  point;
				point.x = i * distance_two_circles;
				point.y = j * distance_two_circles;
				point.z = 0;
				//cout << point << endl;
				calib_Points.push_back(point);
			}
			//cout << endl;
		}

	//	cout << "===================================" << endl;
		word_calib_Points.push_back(calib_Points);
	}

	/* 初始化每幅图像中的角点数,这里我们假设每幅图像中都可以看到完整的定标板 */
	for (int i = 0; i < image_count; i++)
	{
		point_counts.push_back(board_size.width * board_size.height);
	}


	/* 开始定标 */
	calibrateCamera(word_calib_Points, corners_Seq, image_size, CamearMatrix, distortion_coeffs, rotation_vectors, translation_vectors);
	std::cout << "打印出标定参数\n";

	cout << "CamearMatrix:" << endl;
	cout << CamearMatrix << endl;

	cout << "*****************************" << endl;
	cout << "distCoeffs:" << endl;
	cout << distortion_coeffs << endl;
	cout << "*****************************" << endl;


	std::cout << "定标完成!\n";

	/************************************************************************
		   对定标结果进行评价
	*************************************************************************/
	std::cout << "开始评价定标结果………………" << endl;
	double total_err = 0.0;                   // 所有图像的平均误差的总和 
	double err = 0.0;                           // 每幅图像的平均误差 
	vector<Point2f>  new_image_points;             //   保存重新计算得到的投影点   

	std::cout << "每幅图像的定标误差:" << endl;
	fout << "每幅图像的定标误差:" << endl << endl;
	for (int i = 0; i < image_count; i++)
	{
		vector<Point3f> tempPointSet = word_calib_Points[i];
		//  通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点    
		projectPoints(tempPointSet, rotation_vectors[i], translation_vectors[i], CamearMatrix, distortion_coeffs, new_image_points);

		// 计算新的投影点和旧的投影点之间的误差
		vector<Point2f> singleImagePoint = corners_Seq[i];
		Mat tempImagePointMat = Mat(1, singleImagePoint.size(), CV_32FC2);
		Mat image_points2Mat = Mat(1, new_image_points.size(), CV_32FC2);
		for (int j = 0; j < singleImagePoint.size(); j++)
		{
			image_points2Mat.at<Vec2f>(0, j) = Vec2f(new_image_points[j].x, new_image_points[j].y);
			tempImagePointMat.at<Vec2f>(0, j) = Vec2f(singleImagePoint[j].x, singleImagePoint[j].y);
		}

		err = norm(image_points2Mat, tempImagePointMat, NORM_L2);
		total_err += err /= point_counts[i];


		std::cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
		fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl;
	}
	std::cout << "总体平均误差:" << total_err / image_count << "像素" << endl;
	fout << "总体平均误差:" << total_err / image_count << "像素" << endl << endl;
	std::cout << "评价完成!" << endl;

	/************************************************************************
		   保存定标结果
	*************************************************************************/
	std::cout << "开始保存定标结果………………" << endl;
	Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); // 保存每幅图像的旋转矩阵 

	fout << "相机内参数矩阵:" << endl;
	fout << CamearMatrix << endl << endl;
	fout << "畸变系数:\n";
	fout << distortion_coeffs << endl << endl << endl;
	for (int i = 0; i < image_count; i++)
	{
		fout << "第" << i + 1 << " 幅图像的旋转向量:" << endl;
		fout << rotation_vectors[i] << endl;

		// 将旋转向量转换为相对应的旋转矩阵 
		Rodrigues(rotation_vectors[i], rotation_matrix);
		fout << "第" << i + 1 << " 幅图像的旋转矩阵:" << endl;
		fout << rotation_matrix << endl;
		fout << "第" << i + 1 << " 幅图像的平移向量:" << endl;
		fout << translation_vectors[i] << endl << endl;
	}
	std::cout << "完成保存" << endl;
	fout << endl;





	 矫正图像
	//std::string   origin_image_path = "D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\30.png";
	//Mat  origion_image = imread(origin_image_path);
	//Mat image_rectify, image_rectify_2;
	 矫正方法1    undistort 
	//undistort(origion_image, image_rectify, CamearMatrix, distortion_coeffs);
	//imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify.png", image_rectify);

	 矫正方法2    initUndistortRectifyMap  和 remap 方法
	//Mat map1, map2;
	//Size image_Size = origion_image.size();


	//initUndistortRectifyMap(CamearMatrix, distortion_coeffs, Mat(), CamearMatrix, image_Size, CV_16SC2, map1, map2);
	//remap(image, image_rectify_2, map1, map2, INTER_LINEAR);
	//imwrite("D:\\work\\pcl_workplaces\\Algorithm2D_opencv\\camera\\images_rectify\\image_rectify_2.png", image_rectify_2);
}

opencv 标定结果

opencv apriltag 标定_角点_08

 内参矩阵

opencv apriltag 标定_opencv_09

 opencv 结果和halcon 结果对比

* Calibration 01: Code generated by Calibration 01
* 相机内参
*halcon 标定结果
CameraParameters := ['area_scan_division',0.00839081,-1987.45,8.35072e-06,8.3e-06,    362.43,  291.71,  768, 576]
* 矫正后的内参       ['area_scan_division', 0.00839081, 0.0,   8.52659e-06,8.3954e-06, 361.554, 291.807, 768, 576]



*opencv 结果
*CamearMatrix:
*[1003.508370362814, 0, 355.145188731808;
 *0, 1004.893192592846, 294.7135512484718;
 *0, 0, 1]



*---将halcon 结果转换为opencv 结果
* opencv_sx=halcon_f/haocon_sx 
* opencv_sy=halcon_f/haocon_sy
 
 x:=0.00839081/8.35072e-06
 y:=0.00839081/8.35072e-06
 halcon2opencv:=[x,0,355.145188731808,0,y,294.7135512484718,0, 0, 1]
*[1003.508370362814, 0, 355.145188731808;
 *0, 1004.893192592846, 294.7135512484718;
 *0, 0, 1]

 

opencv apriltag 标定_角点_10