C#與Halcon混合編程的幾種方式


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的程序和外部函數文件也要一同進行移植。


 

以上所有內容僅為個人看法,如有不當,歡迎指正!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM