【机器视觉】 HDevelop语言基础(二)-变量和表达式
00. 目录
文章目录
01. 概述
变量的名称通常由字母、数字和下划线“_”组成。 变量(图标或控制变量)的类型取决于它在参数列表中的位置,其中变量标识符是首次使用。 变量的类型是在输入过程中算子参数确定的:每当出现新标识符时,都会创建一个具有相同标识符的新变量。控制变量和图标变量必须有不同的名称。 变量(图标或控件)的值未定义的,直到第一次赋值定义了它(变量还没有被实例化)。 对未定义的变量进行访问导致运行时错误。
实例化的变量包含元组的值。 根据变量的种类,数据项是图标对象或控制数据。 元组的长度由执行的操作动态确定。 变量可以多次获得新值,但是一旦分配了值,该变量将始终保持被实例化,除非您选择菜单项 执行-> 重置程序执行。 变量的内容在分配新值之前被删除。
02. 变量的类型
不同类型变量的概念允许对变量(控制或图标数据)进行第一次(“粗略”)分类,而数据的实际类型(例如,实数、整数、字符串等)是未定义的,直到为变量分配了具体值。 因此,新数据项的数据类型可能与旧数据项不同。
在 HDevelop 中,变量的类型以三种不同的方式定义:
• 明确
过程参数定义
全局变量声明
• 隐含地
在代码中的使用
在这三种可能性中,过程参数定义是最有效的一种,并且在任何情况下都将否决其他两种可能性,其次是全局变量声明。 因此,代码中的用法是最不有效的。 在后一种情况下,变量的类型由那些代码行定义,并且仅由那些代码行定义,其中写入变量的值并且新值的确切类型是先验已知的(即,在运行时间之前) .
在其作用域内,变量必须始终具有相同的类型(控制、图标)和相同的大小,否则会导致错误:
• 如果变量的类型从未正确定义,则该变量将具有未定义的类型。 所有使用该变量的行都将变为无效,并且该变量不会显示在变量窗口中。
• 如果变量的类型定义不同两次或更多次,变量类型也将是未定义的,上述情况也适用于这种情况。
• 如果变量的类型定义正确,但在错误的上下文中使用(例如,将图标变量用作控制输入参数),则只有受影响的代码行将变为无效。
03. 变量的作用域
HDevelop 支持局部和全局变量。 默认情况下,所有变量都是局部变量,即它们只存在于它们的过程中。 因此,同名的局部变量可以存在于不同的程序中,互不干扰。 相反,全局变量可以在整个程序中访问。 它们必须使用操作符 global 显式声明。
声明全局变量
global tuple File
global object Image
关键字 def 允许显示地将一个声明标记为定义变量的位置,例如,global def object Image。 这仅在将程序导出为编程语言时才相关。 有关更多信息,请参阅操作符全局的描述。
一旦声明了全局变量,它就可以像在程序中声明局部变量一样的使用。如果要在不同的过程中访问全局变量,则必须使用相同的global… . 调用(否则,将创建一个局部变量)。
main procedure:
* declare global variables
global tuple File
global object Image
...
File := 'particle'
read_image(Image, File)
process_image()
* Image has been changed by process_image()
* File remains unchanged
...
process_image procedure:
* use global variable
global object Image
...
bin_threshold(Image, Region)
File := 'fuse'
read_image(Image, File)
return()
因为程序必须明确声明实用的全局变量,所以不能通过在程序的其他部分引入全局变量来破坏现有程序。 从本质上讲,全局变量的名称在整个 HDevelop 程序中必须是唯一的,即所有加载的外部程序、主程序和所有本地程序。 变量窗口提供了一个特殊选项卡来列出当前声明的所有全局变量。
04. 图像对象操作
图形类型是Halcon独有的类型,在Halcon中图像对象主要以数组的方式进行操作,通过处理图形数组数据,获取的结果依然是以数组的形式返回。有关 HALCON 运算符的详细说明,请参阅 HALCON 参考手册和第 281 页第 8.5.3 节中的备注。
05. 输入控制参数表达式
在HDevelop中,所有的算术表达式或字符串操作都被限制为输入控制参数,所有其它参数则必须是属于变量类型。
5.1 元组操作的一般特征
本节旨在简要概述元组的特性及其操作。 以下各节对此处提到的每个运算符进行了更详细的描述。
请注意,所有变量和常量已被表示允许的数据类型的字母替换。 这些字母提供了数据类型具有一定的范围。 表 8.4 中列出了这些字母及其含义。 对这些符号的操作只能应用于指定类型或返回指定类型结果的表达式。

符号 i、a、b 和 s 可以表示元组(长度为 1 的元组)以及任意长度的元组。如果元组元素操作1个,大多数操作需要按一下规则进行。
-
如果元组之一的长度为 1,则其他元组的所有元素都与所选操作的单个值组合。
-
如果两个元组的长度都大于 1,则两个元组的长度必须相同(否则会发生运行时错误)。 在这种情况下,所选操作将应用于具有相同索引的所有元素。 结果元组的长度与输入元组的长度相同。
-
如果元组之一的长度为 0 ([]),则会发生运行时错误。

在上面的表中,我们可以看到算术运算的案例,运算操作是按次序进行排列的,他们基本的算术运算操作是+、-、*、/,特别注意的是,在二目运算中+运算操作,如果是数值运算,它代表的两个数值之间的相加操作,如果是字符运算操作,他是将两个字符串进行合并操作。
5.2 赋值操作
在 HDevelop 中,赋值被视为运算符。 要使用赋值,您必须选择运算符assign(Input, Result)。 该运算符具有以下语义:它计算 Input(赋值的右侧)并将其存储在 Result(赋值的左侧)中。 但是,在程序文本中,赋值由赋值运算符的常用语法表示:Result := Input。 以下示例概述了 C 语法中的赋值与其在 HDevelop 中的转换版本之间的区别:
C语言赋值操作形式是:
u = sin(x) + cos(y);
在HDevelop中的语句形式为:
assign (sin(x) + cos(y), u)
采用赋值操作方式为
u := sin(x) + cos(y)
如果表达式的结果不需要存储到变量中,则表达式可以直接用作任何运算符的输入值。 因此,仅当必须多次使用该值或必须初始化变量(例如,for 循环)时才需要赋值。
赋值算子assign_at (Index, Value, Result) 用于修改元组元素。
assign_at (Radius-1, Area, Areas)
比较直观的表达如下:
Areas[Radius-1] := Area.
示例:
Areas := [1,2,3]
Areas[1] := 9
等价于
sets Areas to [1,9,3].
使用assign_at构造元组,通常使用空元组作为初始值,并通过循环插入元素:
Tuple := []
for i := 0 to 5 by 1
Tuple[i] := sqrt(real(i))
endfor
从示例中可以看出,元组的索引从 0 开始。
在元组中插入元素通常有一下几种方式:
- 在元组中连续输入元素的值,中间用逗号隔开,操作方式如下:
assign ([Tuple,NewVal],Tuple)
采用赋值方式为:
Tuple := [Tuple,NewVal]
- 如果索引位置介于两者之间,则必须使用运算符 tuple_insert。
To insert the tuple [11,12,13] into the tuple [1,2,3] at position 1, use
tuple_insert ([1,2,3], 1, [11,12,13], Result)
resulting in [1,11,12,13,2,3].
在下面的示例中,使用圆形掩码扩大区域,然后将这些区域存储到元组区域中。 在这种情况下,使用算子assign_at。
read_image (Mreut, 'mreut')
threshold (Mreut, Region, 190, 255)
Areas := []
for Radius := 1 to 50 by 1
dilation_circle (Region, RegionDilation, Radius)
area_center (RegionDilation, Area, Row, Column)
Areas[Radius-1] := Area
endfor
请注意,首先必须初始化变量 Areas 以避免运行时错误。 在示例中,区域是用空元组 ([]) 初始化的。 而不是 assign_at 运算符分配与元组连接
Areas := [Areas,Area]
程序示例
* simple assignment of the result of an expression to a variable
Val := sin(1.2) + cos(1.2)
*
* assign a tuple to a variable
Tuple1 := [1,0,3,4,5,6,7,8,9]
*
* assign a value to a single element of a tuple
Tuple1[1] := 2
*
* assign one value to several tuple elements
Tuple1[1,3,5] := 'abc'
*
* assign several values to several tuple elements at the same time
Tuple1[0,4,8] := ['000','444','888']
*
* setting a value beyond the tuple's range will initialize the
* intermediate values
Tuple2[5] := 5
*
* initialize a tuple via a loop
dev_update_off ()
count_seconds (Tb)
Tuple3 := []
for i := 0 to 10000 by 1
Tuple3[i] := i * i
endfor
count_seconds (Te)
T1 := Te - Tb
stop ()
*
* use a more efficient version of loop initialization
* which avoids the realocation of the tuple
count_seconds (Tb)
Tuple3 := gen_tuple_const(10001,0)
for i := 0 to 10000 by 1
Tuple3[i] := i * i
endfor
count_seconds (Te)
T2 := Te - Tb
stop ()
*
* even better initialization without a loop by generating
* a tuple sequence
count_seconds (Tb)
Tuple3 := [0:10000]
Tuple3 := Tuple3 * Tuple3
count_seconds (Te)
T3 := Te - Tb
*
* other ways for initializing sequence tuples with different steps
Tuple4 := [3:0.1:5]
Tuple5 := [100:-10:-100]

5.3 元组基本操作
一个基本的元组操作可能是选择一个或多个值、组合元组(串联)或获取元素的数量(有关包含控制数据的元组的操作,请参见表 8.6)。
连接接受一个或多个变量或常量作为输入。 它们都列在括号之间,用逗号分隔。 结果又是一个元组。 请注意以下几点:[[t]] = [t] = t。
|t| 返回元组的元素数。 元素的索引范围从零到元素数减一(即 |t|-1)。 因此,选择索引必须在这个范围内。
Tuple := [V1,V2,V3,V4]
for i := 0 to |Tuple|-1 by 1
fwrite_string (FileHandle,Tuple[i]+'\n')
endfor
在下面的例子中 变量Var contains [2,4,8,16,16,32]:

程序示例
* Simple tuple operations
*
Tuple1 := [1,2,3,4,5]
Number := |Tuple1|
SingleElement := Tuple1[3]
Part := Tuple1[1:3]
Copy := Tuple1[0:|Tuple1| - 1]
Further examples can be found in the program tuple.hdev.
5.4 创建元组
创建元组的最简单方法,如上节所述,是将常量与算子assign 一起使用(或者在图标数据的情况下,使用表 8.7 中所示的等效项之一):
assign ([],empty_tuple)
assign (4711,one_integer)
assign ([4711,0.815],two_numbers)
This code is displayed as
empty_tuple := []
one_integer := 4711
two_numbers := [4711,0.815]
这对于具有固定(小)长度的常量元组很有用。 可以通过连续应用连接或算子assign_at 以及变量、表达式或常量来创建更通用的元组。 如果我们想生成一个长度为 100 的元组,其中每个元素的值都是 4711,可以这样完成:
tuple := []
for i := 1 to 100 by 1
tuple := [tuple,4711]
endfor
因为这不是很方便,所以可以使用调用 gen_tuple_const 的特殊函数来构造给定长度的元组,其中每个元素都具有相同的值。 使用这个函数,上面的程序简化为:
tuple := gen_tuple_const(100,4711)
创建具有连续值序列的一种快速方法是使用 tuple_gen_sequence。 例如,要创建一个包含值 1…1000 的元组,请使用
tuple_gen_sequence(1,1000,1,Sequence)
上面的另一种语法是写:
Sequence := [1:1:1000]
如果增量值为 1(如上例所示),也可以写成:
Sequence := [1:1000]
如果我们想构造一个与给定元组长度相同的元组,有两种方法可以得到。
The first one is based on gen_tuple_const:
tuple_new := gen_tuple_const(|tuple_old|,4711)
The second one is a bit tricky and uses arithmetic functions:
tuple_new := (tuple_old * 0) + 4711
这里我们首先得到一个长度相同的元组,每个元素都设置为零。 然后,我们将常量添加到每个元素。
对于具有不同值的元组,我们必须使用循环版本将值分配给每个位置:
tuple := []
for i := 1 to 100 by 1
tuple := [tuple,i*i]
endfor
5.5 类型操作
类型操作允许测试或查询控制数据的值类型。 请参阅第 275 页的表 8.3 了解对应的类型常量。
也有相应的操作来测试输入元组的每个元素。
5.6 基本算术运算
所有操作都是左关联的,除了右关联的一元减运算符。 计算通常是从左到右进行的。 然而,括号可以改变计算顺序,并且一些运算符的优先级高于其他运算符(参见第 8.5.17 节)。
HDevelop 中的算术运算与通常的定义相匹配。 表达式可以有任意数量的括号。
除法运算符 (a1 / a2) 可以应用于整数和实数。 如果至少一个操作数是实数类型,则结果是实数类型。 如果两个操作数都是整数类型,则除法的结果是整型。其余算术运算符(乘法、加法、减法和求反)可以应用于整数或实数。 如果至少一个操作数是实数类型,则结果也将是实数。

Simple examples can be found in the program arithmetic.hdev.
* Arithmetic operations
R1 := 5 * 5
R2 := 5 * 5
R3 := [1,2,3] * 2
R4 := [1,2,3] * 2.1 + 10
R5 := [1,2,3] * [1,2,3]
R6 := 'Text1' + 'Text2'
R7 := 17.23 + 'Text'
R8 := 'Text1 ' + 99 + ' Text2'
R9 := 'Text ' + 3.1 * 2
R10 := 3.1 + 2 + ' Text'
R11 := 3.1 + (2 + ' Text')
R12 := 'Text ' + 2.1 + 3
ThatIsAnError := 3.1 * (2 + ' Text')
ThatIsAnError := [1,2,3] * [1,2]

5.7 位操作
本节介绍数字的位处理操作。 操作数必须是整数。
lsh(i1, i2)的操作结果是对i1进行左移位操作,移位的数量通过i2指定,如果没有溢出的话等价于做乘法(乘以2倍)操作,同样的,rsh(i1, i2)的操作结果是对i1进行右移位操作,移位的数量通过i2提现出来,如果没有错位的话等价于做除法(除以2倍)操作。对i1的长度,主要取决于计算机的字节长度,lsh和rsh操作的结果是未知的。这主要取决于i2值是否超过32,较多的例子可以到bit.hdev中找到。

程序示例
Number := 128
LShift := lsh(Number,2)
RShift := rsh(Number,2)
Select := Number band 1
Select := 127 band 4
Set := 127 bor lsh(1,7)
5.8 字符串操作
有多种字符串操作可用于修改、选择和组合字符串。 此外,某些操作允许将数字(实数和整数)转换为字符串。

$ 将数字转换为字符串或修改字符串。 该操作有两个操作数:第一个($ 的左侧)是必须转换的数字。 第二个($ 右侧)指定转换的内容。 它相当于 C 编程语言中 printf() 函数的格式字符串。 这个格式字符串由以下四部分组成:
<flags><width>.<precision><conversion>
或者正则表达式
[-+ #]?([0-9]+)?(\.[0-9]*)?[doxXfeEgGsb]?

程序示例
* String types
*
i1 := 123$'#o'
i2 := 123$'#x'
i3 := 123$'#X'
i4 := 123$'#f'
i5 := 123$'#g'
i6 := 123$'#G'
i7 := 123$'#e'
i8 := 123$'#E'
d1 := 123$'6'
d2 := 123$'-6'
d3 := 123$'.6'
d4 := 123$'10.5'
g1 := 123.456$'.1'
g2 := 123.456$'.2'
g3 := 123.456$'.3'
g4 := 123.456$'.4'
g5 := 123.456$'.5'
g6 := 123.456$'.15'
g7 := 123.456$'2.2'
e1 := 123.456$'.1e'
e2 := 123.456$'.2e'
e4 := 123.456$'.10e'
s1 := 'Hugo'$'10'
s2 := 'Hugo'$'-10'
5.9 集合操作

5.10 比较操作
在 HDevelop 中,比较操作不仅定义在原子值上,还定义在具有任意数量元素的元组上。 它们总是返回布尔类型的值。 表 8.15 显示了所有比较操作。


t1 == t2 和 t1 != t2 定义在所有类型上。 两个元组相等(真),如果它们具有相同的长度并且每个索引位置上的所有数据项都相等。 如果操作数具有不同的类型(整数和实数),则首先将整数值转换为实数。 字符串类型的值不能与数字混合,即字符串值被认为不等于其他类型的值。
四个比较操作计算元组的字典顺序。 在相等的索引位置上,类型必须相同,但是,integer、real 和 boolean 类型的值会自动调整。 字典顺序适用于字符串,布尔值 false 被认为小于布尔值 true (false < true)。 在程序 compare.hdev 中,您可以找到比较操作的示例。
程序示例
R1 := 1 == 1.0
R2 := [] == []
R3 := '' == []
R4 := [1,'2'] == [1,2]
R5 := [1,2,3] == [1,2]
R6 := [4711,'Hugo'] == [4711,'Hugo']
R7 := 'Hugo' == 'hugo'
R8 := 2 > 1
R9 := 2 > 1.0
R10 := [5,4,1] > [5,4]
R11 := [2,1] > [2,0]
R12 := 'Hugo' < 'hugo'
R13 := true > false
5.11 元素比较操作
这些比较操作按元素比较输入元组 t1 和 t2。 如果两个元组的长度相同,则比较两个元组的对应元素。 否则,t1 或 t2 的长度必须为 1。在这种情况下,将针对较长元组的每个元素与另一个元组的单个元素进行比较。 作为按元素比较元组的先决条件,两个相应的元素必须都是(整数或浮点数)数字或都是字符串。


5.12 布尔操作
布尔运算 and、xor、or 和 not 仅针对长度为 1 的元组定义。如果两个操作数都为真 (1),则 l1 和 l2 设置为真 (1)。如果两个操作数之一为真,则 l1 xor l2 返回真 (1) 。 如果至少有一个操作数为真 (1),则 l1 或 l2 返回真 (1)。 如果输入为假 (0),则 not l 返回真 (1);如果输入为真 (1),则返回假 (0)。

5.13 三角函数操作
所有这些函数都将数字元组作为参数工作。 输入可以是整数类型或实数类型。但是,结果类型将是实数类型。 这些函数应用于所有元组值,生成的元组与输入元组的长度相同。 对于 atan2,两个输入元组的长度必须相等或其中之一的长度为 1。表 8.20 显示了提供的三角函数。 对于三角函数,角度以弧度指定。

5.14 指数函数操作
所有这些函数都将数字元组作为参数工作。 输入可以是整数或实数类型。 但是,结果类型将是实数类型。 这些函数应用于所有元组值,生成的元组与输入元组的长度相同。 对于 pow 和 ldexp,两个输入元组的长度必须相等,或者其中之一的长度为 1。有关提供的指数函数,请参见表 8.21。

5.15 数学运算操作
表 8.22 中显示的数值函数适用于不同的数据类型。
函数 min 和 max 选择元组值的最小值和最大值。 所有这些值要么必须是字符串类型,要么是整数/实数。 不允许将字符串与数值混合。 如果至少一个元素是实数类型,则结果值将是实数类型。 如果所有元素都是整数类型,则结果值也将是整数类型。 这同样适用于确定所有值总和的函数 sum。 如果输入参数是字符串,则将使用字符串连接而不是加法。

函数 mean, deviation, sqrt, deg, rad, fabs, ceil, floor和 fmod 处理整数和实数; 结果总是real类型。 函数 mean 计算平均值和偏差数字的标准偏差。 sqrt 计算一个数的平方根。
cumul 返回输入元组对应元素的不同累积和,median 计算一个元组的中位数。 对于这两个函数,如果至少一个元素是实数类型,则结果值将是实数类型。 如果所有元素都是整数类型,则结果值也将是整数类型。select_rank 返回排名 i 的元素并适用于包含 int 或 real 值的元组。 索引 i 是 int 类型。
deg 和 rad 分别将数字从弧度转换为度数,将度数转换为弧度。real 将整数转换为实数。 对于 real 作为输入,它返回输入。 int 将实数转换为整数并截断它。 round 将实数转换为整数并舍入该值。 对于整数,它返回输入。
函数 abs 始终返回与输入值类型相同的绝对值。
程序示例
* The following example (file name: euclid_distance.hdev) shows the use of some numerical functions:
V1 := [18.8,132.4,33,19.3]
V2 := [233.23,32.786,234.4224,63.33]
Diff := V1 - V2
Distance := sqrt(sum(Diff * Diff))
Scalarproduct := sum(V1 * V2)
首先,使用以下公式计算两个向量 V1 和 V2 的欧几里德距离:

差值和乘法(平方)依次应用于两个向量的每个元素。 然后sum 计算平方和。 然后计算总和的平方根。 之后 V1 和 V2 的点积由以下公式确定:

5.16 杂项函数
sort 按升序对元组值进行排序,这意味着结果元组的第一个值是最小的。但同样:字符串不能与数字混淆。
sort_index 按升序对元组值进行排序,但与 sort 不同的是,它返回已排序值的索引位置 (0…)。
函数 inverse 反转元组值的顺序。如果输入为空,如果元组的长度为 1,或者如果元组在所有位置只包含一个值,例如 [1,1,…,1],排序和逆是相同的。
对于整数或实数类型的变量以及表示数字的字符串类型的变量,is_number 返回 true。
函数 number 根据数字的类型将表示数字的字符串转换为整数或实数。请注意,以 0x 开头的字符串被解释为十六进制数,以 0(零)开头的字符串被解释为八进制数;例如,字符串 ‘20’ 被转换为整数 20,‘020’ 为 16,‘0x20’ 为 32。如果使用不代表数字的字符串或整数或实数类型的变量调用, number 返回输入的副本。
environment 返回环境变量的值。 输入是字符串形式的环境变量的名称。
ord 将长度为 1 的字符串元组转换为整数元组(Unicode 字符代码或 ANSI 代码)。
chr 将 Unicode 字符代码或 ANSI 代码转换为字符串元组,每个字符串的长度为 1。
ords 将字符串元组转换为整数元组(Unicode 字符代码或 ANSI 代码)。
chrt 将 Unicode 字符代码或 ANSI 代码转换为由数字 0 分隔的字符串组成的元组。
5.17 操作优先级
控制数据操作的优先级见表8.24。 一些操作(如函数、| |、t[]、等)没有描述出来,因为他们清楚地标记了他们的参数。

06. 附录
6.1 机器视觉博客汇总
网址:https://dengjin.blog.csdn.net/article/details/116837497
更多推荐



所有评论(0)