基于opencv的用于精确测量的单目相机标定,世界坐标和像素坐标相互转换
opencv
OpenCV: 开源计算机视觉库
项目地址:https://gitcode.com/gh_mirrors/opencv31/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;
}
GitHub 加速计划 / opencv31 / opencv
77.38 K
55.71 K
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:2 个月前 )
c3747a68
Added Universal Windows Package build to CI. 5 天前
9b635da5 - 5 天前
更多推荐
已为社区贡献1条内容
所有评论(0)