# 1 簡介 計算傾斜與旋轉對象在世界座標系下的尺寸。該樣例主要計算HALCON標定板的邊距與標誌點的直徑。
2 測量流程
2.1 相機標定
- 創建相機標定模型
- 設置標定模型的參數
- 提取每一張圖像的標誌點與外輪廓
- 執行相機標定,獲取標定誤差
2.2 邊距
- 獲取標定板的黑色外框
- 通過輪廓選擇平行的輪廓線
- 獲取過中心且垂直於輪廓線的標誌點座標
- 計算交點
- 構建二維測量模型
- 通過測量模型計算邊緣點座標
- 映射到世界座標系
- 在世界座標系中計算邊距
2.3 標誌點半徑
- 提取標誌點外輪廓
- 橢圓擬合
- 映射到世界座標系
- 世界座標系下的橢圓擬合
- 計算半徑的平均值於方差
3 代碼解析
3.1 程序初始化
// 關閉當前的圖像窗口
dev_close_window ()
// 以指定的位置、尺寸、背景顏色打開一個新的窗口
dev_open_window (0, 0, 768, 576, 'black', WindowHandle)
// 取消所有自動更新
dev_update_off ()
// 設置繪製模式
dev_set_draw ('margin')
// 設置顯示線寬
dev_set_line_width (3)
// 設置顯示字體
set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
3.2 相機標定
// 指定標定板描述文件
CalTabDescrFile := 'caltab_big.descr'
/* 設置初始相機內參
* 參數按位置順序依次爲:
* 鏡頭焦距
* 徑向扭曲係數
* 像元水平尺寸(單位:米)
* 像元垂直尺寸(單位:米)
* 圖像座標系下的主點列座標(單位:像素)
* 圖像座標系下的主點行座標(單位:像素)
* 圖像寬度(單位:像素)
* 圖像高度(單位:像素)
*/
gen_cam_par_area_scan_division (0.008, 0, 0.0000086, 0.0000086, 384, 288, 768, 576, StartCamPar)
/* 創建相機標定模型
* 參數按位置順序依次爲:
* 標定的類型
* 同時標定的相機數量
* 標定板對象的數量
* 輸出的標定模型句柄
*/
create_calib_data ('calibration_object', 1, 1, CalibDataID)
/* 設置標定模型的初始相機內參
* 參數按位置順序依次爲:
* 標定模型句柄
* 相機索引
* 相機類型
* 相機內參
*/
set_calib_data_cam_param (CalibDataID, 0, [], StartCamPar)
/* 通過標定板描述文件確定標定板對象
* 參數按位置順序依次爲:
* 標定句柄
* 標定對象索引
* 標定對象描述文件
*/
set_calib_data_calib_object (CalibDataID, 0, CalTabDescrFile)
// 指定用於標定的標定板圖像的數量
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')
/* 在圖像中查找標定對象
* 參數按位置順序依次爲:
* 標定對象圖像
* 標定句柄
* 相機索引
* 標定對象索引
* 額外參數名稱集合
* 額外參數值集合
*/
find_calib_object (Image, CalibDataID, 0, 0, I - 1, [], [])
/* 獲取初始標定參數
* 參數按位置順序依次爲:
* 標定句柄
* 標定數據類型,'calib_obj', 'calib_obj_pose', 'camera', 'model', 'tool'
* 索引
* 數據名
* 返回數據值
*/
get_calib_data (CalibDataID, 'camera', 0, 'init_params', StartCamPar)
/* 獲取標誌點的圖像座標與姿態
* 參數按位置順序依次爲:
* 標定句柄
* 相機索引
* 標定對象索引
* 標定對象姿態索引
* 標誌點行座標
* 標誌點列座標
* 標誌點索引號
* 標定對象相對於相機座標系的姿態
*/
get_calib_data_observ_points (CalibDataID, 0, 0, I - 1, Row, Column, Index, Pose)
/* 獲取標定對象邊界輪廓
* 參數按位置順序依次爲:
* 輸出輪廓
* 標定句柄
* 輪廓名,'caltab', 'last_caltab', 'marks'
* 相機索引
* 標定對象索引
* 標定對象姿態索引
*/
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')
stop ()
// 執行標定
calibrate_cameras (CalibDataID, Error)
// 獲取標定後的相機參
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
3.3 標定對象中心點提取
爲了計算邊距或標誌點的半徑,都需要對圖像進行預處理。因此,先介紹預處理函數。
/* 函數的參數依次爲:
* Image : (IN) 標定對象圖像
* PlateRegion : (OUT) 標定對象區域
* CalibDataID : (IN) 標定句柄
* PoseIndex : (IN) 標定姿態索引
* Distance : (OUT) 邊距的粗略距離
* Phi : (OUT) 標定對象的旋轉角度
* RowCenter : (OUT) 標定板中心行座標
* ColumnCenter: (OUT) 標定板中心列座標
*/
// 固定閾值二值化
threshold (Image, Region, 0, 120)
// 連通域提取
connection (Region, ConnectedRegions)
// 根據條件選擇區域
select_shape (ConnectedRegions, SelectedRegions, ['holes_num','rect2_len1','rect2_len2'], 'and', [1,120,120], [1,200,200])
// 填充區域
fill_up (SelectedRegions, PlateRegion)
/* 根據區域的邊界生成輪廓
* 第三個參數爲提取邊界的模式,有以下值:
* center : 邊界像素的中心點連接成的輪廓
* border : 邊界像素的外輪廓
* border_holes : 除了邊界像素的被提取,孔邊界也被提取
*/
gen_contour_region_xld (PlateRegion, Contours, 'center')
/* 將輪廓分割成直線段和圓弧段或橢圓弧段
* 參數按位置順序依次爲:
* 輸入輪廓
* 分割後輪廓
* 分割模式,'lines', 'lines_circles', 'lines_ellipses'
* 平滑輪廓使用的點數
* 第一次迭代使用弧替代直線段的最大距離
* 第二次迭代使用弧替代直線段的最大距離
*/
segment_contours_xld (Contours, ContoursSplit, 'lines', 7, 4, 2)
// 計算迴歸線
regress_contours_xld (ContoursSplit, RegressContours, 'no', 1)
// 輪廓選擇
select_contours_xld (RegressContours, VerticalContours, 'direction', rad(45), rad(135), -0.5, 0.5)
// 輪廓選擇
select_contours_xld (VerticalContours, LongContours, 'length', 150, 500, -0.5, 0.5)
// 選擇第一個垂直邊緣
select_obj (LongContours, Contour, 1)
// 獲取輪廓點
get_contour_xld (Contour, Rows, Columns)
// 獲取起始點
RowBegin1 := Rows[0]
ColBegin1 := Columns[0]
// 獲取終止點
RowEnd1 := Rows[|Rows| - 1]
ColEnd1 := Columns[|Columns| - 1]
// 選擇第二個垂直邊緣
select_obj (LongContours, Contour, 2)
// 獲取輪廓點
get_contour_xld (Contour, Rows, Columns)
// 獲取起始點
RowBegin2 := Rows[0]
ColBegin2 := Columns[0]
// 獲取終止點
RowEnd2 := Rows[|Rows| - 1]
ColEnd2 := Columns[|Columns| - 1]
// 獲取標誌點的中心座標
get_calib_data_observ_points (CalibDataID, 0, 0, PoseIndex - 1, Row, Column, PoseIndex, _Pose)
// 選取過中心且垂直於提取垂直邊緣的標誌點中心
Row1 := Row[find(PoseIndex,21)]
Row2 := Row[find(PoseIndex,27)]
Column1 := Column[find(PoseIndex,21)]
Column2 := Column[find(PoseIndex,27)]
// 顯示直線段,並計算焦點
dev_get_window (WindowHandle)
disp_line (WindowHandle, Row1, Column1, Row2, Column2)
disp_line (WindowHandle, RowBegin1, ColBegin1, RowEnd1, ColEnd1)
intersection_lines (Row1, Column1, Row2, Column2, RowBegin1, ColBegin1, RowEnd1, ColEnd1, RowA, ColA, IsOverlapping)
// 顯示直線段,並計算焦點
dev_get_window (WindowHandle)
disp_line (WindowHandle, Row1, Column1, Row2, Column2)
disp_line (WindowHandle, RowBegin2, ColBegin2, RowEnd2, ColEnd2)
intersection_lines (Row1, Column1, Row2, Column2, RowBegin2, ColBegin2, RowEnd2, ColEnd2, RowB, ColB, IsOverlapping)
// 計算距離
distance_pp (RowA, ColA, RowB, ColB, Distance)
// 計算角度
line_orientation (RowA, ColA, RowB, ColB, Phi)
// 計算中心座標
RowCenter := (RowA + RowB) / 2
ColumnCenter := (ColA + ColB) / 2
3.4 邊距測量
// 讀取圖像
read_image (Image, 'calib/calib-3d-coord-01')
// 獲取測量參數
get_measure_positions (Image, PlateRegion, CalibDataID, 0, Distance, Phi, RowCenter, ColumnCenter)
// 生成測量矩形,用於顯示
gen_rectangle2_contour_xld (Rectangle, RowCenter, ColumnCenter, Phi, Distance * 0.52, 8)
// 矩形區域測量
gen_measure_rectangle2 (RowCenter, ColumnCenter, Phi, Distance * 0.52, 8, 768, 576, 'nearest_neighbor', MeasureHandle)
// 執行測量
measure_pos (Image, MeasureHandle, 1, 40, 'all', 'all', RowEdge, ColumnEdge, Amplitude, Distance1)
// 顯示測量點的位置
Rows := [RowEdge[0],RowEdge[|RowEdge| - 1]]
Columns := [ColumnEdge[0],ColumnEdge[|RowEdge| - 1]]
gen_cross_contour_xld (Cross, Rows, Columns, 16, Phi)
// 獲取標定板的姿態
get_calib_data (CalibDataID, 'calib_obj_pose', [0,0], 'pose', Pose)
// 圖像座標系到世界座標系的映射
image_points_to_world_plane (CamParam, Pose, Rows, Columns, 'm', SX, SY)
// 世界座標系的距離季孫
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')
3.5 半徑測量
// 腐蝕區域
erosion_circle (PlateRegion, ROI, 17.5)
// 摳圖
reduce_domain (Image, ROI, ImageReduced)
// 亞像素邊緣提取
edges_sub_pix (ImageReduced, Edges, 'canny', 1, 20, 60)
// 輪廓選擇
select_contours_xld (Edges, SelectedEdges, 'contour_length', 20, 99999999, -0.5, 0.5)
// 橢圓擬合
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)
// 映射到世界座標
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')