相机标定简介
⾸先镜头有畸变,也就是说照出的图像与实际不符产⽣了形变。即使⼯业镜头也是有千分之⼏的畸变率的。上个图告诉⼤家畸变
这个图⾥,第⼀个图就是我们相机下的真实的形状,后边两个就是照出来有畸变的图⽚。
其次镜头与相机⽆论你的机械结构精度多⾼,也不容易或者说没办法将相机安装的特别正,那相机安装不正也是会导致误差的。⼤家想知道具体数学模型的话可以搜⼀下相机标定的理论⽅⾯的知识,我侧重怎么做。标定就是把上述两个东西转化成正常的。
⽆论是在图像测量或者机器视觉应⽤中,相机参数的标定都是⾮常关键的环节,其标定结果的精度及算法的稳定性直接影响相机⼯作产⽣结果的准确性。
深度说明
1、相机标定参数介绍
内参:确定摄像机从三维空间到⼆维空间的投影关系。
针孔相机(FA镜头相机)模型为6个参数(f,kSx,Sy,Cx,Cy);远⼼镜头相机模型为5个参数(f,Sx,Sy,Cx,Cy);线阵相机为11个参数(f,k,Sx,Sy,Cx,Cy,Width,Highth,Vx,Vy,Vz)。其中:f为焦距;
k表⽰径向畸变量级。如果k为负值,畸变为桶形畸变,如果为正值,那么畸变为枕形畸变。
Sx,Sy是缩放⽐例因⼦。对于相机(FA镜头)表⽰图像传感器⽔平和垂直⽅向上相邻像素之间的距离,初始值与真实值越接近计算速度越快。对于远⼼摄像机模型,表⽰像素在世界坐标系中的尺⼨。
Cx,Cy是图像的主点,对于相机,这个点是投影中⼼在成像平⾯上的垂直投影,同时也是径向畸变的中⼼。对于远⼼摄像机模型,只表⽰畸变的中⼼。Vx,Vy,Vz:线阵相机必须与被拍摄物体之间有相对移动才能拍摄到⼀幅有⽤的图像。这是运动向量。Sx,Sy对于线阵相机是相邻像元的⽔平和垂直距离。
2、标定板详细介绍
问题1:halcon是否只能使⽤halcon专⽤的标定板?
halcon提供了简便、精准的标定算⼦与标定助⼿,这在实际使⽤中极⼤地⽅便了使⽤者在halcon中有两种标定⽅式:
halcon⾃带例程中出现的,⽤halcon定义的标定板,如下图:
⽤户⾃定义标定板,⽤户可以制作任何形状、形式的标定板,如下图:
所以,halcon并⾮只能使⽤专⽤标定板,也可以使⽤⾃定义标定板就可以进⾏标定。
问题2:halcon标定板如何⽣成?
使⽤gen_caltab算⼦来制作⼀个标定板
gen_caltab( : : XNum, YNum, MarkDist, DiameterRatio, CalPlateDescr, CalPlatePSFile : )XNum:每⾏⿊⾊标志圆点的数量YNum:每列⿊⾊标志圆点的数量
MarkDist:两个就近⿊⾊圆点中⼼之间的距离,单位是mDiameterRatio:⿊⾊圆点直径与两圆点中⼼距离的⽐值
CalPlateDescr:标定板描述⽂件的⽂件路径
CalPlatePSFile :标定板图像⽂件路径,可以⽤ps打开⽰例:
gen_caltab( 7, 7, 0.1, 0.5, ‘caltab.descr’, ‘caltab.ps’)⼤家可以⾃⼰思考⼀下每个参数的含义⾏数:7列数:7
⿊⾊圆点半径:0.05m圆点中⼼间距:0.1m
问题3:halcon标定板如何摆放,拍照数量有⽆限制?
并⾮拍照标定数量越多,越能取得⾼的精度,halcon建议拍摄数量在9-16张,并且对摆放位置做了建议。标定板占标定视野的1/3-1/4,对于标定板成像灰度值应⼤于128,以便顺利提取。如下图所⽰:
标定步骤
注意只介绍⾯阵相机,其他的相机和⾯阵道理想同。
1、设置相机内部初始值
使⽤set_calib_data_cam_param 算⼦设置相机内部初始值
set_calib_data_cam_param( : : CalibDataID, CameraIdx, CameraType, CameraParam : )CalibDataID:标定句柄CameraIdx:相机序号
CameraType:相机模型种类;⾯阵相机Division畸变模型’area_scan_division’;polynomial畸变模型’area_scan_polynomial’。。。。。。
CameraParam :与相机模型种类相对应的参数;⾯阵相机Division畸变模型’area_scan_division’([Focus, Kappa, Sx, Sy, Cx, Cy, ImageWidth, ImageHeight]);polynomial畸变模型’area_scan_polynomial’([Focus, K1, K2, K3, P1, P2, Sx, Sy, Cx, Cy, ImageWidth, ImageHeight])。。。。。。相机模型种类与相机参数对应表:
畸变类型选择与参数确定技巧
畸变模型选择
division畸变模型只适⽤于精度不⾼的,标定图⽚数量较少的情况:
polynomial畸变模型对径向畸变、切向畸变都进⾏矫正,精度较⾼,花费时间较长。参数确定技巧
Focus代表焦距,按照我们镜头参数进⾏填写,远⼼镜头填写0Kappa为畸变⼤⼩,因为在标定之前,所以默认填写0
Sx, Sy像元的宽⾼填写相机的像元尺⼨。可以查相机⼿册,或者咨询相机⼚家。Cx, Cy填写图像的中⼼坐标
ImageWidth, ImageHeight填写图像的宽⾼
2、标定板初始化
使⽤算⼦ set_calib_data_calib_object例如:
CaltabDescr := ‘caltab_100mm.descr’
set_calib_data_calib_object (CalibDataID, 0, CaltabDescr)这个⽐较简单,⼤家看帮助就可以懂,就不进⾏介绍了
3、创建标定数据模型
使⽤算⼦ create_calib_data( : : CalibSetup, NumCameras, NumCalibObjects : CalibDataID)CalibSetup:创建的内容
NumCameras:相机个数
NumCalibObjects :标定项⽬数CalibDataID:标定句柄例如:
create_calib_data (‘calibration_object’, 1, 1, CalibDataID)
4、获取标定图⽚
标定板为正⽅形,尺⼨⼤⼩为要照射区域宽度的1/3,相机拍摄或者读⼊9-16张图⽚,拍摄图⽚时标定板尽量覆盖整个视场;标定板圆点的直径不能⼩于10个像素。
5、使⽤图像进⾏相机内参标定,得到结果
⽅式⼀:
使⽤标定图像,直接⽤halcon全⾃动,进⾏标定
find_calib_object (Image, CalibDataID, CameraIndex, 0, PoseIndex, [], [])得到标定数据
get_calib_data (CalibDataID, ‘camera’, 0, ‘type’, CameraType)
⽅式⼆:
1、寻找标定板区域,确定圆⼼,将结果加载到组元中
find_caltab(Image : CalPlate : CalPlateDescr, SizeGauss, MarkThresh, MinDiamMarks : )
find_marks_and_pose(Image, CalPlateRegion : : CalPlateDescr, StartCamParam, StartThresh, DeltaThresh, MinThresh, Alpha, MinContLength, MaxDiamMarks : RCoord,CCoord, StartPose)
set_calib_data_observ_points( : : CalibDataID, CameraIdx, CalibObjIdx, CalibObjPoseIdx, Row, Column, Index, Pose : )
原理:⾸先find_caltab算⼦对图像⾼斯滤波(核⼤⼩为SizeGauss),接着阈值分割(阈值⼤⼩为MarkThresh)将标定板的区域找出来;find_marks_and_pose算⼦对区域中的圆进⾏分割,找到圆的个数,周长,坐标等应该和标定板描述⽂件中的⼀致,否则会⾃动调整StartThresh,使得StartThresh按照DeltaThresh步长减⼩到MinDiamMarks ,直到找到准确的圆⼼。2、进⾏标定
calibrate_cameras( : : CalibDataID : Error)返回平均投影误差Error
6、使⽤图像进⾏相机外参标定,得到结果
相机外参:决定摄像机坐标系与世界坐标系之间相对位置关系
其中Pw为世界坐标,Pc为相机坐标,T=(Tx,Ty,Tz)是平移向量,R=(a,b,r)是旋转矩阵,分别是绕相机坐标系Z轴旋转⾓度为r;绕相机坐标系Y轴旋转⾓度为b;绕相机坐标系X轴旋转⾓度为a。6个参数组成相机外参(a,b,r,Tx,Ty,Tz)并且满⾜下式:Pc=R*Pw+T
具体步骤:
set_calib_data_cam_param( : : CalibDataID, CameraIdx, CameraType, CameraParam : )set_calib_data_calib_object( : : CalibDataID, CalibObjIdx, CalibObjDescr : )
find_calib_object(Image : : CalibDataID, CameraIdx, CalibObjIdx, CalibObjPoseIdx, GenParamName, GenParamValue : )get_calib_data_observ_contours( : Contours : CalibDataID, ContourName, CameraIdx, CalibObjIdx, CalibObjPoseIdx : )get_calib_data_observ_points( : : CalibDataID, CameraIdx, CalibObjIdx, CalibObjPoseIdx : Row, Column, Index, Pose)set_origin_pose( : : PoseIn, DX, DY, DZ : PoseNewOrigin)
Halcon相机标定代码与解析
**创建标定板
gen_caltab(7,7,0.008,0.5,'48_48mm.descr','48_48mm.ps')
*=======标定内参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') *标定相机
StartCamPar := [0.0,0.0,0.0000299,0.0000299,4896/2,3264/2,4896,3264]create_calib_data ('calibration_object', 1, 1, CalibDataID)
set_calib_data_cam_param (CalibDataID, 0, 'area_scan_telecentric_division', StartCamPar)set_calib_data_calib_object (CalibDataID, 0, '48_48mm.descr')
for index := 1 to 13 by 1
read_image (Image, '标定20/' + index + '.png') get_image_size(Image, Width, Height) dev_display (Image)
find_calib_object (Image, CalibDataID, 0, 0, index, [], [])
get_calib_data_observ_contours (Caltab, CalibDataID, 'caltab', 0, 0, index) dev_set_color ('green') dev_display (Caltab)endfor
calibrate_cameras (CalibDataID, Error)
get_calib_data (CalibDataID, 'camera', 0, 'params', CamParam)
get_calib_data (CalibDataID, 'calib_obj_pose', [0,1], 'pose', PoseCalib)
*输出计算的相机内参
write_cam_par (CamParam, 'camera_parameters.dat')
Message:= '相机内参已经写⼊⽂件中'
disp_message (WindowHandle, Message, 'window', 12, 12, 'red', 'false')clear_calib_data (CalibDataID)stop()
*=====标定外参
dev_set_draw ('margin')dev_set_line_width (1)
set_display_font (WindowHandle, 14, 'mono', 'true', 'false')
*从⽂件中读取内参 存储⽂件:camera_parameters.dattry
read_cam_par ('camera_parameters.dat', CamParam)catch (Exception) stop ()endtry *开始计算
open_file('data.csv','output', FileHandle)
fwrite_string(FileHandle,'Dis_pix*0.0299204,Dis_m*1000,Distance')fnew_line (FileHandle)close_file(FileHandle)
*选择⼀张作为标定作为最终标定位姿(任意⼀张都可以)index:=1
read_image (Image,'标定20/'+index+'.png')dev_display (Image)
CaltabName := '48_48mm.descr'
create_calib_data ('calibration_object', 1, 1, CalibDataID)
set_calib_data_cam_param (CalibDataID, 0, 'area_scan_telecentric_division', CamParam)set_calib_data_calib_object (CalibDataID, 0, CaltabName)find_calib_object (Image, CalibDataID, 0, 0, 1, [], [])
get_calib_data_observ_contours (Caltab, CalibDataID, 'caltab', 0, 0, 1)
get_calib_data_observ_points (CalibDataID, 0, 0, 1, RCoord, CCoord, Index, PoseForCalibrationPlate)dev_set_color ('green')dev_display (Caltab)dev_set_color ('red')
disp_caltab (WindowHandle, CaltabName, CamParam, PoseForCalibrationPlate, 1)dev_set_line_width (1)
disp_circle (WindowHandle, RCoord, CCoord, gen_tuple_const(|RCoord|,1.5))* caltab_points (CaltabName, X, Y, Z)* calibrate_cameras (CalibDataID, Error)
* 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.set_origin_pose (PoseForCalibrationPlate, 0, 0, 0, PoseCalib)* disp_continue_message (WindowHandle, 'black', 'true')* stop () *像素距离
distance_pp(RCoord[0],CCoord[0],RCoord[48],CCoord[48], Dis_pix)*像素直接转换mm然后计算
pix2mm(RCoord, CCoord,CamParam[2],CamParam[3],newCol,newRow)distance_pp(newRow[0],newCol[0],newRow[48],newCol[48], Dis_m)*⽤同⼀个世界坐标系来计算
image_points_to_world_plane(CamParam, PoseCalib,[RCoord[0],RCoord[48]], [CCoord[0],CCoord[48]], 'mm', X1, Y1)distance_pp(Y1[0],X1[0],Y1[1],X1[1],Distance)
*输出计算结果⽐较
open_file('data.csv','append', FileHandle)
fwrite_string(FileHandle, Dis_pix*0.0299+','+Dis_m*1000+','+Distance+'\\n')close_file(FileHandle)
Message:= '计算完毕'
disp_message (WindowHandle, Message, 'window', 12, 12, 'red', 'false')stop()
因篇幅问题不能全部显示,请点此查看更多更全内容