一、前言
在我的工業相機專欄裡已經将相機标定涉及到的理論部分講解完畢,為什麼要标定以及标定要求出什麼參數呢,用一個Halcon 例程來幫助了解。
這個例程是比較經典的标定程式,基本将标定過程講的比較清楚,用的标定圖像是系統自帶的,如果想自己做可以在Halcon助手選項裡拍照生成。
二、代碼
* 設定視窗和字型
ImgPath := '3d_machine_vision/calib/'
dev_close_window ()
dev_open_window (0, 0, 640, 480, 'black', WindowHandle)
dev_update_off ()
dev_set_draw ('margin')
dev_set_line_width (3)
set_display_font (WindowHandle, 22, 'mono', 'true', 'false')
*
* 相機标定過程
*
* 生成面陣相機初始參數,參數均為相機已知參數
* (: : 焦距, 畸變因子, Sx, Sy, Cx, Cy, 圖像寬度, 圖像高度 : CameraParam存儲元組)
* (Sx, Sy為傳感器晶片上兩個相鄰單元之間的水準豎直距離,也就是像素的大小,機關為m/像素;Cx, Cy為圖像原點的行列坐标,機關為像素;)
gen_cam_par_area_scan_division (0.012, 0, 0.00000375, 0.00000375, 640, 480, 1280, 960, StartCamPar)
*建立Halcon标定資料模型(标定類型,相機數量,标定物數量,模型句柄)
*其作用在于指定相機标定類型,設定标定過程,存儲标定資料和結果。
create_calib_data ('calibration_object', 1, 1, CalibDataID)
*設定相機初始參數(句柄,相機參數索引,相機類型,起始相機參數)
*此段程式跑完的句柄見下圖。
set_calib_data_cam_param (CalibDataID, 0, [], StartCamPar)
*設定标定闆初始參數(句柄,标定闆數量索引,标定闆描述(檔案名或者标定闆所有點的三維坐标))此例生成三維坐标系坐标值(x,y,z),其中z為0;坐标原點在finder模式的中心Mark點中心,坐标系的z軸指向标定闆,x軸指向右側,y軸指向下方,視角沿z軸。
set_calib_data_calib_object (CalibDataID, 0, 'calplate_80mm.cpd')//此檔案可用記事本打開,裡面記錄了标定闆五個mark模式的點坐标和半徑。
calibration marks at y = -0.0290538 m
-0.0374193548387097 -0.0290537554818005 0.000645161290322581
*讀圖,并生成落輪廓和點的特征參數值
NumImages := 7
for I := 1 to NumImages by 1
*讀圖,路徑在C槽使用者公共文檔裡,% 2d是将數字按寬度為2,采用右對齊方式輸出,若資料位數不到2位,則左邊補空格,是以檔案名為calib_0X。
read_image (Image, ImgPath + 'calib_image_' + I$'02d')
dev_display (Image)
*尋找标定闆并在模型中設定提取的點和輪廓資訊
*(圖像變量,句柄,相機的索引,标定闆的索引,計數變量,參數名(使用六邊形标定闆,設定額外的參數值,可以使圖像平滑等),參數值)
*該算子将标定闆每個點的輪廓和點坐标,索引和此圖像标定闆相對于相機坐标系的位姿提取出來,儲存在句柄中。
*find_caltab在圖像中尋找标定闆是基于标定闆的特征——在一個亮的區域中存在黑色Mark點
*算子對圖像高斯濾波(核大小為SizeGauss),接着門檻值分割(與之大小為MarkThresh)将标定闆的區域找出來.
find_calib_object (Image, CalibDataID, 0, 0, I, [], [])
*從标定資料模型中擷取基于輪廓的觀測資料
*(輪廓變量,句柄,傳回标定闆查找模式的輪廓内含三個參數(marks(傳回每一個輪廓,calib(傳回查找模式的輪廓),last_caltab(會傳回上次成功的查找結果,但會忽略索引資訊)),相機索引,标定闆索引,變量)
* 标定闆查找器:有兩種模式,一種是特殊标記六邊形(即一個标記及其六個相鄰标記),其中四個或六個标記包含一個孔;另外是帶有矩形排列标記的校準闆:校準闆的邊緣在一角有一個三角形。這裡是第一種,結果如下圖所示。
get_calib_data_observ_contours (Caltab, CalibDataID, 'caltab', 0, 0, I)
*從标定資料模型中擷取基于點的觀測資料
get_calib_data_observ_points (CalibDataID, 0, 0, I, Row, Column, Index, StartPose)
dev_set_color ('green')
dev_display (Caltab)
dev_set_color ('red')
*畫出坐标的輪廓,輪廓以中心點的方式顯示(視窗句柄,中心點行坐标,列坐标,半徑)tuple_gen_const(: : Length, Const : Newtuple)(元組長度,常量初始值)
disp_circle (WindowHandle, Row, Column, gen_tuple_const(|Row|,1.5))
endfor
關于find_calib的更多細節見連結:
Halcon相機标定
*最重要的算子:相機标定,通過同步的最小化過程确定所有相機參數;
*計算相機内外參矩陣,原理見連結(https://blog.csdn.net/baidu_35536188/article/details/109772056)
*(句柄,優化後的反投影的均方根誤差(RMSE),機關為像素,該誤差用來反映優化是否成功,越接近0表示效果越好)
calibrate_cameras (CalibDataID, Errors)
*擷取相機标定資料--内參值,将其存在CamParam上。
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
*擷取标定闆資料,将其第一幅圖的位姿存在Pose裡。
get_calib_data (CalibDataID, 'calib_obj_pose', [0,1], 'pose', Pose)
* To take the thickness of the calibration plate into account, the z-value
* of the origin given by the camera pose has to be translated by the
* thickness of the calibration plate.
* Deactivate the following line if you do not want to add the correction.
* *設定新的坐标原點。在Z軸坐标加0.02,主要是考慮标定闆的厚度,該算子通過DX、DY和DZ給定的向量轉換3D poseIn的原點,并以poseNewOrigin形式傳回結果。
set_origin_pose (Pose, 0, 0, 0.002, Pose)
* measure the distance between the pitch lines
read_image (Image, ImgPath + 'ruler')
dev_display (Image)
* 準備提取垂直于矩形長軸的直邊。 矩形的中心在(Row,Column),Phi為矩形主軸的角度,Lenth1和Lenth2為兩軸的長度,即矩形兩邊長度的一半。
* (矩形中心點的行坐标,列坐标,矩形的縱軸與水準的角度(弧度),矩形的半寬,矩形的半高,圖像的寬,高,要使用的插值類型,測量對象句柄)
gen_measure_rectangle2 (690, 680, rad(-0.25), 480, 8, 1280, 960, 'bilinear', MeasureHandle)
*提取垂直于矩形或環形弧的邊緣對
*(圖像,句柄,高斯平滑的西格瑪參數值,最小邊緣幅度,灰階值轉換的類型以确定邊緣如何成對,邊緣對第一條邊的中心的Row坐标,列坐标,
邊緣對第一條邊的邊緣幅度(帶符号),邊緣對第二條邊的中心的Row坐标,列坐标,邊緣幅度,邊緣對内部之間的距離,邊緣間距離)
measure_pairs (Image, MeasureHandle, 0.5, 5, 'all', 'all', RowEdgeFirst, ColumnEdgeFirst, AmplitudeFirst, RowEdgeSecond, ColumnEdgeSecond, AmplitudeSecond, IntraDistance, InterDistance)
Row := (RowEdgeFirst + RowEdgeSecond) / 2.0
Col := (ColumnEdgeFirst + ColumnEdgeSecond) / 2.0
*顯示X點,如下圖所示。
disp_cross (WindowHandle, Row, Col, 20, rad(45))
*将圖像點轉換為世界坐标系的z=0平面
*(相機内參,位姿,行,列,機關,世界坐标系的X坐标點,Y坐标點)
image_points_to_world_plane (CamParam, Pose, Row, Col, 'mm', X1, Y1)
*計算兩點間的距離
distance_pp (X1[0:11], Y1[0:11], X1[1:12], Y1[1:12], Distance)
*求平均距離和
tuple_mean (Distance, MeanDistance)
*計算距離的标準差
tuple_deviation (Distance, DeviationDistance)
disp_message (WindowHandle, 'Mean distance: ' + MeanDistance$'.3f' + 'mm +/- ' + DeviationDistance$'.3f' + 'mm', 'window', 30, 60, 'yellow', 'false')
三、總結
Halcon 的标定步驟總結如下:
- gen_cam_par_area_scan_division:生成相機參數矩陣;
- create_calib_data,set_calib_data_cam_param, set_calib_data_calib_object:建立标定資料模型,并設定相機和标定闆的參數值,包括相機起始内參和标定闆的坐标系。
- find_calib_object,在多幅标定圖像中尋找标定闆,并提取其輪廓和點特征,生成相對于相機坐标系的位姿矩陣(7維)。
- calibrate_cameras,計算内外參,并計算誤內插補點。
-
讀實際要測量的圖,設定感興趣矩形區域,并通過邊緣檢測計算邊緣點坐标并标記出來,并根據計算出的内外參将二維坐标轉為3維坐标,計算出偏差。
綜合下來,感覺HALCON标定的過程還是比較清晰的,關鍵算子裡面的程式還是需要了解一下。