机器视觉_HALCON_HDevelop用户指南_6.代码导出


前言

按照HDevelop用户指南/手册的章节顺序,代码导出是第十章的内容。
但6789这四章,是偏查阅的内容,不是那种流程介绍的。拿第六章GUI来说,里面子章节对HDevelop中的各种窗口做详细说明,显然前期学习阶段没有必要把里面的内容全看一遍,在用到时查阅更好,剩下的789章同理。
在这里插入图片描述
从个人使用的角度来讲,在学完1-5章后,已经对HALCON基本概念和HDevelop基本使用有了一定了解,之后就想把HDevelop的功能导出来,试着集成到自己的程序中。所以接下来,我决定直接跳到第十章——代码导出,进行学习。

十、代码导出

代码导出(或者说代码生成)的思想如下:根据需求开发出HDevelop程序后,要将其转换到最终的环境中(毕竟HDevelop不是专门做应用程序开发的)。为此,需要将HDevelop程序转换为另一种编程语言。

HDevelop允许将开发好的HDevelop程序导出成C++、VB .NET、C#和C,然后你只需要将导出的相应代码添加到文件即可。接下来的几节将介绍C#语言中,使用该特性进行程序开发的一般步骤(因为我的目标程序是C#的,所以只介绍C#部分)。

除了一般步骤,还会介绍代码生成和优化方面的内容。

因为HDevelop不仅执行HALCON程序,所以导出程序的行为在某些方面与相应的HDevelop程序有所不同。一个明显的点是,在HDevelop中所有结果都自动显示,而在导出的程序中,必须显式地插入算子来呈现结果

对C++和C#开发人员来说,还有一种方法可以将HDevelop代码集成到自己的项目中。你可以导出一个库项目/工程(library project),而不必将整个HDevelop程序导出到C++或C#,该库项目可以直接集成到你自己的项目中。

接下来做介绍。

10.1. C#代码生成(HALCON/.NET)

本节描述如何从HDevelop开发的程序开始,在C#中用HALCON应用程序。HALCON能与基于.NET接口(HALCON)一起使用。(直译很拗口,反正就是如何在C#中用HALCON)

10.1.1. 基本步骤

10.1.1.1. 导出

第一步是使用菜单中的 文件>导出… 来导出程序,导出格式选中 C#-HALCON/.NET ,导出的结果是一个具有给定名称(默认名称是打开的.hdev名)且扩展名为".cs"的新文件。(导出范围选 程序 , 否则会把过程全导出,动辄几万行代码)
在这里插入图片描述

10.1.1.2. C#模板导出

如果用 使用导出模板(Export Template) 选项来导出文件,则它将与以下目录中预定义的C#项目一起使用(有WPF的和WinForm的)

%HALCONEXAMPLES%\c#\HDevelopTemplate

在这里插入图片描述

该项目包含一个窗体,窗体带有一个显示窗口(HWindowControl)和一个运行按钮。将HDevelop生成的文件添加到 解决方案资源管理器(Solution Explorer) 中(使用 添加现有项(Add Existing Item))。接着,运行项目,然后按下窗体上的运行按钮以调用导出的代码。

几点注意项:

  1. 默认安装在 C:\Users\Public\Documents\MVTec\HALCON-18.11-Progress\examples
  2. .NET框架的版本最好与模板项目匹配,相差太大可能用不了,我一开始框架是.NET6.0的,退回到.NET Framework4.7才行。
  3. 使用导出模板 的方式导出的代码,想集成到最终的程序,你可以参考HALCON的模板工程去写。

10.1.2. 程序结构

如果已经使用 使用导出模板 选项导出了程序,则HDevelop创建的文件包含一个子例程(subroutine,这里你可以看做一个函数),该子例程带有与每个HDevelop过程名对应的名称(除了主过程),主过程包含在子例程 action() 中。此外, 文件要导出为独立应用程序 。过程的图像输入输出参数分别以 HObjectout HObject 传递,控制输入输出参数分别以 HTupleout HTuple 传递。子例程 RunHalcon() 包含对子例程 action() 的调用,并且有一个参数 Window ,该参数属于 HTuple 类型。这是窗体上所有输出操作传递到的窗口的链接。此外,还创建了另一个名为 InitHalcon() 的子例程。这个子例程应用HDevelop执行的相同初始化。

大多数变量(图像和控制)都局部声明在相应的子例程中。图像变量属于类 HObject ,控制变量属于 HTuple

根据程序的不同,还会声明额外的子例程和变量。
下面看一下导出程序的大致结构,
在这里插入图片描述

10.1.2.1. 停止

HDevelop的 stop 算子在C#中被转换为创建消息框的子例程。该消息框会导致程序暂停直到按下按钮。

10.1.2.2. 用到的类

仅有四个类/类型被用到:HTuple 用于控制参数,HObject 用于图像数据。此外,还有类 HWindowControl 。它在项目中用于输出窗口,且一个类型为 HTuple 的变量将输出指向该窗口。最后,类 HOperatorSet 用作所有HALCON算子的容器。只要程序有与HDevelop中相同的功能,就不需要其他类了。当编辑生成的程序时,你可以自由使用任何HALCON/.NET类来扩展功能。

10.1.3. 限制与故障排除

除了本节和 “代码生成的一般方面” 小节中提到的限制外,也请查看 开发 章节中对HDevelop算子的描述。

10.1.3.1. 变量名

导出为所有本地图像变量增加了前缀 ho_ ,为控制变量增加了前缀 hv_ ,以避免与保留词发生冲突。

10.1.3.2. 异常处理

在HDevelop中,每个异常通常都会导致程序停止,并在对话框窗口中报告错误消息。这点在C#中可能没啥用。在C#中处理这个问题的标准方法是使用 try/catch 机制。这允许访问异常的原因并继续相应的操作。因此,对于包含错误处理 ((dev_)set_check(“~give_error”)) 的HDevelop程序,相应的代码会自动被包含。假设HALCON错误机制被关闭,则每个算子调用都包含在 try块(后面接着catch块)中。后者(指catch块)会处理异常,并将相应的HALCON错误号分配给由 dev_error_var 激活的错误变量 或 本地错误变量。

请注意,(dev_)set_check(“~give_error”) 的调用对算子调用没有影响。异常始终会被触发。对于像 H_MSG_FAIL 这样的消息也是如此,在C++中不会被当作异常处理。

10.1.3.3. 内存管理

.NET Framework运行时环境CLR(Common Language Runtime,公共语言运行时)有一种称为垃圾收集器(简称GC)的机制,CLR用它来移除内存中不再需要的.NET对象。正如前面所提,在导出的C#代码中,每个图像对象都由.NET HObject 对象表示。从GC的角度来看,.NET HObject 对象相当小。因此,它可能不会从内存中被回收,尽管底层的图像对象(例如,image)实际可能占有很大的内存。为了避免这种情况引起的内存泄漏(memory leak),在导出的代码中,每个图像对象在被分配一个新值之前都被显式删除

10.2. 代码生成的一般性质⭐

接下来,将介绍HDevelop程序和它导出程序之间行为的一般差异。

10.2.1. 任意程序代码

可以将任意代码嵌入到HDevelop程序中。该代码在HDevelop中会被忽略。当你将程序导出为编程语言时,嵌入的代码将会一字不差的导出。

# 为首字符的程序行会标记任意代码行。在导出程序时,标记及其后面的第一个空格字符会被丢弃。例如,下面代码行

# Call MsgBox("Press button to continue",vbYes,"Program stop","",1000)

在HDevelop中会导出以下VB代码行:

Call MsgBox("Press button to continue",vbYes,"Program stop","",1000)

#后面可以跟其他特殊字符,以进一步制定导出时放置代码块的位置。例如,下面代码:

#^^ #define NO_EXPORT_APP_MAIN

它会在导出程序最开始位置生成下面代码:

#define NO_EXPORT_APP_MAIN

这种格式的代码行首先会从主过程中收集,然后从其它过程中的 #^^ 中收集。

这种宏定义一般都是放程序开头处的,所以不难理解会先从主过程中寻找,然后再从其他过程中找并放在程序开头处。

公用的特殊标记在下表中展示。

若你使用算子窗口来输入任意代码行,则必须选用特殊算子 export_def 。它的第一个参数指定导出代码行的目标位置(见下表最后一列);第二个参数是代码行本身;
在这里插入图片描述
当你将算子提交到程序窗口时,算子调用将被转换为特殊的前缀字符以增强可读性。

前缀目标位置export_def中的表示
#IC的位置‘in_place’
#^^程序的最开始处‘at_file_begin’
#$$程序的结尾处‘at_file_end’
#^当前过程前‘before_procedure’
#$当前过程后‘after_procedure’

10.2.2. 分配

在HDevelop中,每当一个新值被赋给一个变量时,它的旧内容就会自动删除,无论变量是什么类型。在导出的代码中,图像变量也是如此(HALCON/.NET中的 HObject)。关于HALCON/.NET中的图像对象的内存问题,后面内存管理(C#)章节还会详细介绍。

10.2.3. 变量名

HDevelop中的变量名是区分大小写的,即 xX 在HDevelop程序中是不同的变量名。如果你将这样的程序导出到不区分大小写的目标语言(如VB .NET),开发环境会抱怨有个多个声明。要么一开始就计划好不使用这些变量名,要么在导出程序之前替换掉冲突的变量名。

10.2.4. for循环

与其他编程语言相比,旧的HDevelop程序(HALCON11之前)具有不同的for循环语义。如果打开了旧的HDevelop程序,for循环会被标记为以兼容模式运行,以模拟旧的行为 (windows系统上也有兼容模式运行,或许是类似原理?)。为了避免导出代码的混淆,建议通过删除特殊的 use_internal_index 标记来禁用兼容模式。

下面是一些兼容模式的细节问题,我这边略过了,因为比较罕见,而且现在也不会去用老版本的HALCON。

总之,建议按照以下规则来编程:

  1. 不要修改循环变量或循环中的步长值。若你确实需要该行为,使用 while 循环。
  2. 不要在循环后使用循环变量。

10.2.5. 受保护的过程🔺

正如对不同编程语言所描述的,HDevelop过程会自动导出到所选编程语言的过程或子例程/程序(subroutine)。但这并不适用于受保护的过程。由于这些过程由密码保护,因此未经授权的用户无法查看和修改它们。也因此,只要它们被密码锁定,就不能导出到任何编程语言。

10.2.6. 系统参数🔺

你应该知道HDevelop通过调用 set_system 算子来执行HALCON的一些系统参数的更改(详情见参考手册)。这可能导致导出的程序不能产生相同的输出。如果出现了这样的问题,你可以在运行程序的原始HDevelop版本后或运行时,通过HDevelop中的 get_system 方法查询系统参数。根据问题不同,可以通过显式调用导出程序中的 set_system 算子来修改相关参数。

10.2.7. 图形窗口⭐

HALCON为HALCON窗口(这个窗口可能是抽象的)提供了一种功能——模拟HDevelop图形窗口行为。
这个HALCON窗口栈可以通过HALCON接口中的类方法和函数访问,并且从HDevelop导出的代码在打开、关闭、设置或访问活动窗口时会使用该功能(有点操作窗口对象的感觉)。HALCON窗口栈机制是线程安全的,且能在线程之间共享。不过,在多线程应用程序中,用户在不同线程中切换活动窗口时必须小心,因为在一个线程中做的设置在其他线程也会生效。(就是操作时是安全的,但是线程间共用的话,可能会产生你意想不到的效果(不好的方面))

对于.NET代码导出,是使用HDevelop导出示例模板来将程序导出为代码,还是在图形窗口输出时使用前面提到的HALCON窗口栈将程序导出为代码,这是可选的。在后一种情况下,导出的代码包含一个主函数,因此可以作为一个独立的应用程序使用。HDevelop 导出 对话框允许选择相应的选项。

HDevelop的图形窗口和HALCON库的基本窗口

  • HALCON/C++:class HWindow
  • HALCON/.NET:class HWindowControl
  • HALCON/C:addressed via handles

有着不同的功能。

多窗口(Multiple windows)
如果在HDevelop中使用 dev_open_window 算子打开多个图形窗口,只有选项 使用HALCON窗口 被选中时,这些打开窗口的调用才会转换为对应的 open_window 调用。在导出 VB .NET 和 使用选项 使用导出模板 的C#程序时,所有窗口操作都将被抑制,因为导出的代码将会和模板一起工作。如果要在此模式导出的程序中使用多个窗口,则必须手动修改代码和项目。注意,如果在程序执行期间用鼠标更改了活动的图形窗口,则使用 使用HALCON窗口 选项导出的包含多窗口的程序可能是不正确的。建议显式地使用 dev_set_window 算子来实现相同的功能。
窗口尺寸
在导出的VB .NET 和 C# 程序中,窗体中的窗口尺寸预定义为512×512;因此,通常并不适配你的图片大小。也因此,必须以交互方式或通过窗口的属性来调整大小。
显示结果
通常,每个算子的结果都会显示在HDevelop的图形窗口中。但在导出程序中,情况并非如此。导出程序的行为类似于HDevelop程序运行时的选项:“update window = off” 。建议在HDevelop程序中的每个想要显示数据的点插入算子 dev_display 。这不会改变HDevelop程序的行为,但会使导出代码中能显示图像。

当使用 使用HALCON窗口 选项生成代码时,在第一次调用 dev_display 之前关闭默认的图形窗口(使用 dev_close_window) 并 打开一个新窗口(使用 dev_open_window),以确保正确的导出。
显示图像
在HDevelop中,图像会自动缩放以适应当前窗口的大小,但在导出程序中不是这样的。例如,如果你加载并显示两个不同大小的图像,若第二个图像比第一个图像大,则第二个图像将会被剪辑;如果第二个图像较小,则会被黑色区域填充。为了正确显示,在使用 dev_display 显示图像前,必须使用 dev_set_part 算子,如下:
dev_set_part(0, 0, ImageHeight - 1, ImageWidth - 1)
dev_display(Image)
本例中, Image 是图像变量, ImageHeightImageWidth 表示它的大小。你可以使用 get_image_size 算子查询图像的大小。

注意, dev_set_part 算子(HALCON库中与之等效的 set_part )更常用于显示(从而缩放)图像的部分。通过使用上面所示的图像的完整大小来调用它,可以确保图像完全适配窗口。
更改显示参数
如果你在HDevelop中通过菜单项 可视化 来交互式地更改了结果的显示方式(颜色、线宽等),这些更改不会被合并到导出程序中。建议显式地在HDevelop程序中插入对应的 开发 算子(如 dev_set_colordev_set_line_width ),这会使导出的代码中进行( set_colorset_line_width 等)适当调用。

10.2.8. 过程中未设的输出参数

如果在过程中没有为输出参数分配值,HDevelop和导出的代码对于过程的输出参数的值可能表现不同。如果过程在设置输出参数前返回异常,或者故意不设置参数,这种情况下,输出参数的值未定义,因此HDevelop和不同的代码导出可能表现会不同。代码导出不适配HDevelop行为的主要原因是,这会导致导出代码的运行时效率下降。

10.2.9. 访问未初始化的元组或向量元素

当用HDevelop访问未初始化的元组或向量元素时,会引发异常。由于性能原因,HALCON语言接口中没有相应的检查。代替引发异常的是,返回一个默认类型的元素。

a := [0, 1]
b:= a[2]

例如,这段代码在HDevelop中引发异常,但可能在某种语言中,b返回任意值。因此,必须避免这种编码风格。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值