C#/Halcon联合编程——C#中使用Halcon引擎

环境配置

从Halcon安装目录中引入下面的两个Dll文件。
image

Halcon文件主要分为以下几种:

  1. hdev:本地函数文件,函数定义在hdev文件内部,只能当前hdev内部使用,其他hdev程序无法使用
  2. hdvp:外部函数文件,函数定义在hdvp中可以传输给任何hdev使用,即可以发给别人使用。同时允许对hdvp进行加密
  3. hdpl:库函数,即内部可以定义多个hdvp函数的文件

使用Halcon引擎好处很多,其中最重要的一点就是避免了我们调用Halcon算子时程序可能存在的内存溢出问题。


通常情况下,我们选择使用Halcon引擎在C#中调用封装好的.hdvp文件。在这种方式下,我们可以自由的获取或设置对应函数的返回值。第一种方式我们只能获取主函数的参数。


image
上面的两种文件,整体的调用流程是一致的。本文介绍最常用的方法——调用Halcon函数(.hdvp文件)。
项目基于 .NetFrameWork4.8 开发,运行环境为64位。

使用方法

  1. 加载Halcon引擎,(并设置Halcon文件路径)。
  2. 获取调用程序文件。
  3. 初始化调用函数实例。
  4. 设置参数并执行。
  5. 获取结果。

函数文件创建

此处要注意,定义的函数文件输入变量名称和输出变量名称,我们在C#就是通过这里的名称实现传参和取值的。
image

使用函数文件

整体调用流程
private void Button_Click(object sender, RoutedEventArgs e)
{
    HDevEngine hDevEngine = new HDevEngine();
    var path = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "method");
    //设置方法路径
    hDevEngine.SetProcedurePath(@"C:\Users\Yty\Desktop\method");
    //直接传入方法名(此处会对函数文件进行一次编译,如果有错误会直接报异常)
    //如果没设置引擎路径,就要传全路径
    HDevProcedure hDevProcedure = new HDevProcedure("MyProcedure");
    //生成调用实例 用来传参 取值  执行
    HDevProcedureCall hDevProgramCall = new HDevProcedureCall(hDevProcedure);
    hDevProgramCall.SetInputCtrlParamTuple("MyImageFile", new HTuple(@"C:\Users\Yty\Desktop\通讯服务.png"));
    HOperatorSet.ReadImage(out var image, @"C:\Users\Yty\Desktop\通讯服务.png");
    //设置图形参数
    hDevProgramCall.SetInputIconicParamObject("MyImage",image);
    //执行方法后才能取值
    hDevProgramCall.Execute();
    //获取控制参数
    HTuple num= hDevProgramCall.GetOutputCtrlParamTuple("MyOutputNum");
    //获取图形参数
    HObject region= hDevProgramCall.GetOutputIconicParamObject("MyRegion");
    this.window.HalconWindow.DispObj(region);
    HOperatorSet.DispText(this.window.HalconWindow, num,"window",20,20,"red",null,null);
}

由于 HDevProcedure hDevProcedure = new HDevProcedure("MyProcedure") 会直接编译获取到的文件,因此我们也可以通过这种方式去检验文件语法,从而实现一个Halcon文件的编译功能。但是,对于代码中的逻辑错误(如0/N这种),就需要等到对应函数文件执行时才能获取到异常信息。

使用Hdev文件

这种方法只能获取到在main方法中调用的算子返回的参数。不能去设置参数。

流程
HDevEngine hDevEngine = new HDevEngine();
var path = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "method");
//设置默认路径
hDevEngine.SetProcedurePath(@"C:\Users\Yty\Desktop\method");
//直接传入方法名(此处会对函数文件进行一次编译,如果有错误会直接报异常)
//如果没设置引擎路径,就要传全路径
var Program = new HDevProgram(ProgramPathString);
var ProgramCall = new HDevProgramCall(Program);
//这个类是没有设置参数的方法的
ProgramCall.Execute();//执行程序
double area;
area = ProgramCall.GetCtrlVarTuple("Area");//获取程序中的某个参数

混合使用方法

上面的代码中,我只介绍了单一的使用方法要么向hdev文件,要么向hdvp文件。如果,我们想运行某一个hdev文件下的某个封装函数,并获取其结果。我们可以使用如下的方法。这是重点内容,最灵活的使用方法。

点击查看代码
//新建一个Program读取hdev文件
HDevProgram program = new HDevProgram(tempName);
// 方法:将HDevProgram作为参数创建HDevProcedure,并根据函数名获取函数。
HDevProcedure procedure = new HDevProcedure(program, RunProcedureMethodName);
// 核心:生成执行实例,后续操作与上面相同
HDevProcedureCall hDevProgramCall = new HDevProcedureCall(procedure );

结合Halcon引擎开发相关功能

Compile

上面已经讲过这个了,就是在创建HDevProcedure或者HDevProgram,这个类就会自动编译一下传入的文件检查对应是否存在语法错误。这就实现了编译功能。

Debug

此处的Debug方案依旧是借助Halcon引擎实现的。在Halcon中,提供了以下方式帮助我们实现C#和Halcon软件的联合调试。
image

  1. C#中启动Halcon引擎的调试服务
  2. 在Halcon中对需要调试的部分添加断点。
  3. 在Halcon软件中进行附加到C#进程即可实现联调。
    image

因为,我们此处希望实现的是打上断点后一步步进行调试,因此在开启调试之前,我们关闭了JIT(防止因为JIT生成机器码影响调试过程)。当我们结束调试时,在重新打开JIT。

调试逻辑代码
private void StartDebug_Click(object sender, RoutedEventArgs e)
{
    try
    {
        //一步一步向下调试
        // 关闭即时编译(JIT, Just-In-Time)调试需求:解释执行模式下,代码逐行执行,更便于调试(如设置断点、单步执行)。
        SysProcessSln.s_HDevEngine.SetEngineAttribute("execute_procedures_jit_compiled", "false");
        //先停止调试 再打开调试
        SysProcessSln.s_HDevEngine.StopDebugServer();
        SysProcessSln.s_HDevEngine.StartDebugServer();
        this.bd_DebugStatus.Background = new SolidColorBrush(Colors.Green);
        frm_ModuleObj.isDebug = true;
    }
    catch (Exception ex)
    {
        Log.Error(ex.ToString());
        System.Windows.Forms.MessageBox.Show(ex.ToString());
        this.bd_DebugStatus.Background = new SolidColorBrush(Colors.Red);
        frm_ModuleObj.isDebug = false;
    }
}

private void StopDebug_Click(object sender, RoutedEventArgs e)
{
    try
    {
        //重新打开JIT
        SysProcessSln.s_HDevEngine.SetEngineAttribute("execute_procedures_jit_compiled", "true");
        SysProcessSln.s_HDevEngine.StopDebugServer();
        this.bd_DebugStatus.Background = new SolidColorBrush(Colors.Red);
        frm_ModuleObj.isDebug = false;
    }
    catch (Exception ex)
    {
        Log.Error(ex.ToString());
        System.Windows.Forms.MessageBox.Show(ex.ToString());
        this.bd_DebugStatus.Background = new SolidColorBrush(Colors.Red);
        frm_ModuleObj.isDebug = false;
    }
}

补充内容

Halcon的变量分类

上面代码展示了如何向方法传参和取结果,在Halcon中,变量实际上就分为两类一种是图形变量(Image、Region等),还有一种是控制变量(字符串、数值等)。对于取值没必要具体到具体的变量类型,使用泛用方法即可(GetOutputIconicParamObject、GetOutputCtrlParamTuple等)。

Halcon引擎的路径修改问题(8.27)

这个问题会出现在我们使用HDevProgram时,路径被自动修改为了传入的.hdev文件的路径。使用HDevProcedure就不会出现这种问题,需要特别注意。

当我们使用Halcon引擎去编译文件后,会修改项目中Environment.CurrentDirectory的值。
image

从图上可以看到,当我们使用HDevProgram编译代码后,我们的工作路径被自动修改了。所以,如果涉及到一些相对路径的DLL加载问题。最好避免使用Environment.CurrentDirectory属性,使用AppDomain.CurrentDomain.BaseDirectory会更加安全稳定。

2025.9.24补充:通过XML文件解析Halcon的hdev函数文件

实际上,Halcon文件实际上就是使用XML结构来定义的。因此通过XML的解析方法,我们就能够从Halcon文件中解析出每个函数的函数名以及输出参数和函数体等内容。
先给出下面这个简单的Halcon文件(.hdev),其中有我使用的Halcon自带的算子还有一个自定义函数。这个函数里我只是去读一张图片,然后不进行任何操作。
image

该函数文件对应的Txt文本内容如下(内容太长,分两张截图)。

image

image

对应的XML文件中,首先是一个hdevelop的Element,其中给出了当前的Halcon版本以及文件版本。
其次,在一个hdevelop的Element中,每一个函数都对应着一个procedure的Element,并且函数名字由name给出。。我们重点挑一个procedure进行分析,剩下的处理逻辑都是相同的。
image

  • io:对应InputIconicParam,输入图形参数
  • ic:对应InputCtrlParam,输入控制参数
  • oo:对应OutputIconicParam,输出图形参数
  • oc:对应OutputCtrlParam,输出控制参数

对于最下面的docu标签中的内容,其类似于一种注释或说明的作用,对于Halcon文件的解析是可有可无的,即便你把这块内容删去,Halcon文件依然能正常打开并执行。


通过解析不同的标签即可获取到对应Halcon文件中,对应方法的输入输出参数信息和方法名称等消息,与上面的引擎使用方法结合,即可实现使用C#去操作Halcon文件。

最后,再来看一下main方法的内容
image

综上,结合C#解析XML的方法,我们就能顺利解析Halcon文件了。

posted @ 2025-08-19 22:27  Ytytyty  阅读(823)  评论(0)    收藏  举报