C#与Halcon混合编程的几种方式

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C#与Halcon混合编程的⼏种⽅式
Halcon的学习过程中,关于Halcon的混合编程是⽆法避免的,Halcon可以和很多种语⾔进⾏混编,这⾥仅赘述与C#语⾔进⾏混编的⼀些简单⽅式。

C#与Halcon进⾏混编的⽅式⼤体可以分为:使⽤Halcon导出功能、⾯向对象的⽅式、Halcon引擎。

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

⼀、Halcon导出功能
下图为使⽤Halcon编写的简单程序
然后点击⽂件-》导出,或者直接点击如下按钮
点击导出之后,选择语⾔为C#-Halcon/.NET,选择好导出⽂件的路径,则会得到⼀个和Halcon程序同名的.cs⽂件
打开.cs⽂件,我们看到导出的代码中有⼀个action的⽅法,这个⽅法是即是我们需要重点关注的部分,我们在Halcon中写的代码都在这个⽅法中有所实现。

以下为action的代码:
1private 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);
21if (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);
31if (HDevWindowStack.IsOpen())
32 {
33 HOperatorSet.DispObj(ho_Image, HDevWindowStack.GetActive());
34 }
35if (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事件对应的⽅法中添加如下代码即可:
代码如下:
1namespace Halconprogram
2 {
3public partial class Form1 : Form
4 {
5public Form1()
6 {
7 InitializeComponent();
8 }
9
10private 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#环境下编写:
1namespace Halconprogram
2 {
3public partial class Form1 : Form
4 {
5public Form1()
6 {
7 InitializeComponent();
8 }
9
10private 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⽂件夹内。

最终效果: 
代码:
1namespace MyProgramCall
2 {
3public partial class Form1 : Form
4 {
5
6public 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程序操作显⽰的实例
15string exePath = "";
16private 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
27private void Form1_Load(object sender, EventArgs e)
28 {
29 exePath = AppDomain.CurrentDomain.BaseDirectory;
30 }
31
32private void btn_Execute_Click(object sender, EventArgs e)
33 {
34try
35 {
36 HDevProgramCall.Execute();
37 }
38catch (HDevEngineException Ex)
39 {
40 MessageBox.Show(Ex.Message, "HDevEngine Exception");
41return;
42 }
43
44//建议不使⽤以上显⽰的两句,⼿动显⽰想要显⽰的部分
45 HImage img = HDevProgramCall.GetIconicVarImage("Image");
46 HRegion region = HDevProgramCall.GetIconicVarRegion("RegionDilation");
47if(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表⽰通过引擎调⽤这个函数的消耗时间
效果如下:
代码:
1namespace hdvpFUc
2 {
3public partial class Form1 : Form
4 {
5
6public Form1()
7 {
8 InitializeComponent();
9 }
10
11 HDevEngine hDevEngine = new HDevEngine();//引擎
12 HDevProcedure HDevProcedure;//Halcon函数
13 Stopwatch sw = new Stopwatch();
14string exePath = "";
15public void LoadAlgorithm()
16 {
17string 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));
23try
24 {
25//设置函数参数,这⾥的参数名要和函数内的参数名⼀⼀对应
26 hDevProcedureCall.SetInputCtrlParamTuple("num1", num1);
27 hDevProcedureCall.SetInputCtrlParamTuple("num2", num2);
28 sw.Start();
29 hDevProcedureCall.Execute();//执⾏函数
30 sw.Stop();
31 }
32catch (HDevEngineException EX)
33 {
34 MessageBox.Show(EX.ToString());
35return;
36 }
37string time = sw.ElapsedMilliseconds.ToString();
38 label2.Text = time;
39 HTuple a = hDevProcedureCall.GetOutputCtrlParamTuple("a");//获得函数的输出的控制参数”a"
40 textBox3.Text = a.ToString();
41 }
42private void button1_Click(object sender, EventArgs e)
43 {
44 LoadAlgorithm();
45 }
46
47private 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⽂件
效果
代码:
1using System;
2using System.Collections.Generic;
3using ponentModel;
4using System.Data;
5using System.Drawing;
6using System.IO;
7using System.Linq;
8using System.Text;
9using System.Threading;
10using System.Threading.Tasks;
11using System.Windows.Forms;
12using HalconDotNet;
13namespace MultiDisplayEngeeni
14 {
15public 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");
27int _Index = 0;
28 ManualResetEvent ManualResetEvent = new ManualResetEvent(false);
29string exePath = "";
30public Form1()
31 {
32 InitializeComponent();
33 }
34
35private 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
43public 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
53public void Action()
54 {
55 Action ac = new Action(() => {
56 hWindowControl1.HalconWindow.DispImage(img);
57 hWindowControl1.HalconWindow.DispObj(affContour);
58 });
59 Task.Run(() =>
60 {
61while (true)
62 {
63try
64 {
65 ManualResetEvent.WaitOne();
66 img.Dispose();
67 affContour.Dispose();
68string 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++;
79if (_Index == _fileNames.Length - 1)
80 {
81 _Index = 0;
82 }
83 Thread.Sleep(10);
84 }
85catch (Exception)
86 {
87return;
88 }
89 }
90 });
91 }
92
93private 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
104private void btn_Start_Click(object sender, EventArgs e)
105 {
106 btn_Start.Enabled = false;
107 btn_Stop.Enabled = true;
108 ManualResetEvent.Set();
109 }
110
111private 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的程序和外部函数⽂件也要⼀同进⾏移植。

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

相关文档
最新文档