深入理解halcon相机标定[通俗易懂]

深入理解halcon相机标定[通俗易懂]目录相机标定简介深度说明1、相机标定参数介绍2、标定板详细介绍问题1:halcon是否只能使用halcon专用的标定板?问题2:halcon标定板如何生成?问题3:halcon标定板如何摆放,拍照数量有无限制?标定步骤1、设置相机内部初始值使用set_calib_data_cam_param算子设置相机内部初始值畸变类型选择与参数确定技巧2、标定板初始化3、创建标定数据模型4、获取标定图片5、使…

大家好,又见面了,我是你们的朋友全栈君。

  • 博主写作不容易,还是需要您鼓励
  • 万水千山总是情 , 先点个赞行不行

最近有的人私信我,说看了我的《HALCON相机标定相机内参相机外参》https://blog.csdn.net/cashmood/article/details/100089295一文后,还是有不懂的地方,这里给大家做一个深入的教程。

相机标定简介

首先镜头有畸变,也就是说照出的图像与实际不符产生了形变。即使工业镜头也是有千分之几的畸变率的。
上个图告诉大家畸变
在这里插入图片描述
这个图里,第一个图就是我们相机下的真实的形状,后边两个就是照出来有畸变的图片。

其次镜头与相机无论你的机械结构精度多高,也不容易或者说没办法将相机安装的特别正,那相机安装不正也是会导致误差的。大家想知道具体数学模型的话可以搜一下相机标定的理论方面的知识,我侧重怎么做。
标定就是把上述两个东西转化成正常的。
无论是在图像测量或者机器视觉应用中,相机参数的标定都是非常关键的环节,其标定结果的精度及算法的稳定性直接影响相机工作产生结果的准确性。

深度说明

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:两个就近黑色圆点中心之间的距离,单位是m
DiameterRatio:黑色圆点直径与两圆点中心距离的比值
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代表焦距,按照我们镜头参数进行填写,远心镜头填写0

Kappa为畸变大小,因为在标定之前,所以默认填写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.dat
try
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()

此文档由http://www.ihalcon.com/read-7598.html分享整理得到。

  • 博主简介:
  • 工业自动化上位机软件工程师、机器视觉算法工程师、运动控制算法工程师。目前从业于智能制造自动化行业。 博主邮箱:2296776525@qq.com
  • 帮忙点个赞吧。哈哈。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/140443.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(4)
blank

相关推荐

  • springaop实现原理面试_springmvc模式的工作原理

    springaop实现原理面试_springmvc模式的工作原理SpringAop实现原理什么是AOPAOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-OrientedPrograming,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的

  • finsh AJax

    finsh AJax2019独角兽企业重金招聘Python工程师标准>>>…

  • Spring的静态代理与动态代理[通俗易懂]

    Spring的静态代理与动态代理[通俗易懂]简述AOP是可以横向扩展功能的,能够在不修改原来代码的前提下实现功能扩展,AOP的实现原理即是动态代理+反射。为什么要使用代理1.什么是代理  代理即是将被代理对象进一步封装后,隐藏被代理对象,在不修改被代理对象代码的前提下完成一些额外的处理。2.场景描述  有一个BookService类,其中有一个add方法,现在想在执行hello方法之前打印一句话,例如是打印”…

    2022年10月17日
  • 开源渗透测试工具_渗透测试实战

    开源渗透测试工具_渗透测试实战渗透测试工具库,黑客工具,脚本,APT攻击,痕迹清除,

  • 机器学习:Multinoulli分布与多项式分布

    机器学习:Multinoulli分布与多项式分布学习深度学习时遇见multinoulli分布,在此总结一下机器学习中常用的multinoulli分布与多项式分布之间的区别于关系,以便更好的理解其在机器学习和深度学习中的使用。首先介绍一下其他相关知识。Bernoulli分布(两点分布)Bernoulli分布是单个二值随机变量的分布。它由单个参数控制,给出了随机变量等于1的概率。             …

    2022年10月12日
  • matlab中importdata函数导入数据 到工作空间[通俗易懂]

    用load函数导入mat文件大家都会。但是今天我拿到一个数据,文件后缀名居然是‘.data’。该怎么读呢?我只好用matlab界面Workspace区域的“importdata”按钮手工导入该文件。恩,还好,居然成功了。顺便提一下,这个“importdata”按钮功能很强大,连excel文件都能导入。但是如果在脚本里如何导入这种非mat文件呢?这时候就轮到“import

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号