提示:Halcon相机标定过程


相机标定

  • 前言
  • 一、标定是什么?
  • 二、标定步骤
  • 1.制作标定板和生成标定板描述文件。有两种标准标定板。
  • 2.创建标定模型
  • 3.设置参数
  • 4.找标定板,即找到标定区域和阵列点。(采集图像或读取图像)
  • 4.标定计算
  • 5. 保存参数(计算和保存参数)
  • 6.矫正
  • 总结



前言


标定目的是**矫正畸变**和**测量**。


提示:

一、标定是什么?

畸变矫正


标定是确定相机内外参数。相机参数和相机姿态参数

二、标定步骤

粗略流程如下:

开始

创建模型

设置参数(包括相机和标定板的初始值)

找到标定板(拍摄或读取图片)

标定计算

保存参数

结束

1.制作标定板和生成标定板描述文件。有两种标准标定板。

Halcon(示例):

gen_caltab()

2.创建标定模型

Halcon(示例):

create_calib_data ('calibration_object', 1, 1, CalibDataID)

3.设置参数

拍摄标定图片
1.标定板材质:光源在前,选用陶瓷标定板;光源在后选用玻璃标定板。
2.拍摄标定图片,最好覆盖相机视野的大小,标定板占视野的1/3-1/4,大概左右9-16张左右(可以多拍,但要选择)。值得注意的是,并非标定数量越多,越能取得高的精度,对于标定板成像灰度值亮度应大于100以上(128),以便halcon算子能较顺利的提取到标定板。
3.标定点圆直径不得小于10个像素。
4.标定图像采集过程中,相机光圈不能发生改变,否则要重新标定。
5.图像覆盖标定板四角,标定板四角完全在图像内,标定板应该被放置在视野中间边缘四角处,还应使标定板适当旋转和倾斜。

3.1设置相机内部参数初始值

Halcon(示例):

StartCamPar := [0.016,0,0.0000074,0.0000074,326,247,652,494]
set_calib_data_cam_param (CalibDataID, 0, 'area_scan_division', StartCamPar)

3.1.1 相机型号

(1)面阵

  (2)线阵

3.1.2 参数设置(这里只讲面阵相机)

(1)Division畸变模型

      CameraParam:[Focus, Kappa, Sx, Sy, Cx, Cy, ImageWidth, ImageHeight]

  (2)Polynomial 畸变模型

      CameraParam:[Focus, K1, K2, K3, P1, P2, Sx, Sy, Cx, Cy, ImageWidth, ImageHeight]

      注:当镜头为远心镜头时,Focus=0;

3.1.3 畸变类型的选择

Division畸变模型只适用于进度要求不是很高,标定图片数量较少的情况;
  Polynomial 畸变模型对镜像畸变和切向畸变都校正,精度较高,花费时间长。

3.1.4 标定时参数值的确定技巧

Focus f:镜头的标称焦距。
	例如:0.016 m.; 对于远心镜头为0.
  κ: 一般去0.0  或者 K1, K2, K3,P1, P2:可全部初始化为0
  Sx: 由CCDCMOS传感器确定,通常相机厂商提供。
	例如:
       Full image (640*480) Subsampling (320*240)
       1/3"-Chip 0.0000055 m 0.0000110 m
       1/2"-Chip 0.0000086 m 0.0000172 m
       2/3"-Chip 0.0000110 m 0.0000220 m
  Sy: 由CCDCMOS传感器确定,通常相机厂商提供。
    例如:
       Full image (640*480) Subsampling (320*240)
       1/3"-Chip 0.0000055 m 0.0000110 m
       1/2"-Chip 0.0000086 m 0.0000172 m
       2/3"-Chip 0.0000110 m 0.0000220 m
  Cx and Cy: 光心坐标初始值,即图像中心。
     例如:
      Full image (640*480) Subsampling (320*240)
      Cx 320.0 160.0
      Cy 240.0 120.0
  ImageWidth,ImageHeight:有实际图片大小来初始化该值
      例如:
      Full image (640*480) Subsampling (320*240)
      ImageWidth 640 320
      ImageHeight 480 240

3.2.0 指定标定板描述文件

set_calib_data_calib_object (DivCalibDataID, 0, 'caltab_100mm.descr')

4.找标定板,即找到标定区域和阵列点。(采集图像或读取图像)

halcon(示例):

*找标定板
	find_caltab(Image1, CalPlate1, 'caltab_big.descr', 3, 112, 5)
 	*找标定对象
    find_calib_object (Image, DivCalibDataID, 0, 0, K, [], [])
    find_calib_object (Image, PolyCalibDataID, 0, 0, K, [], [])

... 采集图像 ...
  find_caltab (Image, Caltab, CaltabName, SizeGauss, MarkThresh, MinDiamMarks)
  find_marks_and_pose (Image, Caltab, CaltabName, StartCamPar, StartThresh, 
      DeltaThresh, MinThresh, Alpha, MinContLength, MaxDiamMarks, RCoord, CCoord, StartPose)
  set_calib_data_observ_points (CalibDataID, 0, 0, I, RCoord, CCoord, 'all', StartPose)

下面将Halcon中提取目标点的大致原理说一下:
首先find_caltab 算子对图像高斯滤波(核大小为SizeGauss),接着阈值分割(与之大小为MarkThresh)将标定板的区域找出来, find_marks_and_pose 算子对区域中的圆进行分割,找到圆的个数,周长,坐标位置等应该和标定板描述文件中的一致,否则会自动调整StartThresh,使得StartThresh按照DeltaThresh步长减小到MinThresh,知道找到准确的圆心。




4.标定计算

代码如下(示例):

camera_calibration(Operator)

5. 保存参数(计算和保存参数)

相机内参和外参分别保存。以备后用。

write_cam_par (CamParam, 'campar.dat')//保存相机参数,即内参

write_pose (PoseIn, 'pos_right2left.dat')//保存姿态参数,即外参
read_cam_par ('D:/halconvc/campar.dat', CameraParam)

read_pose ('D:/halconvc/campos.dat', CameraPose)

6.矫正

gen_radial_distortion_map (Map, CameraParameters, CamParamOut, 'bilinear')

    map_image (Image, Map, ImageMapped)

总结

注意:相机标定之后,相机焦距、上下位置不能再动,否则需要重新标定。

下边整个过程例程

* In this example, we measure a ruler whose surface is 2 mm below the
* surface of the calibration plate used for calibration.
* 
* When calibrating with a single calibration image, 'focus' and 'Z'
* (the distance between camera and calibration plate) cannot be
* determined unambiguously.
* This leads to systematic measurement errors when measuring
* outside of the calibration plane, e.g., if 'set_origin_pose' is used.
* 
* Initialization and introduction
* 
dev_update_off ()
read_image (CalibImage, '3d_machine_vision/calib/calib_image_01')###读图片
read_image (MeasureImage, '3d_machine_vision/calib/ruler')###读图片
get_image_size (MeasureImage, Width, Height)
dev_close_window ()
dev_open_window_fit_image (MeasureImage, 0, 0, -1, -1, WindowHandle)
set_display_font (WindowHandle, 16, 'mono', 'true', 'false')
dev_disp_introduction_text (CalibImage)
stop ()
* 
* The systematic error depends on the relative error of 'focus'
* and the relative offset of the measurement plane compared to the
* real working distance.
* 
* In this example, our initial guess for 'focus' is about 3% off
* from the real value 12.34 mm. Together with the real 'Z' value
* (obtained with multi-image calibration) of 216 mm, the
* systematic error in this case is only about 0.025 %.
* 
* The error can be estimated with the following formula:
StartFocus := 0.012
RealFocus := 0.01234
PlaneOffset := 0.002
RealCaltabPoseZ := 0.216
* 
FocusErrorFactor := StartFocus / RealFocus
OffsetFactor := RealCaltabPoseZ / (RealCaltabPoseZ + PlaneOffset)
SystematicErrorInPercent := abs(OffsetFactor + (1 - OffsetFactor) / FocusErrorFactor - 1) * 100
* 
* 
* Single image calibration and measurement
* 
* First iteration:
*   Good guess for the 'focus' start parameter.
*   'focus' is fixed during the calibration.
*   This leads to good results.
* Second iteration:
*   'focus' is NOT fixed.
*   This leads to a high systematic error outside of the measurement plane.
* 
assemble_title_text (Title)
for Index := 0 to 1 by 1
    * 
    * Single image calibration
    * 
    * Calculate offset of measurement plane.
    CalPlateThickness := 0.002
    MeasurementPlaneZOffset := CalPlateThickness
    * Set start parameters according to camera and lens data sheet.
    StartFocus := 0.012
    gen_cam_par_area_scan_division (0.012, 0, 0.00000375, 0.00000375, 640, 480, 1280, 960, StartCamPar)###设置相机模型(线性)
    * Specify calibration plate description file.
    CalPlateDescr := 'calplate_80mm.cpd'###标定板描述文件
    * Create and initialize calibration data.
    create_calib_data ('calibration_object', 1, 1, CalibHandle)###创建标定模型
    get_cam_par_data (StartCamPar, 'camera_type', CameraType)###设置相机初始模型到相机初始参数
    set_calib_data_cam_param (CalibHandle, 0, CameraType, StartCamPar)###设置相机初始参数到模型
    set_calib_data_calib_object (CalibHandle, 0, CalPlateDescr)###设置标定板描述文件到模型
    * Perform actual calibration.
    find_calib_object (CalibImage, CalibHandle, 0, 0, 0, [], [])###找标定板
    if (Index != 1)
        * Excluding focus is highly recommended to bound the systematic error.
        set_calib_data (CalibHandle, 'camera', 'general', 'excluded_settings', 'focus')
    endif
    calibrate_cameras (CalibHandle, Errors)###标定计算
    get_calib_data (CalibHandle, 'camera', 0, 'params', CameraParameters)###获得相机标定后相机参数
    get_calib_data (CalibHandle, 'calib_obj_pose', [0,0], 'pose', CameraPose)###获得相机标定后相机姿态
    * Move origin to measurement plane.
    set_origin_pose (CameraPose, 0, 0, MeasurementPlaneZOffset, MeasurementPlanePose)
    * 
    * Measurement
    * 
    * Segment ruler.
    threshold (MeasureImage, Region, 75, 255)
    closing_rectangle1 (Region, RegionClosing, 3, 25)
    smallest_rectangle2 (RegionClosing, Row1, Column1, Phi, Length1, Length2)
    * 
    * Measure ruler marks.
    gen_measure_rectangle2 (Row1 + Length2 * .9, Column1, Phi, Length1, Length2 * .08, Width, Height, 'bicubic', MeasureHandle)
    AmplitudeThreshold := 5
    measure_pos (MeasureImage, MeasureHandle, 1, AmplitudeThreshold, 'positive', 'all', RowEdge, ColumnEdge, Amplitude, Distance1)
    close_measure (MeasureHandle)
    * 
    * Transform results into world coordinates.
    image_points_to_world_plane (CameraParameters, MeasurementPlanePose, RowEdge, ColumnEdge, 'mm', X1, Y1)
    * Calculate the distances between the pitch lines.
    distance_pp (Y1[0:|Y1| - 2], X1[0:|X1| - 2], Y1[1:|Y1| - 1], X1[1:|X1| - 1], Distance)
    * 
    * Display calibration results.
    assemble_result_text (CameraParameters, Errors, StartFocus, MeasurementPlanePose, MeasurementPlaneZOffset, Text)
    dev_disp_calibration_result (CalibImage, Text, Index, Title)
    disp_3d_coord_system (WindowHandle, CameraParameters, CameraPose, 0.01)
    stop ()
    * Display measurement results.
    MeanDistance := mean(Distance)
    ErrorFrom1mm := fabs(1 - MeanDistance)
    dev_disp_measurement_result (MeasureImage, RowEdge, ColumnEdge, Phi, MeanDistance, ErrorFrom1mm, Text, Index, Title)
    stop ()
    clear_calib_data (CalibHandle)
endfor
*相机的内参数:
*f:相机的主矩,即焦距
*k:径向扭曲的大小,即径向畸变,一般不考虑切向畸变
*sx,sy:图像传感器在水平和垂直方向上相邻像素之间的距离
*cx,cy: 投影中心在成像平面的垂直投影

*相机外参数
*平移向量X,Y,Z
*旋转向量X,Y,Z

*透视矫正

dev_close_window()
dev_update_off()
dev_set_draw('margin')
read_image(Image, 'scratch/scratch_perspective')
get_image_size(Image, Width, Height)
dev_open_window(0, 0, Width, Height, 'black', WindowHandle)
set_display_font(WindowHandle, 16, 'mono', 'true', 'false')
dev_display(Image)
disp_continue_message(WindowHandle, 'black', 'true')

*标定相机

*标定板描述文件
CaltabName := 'caltab_30mm.descr'

*初始内参数
*参数:焦距,厚度,单个像元的宽,单个像元的高,图形中心x,图像中心y,图形宽,图像高
StartCamPar := [0.012,0,0.0000055,0.0000055, Width/2, Height/2, Width, Height]

*创建标定数据模型
create_calib_data('calibration_object', 1, 1, CalibDataID)

*设置标定相机参数模型
set_calib_data_cam_param(CalibDataID, 0, 'area_scan_division', StartCamPar)

*在标定模型中制定标定板描述文件
set_calib_data_calib_object(CalibDataID, 0, CaltabName)

***********************************
*获得标定板描述文件中标志点中心坐标
* caltab_points(CaltabName,X, Y, Z)
* NStartPose := []
* NRow := []
* NCol := []
***********************************

NumImages := 12
for i :=1 to NumImages by 1
read_image(Image, 'scratch/scratch_calib_'+i$'02d')
dev_display(Image)
*寻找标定板区域
find_caltab(Image, Caltab, CaltabName, 3, 112, 5)
dev_set_color('green')
dev_display(Caltab)

*寻找标定板标志点坐标和预估外参
find_marks_and_pose(Image, Caltab, CaltabName, StartCamPar,128, 10, 18, 0.9, 15, 100, RCoord, CCoord, StartPose)
dev_set_color('red')
disp_circle(WindowHandle, RCoord, CCoord, gen_tuple_const(|RCoord|,2.5))
dev_set_part(0, 0, Height-1, Width-1)

*储存标定信息到标定板模型
set_calib_data_observ_points(CalibDataID, 0, 0, i, RCoord, CCoord,'all', StartPose)

******************************
*组合所有标定外参数和中心坐标
* NStartPose := [NStartPose, StartPose]
* NRow := [NRow, RCoord]
* NCol := [NCol, CCoord]
******************************
endfor

******************************
*标定相机所有参数(可以不使用算子set_calib_data_observ_points)
* camera_calibration(X, Y, Z, NRow, NCol, StartCamPar, NStartPose, 'all', CameraParam, NFinalPose, Errors)
******************************

*标定相机参数
calibrate_cameras(CalibDataID, Error)

*获取相机内参数
get_calib_data(CalibDataID,'camera',0, 'params', CamParam)

*获取相机外参数
get_calib_data(CalibDataID, 'calib_obj_pose',[0,1],'pose',PoseCalib)


*转换原点坐标
set_origin_pose(StartPose, -0.04, -0.03, 0.00075, Pose)

*设置标定后的图像一个像素代表的实际距离
PixelDist := 0.00013

*生成映像图像
gen_image_to_world_plane_map(Map, CamParam, Pose, Width, Height, Width, Height, PixelDist, 'bilinear')

Imagefiles := ['scratch/scratch_calib_01','scratch/scratch_perspective']
for i:= 1 to 2 by 1
read_image(Image, Imagefiles[i-1])
dev_set_window(WindowHandle)
dev_display(Image)


*应用映像转换图像
map_image(Image, Map,ImageMapped)

endfor
ImgPath := '3d_machine_vision/calib/'
dev_close_window ()
dev_open_window (0, 0, 652, 494, 'black', WindowHandle)
dev_update_off ()
dev_set_draw ('margin')
dev_set_line_width (3)
OpSystem := environment('OS')
set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
*
* Calibrate the camera.
*
StartCamPar := [0.016,0,0.0000074,0.0000074,326,247,652,494]
//创建标定数据模型,单相机标定选“calibration_obiect” 得到标定数据模型的句柄
create_calib_data ('calibration_object', 1, 1, CalibDataID)
//在标定模型中设定相机初始内参, area_scan_division为面扫相机
set_calib_data_cam_param (CalibDataID, 0, 'area_scan_division', StartCamPar)
//在标定模型中设定标定板描述文件
set_calib_data_calib_object (CalibDataID, 0, 'caltab_30mm.descr')
NumImages := 10
* Note, we do not use the image from which the pose of the measurement plane can be derived
for I := 1 to NumImages by 1
    read_image (Image, ImgPath + 'calib_' + I$'02d')
    dev_display (Image)
//提取标定板的centers and the contours 估计标定板相对相机的pose 所有收集的数据都存储在校准对象姿态CalibObjPoseIdx的校准数据模型中 
   find_calib_object (Image, CalibDataID, 0, 0, I, [], [])
//上面那一句把centers and contours 写到句柄里,下面这句在句柄里把contours数据取出来
   get_calib_data_observ_contours (Caltab, CalibDataID, 'caltab', 0, 0, I)
   dev_set_color ('green')
    dev_display (Caltab)
endfor
//标定相机 相机标定完成
calibrate_cameras (CalibDataID, Error)
//在句柄里把'camera'参数取出来
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
* Write the internal camera parameters to a file
//把'camera'参数写到'camera_parameters.dat'里
write_cam_par (CamParam, 'camera_parameters.dat')
Message := 'Interior camera parameters have'
Message[1] := 'been written to file'
disp_message (WindowHandle, Message, 'window', 12, 12, 'red', 'false')
clear_calib_data (CalibDataID)