[Halcon&标定] 单相机标定「建议收藏」

[Halcon&标定] 单相机标定「建议收藏」为什么要进行单相机标定?广义:畸变矫正和一维和二维测量1)畸变矫正:    在几何光学和阴极射线管(CRT)显示中。畸变是对直线投影的一种偏移。简单来说直线投影是场景内的一条直线投影到图片上也保持为一条直线。那畸变简单来说就是一条直线投影到图片上不能保持为一条直线了。这是一种光学畸变(opticalaberration)。畸变是一种相差…

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

  • ?博客主页:https://blog.csdn.net/weixin_43197380
  • ?欢迎点赞 ? 收藏 ⭐留言 ? 如有错误敬请指正!
  • ?本文由 Loewen丶原创,首发于 CSDN,转载注明出处?
  • ?现在的付出,都会是一种沉淀,只为让你成为更好的人✨


一、理论描述

1. 为什么要进行单相机标定?

广义:畸变矫正和一维和二维测量

畸变矫正:

        我们在摄像机坐标系到图像坐标系变换时谈到透视投影。摄像机拍照时通过透镜把实物投影到像平面上,但是透镜由于制造精度以及组装工艺的偏差会引入畸变,导致原始图像的失真,会对拍摄的物体的形状产生变化,影响测量。因此我们需要考虑成像畸变的问题。

        我自己对畸变矫正的理解:当相机进行标定时,虽然标定板也产生了畸变,但是标定板的准确数据我们已经告诉了halcon,比如,圆的排列方式、直径、中心距等(本文使用圆形标定板)。通过加载多张标定板图片,halcon可以通过函数求出镜头里发生畸变的标定板与未发生畸变的标定板之间的映射关系,也就是相机本身的内参(拍摄有畸变)和矫正之后无畸变内参之间的映射关系,将这种映射关系作用到拍摄时发生畸变的物体当中,就完成了畸变校正.。

步骤:

  1. 通过标定求出相机内参。
  2. 通过有畸变的内参求出无畸变的内参。用chage_radial_distortion_cam_par()函数完成。
  3. 利用gen_radial_distortion_map()算子,求出有畸变的内参和无畸变的内参之间的映射关系。
  4. 将上边的映射关系作用到产生畸变的物体当中,完成畸变校正。

一维和二维的测量:

        图片上的尺寸是像素距离,标定后,可以求出像素距离与物理空间距离的换算关系,从而计算出实际的物理尺寸。


狭义:求解相机的内参和外参以及畸变参数

相机内参:

       相机的固有属性,在进行畸变校正时需要用到相机的内参。

相机外参:

       摄像机的外参表示摄像机与测量平面之间的位置关系,因为物体在经过透镜成像之后,实际上是经过了旋转和平移,而外参就是告诉我们物体成像后经过了那种旋转和平移,相机的外参包括平移矢量和旋转矩阵。也就是说只有知道了摄像机的外参,才能够在测量平面坐标系和摄像机坐标系之间的坐标转换。在halcon中,测量平面定义为世界坐标系中平面z=0

畸变参数:

       采用理想针孔模型,由于通过针孔的光线少,摄像机曝光太慢,在实际使用中均采用透镜,可以使图像生成迅速,但代价是引入了畸变。有两种畸变对投影图像影响较大: 径向畸变和切向畸变。

小结: 相机的畸变和内参是相机本身的固有特性,标定一次即可一直使用。但由于相机本身并非理想的小孔成像模型以及计算误差,采用不同的图片进行标定时得到的结果都有差异。一般重投影误差很小的话,标定结果均可用。


2. 为什么要求解相机的内参和外参?

相机标定的过程中涉及到了四大坐标系,分别为:

  • 图像像素坐标系(u,v):为了描述物体成像后的像点在数字图像上(相片)的坐标而引入,是我们真正从相机内读取到的图像信息所在的坐标系。单位:pix(像素数目)
  • 图像物理坐标系(x,y):为了描述成像过程中物体从相机坐标系到图像坐标系的投影透射关系而引入,方便进一步得到像素坐标系下的坐标。单位:mm
  • 相机坐标系(Xc,Yc,Zc):在相机上建立的坐标系,为了从相机的角度描述物体位置而定义,作为沟通世界坐标系和图像/像素坐标系的中间一环。单位:mm
  • 世界坐标系(Xw,Yw,Zw):用户定义的三维世界的坐标系,为了描述目标物在真实世界里的位置而被引入(标定板所在的坐标系)。单位:mm

下面,我们来详细推导从世界坐标系到像素坐标的过程:

2.1 像素坐标系(u,v)与CCD的图像物理坐标系(x,y)的关系

       像素坐标系不利于坐标变换,因此需要建立图像坐标系XOY,其坐标轴的单位通常为毫米(mm),原点是相机光轴与相面的交点(称为主点),即图像的中心点。X轴、Y轴分别与u轴、v轴平行。故两个坐标系实际是平移关系,即可以通过平移就可得到。

  • 两坐标轴互相垂直

                                              在这里插入图片描述
       此时有:
                                        在这里插入图片描述
其中,dX、dY分别为像素在X、Y轴方向上的物理尺寸,(u0,v0)为主点(图像原点)坐标。

       这里引用了齐次矩阵(就是将一个原本是2维的x,y向量用一个3维向量(加个1)来表示),引入齐次矩阵的目的主要是合并矩阵运算中的乘法和加法,表示为x=P*X的形式。即它提供了用矩阵运算把二维、三维甚至高维空间中的一个点集从一个坐标系变换到另一个坐标系的有效方法

  • 两坐标轴不互相垂直
                                        在这里插入图片描述
    此时有
                             在这里插入图片描述
    写成矩阵形式:
    在这里插入图片描述

2.2 CCD的图像物理坐标系(x,y)与相机坐标系(Xc,Yc,Zc)的关系

        根据小孔成像原理:
                                               图像坐标系应在相机坐标系的另一边,为倒立反向成像,但为方便理解和计算,故投影至同侧
        如图,空间任意一点P与其图像点p之间的关系,P与相机光心o的连线为oP,oP与像面的交点p即为空间点P在图像平面上的投影。
该过程为透视投影,根据三角形相似性原理得如下矩阵:
                                                  在这里插入图片描述
其中,s为比例因子(s不为0),f为有效焦距(光心到图像平面的距离),(x,y,z,1)T是空间点P在相机坐标系oxyz中的齐次坐标,(X,Y,1)T是像点pp在图像坐标系OXY中的齐次坐标。

2.3 相机坐标系(Xc,Yc,Zc)与世界坐标系(Xw,Yw,Zw)的关系
                                              在这里插入图片描述
   
其中:R3* 3的旋转矩阵,T3*1的平移矩阵,(xc,yc,zc,1)*T为相机坐标系的齐次坐标,(xw,yw,zw,1) *T为世界坐标系的齐次坐标。

2.4 世界坐标系(Xw,Yw,Zw)转化为像素坐标系(u,v)

                      在这里插入图片描述
                      在这里插入图片描述
       其中,M1称为相机的内部参数矩阵,完全由相机的内部参数ax,ay,uo,vo决定,(uo,vo)为主点坐标,ax,ay分别表示图像u轴和v轴的尺度因子;M2完全由摄像机相对于世界坐标系的方位决定,称为摄像机的外部参数Xw为空间点在世界坐标系下的齐次坐标M为一个3*4的矩阵,称为投影矩阵

       总结:其实说白了,我们需要找到世界坐标系和像素坐标系之间的关系,而两者的关系可以通过将世界坐标系转化为相机坐标系,相机坐标系通过投影变换转化为CCD的图像物理坐标系,在通过变换可以把图像物理坐标系的物理单位转化为图像像素坐标系的像素单位[即(x,y)(u,v)],从而得到世界坐标系和像素坐标系之间的关系,即通过A->BB->CC->D之间的关系,找到A->D的关系。

       要想找到世界坐标系到像素坐标系之间的转换关系,必须要求出相机内参,外参,而标定就是求相机内外参的过程,通过求得的内外参,达到对成像物体的一、二维测量和畸变矫正的目的。


二、标定流程

      实现的原理是根据相机的像元尺寸、焦距和标定板的描述文件(.descr)来找到显示的标定板图像上面的标志点,从而确定标定板实际输入的参数(Distance、Diameter等)和这些参数对应的图像像素大小的关系,完成标定。

      这里我用的标定板是7*7圆点标定板,型号:HC070—3.75

在这里插入图片描述
      标定板数据可参考:

在这里插入图片描述
标定步骤:

1. 使用gen_caltab算子生成一个标定文件

       标定前需要生成一个.descr的描述文件,也就是世界坐标系与像素坐标系的关系,当我们下一次做别的项目时,只需要调用描述文件,即可完成标定过程。生成标记文件的算子:

        这里我使用的是7*7的圆形标定板,具体设置参数如下:

      函数gen_caltab(7, 7, 0.0075, 0.5, 'C:/Users/Administrator/Desktop/caltab.descr ', 'caltab.ps')

      参数
      7(in) x方向黑色标志圆点的数量
      7(in)y方向黑色标志圆点的数量
      0.0075(in)两个相邻的黑色圆点之间的圆心距离,单位:米
      0.5(in)标记点直径与标记点圆心之间距离的比值
      ‘C:/Users/Administrator/Desktop/caltab.descr ‘(in)标定板的描述文件的保存路径
      ‘caltab.ps’(in) 标定板图形文件,描述标定板的一些信息,打印标定板时会用到,可以用photoshop打开

2. 打开标定助手,加载标定文件,设置相机参数

在这里插入图片描述
注:选择摄像机模型,我的是面阵相机。镜头是远心镜头就选择远心;像元的宽高填写相机的像元尺寸,如果不知道去问相机厂家或查相机型号。焦距填写镜头的焦距

3. 标定、获取标定结果图

实时获取各个角度、位置的标定板图片(9-16张左右),并选择其中一种图片设置参考位姿,然后标定。

在这里插入图片描述
可以看到相机的参数:

在这里插入图片描述

4. 保存相机内、外参,下次使用测量助手直接调用内外参文件

在这里插入图片描述
在这里插入图片描述

注:内参后缀名是.dathalcon标定助手默认的内参后缀为.cal,保存的时候需要更改


三、标定后的畸变校正与实际测量

       截止到上面,相机标定已经完成了,得到相机内外参之后就可以根据自己的实际需求对图像进行畸变校正或像素尺寸到实际尺寸的转换来进行实际测量了,方法如下:

1. 畸变校正

可以接上面的第三步之后:生成标定数据(相机内、外参)代码,从而进行畸变校正
在这里插入图片描述代码

*相机内参
CameraParameters := [0.0375147,-270.806,8.30152e-006,8.3e-006,647.48,520.914,1280,960]
*相机位姿,即外参(旋转矩阵+平移矢量)
CameraPose := [-0.0091626,-0.00625214,0.700967,2.46926,358.933,179.443,0]
*1、校正径向畸变,得到新的相机内参
change_radial_distortion_cam_par ('adaptive', CameraParameters, 0, CamParamOut)
stop ()
 - Image Acquisition 02: Code generated by Image Acquisition 02
open_framegrabber ('GigEVision', 0, 0, 0, 0, 0, 0, 'default', -1, 'default', -1, 'false', 'default', 'CAMERA_QBY_DM', 0, -1, AcqHandle)
grab_image_start (AcqHandle, -1)
while (true)
    grab_image_async (Image, AcqHandle, -1)
    *2、对发生径向畸变的图像生成投影映射,图像的映射数据存在第一个参数中
    gen_radial_distortion_map (Map, CameraParameters, CamParamOut, 'bilinear')
    *3、对图像进行畸变校正
    map_image (Image, Map, ImageMapped)
endwhile
close_framegrabber (AcqHandle)

ImageMapped就是进行了畸变矫正的图像

2. 像素坐标系转化成世界坐标系(求实际尺寸)

*相机内参
CameraParameters := [0.0375147,-270.806,8.30152e-006,8.3e-006,647.48,520.914,1280,960]
*相机位姿,即外参(旋转矩阵+平移矢量)
CameraPose := [-0.0091626,-0.00625214,0.700967,2.46926,358.933,179.443,0]
read_image (Image, ImageFiles[Index])
***
*这里省略的是检测得到像素坐标尺寸
***
*将像素坐标尺寸转换成实际坐标尺寸(参考后面实战测量硬币)
image_points_to_world_plane (CameraParameters, CameraPose, TmpCtrl_ImageRows, TmpCtrl_ImageColumns, 'mm', TmpCtrl_WorldX, TmpCtrl_WorldY)

3. 图像坐标系转化成世界坐标系(可选项,图像校正时会用到,也可以仿射变换对图像进行校正)

标定完之后可以插入代码看一下
在这里插入图片描述
代码

*相机内参
CameraParameters := [0.0375147,-270.806,8.30152e-006,8.3e-006,647.48,520.914,1280,960]
*相机位姿,即外参(旋转矩阵+平移矢量)
CameraPose := [-0.0091626,-0.00625214,0.700967,2.46926,358.933,179.443,0]
    read_image (Image, ImageFiles[Index])
    get_image_size (Image, Width, Height)
    相机视野宽度
    TmpCtrl_RectificationWidth := 79
	转换成米单位
	TmpCtrl_RectificationWidth := TmpCtrl_RectificationWidth / 1000.0
	生成转换矩阵
	gen_image_to_world_plane_map (TmpObj_RectificationMap, CameraParameters, CameraPose, Width, Height, Width, Height, TmpCtrl_RectificationWidth / Width, 'bilinear')
	转换
	map_image (Image, TmpObj_RectificationMap, TmpObj_RectifiedImage)

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


四、实战演练:测量一元硬币直径

1. 打开测量助手,加载上面得到的相机内、外参

在这里插入图片描述

2. 绘制测量直线,获取硬币边缘坐标

       直接拉一条直线,得到硬币边缘的像素坐标信息,再通过上面标定得到的像素坐标和实际坐标之间的转换关系,可以计算出硬币的最终直径大小为24.1232mm

3.

3. 直接生成代码

生成代码后,加入上面的畸变校正,即可完成完整的标定过程

open_framegrabber ('GigEVision', 0, 0, 0, 0, 0, 0, 'default', -1, 'default', -1, 'false', 'default', 'CAMERA_QBY_DM', 0, -1, AcqHandle)
*加载相机内外参
CameraParameters := [0.0220788,-613.469,6.00309e-006,6e-006,613.491,509.255,1280,960]
CameraPose := [-0.00464111,-0.00298404,0.341764,2.05327,359.502,89.9295,0]
*1、校正径向畸变,得到新的相机内参
change_radial_distortion_cam_par ('adaptive', CameraParameters, 0, CamParamOut)
AmplitudeThreshold := 40
RoiWidthLen2 := 25
set_system ('int_zooming', 'true')
*测量矩形尺寸
LineRowStart_Measure_01_0 := 495.5
LineColumnStart_Measure_01_0 := 385.5
LineRowEnd_Measure_01_0 := 498.167
LineColumnEnd_Measure_01_0 := 745.5
TmpCtrl_Row := 0.5*(LineRowStart_Measure_01_0+LineRowEnd_Measure_01_0)
TmpCtrl_Column := 0.5*(LineColumnStart_Measure_01_0+LineColumnEnd_Measure_01_0)
TmpCtrl_Dr := LineRowStart_Measure_01_0-LineRowEnd_Measure_01_0
TmpCtrl_Dc := LineColumnEnd_Measure_01_0-LineColumnStart_Measure_01_0
TmpCtrl_Phi := atan2(TmpCtrl_Dr, TmpCtrl_Dc)
TmpCtrl_Len1 := 0.5*sqrt(TmpCtrl_Dr*TmpCtrl_Dr + TmpCtrl_Dc*TmpCtrl_Dc)
TmpCtrl_Len2 := RoiWidthLen2
*生成一个测量矩形
gen_measure_rectangle2 (TmpCtrl_Row, TmpCtrl_Column, TmpCtrl_Phi, TmpCtrl_Len1, TmpCtrl_Len2, 1280, 960, 'nearest_neighbor', MsrHandle_Measure_01_0)
while (true)
grab_image (Image, AcqHandle)
*2、对发生径向畸变的图像生成投影映射,图像的映射数据存在第一个参数中
gen_radial_distortion_map (Map, CameraParameters, CamParamOut, 'bilinear')
*3、对图像进行畸变校正
map_image (Image, Map, ImageMapped)
measure_pos (ImageMapped, MsrHandle_Measure_01_0, 19.5, 40, 'all', 'all', Row_Measure_01_0, Column_Measure_01_0, Amplitude_Measure_01_0, Distance_Measure_01_0)
* 通过image_points_to_world_plane将像素尺寸转换成实际尺寸
image_points_to_world_plane (CameraParameters, CameraPose, Row_Measure_01_0, Column_Measure_01_0, 0.001, Column_World_Measure_01_0, Row_World_Measure_01_0)
endif
endwhile
close_measure (MsrHandle_Measure_01_0)

五、疑惑解答

这里主要回答一下有人问我的问题:

1.halcon是否只能使用halcon专用的标定板?

  • halcon自带例程中出现的,用halcon定义的标定板 ,如下图
    在这里插入图片描述
    halcon提供了简便、精准的标定算子并且提供了标定助手,这无疑大大方便了广大开发者。但是也不是一定要使用该标定板才能完成标定。
  • 用户自定义标定板,用户可以制作任何形状、形式的标定板
    在这里插入图片描述

       所以,halcon并非只能使用专用标定板,也可以使用自定义标定板就行标定。

       使用halcon定义标定板的优势是可以使用halcon的标定板提取算子,提取标记点,而当使用自己定义的标定板格式则需要自己完成此部分工作。

2.halcon标定板如何摆放,拍照数量有无限制?

       halcon建议拍摄数量在9-16张,并非拍的越多越好,并且对摆放位置做了建议,如下图所示(注:上面我标定的时候并没有做Z轴的倾斜摆放,因为我测量时不涉及Z平面的,所以只是在XY平面做旋转摆放)
在这里插入图片描述
       标定板充满标定视野的1/3-1/4,对于标定板成像灰度值亮度应大于128,以便halcon算子能较顺利的提取到标定板

       标定时候注意标定图像的倾角需要有变化。若采集的图像里标定板与相机所成角度相近,标定板只在某一个平面平移和旋转,则焦距计算会出现错误。

3.halcon的标定板其中一个角上为什么有缺口?

       halcon的标定板其中一个角上有缺口,这是为了在标定板上建立参考坐标系用的,标定相机的同时可以完成手眼标定。

 


戳戳小手帮忙点个免费的赞和关注吧,嘿嘿。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

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

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

(0)
blank

相关推荐

发表回复

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

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