欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

C#和Halcon混编的多种方法

最编程 2024-02-01 13:29:39
...

Halcon的学习过程中,关于Halcon的混合编程是无法避免的,Halcon可以和很多种语言进行混编,这里仅赘述与C#语言进行混编的一些简单方式。

C#与Halcon进行混编的方式大体可以分为:使用Halcon导出功能、面向对象的方式、Halcon引擎。

当然,除了以上方法,还有导出库工程这样的方式可以选择,这里就不再阐述。


一、Halcon导出功能

下图为使用Halcon编写的简单程序

然后点击文件-》导出,或者直接点击如下按钮

点击导出之后,选择语言为C#-Halcon/.NET,选择好导出文件的路径,则会得到一个和Halcon程序同名的.cs文件

 打开.cs文件,我们看到导出的代码中有一个action的方法,这个方法是即是我们需要重点关注的部分,我们在Halcon中写的代码都在这个方法中有所实现。以下为action的代码:

 1  private void action()
 2   {
 3 
 4 
 5     // Local iconic variables 
 6 
 7     HObject ho_Image, ho_Region, ho_ConnectedRegions;
 8     HObject ho_RegionDilation;
 9 
10     // Local control variables 
11 
12     HTuple hv_Width = null, hv_Height = null;
13     // Initialize local and output iconic variables 
14     HOperatorSet.GenEmptyObj(out ho_Image);
15     HOperatorSet.GenEmptyObj(out ho_Region);
16     HOperatorSet.GenEmptyObj(out ho_ConnectedRegions);
17     HOperatorSet.GenEmptyObj(out ho_RegionDilation);
18     ho_Image.Dispose();
19     HOperatorSet.ReadImage(out ho_Image, "printer_chip/printer_chip_01");
20     HOperatorSet.GetImageSize(ho_Image, out hv_Width, out hv_Height);
21     if (HDevWindowStack.IsOpen())
22     {
23       HOperatorSet.SetPart(HDevWindowStack.GetActive(), 0, 0, hv_Height, hv_Width);
24     }
25     ho_Region.Dispose();
26     HOperatorSet.Threshold(ho_Image, out ho_Region, 128, 255);
27     ho_ConnectedRegions.Dispose();
28     HOperatorSet.Connection(ho_Region, out ho_ConnectedRegions);
29     ho_RegionDilation.Dispose();
30     HOperatorSet.DilationCircle(ho_ConnectedRegions, out ho_RegionDilation, 3.5);
31     if (HDevWindowStack.IsOpen())
32     {
33       HOperatorSet.DispObj(ho_Image, HDevWindowStack.GetActive());
34     }
35     if (HDevWindowStack.IsOpen())
36     {
37       HOperatorSet.DispObj(ho_ConnectedRegions, HDevWindowStack.GetActive());
38     }
39 
40 
41     ho_Image.Dispose();
42     ho_Region.Dispose();
43     ho_ConnectedRegions.Dispose();
44     ho_RegionDilation.Dispose();
45 
46   }
action

现在,需要我们对这个代码进行简单的更改,就可以在C#的Halcon窗体控件中将其显示出来,首先,新建一个C#窗体程序,添加halcondotnet的引用,并且添加其命名空间,在主窗体上添加一个Halcon的窗体控件和一个Button控件,并在Button的Click事件对应的方法中添加如下代码即可:

 代码如下:

 1 namespace Halconprogram
 2 {
 3     public partial class Form1 : Form
 4     {
 5         public Form1()
 6         {
 7             InitializeComponent();
 8         }
 9 
10         private void button1_Click(object sender, EventArgs e)
11         {
12 
13             // Local iconic variables 
14 
15             HObject ho_Image, ho_Region, ho_ConnectedRegions;
16             HObject ho_RegionDilation;
17 
18             // Local control variables 
19 
20             HTuple hv_Width = null, hv_Height = null;
21             // Initialize local and output iconic variables 
22             HOperatorSet.GenEmptyObj(out ho_Image);
23             HOperatorSet.GenEmptyObj(out ho_Region);
24             HOperatorSet.GenEmptyObj(out ho_ConnectedRegions);
25             HOperatorSet.GenEmptyObj(out ho_RegionDilation);
26             ho_Image.Dispose();
27             HOperatorSet.ReadImage(out ho_Image, "printer_chip/printer_chip_01");
28             HOperatorSet.GetImageSize(ho_Image, out hv_Width, out hv_Height);
29 
30             HOperatorSet.SetPart(hWindowControl1.HalconID, 0, 0, hv_Height, hv_Width);
31 
32             ho_Region.Dispose();
33             HOperatorSet.Threshold(ho_Image, out ho_Region, 128, 255);
34             ho_ConnectedRegions.Dispose();
35             HOperatorSet.Connection(ho_Region, out ho_ConnectedRegions);
36             ho_RegionDilation.Dispose();
37             HOperatorSet.DilationCircle(ho_ConnectedRegions, out ho_RegionDilation, 3.5);
38 
39             HOperatorSet.DispObj(ho_Image, hWindowControl1.HalconID);
40 
41 
42             HOperatorSet.DispObj(ho_ConnectedRegions, hWindowControl1.HalconID);
43 
44 
45             ho_Image.Dispose();
46             ho_Region.Dispose();
47             ho_ConnectedRegions.Dispose();
48             ho_RegionDilation.Dispose();
49         }
50     }
51 }
HalconProgram

比较前两段代码可以发现,只是对Halcon中导出的.cs文件进行了简单的窗体句柄的更改,其他地方没有任何改变,即可实现想要的效果。

需要注意的是,如果Halcon代码中存在外部函数的话,在导出时,除了action方法外,还需要将导出的cs文件中对应外部函数的整个方法实现也要拷贝到自己的工程中。


二、面向对象的方式

使用Halcon程序导出为C#程序之后,可以发现它的可读性并不太好,而且更改起来不太方便,所有的对象都是HObject类型。

以下程序不使用Halcon的IDE,直接添加引用和命名空间后,在C#环境下编写:

 1 namespace Halconprogram
 2 {
 3     public partial class Form1 : Form
 4     {
 5         public Form1()
 6         {
 7             InitializeComponent();
 8         }
 9 
10         private void button1_Click(object sender, EventArgs e)
11         {
12             HImage img = new HImage("printer_chip/printer_chip_01");
13             img.GetImageSize(out HTuple width, out HTuple height);
14             hWindowControl1.HalconWindow.SetPart(0D, 0, height, width);
15             HRegion region = img.Threshold(128D, 255);
16             HRegion conRegion = region.Connection();
17             HRegion dilationRegion = conRegion.DilationCircle(3.5);
18             hWindowControl1.HalconWindow.SetColored(12);
19             hWindowControl1.HalconWindow.DispObj(img);
20             hWindowControl1.HalconWindow.DispObj(dilationRegion);
21 
22             img.Dispose();
23             region.Dispose();
24             conRegion.Dispose();
25             dilationRegion.Dispose();
26         }
27     }
28 }
面向对象方式的代码

可以看出,使用这种方式同样能达到相同的效果,并且对于图像、区域、XLD都有相关的HImage、HRegion、HXLD类与其对应,看起来更加直观。其实在使用导出功能时,调用Halcon的方法基本使用的都是HOperatorSet这个类,这个类里面包含了Halcon的几乎所有的算子的静态方法,那么为什么Halcon还要提供如HImage、HRegion、HXLD等许多的其他的类呢,我觉得是因为在一些情况下,使用面向对象的方式更容易进行代码封装,而且更容易去修改代码。不过,存在即合理,我觉得在一些代码量较小的Halcon算法中,使用这种方法的确挺好的,可以很好的锻炼自己脱离Halcon的IDE去写Halcon算法的能力,但是面对代码量很大的Halcon算法,个人觉得这不是一个很好好的方式,写起来会比较累,不那么容易调试,前提还是在你对这种方式足够熟悉的基础上。


三、Halcon引擎

在了解Halcon引擎之前,我们最好先了解HDevelop函数文件,即.hdvp后缀的Halcon文件。使用Halcon引擎在C#中进行编程的方式我觉得主要是面向三种Halcon程序:

1.不含自己创建的外部函数的Halcon程序

这样的程序只需要new一个HDevEngine实例,一个HDevProgram实例,以及一个HDevProgramCall实例(其实可以不用这个实例也可以,为了方便可以new一个),在编写时主要需要注意的是HDev文件的路径,我这里将HDev文件放在了Debug目录下的HDEV文件夹内。

 

 最终效果: 

代码:

 1 namespace MyProgramCall
 2 {
 3     public partial class Form1 : Form
 4     {
 5 
 6         public Form1()
 7         {
 8             InitializeComponent();
 9         }
10         HDevEngine hDevEngine = new HDevEngine();//引擎
11         HDevProgram HDevProgram;//Halcon程序
12         HDevProgramCall HDevProgramCall;//Halcon程序执行实例
13         HWindow window;//窗体
14         HDevOpMultiWindowImpl HDevOpMultiWindowImpl;//方便Halcon程序操作显示的实例
15         string exePath = "";
16         private void btn_Init_Click(object sender, EventArgs e)
17         {
18             window = hWindowControl1.HalconWindow;
19             //通过以下两句,表示整个要执行的Halcon的HDev程序的父窗口(即显示窗口)为当前的窗口控件,如果不添加以下两句,则无法将Halcon程序的显示内容显示在窗体控件上,不过写程序时不建议通过将Halcon程序内的内容通过这种方式显示在控件上,可以直接拿到想要的对象然后再显示比较好
20             //HDevOpMultiWindowImpl = new HDevOpMultiWindowImpl(window);
21             //hDevEngine.SetHDevOperators(HDevOpMultiWindowImpl);
22             HDevProgram = new HDevProgram(exePath + "HDEV\\test.hdev");//实例化一个HDevProgram的实例
23             HDevProgramCall = new HDevProgramCall(HDevProgram);//实例化一个执行Halcon程序的实例
24             MessageBox.Show("初始化成功!!!");
25         }
26 
27         private void Form1_Load(object sender, EventArgs e)
28         {
29             exePath = AppDomain.CurrentDomain.BaseDirectory;
30         }
31 
32         private void btn_Execute_Click(object sender, EventArgs e)
33         {
34             try
35             {
36                 HDevProgramCall.Execute();
37             }
38             catch (HDevEngineException Ex)
39             {
40                 MessageBox.Show(Ex.Message, "HDevEngine Exception");
41                 return;
42             }
43 
44             //建议不使用以上显示的两句,手动显示想要显示的部分
45             HImage img =  HDevProgramCall.GetIconicVarImage("Image");
46             HRegion region = HDevProgramCall.GetIconicVarRegion("RegionDilation");
47             if(img.IsInitialized())
48             {
49                 img.GetImageSize(out HTuple width, out HTuple height);
50                 window.SetPart(0D, 0, height, width);
51                 //window.SetColor("green");
52                 window.SetColored(12);
53                 window.SetDraw("fill");
54                 window.DispObj(img);
55                 window.DispObj(region);
56             }
57             img.Dispose();
58         }
59     }
60 }
无外部函数

2.一个单一的HDevlop函数文件

如果只是封装好的一个.hdvp后缀的外部函数的文件,同样可以利用Halcon引擎来调用。这里我封装了一个十分简单的加法的Halcon函数文件,在调用时,只需要传入适当的参数,即可调用函数

 这个函数有两个输入的控制参数分别为num1和num2,有一个输出的控制参数为a,在C#中建立一个如下的窗口界面:

 其中三个textbox分别代表两个输入和一个输出,ADD按钮表示执行这个函数,label2表示通过引擎调用这个函数的消耗时间

 效果如下:

 代码:

 1 namespace hdvpFUc
 2 {
 3     public partial class Form1 : Form
 4     {
 5         
 6         public Form1()
 7         {
 8             InitializeComponent();
 9         }
10 
11         HDevEngine hDevEngine = new HDevEngine();//引擎
12         HDevProcedure HDevProcedure;//Halcon函数
13         Stopwatch sw = new Stopwatch();
14         string exePath = "";
15         public void LoadAlgorithm()
16         {
17             string procedurePath = exePath + "HDEV";
18             hDevEngine.SetProcedurePath(procedurePath);//使用引擎配置函数的路径,参数为这个函数的目录
19             HDevProcedure = new HDevProcedure("add_Num");//new一个Halcon外部函数的实例,参数为函数名
20             HDevProcedureCall hDevProcedureCall = new HDevProcedureCall(HDevProcedure);//new一个函数执行的实例
21             HTuple num1 = new HTuple(int.Parse(textBox1.Text));
22             HTuple num2 = new HTuple(int.Parse(textBox2.Text));
23             try
24             {
25                 //设置函数参数,这里的参数名要和函数内的参数名一一对应
26                 hDevProcedureCall.SetInputCtrlParamTuple("num1", num1);
27                 hDevProcedureCall.SetInputCtrlParamTuple("num2", num2);
28                 sw.Start();
29                 hDevProcedureCall.Execute();//执行函数
30                 sw.Stop();
31             }
32             catch (HDevEngineException EX)
33             {
34                 MessageBox.Show(EX.ToString());
35                 return;
36             }
37             string time = sw.ElapsedMilliseconds.ToString();
38             label2.Text = time;
39             HTuple a = hDevProcedureCall.GetOutputCtrlParamTuple("a");//获得函数的输出的控制参数”a"
40             textBox3.Text = a.ToString();
41         }
42         private void button1_Click(object sender, EventArgs e)
43         {
44             LoadAlgorithm();
45         }
46 
47         private void Form1_Load(object sender, EventArgs e)
48         {
49             exePath = AppDomain.CurrentDomain.BaseDirectory;
50         }
51     }
52 }
一个单独的外部函数

3.包含外部函数的Halcon程序

个人觉得这个是最常用的情况,使用Halcon引擎的情况下,在Halcon程序写好后,我们往往会对其中的核心代码封装成一个外部函数文件,并设置好输入输出的参数。

其实就是将1、2两步结合在一起即可,想执行整个程序就通过HDevProgramCall的实例调用其Execute方法,并通过这个实例的Getxxxxxx开头的方法获取想得到的控制参数或者图标参数,如果想执行Halcon程序中的某个外部函数,那么就通过HDevProcedureCall的实例,在执行其Execute方法前通过这个实例的Setxxxxx开头的方法传入相关参数,然后执行即可,获取参数的方法和前面一样。这里只通过一个简单的示例说明

路径:

 .hdev文件

.hdvp文件

效果

 代码:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.IO;
  7 using System.Linq;
  8 using System.Text;
  9 using System.Threading;
 10 using System.Threading.Tasks;
 11 using System.Windows.Forms;
 12 using HalconDotNet;
 13 namespace MultiDisplayEngeeni
 14 {
 15     public partial class Form1 : Form
 16     {
 17         HDevEngine hDevEngine;
 18         HDevProgram hDevProgram;
 19         HDevProgramCall hDevProgramCall;
 20         HDevProcedure hDevProcedure;
 21         HDevProcedureCall hDevProcedureCall;
 22         String[] _fileNames;
 23         HImage img = new HImage();
 24         HXLDCont contour = new HXLDCont();
 25         HXLD affContour = new HXLD();
 26         HShapeModel HShapeModel = new HShapeModel("board.shm");
 27         int _Index = 0;
 28         ManualResetEvent ManualResetEvent = new ManualResetEvent(false);
 29         string exePath = "";
 30         public Form1()
 31         {
 32             InitializeComponent();
 33         }
 34 
 35         private void btn_Init_Click(object sender, EventArgs e)
 36         {
 37             InitHdevlop();
 38             Action();
 39             btn_Init.Enabled = false;
 40             btn_Start.Enabled = true;
 41         }
 42 
 43         public void InitHdevlop()
 44         {
 45             hDevEngine = new HDevEngine();
 46             hDevEngine.SetProcedurePath(@"./HDVP");
 47             hDevProgram = new HDevProgram(@"./HDEV\affine_trans_model.hdev");
 48             hDevProgramCall = new HDevProgramCall(hDevProgram);
 49             hDevProcedure = new HDevProcedure(hDevProgram, "affine_trans_find_model");
 50             hDevProcedureCall = new HDevProcedureCall(hDevProcedure);
 51         }
 52         
 53         public void Action()
 54         {
 55             Action ac = new Action(() => {
 56                 hWindowControl1.HalconWindow.DispImage(img);
 57                 hWindowControl1.HalconWindow.DispObj(affContour);
 58             });
 59             Task.Run(() =>
 60             {
 61                 while (true)
 62                 {
 63                     try
 64                     {
 65                         ManualResetEvent.WaitOne();
 66                         img.Dispose();
 67                         affContour.Dispose();
 68                         string imgPath = exePath + _fileNames[_Index];
 69                         img = new HImage(imgPath);
 70                         img.GetImageSize(out HTuple width, out HTuple height);
 71                         hWindowControl1.HalconWindow.SetPart(0D, 0, height, width);
 72                         hDevProcedureCall.SetInputCtrlParamTuple("ModelID", HShapeModel);
 73                         hDevProcedureCall.SetInputIconicParamObject("Image", img);
 74                         hDevProcedureCall.SetInputIconicParamObject("ModelContours", contour);
 75                         hDevProcedureCall.Execute();
 76                         affContour = hDevProcedureCall.GetOutputIconicParamXld("ContoursAffineTrans");
 77                         Invoke(ac);
 78                         _Index++;
 79                         if (_Index == _fileNames.Length - 1)
 80                         {
 81                             _Index = 0;
 82                         }
 83                         Thread.Sleep(10);
 84                     }
 85                     catch (Exception)
 86                     {
 87                         return;
 88                     }
 89                 }
 90             });
 91         }
 92 
 93         private void Form1_Load(object sender, EventArgs e)
 94         {
 95             exePath = AppDomain.CurrentDomain.BaseDirectory;
 96             _fileNames = Directory.GetFiles("Images");
 97             hWindowControl1.HalconWindow.SetColor("blue");
 98             contour.ReadObject("contours.hobj");
 99             btn_Init.Enabled = true;
100             btn_Start.Enabled = false;
101             btn_Stop.Enabled = false;
102         }
103 
104         private void btn_Start_Click(object sender, EventArgs e)
105         {
106             btn_Start.Enabled = false;
107             btn_Stop.Enabled = true;
108             ManualResetEvent.Set();
109         }
110 
111         private void btn_Stop_Click(object sender, EventArgs e)
112         {
113             btn_Start.Enabled = true;
114             btn_Stop.Enabled = false;
115             ManualResetEvent.Reset();
116         }
117     }
118 }
View Code

在使用Halcon引擎时,个人觉得需要注意的是路径的配置(最好使用相对路径,方便程序移植),以及传参和获取参数时要保持和Halcon程序内部的参数名保持一致。


最后,做个小总结,关于具体使用哪种方法较好,我觉得因人而异,因为每种方法都可以实现相同的效果,并且各有优缺点

1.导出比较方便快捷,但是其不易封装和修改,可读性差。

2.面向对象的方式易封装和修改,并且完全可以脱离Halcon的IDE,但是它要求比较高的熟练度,而且面对较为复杂的程序和较多的代码量,这样的效率比较低。

3.使用Halcon引擎在代码修改方面十分方便,只需要在Halcon的IDE中修改好保存即可,并且也不存在可读性差的问题,无论是较为复杂的程序还是很简单的程序都是适用的,但是它比较依赖Halcon的软件,在移植的时候,Halcon的程序和外部函数文件也要一同进行移植。


以上所有内容仅为个人看法,如有不当,欢迎指正!

原文地址:https://www.cnblogs.com/Nq1996/p/13410567.html

推荐阅读