一、前言
在我的工業相機專欄里已經將相機標定涉及到的理論部分講解完畢,為什么要標定以及標定要求出什么參數呢,用一個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標定的過程還是比較清晰的,關鍵算子里面的程序還是需要理解一下。