文章目录
- 一、前言
- 二、图像采集接口编程第一章
- 2.1 HALCON的通用图像采集接口
- 2.2 图像采集基础
- 2.3 同步抓取 vs. 异步抓取⭐
- 2.4 缓冲策略⭐
- 2.5 A/D转换和多路复用
- 2.6 HALCON图像采集算子⭐
- 2.6.1 open_framgrabber
- 2.6.2 close_framegrabber
- 2.6.3 info_framegrabber
- 2.6.4 grab_image
- 2.6.5 grab_image_async
- 2.6.6 grab_image_start
- 2.6.7 grab_data
- 2.6.8 grab_data_async
- 2.6.9 set_framegrabber_param、get_framegrabber_param
- 2.6.10 set_framegrabber_lut、get_framegrabber_lut
- 2.6.11 set_framegrabber_callback、get_framegrabber_callback
- 2.7 参数命名约定
- 2.8 与HDevelop图像采集助手的最佳交互
- 2.9 图像采集集成接口 vs. 图像采集接口
- 三、总结
一、前言
用HALCON联合C#进行编程时,发现图像采集设备(/相机)初始化参数列表有许多参数,如下:
framegrabber = new HFramegrabber("GigEVision2", 0, 0, 0, 0, 0, 0, "progressive",
-1, "default", -1, "false", "default", cameraName, 0, -1);
此外,我还发现部分参数在刚初始化完是可以正确设置的:
framegrabber.SetFramegrabberParam("Width", _cameraParamSetting.Width);
framegrabber.SetFramegrabberParam("Height", _cameraParamSetting.Height);
framegrabber.SetFramegrabberParam("OffsetX", _cameraParamSetting.OffsetX);
framegrabber.SetFramegrabberParam("AcquisitionFrameRate", _cameraParamSetting.AcquisitionFrameRate);
framegrabber.SetFramegrabberParam("Gain", _cameraParamSetting.Gain);
但在framegrabber?.GrabImageStart(-1);
开启采集之后,再设置就会报错(比如上面"Width"和"Height"的设置)。
于是,我查阅了算子文档中的SetFramegrabberParam
,发现里面并没有这部分的描述,甚至第一个参数(param)的建议值列表中都没提到"Width"、“Height”、“Gain”。
这使得我不太敢继续往下写代码了。
在我一番查阅后,发现HALCON中有一个文档叫 image_acquisition_interface_programmers_manual ,快速浏览了一下,了解到该文档主要描述如何创建一个HALCON图像采集接口,以及介绍如何将第三方图像采集硬件集成到HALCON系统。
直觉告诉我,它会给我一些有用信息,让我对相机初始化与配置有更清晰的认识。
注意
HALCON的文档非常多,很多操作都有专门文档。
所以使用HALCON的过程也是一个漫长的文档熟悉过程。很多时候如果不详细翻看文档而直接编程,容易引入一些难以排查的错误,得不偿失。
二、图像采集接口编程第一章
这章内容较多,主要介绍HALCON图像采集接口及其底层概念。适用于对图像采集卡/帧捕获器(frame grabber)、A/D转换、同步和异步操作模式、缓冲策略等不熟悉的用户。
2.1 HALCON的通用图像采集接口
HALCON提供了一个通用的图像采集接口,该接口允许你即时集成新的图像采集卡/相机,甚至都不用重启HALCON应用程序。
使用时有两个基本概念:
- 接口代码封装在动态可加载模块中(Windows中的DLL)。
- 它包含了一组预定义好的(用于设置和检索特定硬件参数)算子,用于图像采集。后者允许参数化一些“非标\非常规\特殊”的采集设备。
若你已经成功开发了一个新的HALCON图像采集接口(基于本手册给定的信息),接着你要使用新的图像采集设备需要以下几步:
- 插入硬件并安装特定厂商的设备驱动程序和库。
- 将新的HALCON接口(即封装了硬件相关代码的模块库)复制到DLL或共享库的搜索路径中。
- 在
open_framegrabber
算子中指定新图像采集设备的名称(即对应接口的名称)。 - 体验你在新图像采集接口中集成的所有功能。
这个过程简单讲就是:
- 物理连接上硬件并安装好驱动 2. 将相关库复制到库目录下(程序执行能找到的) 3. open_framegrabber中指定设备接口名称 4. 连接并使用
用于图像采集的HALCON算子还是与原来一样的,因此大多数情况下,现有的程序代码不需要进行大改。在首次调用 info_framegrabber 和 open_framegrabber 时,HALCON会自动加载接口。因此,你甚至可以在不重启应用程序的情况下替换或添加图像采集接口。通过通用参数设置机制,可以访问图像采集设备的特殊功能。
由于通过USB、IEEE 1394或GigE连接的数字相机,严格意义上并不是图像采集卡/板,所以我们不再使用 “HALCON帧捕获器/图像采集卡(HALCON frame grabber)” 这个术语,而使用 “HALCON采集接口(HALCON acquisition interface)” ,并用“ 图像采集设备(image acquisition device) ” 这个术语来替代 帧捕获器/图像采集卡和数字相机。出于向后兼容的原因,HALCON算子的名称以及底层数据结构和错误代码的名称保持不变。因此,像open_framegrabber、info_framegrabber和close_framegrabber这样的算子名称可能听起来有点过时(old-fashioned)。
示例文件
作为图像采集集成的指南,HALCON发行版包含一个图像采集接口模板(参阅%HALCONEXAMPLES%\ia_integration\source中的hAcqTemplate.c)。它涵盖了在编程此类接口时可能遇到的大多数情况(如支持有多个相机的多个板等)。尽管模板有很多注释,但在阅读本手册前,理解代码可能还是相当困难。另一方面,该模板也为集成提供了强大的框架。
2.2 图像采集基础
注意,本章以下部分主要描述模拟相机(analog camera)连接到图像采集卡的情况。不过,对于数字采集设备(如IEEE 1394、USB或GigE相机)来说,大部分这些准则也很重要。
总的来说,图像采集卡的作用是接收视频信号,可以理解为连续的视频帧流,并在触发时从序列中抓取一个或多个视频帧。在许多情况下,视频信号是模拟的,尽管现在更专业的设备经常使用数字信号。最常见的模拟视频格式有:
- NTSC:640×480 pixel,30fps
- PAL:768×576 pixel,25fps
这两种格式都携带了颜色信息,尽管许多图像采集设备只提供灰度图像(grayscale image),即使是从彩色视频信号中。以下解释假设你使用的是模拟图像采集卡。对于数字图像采集卡,情况可能有所不同。
先看一下模拟输入信号:实际上,它由许多不同的信号组成:有垂直和水平同步信号,当然还有原始的数据信号。有时,颜色和亮度信号是叠加的(复合信号),有时它们在不同的输入线上传输(Y/C、RGB)。由于图像采集卡通常由视频源同步,因此它必须等待下一个垂直同步信号以开始抓取新图像。
这将导致在抓取任意帧图像时平均有半帧的延时。这也意味着,如果你想达到全帧速率,你必须在接收到上一帧后立即开始抓取下一帧。因此,根本没有剩余的时间来处理图像。在这种同步模式下,主机计算机(host computer)专注于触发一个接一个的抓取。也因此,HALCON也支持异步抓取。
2.3 同步抓取 vs. 异步抓取⭐
要理解异步抓取(asynchronous grab)的含义,首先需要了解图像采集设备在抓取帧时做了什么。这很好理解,数字化的帧必须存储在某类内存中。基本上,有三种可能:
- 图像采集设备上的设备内存。
- 主机上的设备内存。
- 主机内存。
板载设备内存是指专用的内存,物理安装在板卡上。这样,图像采集设备可以直接将采集到的图像存储在其自身内存中,主机上的每个进程都可以随时获取数据,另一方面,内存大小是固定的。如果太小,也许无法存多个图像;如果太大,整个板卡可能会很贵。主机上的设备内存是由图像采集卡的设备驱动动态分配的非分页(no-paged)系统内存。因此,内存大小可轻松调整。另一方面,如果图像采集设备持续将数据传输到主机计算机的内存中,可能会导致总线流量繁忙。因此,我们通常不使用某些图像采集设备提供的连续抓取模式,而只根据需要抓取图像。主机内存由用户在应用程序的某个地址空间处分配。因此,该内存可以使用分页,通常需要将图像采集设备传输的图像显式复制到该内存中(DMA会失败)。
如上所述,主机计算机的任务是在需要新图像时触发图像采集,但在设备数字化帧时并不一定需要等待。当设备内存位于板卡上,这不言而喻(当设备内存位于板卡上时,意味着图像采集设备拥有自己的独立内存空间,可以在不占用主机计算机资源的情况下独立完成图像的数字化过程;该情况下主机计算机只需要触发图像采集,然后设备会将数字化的帧存储到板卡的内存中;这样,主机计算机无需在整个数字化过程中等待,可以在此期间执行其他任务),但即使目标内存在主机上,通常也可以使用像DMA这样的技术进行外部启动的数据传输。所以主机进程要做的“唯一的事情”就是在实际需要图像之前平均1.5帧的时间触发图像采集,然后在新的帧在后台被板卡捕获时进行其他处理。这种技术称为异步抓取。很好理解,这简化了实时抓取,因为与视频流中两个相邻帧之间的短时间间隔相比,完成帧所需的时间相当长(PAL视频为40ms,NTSC视频为33ms)。
大多数图像采集设备支持异步数据传输。HALCON提供了同步(grab_image)和异步抓取(grab_image_async)两种方式。支持相对较弱的同步模式的原因是它具有更“清晰”的语义:grab_image算子启动抓取并等待直到完成。因此,拿到的图像总是最新的。使用异步抓取则需要对应用程序的计时功能有更多了解。否则,抓取到的图像可能已经过时,无法使用。
2.4 缓冲策略⭐
我们回顾一下实时抓取的问题:假设有一个支持异步传输的板卡,一个可能的操作序列如下:
- 触发抓取(发出控制命令并立即返回到调用进程)。
- 等待抓取完成。
- 触发下一次抓取。
- 处理步骤2中得到的图像。
- 返回到步骤2.
这个操作顺序对应的HALCON程序很简单:
while(1)
grab_image_async(Image, AcqHandle, -1)
< process Image>
endwhile
由于步骤1和步骤3不会阻塞进程(启动的是异步抓取),所以在图像采集设备繁忙时不会浪费时间。剩下要考虑的问题就是抓取所用的内存的组织方式:假设步骤3使图像采集设备将数据传输到专用内存区域,而不知道主机在步骤4中的操作,可以很容易看出,图像采集设备必须使用与主机不同的内存区域。否则,设备可能在主机同时读取的内存中写入数据,处理后的图像将会损坏。解决此问题的最佳方法是使用两个备用缓冲区:一个用于写入新数据,另一个用于保存上一张图像。在循环开始前,这些缓冲区可能只分配一次。每次迭代后,它们交换角色(图像采集设备写入的缓冲区变为处理进程的读取缓冲区,反之亦然)。这是涉及异步数据传输时非常常见的技术,称为 双缓冲(double buffering)。由于旧图像数据被覆盖,我们也用术语——易失性抓取(volatile grabbing)。
这种技术提供了最大的抓取性能,但同时,灵活性降低了。很明显,旧的图像会被一次又一次地覆盖。因此,所有“历史”都丢失了。这种严格的组织方式与HALCON的理念相矛盾,HALCON的理念是允许创建任意数量的图像对象(iconic object),并能并行处理它们,直到你在应用程序中决定不再需要它们。因此,HALCON图像采集接口默认应始终创建新的图像对象,并将易失性抓取仅作为附加选项提供。
2.5 A/D转换和多路复用
请记住,我们正在谈论的是模拟视频(analog video),现在我们快速浏览学习一下连接模拟和数字域的接口:A/D转换器。其中的细节我们并不关心,除了图像采集卡的A/D转换器需要与视频信号同步,以便跟踪后续的行-line(水平同步)和帧-frame(垂直同步)。同步信息要么编码在模拟视频信号中,要么通过额外的输入线传给图像采集卡,这样A/D电路就能与视频源同步。这一点很重要,因为我们考虑具有多个输入线的图像采集卡:大多数情况下,相对昂贵且复杂的额外A/D转换器被一个模拟多路转换器(multiplexer)电路替代,它允许将多个视频源可选地连接到一个A/D转换器。这意味着,每次新视频源要连接到A/D转换器时,电路必须重新同步到新信号,通常会丢失一到两帧(某些情况下,同步可能会花更长时间,长达1s)。为了避免这种情况,可以使用genlocked相机。请记住,通常在切换不同输入线时,你需要调整图像采集板卡上的参数设置。HALCON提供了一个概念,用于处理连接到一个图像采集板卡的多个相机(以及一个主机计算机内的多个图像采集板卡):每个相机/板卡对都由一个图像采集句柄表示(image acquisition handle)。在HALCON中,这样的句柄对应一个图像采集实例。如果你想让你的图像采集接口支持多个相机/板卡,你需要跟踪所有与你的图像采集类对应的实例。
2.6 HALCON图像采集算子⭐
本节简单介绍了HALCON图像采集算子。这些算子内部映射到图像采集接口例程。
2.6.1 open_framgrabber
open_framegrabber用来创建一个新的图像采集句柄。它加载指定的图像采集接口并访问图像采集设备。此外,还会设置标准相机的典型参数(如图像大小、部分、色彩空间、端口等)。如果图像采集设备(这里指驱动和接口)支持在一个主机计算机内的有多个板/设备,你还可以指定目标板/设备(使用Device参数)。当然,你也可以在每个板卡上接多个相机。这种情况下,你可以通过一系列open_framegrabber调用(通过Port或LineIn参数指定相机)来为每个相机创建一个图像采集句柄。请注意,如果你想支持多个相机或板卡,你需要在图像采集接口内处理多个实例。如果你使用的是数字设备(digital device),就不必关心A/D转换器和多路的问题。因此,每个设备都有自己的图像采集句柄(在HALCON内)和一个图像采集实例。具体来讲,这个HALCON算子将调用你的接口例程FGOpenRequest()和FGOpen()。此外,当你第一次访问指定的图像采集设备时,会调用FGInit()。
2.6.2 close_framegrabber
close_framegrabber算子与open_framegrabber相对。它会释放图像采集句柄、释放相关内存,并根据你在底层接口例程FGClose()中编写的程序解锁图像采集设备。
2.6.3 info_framegrabber
算子info_framegrabber用来访问指定图像采集设备(对应接口)的基本信息。请注意,由于许多参数设置依赖于图像采集设备的特有属性,HALCON通常既不能提供有意义的默认值,也不能自动检查参数。不过,对info_framegrabber的调用应该返回有意义的值,尤其是在open_framegrabber参数中的值列表。请尽可能使用动态列表。
无论设备是否已经打开,info_framegrabber的调用应返回相同的值。在某些特殊情况下,如果设备句柄已经存在,可能会返回更多的值。此外,这些返回值中的大部分值都被HDevelop图像采集助手使用,并且应直接在open_framgrabber中工作。有一个例外是info_boards参数。它必须返回实际可用设备的列表,每个设备以"device:"开头。若返回值为空,则在自动检测后或图像采集助手中,图像采集接口的名称不会被列出。info_boards还可以返回有关可用设备的其他信息,如端口、IP地址和序列号等。另一个特殊的参数是camera_type。返回值可以在视频格式、相机型号、配置文件和一些特殊值之间变化。对于配置文件,有种语法可以改进文件的选择。需要返回三个值:“CAMFILE”;配置文件的文件扩展名,以及启用搜索的完整路径。
该算子会调用图像采集接口中的FGInfo()例程。
2.6.4 grab_image
grab_image算子用来同步(synchronously)抓取一帧新图像,这意味着启动一次新的抓取,算子会等待,直到该次抓取结束。
该算子会调用接口例程FGGrab()。
2.6.5 grab_image_async
grab_image_async会异步(asynchronously)抓取新图像。它会等待直到一次挂起的异步抓取完成(如果你把握好了时间,该抓取应该已经完成,以防止在该点上浪费时间)。接着返回该图像,除非它的时间戳比指定的阈值要旧。否则,一次新的(同步)抓取将执行。之后,grab_image_async触发一次新的异步抓取,并且返回无需等待。
该算子会调用图像采集接口中的FGGrabAsync()例程。
2.6.6 grab_image_start
grab_image_start会启动新图像的异步抓取并立即返回。图像本身将由下一次调用grab_image_async或grab_data_async传递。如果之前已经启动了异步抓取,grab_image_start将终止这个挂起的抓起并启动一个新的抓取。如果你的应用程序涉及耗时的处理,此算子会非常有用。在这种情况下(处理很耗时),如果你在抓取上一个图像后(通过grab_image_async)立即开始抓取,异步抓取图像可能会过时。grab_image_start允许你微调(fine-tune)启动抓取的时刻。在自由运行相机的情况下(不同步的),大约在需要下一张图像的前1.5帧调用该算子。
该算子将调用图像采集接口中的FGGrabStartAsync()例程。
2.6.7 grab_data
grab_data会同步抓取新图像,这意味着新的抓取启动后,算子会等待直到该次抓取结束。它还能返回预处理后的数据,包括图像、区域。XLD轮廓和控制数据。使用何种预处理取决于你(也可能取决于图像采集设备的某些特定硬件特性)。
此算子将调用图像采集接口中的FGGrabData()例程。
2.6.8 grab_data_async
grab_data_async用以异步抓取一个新图像,这表示等待一次挂起的抓取完成,并在返回之前启动一次新的异步抓取。和grab_data类似,它也能返回预处理数据。
2.6.9 set_framegrabber_param、get_framegrabber_param
set_framegrabber_param和get_framegrabber_param算子用来设置和获取图像采集实例的特定参数。它们被设计成来对图像采集硬件进行“微调”。任何图像采集设备上你认为有用的部分,只要定义对应的参数即可。你可以设置单个参数值,也可以设置参数值元组。后者可能非常有用,因为某些参数可能相互依赖,因此需要在一次get_framegrabber_param调用中设置。
grab_framegrabber_param和set_framegrabber_param不会评估参数本身,仅会将它们传递到你的接口例程FGSetParam()和FGGetParam()中。注意,由于这些参数的名称、值和语义取决于图像采集设备的特定属性,HALCON既无法提供有意义的默认值,也无法自动检查参数。这些都取决于你的图像采集接口。
2.6.10 set_framegrabber_lut、get_framegrabber_lut
算子set_framegrabber_lut和get_framegrabber_lut用以设置和检索图像采集实例的颜色查找表(从而支持如伽马校正或白平衡等功能)。
2.6.11 set_framegrabber_callback、get_framegrabber_callback
这两个算子处理特定图像采集接口的回调。这些回调允许特定图像采集接口或设备异步通知HALCON应用程序,例如,在曝光或图像传输结束时。请注意,并非所有图像采集接口都必须支持使用这些回调。如果支持回调,你还应该将avalible_callback_types添加到算子get_framegrabber_param中,以便通知用户可用回调的名称。
2.7 参数命名约定
参数名称 | 典型值 |
---|---|
‘continuous_grabbing’ | ‘enable’,‘disable’ |
‘do_abort_grab’ | ‘true’ |
‘grab_timeout’ | <int> |
‘image_available’ | 0,1 |
‘num_buffers’ | <int> |
‘revision’ | ‘4.0’,‘4.1’,… |
‘start_async_after_grab_async’ | ‘enable’,‘disable’ |
‘volatile’ | ‘enable’,‘disable’ |
当开发一个新的图像采集接口时,请遵循以下关于参数名称的注意项:
- 建议使用与其他HALCON图像采集接口相同的参数名称和值,以简化不同图像采集接口的处理。
- 所有参数名称和参数的有效字符串都小写。
- 所有可以通过set_framegrabber_param设置的参数也应通过get_framegrabber_param查询当前设置值。
- 特殊参数(所谓的动作参数),它们不设置新的参数值,而是启动某个特定操作,应该使用前缀’do_'。显然,没有必要通过get_framegrabber_param访问这些参数的当前设置。
- 所有参数都应该有一个相应的参数后缀’_description’,该后缀应该是有提示意义的。
- 如果适用,所有参数都应该有一个后缀为’_range’或’_values’的相应参数,包含可能范围或取值的必要信息。
参数名称 | 典型值 |
---|---|
‘brightness’ | <int> |
‘do_flush_buffers’ | ‘true’ |
‘do_force_trigger’ | ‘true’ |
‘contrast’ | <int> |
‘exposure’ | <int> |
‘frame_rate’ | <float> |
‘gain’ | <int>或<float> |
‘shaft_encoder’ | ‘enable’,‘disable’ |
‘shutter’ | <int> |
‘strobe’ | ‘enable’,‘disable’ |
‘strobe_delay’ | <int> 或<float> |
‘strobe_duration’ | <int>或<float> |
‘timestamp’ | <string> 或<int> |
‘trigger_mode’ | ‘free-run’,‘async’,… |
‘trigger_signal’ | ‘rising’,‘falling’,… |
‘trigger_source’ | ‘line1’,‘line2’ |
2.8 与HDevelop图像采集助手的最佳交互
如果你想结合HDevelop图像采集助手来使用HALCON图像采集接口,请注意以下几个点:
-
自动检测
- 用户可以从列表中手动选择图像采集接口,或者将此列表限制为计算机上实际可用的所有图像采集接口(自动检测)。对于位于目录 bin/%HALCONARCH% (或 lib/$HALCONARCH )中的所有已知接口,自动检测会调用 info_framegrabber(…, ‘info_boards’, …)。如果找到设备并且返回参数包含有效(即非空)字符串,则接口将被添加到检测到的图像采集接口列表中。
- 略
- 略
-
打开一个设备
- 用户必须输入必要的参数来打开设备。要提供这些参数,必要的信息通过调用info_framegrabber来查询。在不打开设备的情况下,也可以调用info_framegrabber。
- 以下查询类型实际上通过info_framegrabber调用:’defaults’,
’info_boards’, ’port’, ’camera_type’, ’horizontal_resolution’, ’vertical_resolution’, ’color_space’, ’bits_per_channel’,’field’, ’external_trigger’, ’generic’, and ’revision’.对于’camera_type’,返回的字符串也可以表示在GUI中使用的文件选择框。
-
参数化
- 连接过程中,所有参数通过info_framegrabber(…,‘paramters’,…)查询。除非存在一个只读参数’available_param_names’,该参数在通用参数情况下查询实际可用参数(例如,在GigEVision接口中),否则这些参数将显示在参数窗口中。
- 如果对单个参数调用get_framegrabber_param,返回了H_ERR_FGPARAM错误码,则该参数被遗弃了。
- 略
- 略
- 略
2.9 图像采集集成接口 vs. 图像采集接口
术语"HALCON图像采集接口"通常是指封装针对图像采集设备的特定代码的外部模块;这是你需要提供的模块。相反,"HALCON图像采集集成接口"是HALCON库内部的模块,负责管理和访问(外部)图像采集接口模块。
每当使用open_framegrabber或info_framegrabber首次访问图像采集设备时,相应的(外部)图像采集接口(一个可动态加载的模块)就会被加载。例如,像这样的调用:
open_framegrabber('GigEVision', ...)
在Windows下,这将会导致HALCON库加载hAcqGigEVision.dll;在Linux下会加载hAcqGigEVision.so。如果你使用的是HALCON XL,它将分别加载库hAcqGigEVisionxl.dll和hAcqGigEVision.so。请注意,为了该机制起作用,所有图像采集库都需要hAcq前缀(在使用HALCON XL的情况下,还要xl后缀)。
首次调用后,该模块内的接口例程(由你编写)将被相应HALCON算子调用。在查看涉及的数据结构之前,我们应该记住图像采集设备管理的某些部分发生在HALCON库中,而其他部分是由你的图像采集接口来完成的。明确谁负责什么是很重要的:
HALCON库的职责是:
- 维护图像采集类列表;
- 为每个类维护实例列表;
- 解码和预处理算子的参数。
接口的职责是:
- 定义一个类(例如,用适当数据填充数据结构);
- 管理多个实例及其相互依赖关系;
- 解释算子参数并将它们映射到底层硬件上;
- 并基于图像采集设备的应用程序编程接口(API)抓取图像。