基于opencv的用于精确测量的单目相机标定,世界坐标和像素坐标相互转换

1.opencv自带标定需要定义棋盘格尺寸,必须拍全,大大限定了标定条件,本示例实现标定板角点提取,算法参考基于生长的棋盘格角点检测,二维码定义棋盘格实际坐标系和棋盘格规格,不用输入任何参数(自动识别棋盘格规格,本示例用的2*2mm标定片),绿色为识别到的角点,上方数字为对应点的棋盘格世界坐标(图片比较大,截取了一部分)

 

 2.将标定结果打包保存成本地文件,其中包括校正前后图片,保存内容如下图结构体ccbi2文件,大小大概5M

 3.读取标定文件,并利用标定文件里的标定参数(相机内参,畸变,外参等)将世界坐标Z=0平面点转换成像素坐标,映射在畸变校正后的标定板图片上,基本每个角点都是对准的,如果用像素坐标(亚像素坐标)转世界坐标误差大概在0.01mm;

 

4.下面为手动估计着找的4个非亚像素坐标点,转换为世界坐标,映射到图片上的效果,如果是输入物理坐标,算出亚像素坐标,再将亚像素坐标转换为世界坐标,基本和输入的差距在0.01mm

5.反向找到畸变校正后图片角点坐标,并把角点坐标用标定参数全部转换为世界坐标,对比和标定之前世界坐标值之间的差异(图片比较大,放大展示一下图片四个角的值吧,一般图片四个角误差最大,中心误差小)

左上

 右上

左下

 

 右下

 

 

 以下是代码结构逻辑图

int main()
{
	
	clock_t start, end;
	start = clock();
	checkerboard cb;	

	//创建并生成标定板
	//cb.checkerboard_create(30,30,2, src1,true);

	//Mat src1 = imread("E:\\Project_Opencv\\Class_Blues\\Class_Blues\\pic\\Image_3.png", IMREAD_GRAYSCALE);	

	相机标定
	//CalibratedCheckerBoardInfo ccbi1 = cb.checkerboard_calibration(src1);
	将标定结果保存于本地文件
	//cb.CaliResultFileWrite("checkerboardresult.yml", ccbi1);
	
	//读取本地标定信息
	CalibratedCheckerBoardInfo ccbi2 ;	
	cb.CaliResultFileRead("checkerboardresult.yml", ccbi2);
	
	//从本地标定文件拿到原始标定板图片
	Mat img_src = ccbi2.Img_checkerboard;
	
	//从本地文件拿到标定后校正图片
	Mat img_cali = ccbi2.Img_Calibrated;

	//校正前后后灰度图片转彩色
	Mat img_cali_color,img_src_color,img_src_color1;

	cvtColor(img_cali, img_cali_color,COLOR_GRAY2BGR);
	cvtColor(img_src, img_src_color, COLOR_GRAY2BGR);
	cvtColor(img_src, img_src_color1, COLOR_GRAY2BGR);
	//undistort(color_img, img_calib, ccbi2.cameraMatrix, ccbi2.distCoeffs);
	
	//定义世界坐标点
	vector<Point3d> worldpoints;
	for (int i = 10; i < 80; i++)
	{
		for (int j = -22; j < 70; j++)
		{
			Point3d temp;
			temp.x = i * 2;
			temp.y = j * 2;
			temp.z = 0;
			worldpoints.push_back(temp);
		}
	}
	//世界坐标转换到图片像素坐标
	struct CalibratedResultInfo caliInfo;
	caliInfo.cameraMatrix = ccbi2.cameraMatrix;
	caliInfo.distCoeffs = ccbi2.distCoeffs;
	caliInfo.rvecsMat = ccbi2.rvecsMat;
	caliInfo.tvecsMat = ccbi2.tvecsMat;
	vector<Point2d> pixel_points;
	cb.WorldPoints2PixelPoints(caliInfo, worldpoints, pixel_points);	
	
	//将世界坐标转换后的像素坐标画在图片上
	for (int i = 0; i < pixel_points.size(); i++)
	{
		drawMarker(img_cali_color, pixel_points[i],Scalar(0,255,0), MARKER_TILTED_CROSS,20,2,8);
		drawMarker(img_src_color, pixel_points[i], Scalar(0, 0, 255), MARKER_TILTED_CROSS, 20, 2, 8);		
	}

	//绘制识别到的棋盘格角点和重投影点
	for (int i = 0; i < ccbi2.Pixel_Points.size(); i++)
	{
		
		drawMarker(img_src_color1, ccbi2.Pixel_Points[i], Scalar(0, 255, 0), MARKER_SQUARE, 15, 2, 8);
		drawMarker(img_src_color1, ccbi2.Reprojected_Points[i], Scalar(255, 0, 0), MARKER_TILTED_CROSS, 20, 2, 8);
	}
	//像素坐标转世界坐标
	//定义图片上四个关键点的像素坐标
	Point2f P0 = Point2f(1299,705);
	Point2f P1 = Point2f(1880, 690);
	Point2f P2 = Point2f(1892, 1282);
	Point2f P3 = Point2f(1309, 1296);
	vector<Point2f> checkerboard_pixel_points;
	vector<Point3f> world_points;
	checkerboard_pixel_points.push_back(P0);
	checkerboard_pixel_points.push_back(P1);
	checkerboard_pixel_points.push_back(P2);
	checkerboard_pixel_points.push_back(P3);

	cb.PixelPoint2WorldPoint(ccbi2.cameraMatrix, ccbi2.rvecsMat, ccbi2.tvecsMat, checkerboard_pixel_points, world_points);
	//图片上画出像素转世界坐标位置
	Mat img_cali_color1;
	cvtColor(img_cali, img_cali_color1, COLOR_GRAY2BGR);
	for (int i = 0; i < world_points.size(); i++)
	{
		cv::circle(img_cali_color1, checkerboard_pixel_points[i], 8, Scalar(0, 255, 0), 2, 8);
		string temp_xy = format("X: %.2f Y:%.2f", world_points[i].x, world_points[i].y);
		putText(img_cali_color1, temp_xy, Point(int(checkerboard_pixel_points[i].x-50), int(checkerboard_pixel_points[i].y-30)), FONT_HERSHEY_SIMPLEX, 1.5, Scalar(255, 255, 0),2, 8, 0);
	}
	imwrite("image_point.png", img_src_color1);
	end = clock();		//程序结束用时
	double endtime = (double)(end - start) / CLOCKS_PER_SEC;
	cout << "Total time:" << endtime * 1000 << "ms" << endl;	//ms为单位
	imwrite("calibcheck.png", img_cali_color);
	imwrite("uncalibcheck.png", img_src_color);
	waitKey(0);
	destroyAllWindows();
	return 0;
}

  • 3
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
OpenCV中,像素坐标系是指像上的点的坐标表示方式,而世界坐标系是指物体在现实世界中的坐标表示方式。通常,我们需要将像素坐标转换世界坐标,或者将世界坐标转换像素坐标。为了实现这种转换,我们可以利用相机的内参、畸变参数和外参等标定参数。 首先,我们需要读取标定文件,并提取出标定参数,包括相机的内参、畸变参数和外参等。然后,我们可以利用这些参数将世界坐标中的点转换像素坐标。这一步骤可以通过使用OpenCV中的函数来实现,例如`cv2.projectPoints()`函数。 另外,我们也可以通过手动估计的非亚像素坐标点将其转换世界坐标,然后将其映射到像上,通过比较转换后的像素坐标和输入的差距来评估转换的准确性。 总的来说,通过读取标定文件中的参数或手动估计的坐标点,我们可以实现Opencv像素坐标系到世界坐标系的转换。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [OpenCV学习——像坐标系,相机坐标系和世界坐标系](https://blog.csdn.net/AileenNut/article/details/76922534)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [基于opencv用于精确测量单目相机标定世界坐标像素坐标相互转换](https://blog.csdn.net/m0_68681926/article/details/128003512)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值