购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第4章
软件图像采集

当硬件环境搭配好了以后,接下来要考虑如何将图像输入到软件模块。软件中的图像采集即可实现这一功能。图像采集是机器视觉的输入项,也是图像处理的基础。采集图像的速度和质量会直接影响后续图像处理的效率。本章主要介绍如何获取输入图像。

本章主要涉及的知识点如下。

●获取非实时图像:用测试图像进行算法的设计与测试。

●获取实时图像:介绍如何通过硬件设备采集图像。

●多相机采集图像:讨论多相机的采集方法。

●Halcon图像的基本结构:介绍关于图像的一些基本数据结构。

●实例:演示如何采集Halcon图像并进行简单处理。

4.1 获取非实时图像

在机器视觉项目初期,由于种种原因,开发方不一定能一直在检测现场进行实时调试。因此,可以先拍摄好一些图像或视频作为测试素材,在编写视觉图像处理软件时,用测试图像进行算法的设计与测试。测试通过之后,再连接相机进行实时采集,这样有利于提高开发效率。

4.1.1 读取图像文件

获取非实时图像的方法比较简单,即从指定路径中读取图片或序列。在Halcon中,可以用read_image算子来读取图像文件。例如:

read_image (Image, 'C: /test.png')

以上是读取单张图像。如果要读取整个文件夹的图像,可以通过循环来实现,代码如下:

*列出指定路径下的文件
list_files ('C: /Picture', ['files','follow_links'], ImageFiles)
*选择符合条件的文件
tuple_regexp_select (ImageFiles,['\\.(tif|tiff|gif|bmp|jpg|jpeg|jp2|png|pcx|
pgm|ppm|pbm|xwd|ima|hobj)$','ignore_case'], ImageFiles)
*循环读取文件夹中的图像
for Index := 0 to |ImageFiles| - 1 by 1
    read_image (Image, ImageFiles[Index])
endfor

在Halcon也可以使用图像采集助手来读取图像文件。选择菜单栏中的“助手”→“打开新的Image Acquisition”选项,将出现Halcon图像采集助手窗口,如图4.1所示。

图4.1 Halcon图像采集助手窗口

在“资源”选项卡中选择“图像文件”的路径。如果采集单张图,则单击“选择文件”按钮,如果采集多张图,可以事先将图放在一个指定的文件夹里,然后单击“选择路径”按钮。如果需要查看代码,可以单击该窗口菜单栏里的“插入代码”按钮,代码将显示在程序编辑器中。单击“运行”按钮,查看读取图像的效果,非实时图像采集完成。

4.1.2 读取视频文件

读取视频文件的方式与读取图像文件类似,还以Halcon图像采集助手为例。打开Halcon图像采集助手窗口,在“资源”选项卡中的“图像获取接口”选项区域选择“DirectFile”选项,这是Halcon读取视频文件的接口,如图4.2所示。

图4.2 Halcon图像采集助手窗口

注意

选择DirectFile时,文件路径应当用英文。

然后连接视频文件,选择“连接”选项卡,在其中设置读取视频的参数,在“媒体文件”中选择视频文件所在的路径,这样即可实现视频的输入,如图4.3所示。

图4.3 使用Halcon图像采集助手设置读取视频的参数

实现代码参考如下:

*开启图像采集接口
open_framegrabber('DirectFile',1,1,0,0,0,0,'default',-1,'default',-1,
'false','test.avi','default',1,-1,AcqHandle)
*开始异步采集
grab_image_start (AcqHandle, -1)
while (true)
   *获取采集的图像
   grab_image_async (Image, AcqHandle, -1)
Endwhile
*关闭采集接口
close_framegrabber (AcqHandle)

Halcon支持的视频格式并不多,文件中可选的只有“.avi”格式的视频,而且并非所有“.avi”格式的文件都能读取。有时可能会出现读取不了的情况,这与视频本身的编解码方式有关。因此还是建议使用图像或图像序列的方式来代替非实时视频输入。

4.2 获取实时图像

本节将介绍如何通过硬件设备采集图像,获得检测场景中的实时图像。获取实时图像有两种主要的方式:一是通过Halcon自带的采集接口获取,二是通过相机配套的SDK获取。下面将分别介绍这两种方式。

4.2.1 Halcon的图像采集步骤

Halcon的图像采集主要分为3个步骤,如图4.4所示。

图4.4 Halcon的图像采集步骤

图4.4说明了实时采集图像的3个基本步骤。

(1)开启图像采集接口:连接相机并返回一个图像采集句柄。

(2)读取图像:设置采集参数并读取图像。

(3)关闭图像采集接口:在图像采集结束后断开与图像采集设备的连接以释放资源。

注意

并非相机的所有功能都可以使用Halcon进行操控,完整的功能控制还需查看SDK或者采集卡软件。

4.2.2 使用Halcon接口连接相机

Halcon的采集功能非常强大,它支持的相机种类非常丰富,为市面上常见的多种机型提供了统一的公用接口。如果系统选择的相机支持Halcon,就可以直接使用Halcon自带的接口库实现连接。

1. 接口文件

如果想要查看Halcon支持哪些相机接口,可以在Halcon的安装路径下找到Bin文件夹,打开对应的操作系统文件夹,即可看到以“hAcqxxx.dll”方式命名的dll文件。例如,“hAcqDahengCAM.dll”或“hAcqDahengCAMxl.dll”对应大恒相机的采集接口,“hAcqpylon.dll”或“hAcqpylonxl.dll”对应Basker相机的采集接口。

在Halcon主界面中选择“文件”→“浏览HDevelop示例程序”选项,在打开的窗口中选择“分类”→“图像采集设备”选项,即可在右侧看到相应的采集设备的示例程序。

2. 连接相机

对于所有的接口来说,采集图像的第一步都是连接相机。在Halcon中,连接相机是通过调用open_framegrabber算子实现的,其作用是连接相机并设置一些基本的采集参数,如选择相机类型和指定采集设备。也可以设置和图像相关的参数,如以下参数。

(1)HorizontalResolution(水平相对分辨率):如果是1,表示采集的图宽度和原图一样大;如果是2,表示采集图的宽度为原图的两倍。默认为1。

(2)VerticalResolution(垂直相对分辨率):与水平相对分辨率类似。默认为1,表示采集的图宽度和原图一样大。

(3)ImageWidth:表示图像的宽,即每行的像素数。默认为0,表示原始图的宽度。

(4)ImageHeight:表示图像的高,即每列的像素数。默认为0,表示原始图的高度。

(5)StartRow、StartColumn:表示采集的图在原始图像上的起始坐标,这两个值都默认为0。

(6)Field:相机的类型,默认为default。

(7)BitsPerChannel:表示像素的位数,默认为-1。

(8)ColorSpace:表示颜色空间,默认为default。也可以选择Gray,表示灰度;或选择RGB,表示彩色。

(9)Generic:表示特定设备,默认为-1。

(10)CameraType:表示相机的类型,默认为default,也可以根据相机的类型选择ntsc、pal或auto。

(11)Device:表示所连接的采集设备的编号,默认为default。如果不确定相机的编号,可使用info_framegrabber算子进行查询。

(12)Port:表示连接的端口,默认为-1。

这个算子执行完后会返回一个图像采集的连接句柄AcqHandle,该句柄就如同Halcon和硬件进行交互的一个接口。使用该句柄可以实现图像捕获、设置采集参数等。

3. 设置采集参数

由于连接相机的接口open_framegrabber是针对大部分相机的公用接口,而相机的种类繁多,功能各异,因此公用接口中只包含了通用的几种简单操作的参数。如果想要充分地利用相机的全部功能,设置其他的特殊参数,可以使用set_framegrabber_param进行设置。

具体的参数种类或值的含义可参考Halcon的算子文档,如果想要查看Halcon具体支持哪些可修改的参数,可以使用info_framegrabber算子。例如:

info_framegrabber ('USB3Vision', 'parameters', ParametersInfo, ParametersValue)

特殊参数将在“变量监视”窗口列出,如图4.5所示。

图4.5 特殊参数列表

如要修改其中的某项参数,使用set_framegrabber_param算子。例如:

set_framegrabber_param(AcqHandle,'color_space','gray')

其中AcqHandle为图像采集的句柄,后面两个参数为参数名称和要修改的值。

如果要查询某一个参数的值,可以用get_framegrabber_param算子。例如:

get_framegrabber_param(AcqHandle, 'name', Value)
注意

如果某个参数在open_framegrabber中设定过,那么该参数将不可在相机工作过程中被修改。

4. 实时采集图像

与相机建立联系后,实时地读取图像是在grab_image或grab_image_async算子中实现的,二者有如下区别。

grab_image用于相机的同步采集。其工作流程是先获取图像,然后等图像转换等处理流程完成之后再获取下一帧图像。图像的获取和处理是两个顺序执行的环节。因此,下一帧图像的获取要等待上一帧图像的处理完成才开始,这样相机的实际帧率可能会低于标定的值,还可能会有采集过程耗时太长的情况。

grab_image_async用于相机的异步采集。异步采集不需要等到上一帧图片处理完成再开始捕获下一帧,图像的获取和处理是两个独立的环节。异步采集可以在当前图像捕获完成后立即捕获下一帧,也可以根据设定的时间间隔获取图像。该算子的最后一个参数可用于设置延时,达到延时时间即可开始捕获下一帧图像。在实际使用中,常常使用多线程同步机制配合异步采集。

在图像采集结束后应使用close_framegrabber算子断开接口与图像采集设备的连接。一个简单的采集图像的代码举例如下:

*开启图像采集接口
open_framegrabber ('USB3Vision', 0, 0, 0, 0, 0, 0, 'progressive', -1,
'default', -1, 'false, default'、 '2676014B7826_Basler_acA1920155um', 0, -1,
AcqHandle)
while (true)
    grab_image (Image, AcqHandle)
Endwhile
*关闭图像采集接口
close_framegrabber (AcqHandle)

运行后即可在图像窗口中看到实时采集的图像。

4.2.3 使用相机的SDK采集图像

也可以使用相机的SDK采集图像,原因如下。

(1)并非所有的相机都能支持Halcon,对于不支持Halcon的相机,无法使用Halcon自带的采集接口采集图像。

(2)即使有些相机支持Halcon,也并非所有的相机功能参数都能通过Halcon进行配置,这样可能会限制某些功能的使用,而相机自带的SDK的功能则强大得多。

(3)有些项目不完全是使用Halcon进行图像处理的,可能会和其他的库(如OpenCV)结合使用。例如,在采集图像之后,需要用OpenCV做一些转化,再给Halcon使用,在这种情况下,直接使用相机的SDK实现采集也是一种有效的方式。

相机厂商一般会配备软件开发包,其中包括操作软件、SDK开发包、各种语言的示例程序等。这种图像采集方式更加稳定可靠,但采集后的图像在传给Halcon之前,需要先转换成Halcon的图像格式。

4.2.4 外部触发采集图像

上文提到的都是使用软件触发相机拍照,然而在有些工业应用中,相机是由外部硬件的信号触发的,因此大多数图像采集设备都装配了至少一条输入信号的线,也就是外触发的输入线。因此,在软件中要做的就是在open_framegrabber算子中把ExternalTrigger参数修改一下,变成Ture,表示支持外触发的模式。

在使用外触发的情况下,相机使用grab_image算子采集图像,然后等待触发信号。如果收到触发信号,就进入下面的流程:相机等待下一帧图像送达,将其数字化,并进入计算机内存,然后Halcon采集接口将图像变成Halcon图像格式HImage,并返回连接句柄AcqHandle。这些执行完以后,再次调用grab_image算子,等待下一个触发信号,如此循环。

仅这样设置会有潜在的问题,一是如果触发信号与曝光时间不同步,有可能采集到的图像是没有内容的;二是触发信号如果在循环中的图像处理流程结束前到达,那么下一个循环等待触发信号的时间可能会超时。

为了解决这些问题,需要在set_framegrabber_param算子中进一步设置grab_timeout参数,该参数用来指定图像采集的延时。如果没有这个参数,则延时默认为-1,grab_image算子会一直等待外触发的信号到达。

设置该参数后,如果因为某种原因,触发信号没有到达,则超过设定的时间后,程序就会返回一个错误代码,以提示存在异常。某些相机的Halcon接口会提供Trigger_timeout参数,作用与之类似,可通过相关文档了解其具体设置方法。Halcon中的外触发模式代码举例如下:

*开启图像采集接口,外触发参数设为true
open_framegrabber ('USB3Vision', 0, 0, 0, 0, 0, 0, 'progressive', 8, 'default',
-1, 'true', 'default', 'camera_name', 0, -1, AcqHandle)
*设置超时时间
set_framegrabber_param (AcqHandle, 'grab_timeout', 50000)
while (true)
    grab_image (Image, AcqHandle)
Endwhile
close_framegrabber (AcqHandle)

如果使用相机的SDK来获取图像,一般会有关于外触发的函数,直接调用即可。

4.3 多相机采集图像

上文提到的是单个相机的采集方法,在实际应用中经常会遇到不止一个相机的情况,如双目视觉应用等。接下来讨论多相机的采集方法。

1. 连接多个采集设备

多相机的情况下可能有多个输入端口(Port),这时需要让多个采集接口分别对应不同的数据端口。可以根据上文所述的采集图像的3个步骤,分别通过每个端口实现图像采集。这个原理类似于多线程,每个采集任务独立进行。

2. 多相机异步采集图像

多相机的情况还有可能是多个相机连接在同一种采集卡上。使用grab_image_async算子进行异步采集时,默认会从连接的第一个相机中连续采集,如果需要切换到另一台相机,可以在set_framegrabber_param算子中设置一个start_async_after_grab_async参数。

注意

这一功能只有在多个相机的型号和采集方式类似的情况下才有效,如双目立体相机。

3. 多相机同步采集图像

用多个相机同步采集图像,有时需要多个相机能同步工作,如双目视觉系统需要两路信号实时采集以计算视差信息,这需要对采集进行同步控制。有些采集接口支持从多个相机同步采集图像,如一个采集卡连接多个相机,或者多个采集卡连接多个相机。但是即使采集接口支持同步采集,也需要配备相应的采集单元才能生效。

即使对应的Halcon采集接口不支持多相机同步采集,也可以通过外部触发实现同步。例如,可以用每一个采集卡连接一个相机,然后将同一个触发信号发送给多个采集卡的方式来实现同步。

还有一种方式,即用软件控制的方式程实现同步采集。可以在Halcon代码导出以后,在C/C#等开发环境中使用相机SDK中的相关接口或多线程等方法实现同步采集。

4.4 Halcon图像的基本结构

接下来介绍关于图像的一些基本数据结构。

(1)Image:指Halcon的图像类型,由矩阵数据组成,矩阵中的每个值表示一个像素。Image中含有单通道或者多通道的颜色信息。

(2)Region:指图像中的一块区域。该区域数据由点的坐标组成,表达的意义类似于一个范围。可以用Region来创建一个感兴趣区域(Region of Interest,ROI),该区域可以是任意形状,可以包含孔洞,甚至可以是不连续的点。

(3)XLD:指图像中某一块区域的轮廓,由Region边缘的连续的点组成。

(4)Tuple:类似于数组,可以用于存储一幅或多幅图像。如果要对一些图像进行批处理,可以将这些图像存入Tuple进行一次性处理。

4.5 实例:采集Halcon图像并进行简单处理

下面介绍一个简单的采集图像的例子。在Halcon中利用图像采集接口,使用USB 3.0相机实时拍摄图像。采集到图像后对图像进行简单的阈值分割处理,将有物体的区域标记出来。

(1)创建一个图像窗口,并连接相机。首先使用dev_close_window清理显示区域,并用dev_open_window创建一个显示图像的窗口,然后连接采集设备。使用open_framegrabber连接相机,并简单地设置一些参数。由于使用的是USB3Vision接口的相机,因此在第一个参数中填入接口名称。在Device参数中选择相机的型号,开始准备采集。

(2)采集图像。由于要连续地采集图像,因此要建立图像采集循环。在循环中使用grab_image获取图像,并使用dev_display将其显示出来。

(3)简单的图像处理。获取到图像后将其保存在Image变量中,接下来可以根据需要对图像做进一步的处理,如阈值分割、图像平滑,以及其他形态学处理等。本例中首先使用rgb1_to_gray将采集到的原始图像转化为单通道的灰度图像,然后使用阈值处理将灰度较深的区域存入一个名为DarkArea的变量中。

接着使用fill_up对Dark区域进行填充,并用connection算子进行区域分割。然后通过select_shape将面积大的区域提取出来,排除无意义的杂点,并用dev_display将填充区域绘制出来。

同时,通过count_obj统计出零件区域的数量,并用字符串的形式显示在窗口中。这是一个简单的关于图像采集与阈值处理的例子,后续可以根据需要进行更复杂的处理。

(4)关闭图像采集接口。图像采集完成后可以结束循环,并使用close_framegrabber关闭采集接口,释放设备资源。其运行结果如图4.6所示。

图4.6 运行结果

实现代码参考如下: zqanCnLJK0jW+SkNaQTh9pxa/xZwWeQZ7Iq9jlPyPlWnsCFyw/x6I+Vm3SjNhT5X

*关闭当前窗口,清空屏幕
dev_close_window ()
*打开图像采集接口,接口类型为USB3Vision,其他参数都是默认
open_framegrabber ('USB3Vision', 0, 0, 0, 0, 0, 0, 'progressive', -1,
'default', -1, 'false', 'default', '2676014B7826_Basler_acA1920155um', 0,
-1, AcqHandle)
*抓取一幅图像,这幅图是为了获取图像的大小以建立合适尺寸的窗口
grab_image (ImageBase, AcqHandle)
*获取图像的大小,以建立合适尺寸的窗口
get_image_size (ImageBase, Width, Height)
*创建新的显示窗口
dev_open_window (0, 0, Width/2, Height/2, 'black', WindowHandle)
*开始进入采集图像的循环
while (true)
   *利用此采集接口的句柄获取图像
   grab_image (Image, AcqHandle)
   *显示采集画面
   dev_display (Image)
   **
   *此处可根据需要对图像做进一步处理。这里举一个简单的阈值处理并计数的例子
   **
   *将图像转换为单通道灰度图像
   rgb1_to_gray (Image, GrayImage)
   dev_display (GrayImage)
   *使用阈值处理提取较暗部分
   threshold (GrayImage, DarkArea, 0, 80)
   *填充区域
   fill_up (DarkArea, RegionFillUp)
   *将不相连的区域整体分割成独立的区域
   connection (RegionFillUp, ConnectedRegions)
   *排除杂点,将面积较大的目标选择出来
   select_shape(ConnectedRegions, SelectedRegions, 'area', 'and', 150, 99999)
   *目标计数
   count_obj (SelectedRegions, Number)
   *即将显示文字,文字颜色设置为黑色
   dev_set_color ('black')
   *确定文字的显示位置
   set_tposition (WindowHandle, 50, 50)
   *设置字体
   set_font (WindowHandle, '-System-24-*-0-0-0-1-GB2312_CHARSET-')
   *窗口输出文字
   write_string (WindowHandle, '有'+Number+'个零件')
   *显示零件形状区域,设置颜色为红色
   dev_set_color ('red')
   *显示模式为填充
   dev_set_draw ('fill')
   *显示提取出的区域
   dev_display (DarkFilled)
endwhile
*采集结束,关闭采集接口,释放相机资源
close_framegrabber (AcqHandle)
点击中间区域
呼出菜单
上一章
目录
下一章
×