勇哥的视觉实验:halcon的标定助手

(一)实验器材准备说明

(1)相机

型号:MV-EM500M

image.png

这个相机品牌是维视智造,500万像素,GigE接口。

通过官方网站,查到它的基础信息如下:

image.png


(2)标定板

实验型号: HG-40-9X9-4.0-2.0-3.0

这型号中的HG不知道啥意思, 40指的是矩形外边之间长宽是40mm。

9X9指的是里面的圆形数量是9*9个。

后面的4.0-2.0-3.0不知道啥意思。

不过这个标定板的厚度是3mm。


image.png

勇哥这块标定板是玻璃光刻的,精度比较高,精度可为+-1um。

具体的标定板的选型知识请参考勇哥的另一篇贴子介绍《halcon与opencv标定板的选择


自己打印标定板


如果你只是实验一下,也可以用纸打印标定板。

这个时候就需要用算子gen_caltab生成标定板图形和描述文件。

如下:

gen_caltab(::XNum,YNum,MarkDist,DiameterRatio,CalTabDescrFile,CalTabPSFile :) 算子来制作一个标定板
           XNum                 每行黑色标志圆点的数量。
           YNum                 每列黑色标志圆点的数量。
           MarkDist                两个就近黑色圆点中心之间的距离。单位是meter
           DiameterRatio       黑色圆点直径与两圆点中心距离的比值。
           CalTabDescrFile   标定板描述文件的文件路径(.descr)。这两个文件路径是用来存放文件的
           CalTabPSFile                  标定板图像文件的文件路径(.ps),
其中,.descr文件为标定板描述文件,.ps文件为标定板图形文件
 
一个30*30的标准标定板的示例
1 gen_caltab(7,7,0.00375,0.5,'E:/halcon/30_30.descr','E:/halcon/30_30.ps')
2    黑色圆点行数:   7
3    黑色圆点列数:   7
4    外边框长度:    30mm*30mm
5    黑色圆点半径:   0.9375mm  (3.75/4)
6    圆点中心间距:   3.75mm

其中.descr描述文件被halcon标定助手使用。

而.ps的文件是Adobe Illustrator软件的格式,或者也可以用Coreldraw打开,它是一种矢量图格式。

用CorelDraw打开后,你可以直接打印,大小是标准的。下图是在coreldraw中打开的效果,你甚至可以用它的测量工具测量两个圆心之间的距离,一定是3.75。

(用喷墨打印机打要更好一点,因为激光打印机会加热让纸张变皱)

image.png


确认halcon是否支持你的标定板


勇哥要确认一下做实验的这个标定板是否在halcon中有现成的描述文件。

打开halcon的标定板的描述文件目录。

勇哥电脑上路径是: E:\Program Files\MVTec\HALCON-18.11-Progress\calib

可以看到有一个40mm的标定描述文件,但是格式不是.descr,而是.cpd的。

halcon支持两种标定版,勇哥手里这块只支持.descr这样的描述文件。

两者的区别见勇哥的另一篇贴子《HALCON有关标定板生成的两个函数create_caltab和gen_caltab的区别


image.png

因此,halcon18.11并没有勇哥手中这块标定板的描述文件,我得自己生成。

问了下这块标定板的淘宝卖家,它提供如下的信息:


由下面两张图的信息我知道了型号HG-40-9X9-4.0-2.0-3.0的含义。

4.0是Distance,即两个圆心距离。

2.0是Diameter,即圆心的直径。

3.0是Thickness,即标准片的厚度。

image.png

image.png

因此,描述文件的生成代码为:

//gen_caltab(9,9,Distance*0.001, 0.5,"calib_40mm.descr","calib_40mm.ps")
gen_caltab(9,9,4.0*0.001, 0.5,"calib_40mm.descr","calib_40mm.ps")

代码中的0.5是Diameter/Distance,即黑色圆点直径与两圆点中心距离的比值,上面的已经有说明。

0.001只是单位转换,因为第算子中参数的单位是m,而标定板上面的参数是mm。

代码运行后,把生成的描述文件拷贝到halcon的calib目录下面。


(二)halcon标定助手


打开助手,填入之前获得参数。

在这里有个问题,“焦距”不知道填多少,因为相机镜头是闲鱼上买的,卖家也不懂这个参数!

image.png

这该怎么办呢?


其实,勇哥这款镜头是定焦镜头,在它的对焦环上已经印有8mm。(下图中水印盖住了)

如果你想知道镜头上其它的数值是什么意思 ,请参考勇哥的另一篇文章《勇哥的视觉实验:工业相机镜头焦距、工作距离、视野等选型的计算》。

image.png

接下来继续标定,采集不大于16张图片(不是越多越好)。

标定板是有朝向的,请让有字一面对着你的CCD,不要放反了。

而且,琉璃材质的标定板,只适合背光源。如果你是正光源,则要用陶瓷的标定板。

勇哥做实验时,用的正光源,不是背光,因此这种琉璃标定其实是不适用的。

没办法,也只能勉强用一下了。

image.png

摆的角度随机,标定块经过视野最大范围。

下图是错误的摆放:所有放置的片都在同一个水平面上。

gHlp62s3pj.gif

下图是正确的摆放:每个片的位置与角度都不同(画面上可以看到勇哥用来垫斜标准片的海绵)

M9ehDCI8rh.gif


标定结果有两部分:摄像机参数(相机内参),摄像机位姿。

看到保存按钮没?把这两个参数保存起来,后面会用到。

image.png

标定完最好在结果栏中勾选原点在图像角上,则世界坐标系和像素坐标系就重合了,此时像素和实际距离只是比例关系了。


image.png

标定至此就完成了。


但是仍然有几个问题要说明一下:


(一) 为什么标定后的焦距和输入参数焦距不一样呢?

下图是输入参数,焦距为8mm

image.png

下图中标定完成后的焦距为1.61238。

image.png


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

image.png

image.png

image.png


另外再补充几点:

  • 值得注意的是,并非标定数量越多,越能取得高的精度,halcon建议拍摄数量在9-16张,并且对摆放位置做了建议,如下图所示

  • 标定板充满标定视野的1/3-1/4,对于标定板成像灰度值亮度应大于128,以便halcon算子能较顺利的提取到标定板

image.png



(二) 为什么标定采图的时候总是说图像品质有问题呢?


见HALCON官方文档USER GUIDE p198-p200,有讲关于图像品质问题的可能原因。
文档中列出的原因主要有这么几点

  • 标定板不要过曝,不要出现255的灰度值

  • 光照要均匀

  • 标定板特征点的对比度要高,黑白区域灰度值差100以上

  • 标定板在图像中至少占1/4面积

  • 特征点应该对焦清晰

  • 所有特征点应该全部落在图像范围内

  • 标定图像至少10幅

  • 标定板应该覆盖整个视野的各个角落

  • 标定板角度变化要明显

  • 图像大小要一致

以上几点可以作为参考,不一定需要完全符合。


(三)改进的实验效果


勇哥经过实验,按上面的原则精细调整。证明确实可以不报“检测出品质问题”。

见下图中“确定”的项目。

根据经验,“品质问题”主要是“没对焦好”还有“对比度不够高“。

另外还有一说法是品质问题不超过70%以上就可以接受,下图中有些位置与角度的标准片很验做到不报”品质问题“,但是都不超过70%。

image.png


另外,每次放片不能在同一个平面内,需要用东西垫高一定倾斜角度。下图是其中一次勇哥摆放的效果。

全部图片都在同一个平台内的后果,就是标定成功后的焦距不对。

但是把标准片垫高后可能造成景深不够,部分位置聚焦不好,这是一个矛盾的问题。

解决办法是尽量调小光圈,这样可以加大景深,但是光圈太小了又会造成对比度不好。

image.png


下面可以看到,焦距跟勇哥的镜头焦距(8mm)比较接近了。

但是还是差了0.5mm,这证明我的本次标定结果仍然不理想。

平均误差为0.2723也可以看出,标定结果不好。

image.png


(四)蜂窝标定板的效果


我们知道halcon支持两种标定板: 一是阵列标定板(如上面所述),二是蜂窝标定板。

注意棋盘格标定板halcon是不支持的。

下面勇哥来实验一下蜂窝标定板的效果。

image.png


采集十张图片,标定助手采集时注意要把标定块倾斜一下,以提供高度差。

umY3N5TF6K.gif


可以看到,勇哥这块无反光的蜂窝标定板采集时候基本上不存在品质问题。

可见对于正面光照来说,无反光村料的标定板很重要。

image.png

结果如下:

image.png

摄像机的内外参:

CameraParameters := ['area_scan_division',0.00708559,-849.084,2.1983e-06,2.2e-06,1301.98,1021.44,2592,1944]
CameraPose := [-0.0112612,-0.00415572,0.159797,1.93383,1.02407,3.3134,0]


我们用这个标定结果实际测量一下,看准不准。

勇哥就用测量助手试一下:

标定文件取上面蜂窝标定板生成的摄像机内参和位姿文件。

image.png

勇哥放上一块棋盘格标定板。

画一条测量线,结果为:4.044mm。

这块棋盘格标定板一个方格子长宽都是4mm。可以看到结果是比较准确的。

image.png

image.png

image.png


插入代码,系统生成的代码如下(勇哥删除了注释):

可以看到,关键的代码就是下面这三个算子:

measure_pos

image_points_to_world_plane

distance_pp


CameraParameters := ['area_scan_division',0.00708559,-849.084,2.1983e-06,2.2e-06,1301.98,1021.44,2592,1944]
CameraPose := [-0.0112612,-0.00415572,0.159797,1.93383,1.02407,3.3134,0]

open_framegrabber ('GigEVision2', 0, 0, 0, 0, 0, 0, 'progressive', -1, 'default', -1, 'false', 'default', 'default', 0, -1, AcqHandle)
grab_image (Image, AcqHandle)
close_framegrabber (AcqHandle)

AmplitudeThreshold := 40
RoiWidthLen2 := 5
set_system ('int_zooming', 'true')

LineRowStart_Measure_01_0 := 952.832
LineColumnStart_Measure_01_0 := 1017.45
LineRowEnd_Measure_01_0 := 952.832
LineColumnEnd_Measure_01_0 := 1215.1

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, 2592, 1944, 'nearest_neighbor', MsrHandle_Measure_01_0)

copy_obj (Image, Image, 1, 1)

measure_pos (Image, MsrHandle_Measure_01_0, 1, AmplitudeThreshold, 'all', 'all', Row_Measure_01_0, 
Column_Measure_01_0, Amplitude_Measure_01_0, Distance_Measure_01_0)

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)

TmpCtrl_Length := |Row_World_Measure_01_0|
if (TmpCtrl_Length > 0)
    tuple_select_range (Row_World_Measure_01_0, 0, TmpCtrl_Length - 2, TmpCtrl_RowFrom)
    tuple_select_range (Column_World_Measure_01_0, 0, TmpCtrl_Length - 2, TmpCtrl_ColumnFrom)
    tuple_select_range (Row_World_Measure_01_0, 1, TmpCtrl_Length - 1, TmpCtrl_RowTo)
    tuple_select_range (Column_World_Measure_01_0, 1, TmpCtrl_Length - 1, TmpCtrl_ColumnTo)
    distance_pp (TmpCtrl_RowFrom, TmpCtrl_ColumnFrom, TmpCtrl_RowTo, TmpCtrl_ColumnTo, Distance_World_Measure_01_0)
endif






(五)应用标定的结果


(1)测量标定片的圆心距离 

勇哥换了一片标准片,其圆心距离是5mm。我们来验证上面的标定结果准不准。


CameraParameters := ['area_scan_division',0.00851579,-863.965,2.50014e-006,2.5e-006,1211.54,1037.6,2592,1944]
CameraPose := [0.0114206,-0.00950129,0.202559,1.38356,0.500628,272.561,0]

open_framegrabber ('GigEVision2', 0, 0, 0, 0, 0, 0, 'progressive', -1, 'default', -1, 'false', 'default', '94aab8029638_Microvision_MVEM500M', 0, -1, AcqHandle)
grab_image (Image, AcqHandle)
close_framegrabber (AcqHandle)


TmpCtrl_PlateDescription := 'E:/Program Files/MVTec/HALCON-18.11-Progress/calib/calib_40mmThick1.5.descr'
TmpCtrl_FindCalObjParNames := ['gap_tolerance','alpha','skip_find_caltab']
TmpCtrl_FindCalObjParValues := [1,1,'false']
create_calib_data ('calibration_object', 1, 1, CalibHandle)
set_calib_data_cam_param (CalibHandle, 0, [], CameraParameters)
set_calib_data_calib_object (CalibHandle, 0, TmpCtrl_PlateDescription)
find_calib_object (Image, CalibHandle, 0, 0, 0, TmpCtrl_FindCalObjParNames, TmpCtrl_FindCalObjParValues)
get_calib_data_observ_points (CalibHandle, 0, 0, 0, TmpCtrl_MarkRows, TmpCtrl_MarkColumns, TmpCtrl_Ind, CameraPose)
set_origin_pose (CameraPose, 0.0, 0.0, 0, CameraPose)
TmpCtrl_ImageRows := [TmpCtrl_MarkRows[0], TmpCtrl_MarkRows[1]]
TmpCtrl_ImageColumns := [TmpCtrl_MarkColumns[0], TmpCtrl_MarkColumns[1]]
gen_contour_polygon_xld (TmpObj_ImageContour, TmpCtrl_ImageRows, TmpCtrl_ImageColumns)
image_points_to_world_plane (CameraParameters, CameraPose, TmpCtrl_ImageRows, TmpCtrl_ImageColumns, 'mm', TmpCtrl_WorldX, TmpCtrl_WorldY)
distance_pp (TmpCtrl_WorldY[0], TmpCtrl_WorldX[0], TmpCtrl_WorldY[1], TmpCtrl_WorldX[1], TmpCtrl_Distance)
stop ()

image.png

image.png

可以看到结果是:4.996mm

结果非常准确。



(2)计算圆的直径

这次换一个胶片进行测试。

我们想知道5mm的圆的直径是不是5mm。

注意代码中关键的算子是image_points_to_world_plane, 用来把坐标转换成世界坐标系。

下面程序中,先找到圆心,然后找到垂直过圆心直径的两个端点,把两个点的坐标转为世界坐标系,再计算两点的距离 。


CameraParameters := ['area_scan_division',0.00851579,-863.965,2.50014e-006,2.5e-006,1211.54,1037.6,2592,1944]
CameraPose := [0.0114206,-0.00950129,0.202559,1.38356,0.500628,272.561,0]

open_framegrabber ('GigEVision2', 0, 0, 0, 0, 0, 0, 'progressive', -1, 'default', -1, 'false', 'default', '94aab8029638_Microvision_MVEM500M', 0, -1, AcqHandle)
grab_image (Image, AcqHandle)
close_framegrabber (AcqHandle)


TmpCtrl_PlateDescription := 'E:/Program Files/MVTec/HALCON-18.11-Progress/calib/calib_40mmThick1.5.descr'
TmpCtrl_FindCalObjParNames := ['gap_tolerance','alpha','skip_find_caltab']
TmpCtrl_FindCalObjParValues := [1,1,'false']
create_calib_data ('calibration_object', 1, 1, CalibHandle)
set_calib_data_cam_param (CalibHandle, 0, [], CameraParameters)
set_calib_data_calib_object (CalibHandle, 0, TmpCtrl_PlateDescription)
*find_calib_object (Image, CalibHandle, 0, 0, 0, TmpCtrl_FindCalObjParNames, TmpCtrl_FindCalObjParValues)
*get_calib_data_observ_points (CalibHandle, 0, 0, 0, TmpCtrl_MarkRows, TmpCtrl_MarkColumns, TmpCtrl_Ind, CameraPose)
set_origin_pose (CameraPose, 0.0, 0.0, 0, CameraPose)

gen_rectangle1 (ROI_0, 1253.59, 1146, 1368.85, 1254.63)
reduce_domain(Image, ROI_0, ImageReduced)
threshold(ImageReduced, Region, 0, 60)
connection(Region, ConnectedRegions)
gen_contour_region_xld(ConnectedRegions, Contours, 'border')
fit_circle_contour_xld(Contours, 'algebraic', -1, 0, 0, 3, 2, Row, Column, Radius, StartPhi, EndPhi, PointOrder)
dev_display(ImageReduced)
gen_circle_contour_xld(ContCircle, Row, Column, 2, StartPhi, StartPhi, 'positive', 1)
dev_display(ContCircle)
gen_circle_contour_xld(ContCircle2, Row-Radius, Column, 2, StartPhi, StartPhi, 'positive', 1)
dev_display(ContCircle2)
gen_circle_contour_xld(ContCircle3, Row+Radius, Column, 2, StartPhi, StartPhi, 'positive', 1)
dev_display(ContCircle3)

image_points_to_world_plane (CameraParameters, CameraPose, Row-Radius, Column, 'mm', x1, y1)
image_points_to_world_plane (CameraParameters, CameraPose, Row+Radius, Column, 'mm', x2, y2)
distance_pp(x1, y1, x2, y2, Distance)

stop ()

image.png

image.png

可以看到,结果是5.016毫米,有一个丝的误差,已经不错了。



最后你会不会想到: 如果不用标定助手用halcon算子怎么实现一样效果的标定呢?

见勇哥的另一个实验《勇哥的视觉实验:halcon标定板单相机标定,并取得内参



2020/6/18勇哥注:

在halcon19.11版本里,勇哥发现面扫描或者线扫描多出一组选项,即(除法)和(多项式)

image.png

两者有何区别? 目前不得而知,以后再补正。



--------------------- 

作者:hackpig
来源:
www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!



本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:
  • 评论列表:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年4月    »
1234567
891011121314
15161718192021
22232425262728
2930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864