3.4 数据结构
在研究机器视觉算法之前,我们必须分析机器视觉应用中涉及的基本数据结构。因此,本节中我们先介绍一下表示图像、区域、亚像素轮廓、句柄以及数组数据结构。
3.4.1 Image
Image指HALCON的图像类型。在机器视觉中,图像是基本的数据结构,它所包含的数据通常是由图像采集设备传送到计算机的内存中的。
图像通道可以被简单看作一个二维数组,这也是程序设计语言中表示图像时所使用的数据结构。因此在像素(r,c)处的灰度值可以被解释为矩阵g=fr,c中的一个元素。更正规的描述方式为:我们视某个宽度为w、高度为h的图像通道f为一个函数,该函数表述从离散二维平面Z2的一个矩形子集r={0,…,h−1}×{0,…,w−1}到某一个实数的关系f:r→R,像素位置(r,c)处的灰度值g定义为g=f(r,c)。同理,一个多通道图像可被视为一个函数f:r→Rn,这里的n表示通道的数目。
在上面的讨论中,我们已经假定了灰度值是由实数表示的。在几乎所有的情况下,图像采集设备不但在空间上把图像离散化,同时也会把灰度值离散化到某一固定的灰度级范围内。多数情况下,灰度值被离散化为8位(一个字节),也就是,所有可能的灰度值所组成的集合是0~255。
简单来说,图像的通道是图像的组成像素的描述方式,如果图像内像素点的值能用一个灰度级数值描述,那么图像有一个通道。比如灰色图像,每个像素的灰度值为0~255;如果像素点的值能用三原色描述,那么图像有三个通道。比如RGB是最常见的颜色表示方式,它的每个像素拥有R(Red,红色)、G(Green,绿色)、B(Blue,蓝色)3个通道,各自的取值范围都是0~255。彩色图像如果只存在红色和绿色、没有蓝色,并不意味着没有蓝色通道。一幅完整的彩色图像,红色、绿色、蓝色三个通道同时存在,图像中不存在蓝色只能说明蓝色通道上各像素值为零。
(1)在HALCON中查看图像变量
在HALCON中,把鼠标移动到HALCON变量窗口中的图像变量上会显示图像变量的类型、通道及尺寸,如图3.24所示。
图3.24 HALCON的变量窗口
值得注意的是,在一般的图像处理中,灰度图像已经可以满足要求。因此,为了节约计算量并加快速度,通常会将彩色图像转换成灰度图像进行处理。在HALCON中,可以使用rgb1_to_gray算子或rgb3_to_gray算子将彩色图像转换成灰度图像。
(2)在HALCON中访问通道
①如要获得某一指定通道的图像,可以使用access_channel算子。
access_channel(MultiChannelImage : Image : Channel :)
MultiChannelImage:输入的多通道图像。
Image:输出从多通道图像中计算得到的指定通道图像。
Channel:输入的要访问的通道索引,默认值:1,可取建议值:1、2、3、4、5、6等,但取值要小于通道数。
②如要获取通道数量,则可以使用count_channels算子。
count_channels(MultiChannelImage:::Channels)
MultiChannelImage:输入的多通道的图像。
Channels:输出计算得到的图像通道数。
(3)通道合并与分离
①如将两图像的通道叠加得到新图像,可以使用append_channel算子。
append_channel(MultiChannelImage, Image:ImageExtended::)
MultiChannelImage:输入的多通道图像。
Image:要叠加的图像。
ImageExtended:叠加后得到的图像。
②如要将三个单通道灰度图像合并成一个三通道彩色图像,可以使用compose3算子。
compose3(Imagel, Image2, Image3:MultiChannelImage::)
Imagel、Image2、Image3:对应三个单通道灰度图像。
MultiChannelImage:转换后得到的三通道彩色图像。
③如要将多幅单通道图像合并成一幅多通道彩色图像,可以使用channels_to_image算子。
channels_to_image(Images:MultiChannellmage::)
Images:要进行合并的单通道图像。
MultiChannelImage:合并得到的多通道彩色图像。
④如要将三通道彩色图像转化为三个单通道灰度图像,可以使用decompose3算子。
decompose3(MultiChannellmage:Imagel, Image2, Image3::)
MultiChannelImage:要进行转换的三通道彩色图像。
Image1:转换得到第一个通道的灰度图像,对应Red通道。
Image2:转换得到第二个通道的灰度图像,对应Green通道。
Image3:转换得到第三个通道的灰度图像,对应Blue通道。
读取一幅红色的三通道彩色图像后利用decompose3算子分解成三个单通道图像,其中得到的红色通道是一幅白色图像,得到的绿色和蓝色通道是黑色图像。所以我们能够知道红色在R通道中比较明显,同理绿色和蓝色分别在G和B通道中比较明显。
⑤如要将多通道图像转换为多幅单通道图像,可以使用image_to_channels算子。
image_to_channels(MultiChannelImage:Images::)
MultiChannelImage:要进行转换的多通道彩色图像。
Images:转换后得到的单通道图像。
⑥若要将彩色图像从RGB空间转换到其他颜色空间,可以使用trans_from_rgb算子。
trans_from_rgb(ImageRed, ImageGreen, ImageBlue:ImageResultl, ImageResult2, ImageResult3:ColorSpace:)
ImageRed、ImageGreen、ImageBlue:分别对应彩色图像的R通道、G通道、B通道的灰度图像。
ImageResult1、ImageResult2、ImageResult3:分别对应转换后得到的三个单通道灰度图像。
ColorSpace:输出的颜色空间,包括‘hsv’、‘hls’、‘hsi’、‘ihs’、‘yiq’、‘yuv’等,RGB颜色空间转换到其他颜色空间有对应的函数关系。
例3.2
图像通道实例。
程序如下:
*读取图像 read_image (Image, 'D:/picture/ship.png') *计算图像通道数 count_channels (Image, Num) *循环读取每个通道的图像 for I := 1 to Num by 1 *获取多通道指定图像 access_channel (Image, channel1, I) endfor *拆分通道 decompose3 (Image, RedImage, GreenImage, BlueImage) *合并通道 compose2 (RedImage, GreenImage, MultiChannelImage) *向图像附加通道 append_channel (MultiChannelImage, BlueImage, ImageExtended)
程序执行结果如图3.25所示。
图3.25 图像通道相关实例
3.4.2 Region
Region指图像中的一块区域。机器视觉的任务之一就是识别图像中包含某些特性的区域,比如执行阈值分割处理。因此至少我们还需要一种能够表示一幅图像中一个任意的像素子集的数据结构。这里我们把区域定义为离散平面的一个任意子集:
R⊂Z2 (3.1)
这里选用R来表示区域是有意与前一节中用来表示矩形图像的R保持一致。在很多情况下,将图像处理限制在图像上某一特定的感兴趣区域(ROI)内是极其有用的。就此而论,我们可以视一幅图像为一个从某感兴趣区域到某一数据集的函数:
f:R→Rn (3.2)
这个感兴趣区域有时也被称为图像的定义域,因为它是图像函数f的定义域。将上述两种图像表示方法统一:对任意一幅图像,可以用一个包含该图像所有像素点的矩形感兴趣区域来表示此图像。所以,从现在开始,我们默认每幅图像都有一个用R来表示的感兴趣区域。
很多时候需要描述一幅图像上的多个物体,它们可以由区域的集合来简单地表示。从数学角度出发可以把区域描述成集合表示,如式(3.3)所示。
(3.3)
这个定义引入了二值图像来描述区域。一个二值图像用灰度值0表示不在区域内的点,用1(或其他非0的数)表示被包含在区域内的点。简单言之,区域就是某种具有结构体性质的二值图。
(1)在HALCON中查看区域特征
区域的特征我们可以通过点击工具栏中的“特征检测”,如图3.26(a)所示。在弹出的对话框中选择region,可以看到region的不同特征属性及相对应的数值,如图3.26(b)所示。
图3.26 特征检测
region的特征主要有以下三个部分:
①基础特征:Region的面积、中心、宽高、左上角与右下角坐标、长半轴、短半轴、椭圆方向、粗糙度、连通数、最大半径、方向等。
②形状特征:外接圆半径、内接圆半径、圆度、紧密度、矩形度、凸性、偏心率、外接矩形的方向等。
③几何矩特征:二阶矩、三阶矩、主惯性轴等。
(2)将Image图像转换成Region区域
在HALCON中,通常需要将Image图像转换成Region区域方便图像处理,转换方法一般为以下两种。
①可以利用阈值分割threshold算子进行转化。
threshold(Image:Region:MinGray, MaxGray:)
Image:要进行阀值分割的图像。
Region:经过阀值分割得到的区域。
MinGray:阀值分割的最小灰度值。
MaxGray:阀值分割的最大灰度值。
区域的灰度值g满足:
MinGray≤g≤MaxGray (3.4)
对彩色图像使用threshold算子最终只针对第一通道进行阈值分割,即使图像中有几个不相连的区域,threshold也只会返回一个区域,即将几个不相连区域合并然后返回合并的区域。
②使用灰度直方图进行转化,步骤如下:
在工具栏中点击“打开灰度直方图”,如图3.27所示。接着打开使能输出按钮,最后拖动图3.27中的红色竖线(阈值为44的竖线)与绿色竖线(阈值为152的竖线),点击插入代码即可。这里绿色竖线、红色竖线与横坐标交点的值对应阈值分割的最小值与最大值。
图3.27 灰度直方图
例3.3
阈值分割算子获得区域实例。
程序如下:
*关闭窗口 dev_close_window () *获得图像 read_image (Aegypt1, 'egypt1') *获得图像尺寸 get_image_size (Aegypt1, Width, Height) *打开窗口 dev_open_window (0, 0, Width, Height, 'black', WindowHandle) *显示图像 dev_display (Aegypt1) *阈值分割图像获得区域 threshold (Aegypt1, Regions, 23, 160)
程序执行结果如图3.28所示。
图3.28 阈值分割获得区域实例图
3.4.3 XLD
XLD(eXtended Line Descriptions)称为亚像素精度轮廓,指图像中某一块区域的轮廓。图像中Image和区域Region这些数据结构是像素精度的,像素越高,分辨率越大,图像就越清晰。点与点之间的最小距离就是一个像素的宽度,在实际工业应用中,可能需要比图像像素分辨率更高的精度,这时就需要提取亚像素精度数据,亚像素精度数据可以通过亚像素阈值分割或者亚像素边缘提取来获得。在HALCON中XLD代表亚像素边缘轮廓和多边形,XLD轮廓如图3.29所示。
图3.29 XLD轮廓
通过图3.29的XLD轮廓可以看出:
①XLD轮廓可以描述直线边缘轮廓或多边形,即一组有序的控制点集合,排序是用来说明哪些控制点是彼此相连的关系。这样就可以理解XLD轮廓由关键点构成,但并不像像素坐标那样一个点紧挨一个点。
②典型的轮廓提取是基于像素网格的,所以轮廓上的控制点之间的距离平均为一个像素。
③轮廓只是用浮点数表示XLD各点的行、列坐标。提取XLD并不是沿着像素与像素交界的地方,而是经过插值之后的位置。
(1)在HALCON中查看XLD的特征
查看XLD特征的步骤与查看Region特征的步骤相似。点击工具栏中的“特征检测”,选择XLD,在图形窗口选择要查看的XLD特征,可看到XLD的特征属性及其相对应的数值,如图3.30所示。
图3.30 XLD特征检测
XLD特征分为四部分:
①基础特征:XLD面积、中心、宽高、左上角及右下角坐标。
②形状特征:圆度、紧密度、长度、矩形度、凸性、偏心率、外接矩形的方向及两边的长度等。
③云点特征:云点面积、中心、等效椭圆半轴及角度、云点方向等。
④几何矩特征:二阶矩等。
(2)Image转换成XLD
将单通道Image转换成XLD可以使用threshold_sub_pix、edges_sub_pix等算子。例如:
threshold_sub_pix(Image:Border:Threshold:)
Image:要提取XLD的单通道图像。
Border:提取得到的XLD轮廓。
Threshold:提取XLD轮廓的阈值。
例3.4
图像转XLD实例。
程序如下:
*关闭窗口 dev_close_window () *获取图像 read_image (Image, 'fabrik') *打开适应图像大小的窗口 dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) *提取图像得到亚像素边缘 edges_sub_pix (Image, Edges, 'canny', 2, 12, 22) *显示边缘 dev_display (Edges)
程序执行结果如图3.29所示。
3.4.4 Handle
Handle句柄是一个标识符,是拿来标识对象或者项目的。它就像我们的车牌号一样,每一辆注册过的车都会有一个确定的号码,不同的车号码各不相同,但是也可能会在不同的时期出现两辆号码相同的车,只不过它们不会同时处于使用之中罢了。从数据类型上来看,它只是一个32位(或64位)的无符号整数。应用程序几乎总是通过调用一个Windows函数来获得一个句柄,之后其他的Windows函数就可以使用该句柄,以引用相应的对象。在Windows编程中会用到大量的句柄,比如HINSTANCE(实例句柄)、HBIT-MAP(位图句柄)、HDC(设备描述表句柄)、HICON(图标句柄)等。
Windows之所以要设立句柄,根本上源于内存管理机制的问题,即虚拟地址。简而言之,数据的地址需要变动,变动以后就需要有人来记录、管理变动,因此系统用句柄来记载数据地址的变更。在程序设计中,句柄是一种特殊的智能指针,当一个应用程序要引用其他系统(如数据库、操作系统)所管理的内存块或对象时,就要使用句柄。句柄与普通指针的区别在于,指针包含的是引用对象的内存地址,而句柄则是由系统所管理的引用标识,该标识可以被系统重新定位到一个内存地址上。这种间接访问对象的模式增强了系统对引用对象的控制。
3.4.5 Tuple
Tuple可以理解为C/C++语言中的数组,数组是编程语言中常见的一种数据结构,可用于存储多个数据,每个数组元素存放一个数据,通常可通过数组元素的索引来访问数组元素,包括为数组元素赋值和取出数组元素的值。C/C++语言中数组的操作大都可以在Tuple中找到对应的操作。
(1)数组的数据类型
①变量类型:int、double、string等。
②变量长度:如果长度为1则可以作为正常变量使用。第一个索引值为0,最大的索引为变量长度减1。
(2)Tuple数组定义和赋值
①定义空数组。
Tuple:=[ ]
②指定数据定义数组。
Tuple:=[1, 2, 3, 4, 5, 6] Tuple2:=[1, 8, 9,'hello'] Tuple3:=[0x01, 010, 9,'hello'] //Tuple2与Tuple3值一样 tuple:=gen_tuple_const(100, 47) //创建一个具有100个元素的,每个元素都为47的数据
③Tuple数组更改指定位置的元素值(数组下标从0开始)。
Tuple[2]=10 Tuple[3]='unsigned'//Tuple数组元素为Tuple:=[1, 2, 10,'unsigned', 5, 6]
④求数组的个数。
Number:=|Tuple| //Number=6
⑤合并数组。
Union: =[Tuple,Tuple2] //Union=[1, 2, 3, 4, 5, 6, 1, 8, 9,'hello']
⑥生成1~100内的数。
数据间隔为1 Numl: =[1, 100] 数据间隔为2 Numl: =[1,2,100]
⑦提取Tuple数组指定下标的元素。
T:=Num1[2] //T=5
⑧已知数组生成子数组。
T: =Num2[2, 4] //T=[5, 7, 9]
小结
本章简要介绍了HALCON的功能特点及其交互式的编程环境HDevelop的开发环境,并概述了利用HALCON进行实时采集和离线采集的图像采集过程。此外,介绍了图像处理过程中的五种常用数据结构,分别是图形Image、Region、XLD、Handle和Tuple,HALCON数据结构是HALCON学习的基础,本章节对后续HALCON编程的学习具有重要意义。