相机标定之后,我们会得到相机的内参,和相机的外参。
然而这些结果我们如何在实际项目中使用?当前这个帖子演示使用标定板标定后,
直接测量标定板的宽与标定板标定黑点的圆心
具体思路是 建立 标定 模型,加载设定相机的初始化信息及标定 板描述文件,
找到标定板并进行多张图像的标定 ,得到相机内参与外参数。
对要测量的区域 进行像素 处理和拟合测量,得像素 距离。
根据 相机内参 外参及像素距离求出对应世界坐标系下点的坐标及对应轮廓长度。
6.4 halcon相机标定结果的应用
下面是实例源代码及注释:
- *关闭当前打开的窗口
- dev_close_window ()
- *打开背景黑色窗口,左上角,右下角坐标为0, 0, 768, 576
- dev_open_window (0, 0, 768, 576, 'black', WindowHandle)
- *关闭窗口更新功能
- dev_update_off ()
- *设置绘制边缘模式
- dev_set_draw ('margin')
- *设置线宽为3
- dev_set_line_width (3)
- set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
- *定义变量指向标定板描述文件。
- CalTabDescrFile := 'caltab_big.descr'
- *定义数组并初始化
- StartCamPar := [0.008, 0, 0.0000086, 0.0000086, 384, 288, 768, 576]
- *创建标定板数据模型,用于存储标定数据,标定 文件,标定过程中的设置等。
- *参数'calibration_object'表示标定类型,这里表示 一般的相机标定,如果是手眼标定
- *则是其他类型,可以参考帮助文档的各种标定类型,参数2表示相机数据为1,
- *参数3表示标定板数量为1,CalibDataID为输出句柄,表示标定模型句柄。
- create_calib_data ('calibration_object', 1, 1, CalibDataID)
- *设置用于标定的相机参数与类型,CalibDataID为前面创建的模型句柄,
- *0为相机索引,当有多个相机时,通过此参数与相机对应, []表示相机类型,这里默认表示面扫相机,
- *StartCamPar表示相机内参,按照顺序分别对应内参为【f, k, Sx, Sy, Cx, Cy, w, h】
- set_calib_data_cam_param (CalibDataID, 0, [], StartCamPar)
- *设置标定板的描述文件,0为标定板索引,CalTabDescrFile为标定板文件名。
- set_calib_data_calib_object (CalibDataID, 0, CalTabDescrFile)
- *循环读入10张图处理。
- NumImages := 10
- for I := 1 to NumImages by 1
- read_image (Image, 'calib/calib-3d-coord-' + I
- 02d')
- dev_display (Image)
- Message := 'Find calibration plate in\nall calibration images (' + I + '/' + NumImages + ')'
- disp_message (WindowHandle, Message, 'window', 12, 12, 'black', 'true')
- * 在图中找到标定板获得相关信息,Image为输入图像, CalibDataID为输入标定模型句柄
- *0, 0分别表示相机和标定板索引为0, I - 1表示标定板姿态索引,从0开始计数,
- *[], []为要设置的参数及参数所对应的数值,这里留空不使用。
- find_calib_object (Image, CalibDataID, 0, 0, I - 1, [], [])
- *获取标定数据,CalibDataID为输入的模型句柄, 'camera'为模型中数据 项类型为相机类型,
- *参数三为数据类型的索引,参数四为数据项名称,也就是想得到哪个数据,'init_params'表示
- *我们想得到相关初始化参数,StartCamPar为所得到的数据项值。
- get_calib_data (CalibDataID, 'camera', 0, 'init_params', StartCamPar)
- *得到模型中的标定信息,CalibDataID, 0, 0分别人模型输入句柄,相机和标定板对应索引为0.
- *I - 1为标定板姿态索引,从0计数。 Row, Column, Index, Pose为输出参数,分别表示为
- *标定点圆心坐标,索引及预估的相机外参。
- get_calib_data_observ_points (CalibDataID, 0, 0, I - 1, Row, Column, Index, Pose)
- *获取轮廓信息,Contours为输出得到的轮廓, CalibDataID为标定模句柄,
- *'caltab'表示要得到哪种轮廓,这里是标定板的轮廓, 0, 0, I - 1,三项分别为相机,标定板,姿态索引。
- get_calib_data_observ_contours (Contours, CalibDataID, 'caltab', 0, 0, I - 1)
- *生成亚像素精度的十字叉
- gen_cross_contour_xld (Cross, Row, Column, 6, 0.785398)
- dev_set_color ('green')
- dev_display (Contours)
- dev_set_color ('yellow')
- dev_display (Cross)
- endfor
- disp_continue_message (WindowHandle, 'black', 'true')
- *暂停程序执行,可以方便查看前面代码效果,按F5后,继续执行代码。
- stop ()
- *执行相机标定
- calibrate_cameras (CalibDataID, Error)
- *算子介绍如上,这里获取的是索引为0的'camera'的'params'参数,参数值保存在CamParam
- get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
- * 上面代码为标定部分,标定好后,就可以使用标定的结果进行测量。
- for I := 1 to NumImages by 1
- read_image (Image, 'calib/calib-3d-coord-' + I
- 02d')
- * 获取测量位置,Image为输入图像, PlateRegion为输出区域, CalibDataID为标定模型句柄,
- *I, Distance, Phi, RowCenter, ColumnCenter分别为位姿索引,区域,距离值,旋转角度,中心行列坐标。
- get_measure_positions (Image, PlateRegion, CalibDataID, I, Distance, Phi, RowCenter, ColumnCenter)
- *使用上面得到的数据生成矩形区域
- gen_rectangle2_contour_xld (Rectangle, RowCenter, ColumnCenter, Phi, Distance * 0.52, 8)
- *创建测量矩形RowCenter, ColumnCenter为矩形直剖面线中心点行列坐标,
- *Phi为直剖面线方向, Distance * 0.52, 8为测量矩形的半宽半高,
- *768, 576为要处理图像 的宽高, 'nearest_neighbor'表示使用近邻插值方法, MeasureHandle为输出的矩形句柄
- gen_measure_rectangle2 (RowCenter, ColumnCenter, Phi, Distance * 0.52, 8, 768, 576, 'nearest_neighbor', MeasureHandle)
- * 测量边缘间距,Image, MeasureHandle为输入图像和矩形句柄,
- *1, 40为高斯平滑系数和最小边缘幅度值, 'all'表示检测所有正负边缘,
- *'all'表示选中所有边缘, RowEdge, ColumnEdge为边缘中心行列坐标,
- *Amplitude, Distance1为边缘幅度大小和相邻边缘间距
- measure_pos (Image, MeasureHandle, 1, 40, 'all', 'all', RowEdge, ColumnEdge, Amplitude, Distance1)
- close_measure (MeasureHandle)
- Rows := [RowEdge[0],RowEdge[|RowEdge| - 1]]
- Columns := [ColumnEdge[0],ColumnEdge[|RowEdge| - 1]]
- gen_cross_contour_xld (Cross, Rows, Columns, 16, Phi)
- * 获取相机外参,CalibDataID为标定模型句柄, 'calib_obj_pose'为数据类型,这里表示 要获得相机外部参数,
- *[0,I - 1]为对应数据类型的索引, 'pose'表示 要获得的是姿态 参数, Pose为输出的相机参数。
- get_calib_data (CalibDataID, 'calib_obj_pose', [0,I - 1], 'pose', Pose)
- *此函数是求实际 空间姿态 坐标的关键函数。主要根据前面求得的相机内外 参数和像素 坐标点,求出对应的实际 点的世界坐标系
- *CamParam为相机内参, Pose为相机外参, Rows, Columns为上边求得的首末边缘行列坐标,
- *'m'表示实际距离单位为米, SX, SY为输出的世界坐标系下对应点实际XY坐标。
- image_points_to_world_plane (CamParam, Pose, Rows, Columns, 'm', SX, SY)
- *求两点间距Width为输出结果间距。
- distance_pp (SY[0], SX[0], SY[1], SX[1], Width)
- *
- *下面为显示结果
- dev_display (Image)
- dev_set_color ('white')
- dev_set_line_width (3)
- dev_display (Rectangle)
- dev_set_color ('green')
- dev_set_draw ('fill')
- dev_set_line_width (2)
- dev_display (Cross)
- dev_set_draw ('margin')
- disp_message (WindowHandle, 'Width = ' + (Width * 100)
- 8.3f' + 'cm', 'window', 12, 12, 'black', 'true')
- disp_continue_message (WindowHandle, 'black', 'true')
- stop ()
- * 下面再进一步提取黑点的大小
- *用半径为17.5的圆形结构进行区域腐蚀处理。
- erosion_circle (PlateRegion, ROI, 17.5)
- *在图像上获取区域ROI对应的子图像ImageReduced
- reduce_domain (Image, ROI, ImageReduced)
- *在子图像中提取亚像素精度边缘。ImageReduced, Edges分别为输入图像输出边缘,
- *'canny'为提取算法你称, 1高斯平滑系数,这里越大边缘细节越少,
- *20, 60表示canny算法中,低高阈值。
- edges_sub_pix (ImageReduced, Edges, 'canny', 1, 20, 60)
- *对提取的边缘进行筛选,选择周长范围在20, 99999999的边缘,后面两参数在这里无效。
- select_contours_xld (Edges, SelectedEdges, 'contour_length', 20, 99999999, -0.5, 0.5)
- *进行椭圆的拟合。SelectedEdges为输入轮廓变量, 'fitzgibbon'为拟合算法名称,
- *-1表示所有点参与拟合, 2表示轮廓首尾相互距离小于2为封闭,
- *0表示轮廓开始结束点参与拟合个数, 200表示使用VOSS方法时分割圆弧的个数,
- *3为最大迭代次数, 2为离群值剪切因子,越小表示 忽略离群值越多,
- *Row, Column, Phi, Radius1, Radius2, StartPhi, EndPhi, PointOrder分别表示输出的椭圆中心行列坐标,
- *主轴角度,椭圆长短半轴长度,开始与结束点角度,椭圆边界点次序。
- fit_ellipse_contour_xld (SelectedEdges, 'fitzgibbon', -1, 2, 0, 200, 3, 2, Row, Column, Phi, Radius1, Radius2, StartPhi, EndPhi, PointOrder)
- MeanRadius1 := mean(Radius1)
- MeanRadius2 := mean(Radius2)
- DevRadius1 := deviation(Radius1)
- DevRadius2 := deviation(Radius2)
- *将椭圆转到世界坐标系下,它们将会是圆形状态,再将圆的单位从米转为毫米
- *将一个亚像素精度轮廓转换到世界坐标系去,此时这些世界坐标系X为0.
- *SelectedEdges为输入轮廓, WorldCircles为输出轮廓, CamParam为相机内参, Pose为相机外参,
- *'mm'表示世界坐标系单位为mm
- contour_to_world_plane_xld (SelectedEdges, WorldCircles, CamParam, Pose, 'mm')
- * 在世界坐标系下进行椭圆拟合。
- fit_ellipse_contour_xld (WorldCircles, 'fitzgibbon', -1, 2, 0, 200, 3, 2, Row, Column, Phi, RadiusW1, RadiusW2, StartPhi, EndPhi, PointOrder)
- MeanRadiusW1 := mean(RadiusW1)
- MeanRadiusW2 := mean(RadiusW2)
- DevRadiusW1 := deviation(RadiusW1)
- DevRadiusW2 := deviation(RadiusW2)
- *
- * 显示结果
- dev_display (Image)
- dev_set_color ('yellow')
- dev_set_line_width (3)
- dev_display (SelectedEdges)
- Message := 'Measured dimensions of the ellipses'
- Message[0] := ' Mean Radius1; Mean Radius2; (Standard deviations [%])'
- Message[1] := 'Image coordinates: ' + MeanRadius1
- 5.2f' + 'px; ' + MeanRadius2
- 5.2f' + 'px (' + (DevRadius1 / MeanRadius1 * 100)
- 4.2f' + ', ' + (DevRadius2 / MeanRadius2 * 100)
- 4.2f' + ')'
- Message[2] := 'World coordinates: ' + (MeanRadiusW1 / 10)
- 5.2f' + 'cm; ' + (MeanRadiusW2 / 10)
- 5.2f' + 'cm (' + (DevRadiusW1 / MeanRadiusW1 * 100)
- 4.2f' + ', ' + (DevRadiusW2 / MeanRadiusW2 * 100)
- 4.2f' + ')'
- disp_message (WindowHandle, Message, 'window', 12, 12, 'black', 'true')
- if (I < 10)
- disp_continue_message (WindowHandle, 'black', 'true')
- stop ()
- endif
- endfor
- clear_calib_data (CalibDataID)
复制代码
|