簡介 本文的初衷是希望幫助那些有其它平臺視覺演算法開發經驗的人能快速轉入Halcon平臺下,通過文中的示例開發者能快速瞭解一個Halcon項目開發的基本步驟,讓開發者能把精力完全集中到演算法的開發上面。 首先,你需要安裝Halcon,HALCON 18.11.0.1的安裝包會放在文章末尾。安裝包分開發和 ...
目錄
簡介
本文的初衷是希望幫助那些有其它平臺視覺演算法開發經驗的人能快速轉入Halcon平臺下,通過文中的示例開發者能快速瞭解一個Halcon項目開發的基本步驟,讓開發者能把精力完全集中到演算法的開發上面。
首先,你需要安裝Halcon,HALCON 18.11.0.1的安裝包會放在文章末尾。安裝包分開發和運行時兩個版本,運行時版本一般用於生產環境。
註:開發版本自帶運行時可替代運行時版本,但安裝的東西會比較多。
然後,你需要學會查看Halcon的幫助手冊,這是很重要的一件事。
本文涉及到幫助文檔的主要章節如下:
原文 HALCON 18.11.0.1 / Programmer's Guide / Programming With HALCON/.NET
翻譯 HALCON 18.11.0.1/程式員指南/使用 HALCON/.NET 編程
原文 HALCON 18.11.0.1 / HALCON Operator Reference
翻譯 HALCON 18.11.0.1/ HALCON 運算符參考
文中的示例是我第一次接觸Halcon時的學習測試用例,在電腦裡面躺了一年,最近才有時間整理一下發出來,希望能對你有所幫助。
註:運行本文示常式序前至少安裝Halcon的運行時,否則Halcon的dll無法正常使用。
將 HALCON/.NET 添加到應用程式
添加控制項
右鍵單擊工具箱,然後選擇“選擇項”,彈出的對話框選擇“.NET Framework組件”,單擊下麵的“瀏覽”,導航到HALCON安裝目錄下的\bin\dotnet35(VS2008以下版本的選擇dotnet20) ,然後選擇halcondotnet.dll。
完成上述操作後,HSmartWindowControl和HWindowControl控制項就會出現在工具箱中,其中HWindowControl控制項已經過時官方不再推薦使用。
與HWindowControl相比,HSmartWindowControl控制項具有以下幾個優點:
- 可以像任何其他控制項一樣使用
- 提供預定義的滑鼠交互(移動視窗內容並使用滑鼠滾輪進行縮放), 可以通過雙擊視窗來重置視圖
- 控制項會自動重新縮放,而不會閃爍
註:與HSmartWindowControlWPF 相反,HSmartWindowControl需要一個回調才能使用滑鼠滾輪進行縮放。
引用dll
在HALCON安裝目錄下的\bin\dotnet35中,引用以下dll:
- hdevenginedotnet.dll
- halcondotnet.dll
註:使用 HALCON XL 開發應用程式時,必須選擇以xl結尾的dll,hhdevelop xl適用於大解析度的圖像(大於 32k x 32k )。
引用以下命名空間:
- HalconDotNet:控制項所在的命名空間
- HalconType:Line、Rectangle2等數據類型所在的命名空間
調用Halcon運算元
以ReadImage操作為例,函數原型如下:
static void HOperatorSet.ReadImage(out HObject image, HTuple fileName)
public HImage(HTuple fileName)
public HImage(string fileName)
void HImage.ReadImage(HTuple fileName)
void HImage.ReadImage(string fileName)
註:這些內容幫助手冊上都有,在文章開頭列出來的章節。
在C#調用HALCON 運算元有兩種選擇:函數式、對象式,前值通過HOperatorSet調用運算元並通過out關鍵字傳入關鍵對象,後者直接在關鍵對象上調用對應的方法。
兩種方法完全等價,C#是一門面向對象的語言,建議使用對象式的方式調用運算元會好一點。
程式示例
本示例只實現下麵幾種關鍵功能:
- 載入、保存圖片
- 畫線、框並保存
- 抓邊演算法、測寬演算法
先新建一個Winform項目,界面設計如下:
註:項目的解決方案平臺不能使用AnyCPU,只能根據安裝的Halcon位數選擇x64或x86,我使用的是x64平臺。
HSmartWindowControl控制項使用
將HSmartWindowControl控制項拖入主界面即可,在窗體類裡面定義一個HWindow類型的成員引用控制項內部的窗體,同時設置控制項的回調函數(WPF則不需要)。代碼如下:
//視窗實例
private HWindow hwindow;
public Form1()
{
InitializeComponent();
hwindow = hSmartWindowControl1.HalconWindow;//初始化視窗變數
hSmartWindowControl1.MouseWheel += HSmartWindow_MouseWheel;
}
//滑鼠滾輪迴調
private void HSmartWindow_MouseWheel(object sender, MouseEventArgs e)
{
Point pt = this.Location;
MouseEventArgs newe = new MouseEventArgs(e.Button, e.Clicks, e.X - pt.X, e.Y - pt.Y, e.Delta);
hSmartWindowControl1.HSmartWindowControl_MouseWheel(sender, newe);
}
載入、保存圖像
載入、保存圖像也比較簡單,我們需要先定義一個HImage實例,然後按鈕單擊事件在該實例上調用對應的運算元,代碼如下:
//圖片變數
private HImage image = new HImage();
//載入圖片
private void button_ReadImage_Click(object sender, EventArgs e)
{
string imagePath = "TestRead.bmp";
image.ReadImage(imagePath);
hwindow.DispImage(image);
//自動適應圖片(相當於控制項上面的雙擊操作)
hwindow.SetPart(0, 0, -2, -2);
}
//保存圖片
private void button_WriteImage_Click(object sender, EventArgs e)
{
string imagePath = "TestWrite.bmp";
image.WriteImage("bmp", 0, imagePath);
hwindow.DispImage(image);
}
上面代碼是從程式啟動目下載入TestRead.bmp圖片,保存圖片到程式啟動目下的TestWrite.bmp,實際路徑可以根據項目情況自己定義。
上面的圖片是自己生成的,不是生產環境下的產品圖片,僅用於程式演示。
擴展:載入相機圖像
大部分項目都是從相機載入圖片,但這涉及到相機驅動的一些知識,全部介紹一邊會偏移文章主題。
簡單來說,載入相機圖像分兩步:
- 將相機圖像保存到記憶體
- 將記憶體中的圖像傳入Halcon
將相機圖像保存到記憶體是相機驅動的工作,下麵只討論怎麼將記憶體中的圖像傳入Halcon,代碼如下:
private void GenImageByPtr()
{
//這三個參數都可以通過相機驅動得到
byte[] imageBuf = null; //圖像緩存數組
int width = 0; //圖像寬度
int heigth = 0; //圖像高度
//獲取記憶體圖像中間的指針
IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(imageBuf, 0);
//載入記憶體中的圖像
image.GenImage1("byte", width, heigth, ptr);
hwindow.DispImage(image);
}
這裡只列一個簡單的示例,類似的運算元還有copy_image、gen_image3等。
畫線、畫框並保存
在圖像上畫線、框是機器視覺裡面常見的需求,根據線、框確定演算法搜索的區域和特征。
在窗體類中定義一個HDrawingObject對象並附加到現有視窗用於交互,同時定義好Line對象、Rectangle2對象用於保存繪圖的結果。
先在圖像視窗上面畫出線和框,然後再用滑鼠手動調整大小、位置,代碼如下:
//繪圖對象
private HDrawingObject drawingObject = new HDrawingObject();
//線ROI
private Line line = new Line();
//框ROI
private Rectangle2 rectangle2 = new Rectangle2();
private void button_DrawLine_Click(object sender, EventArgs e)
{
drawingObject.CreateDrawingObjectLine(100, 100, 200, 200);
//將繪圖對象關聯到Halcon視窗
hwindow.AttachDrawingObjectToWindow(drawingObject);
}
private void button_SaveLine_Click(object sender, EventArgs e)
{
HTuple paramName, param;
paramName = new HTuple(new string[] { "row1", "column1", "row2", "column2" });
param = drawingObject.GetDrawingObjectParams(paramName);
//保存參數
line.SetValue(param.ToDArr());
paramName.Dispose();
param.Dispose();
//清除繪圖內容
drawingObject.ClearDrawingObject();
}
private void button_DrawRect_Click(object sender, EventArgs e)
{
drawingObject.CreateDrawingObjectRectangle2(300, 400, 0, 300, 200);
//將繪圖對象關聯到Halcon視窗
hwindow.AttachDrawingObjectToWindow(drawingObject);
}
private void button_SaveRect_Click(object sender, EventArgs e)
{
HTuple paramName, param;
paramName = new HTuple(new string[] { "row", "column", "phi", "length1", "length2" });
param = drawingObject.GetDrawingObjectParams(paramName);
//保存參數
rectangle2.SetValue(param.ToDArr());
paramName.Dispose();
param.Dispose();
//清除繪圖內容
drawingObject.ClearDrawingObject();
}
上面的paramName可以取以下值,裡面包含了Line、Rectangle2類屬性名:
"color", "column", "column1", "column2", "end_angle", "font", "length1", "length2", "line_style",
"line_width", "phi", "radius", "radius1", "radius2", "row", "row1", "row2", "start_angle", "string", "type"
檢測演算法
用Halcon開發檢測演算法一般有兩種方法:
- 根據直接調用Halcon在對應語言平臺下的運算元介面
- 用Halcon自帶的腳本語言開發演算法然後轉成C#類
第一種自由度比較高,代碼看起來也比較簡潔易懂,但上手比較困難。第二種更簡單,但生成的類很難看,而且與程式集成的時候需要做一些改動。
兩種方法並不是絕對對立的,一般會先用Halcon驗證演算法,然後參考導出的C#類實現自己的檢測演算法。
抓邊演算法
抓變演算法直接調用的是Halcon的C#運算元介面,裡面有用到2D 測量模型:
2D測量模型
簡述一下2D 測量的使用步驟:
-
創建測量模型並指定圖像大小:首先必須使用create_metrology_model創建測量模型,然後使用set_metrology_model_image_size指定測量結果所在的圖像的大小。
-
提供近似值:將測量對象添加到測量模型中,每個測量對象由圖像中相應對象的近似形狀參數和控制測量的參數組成,控制測量的參數包括例如指定測量區域的尺寸和分佈的參數,測量對象有以下幾種:
- 圓:add_metrology_object_circle_measure
- 橢圓:add_metrology_object_ellipse_measure
- 矩形:add_metrology_object_rectangle2_measure
- 線:add_metrology_object_line_measure
- 使用一個運算符創建不同形狀:add_metrology_object_generic
要直觀檢查定義的度量對象,可以使用運算符get_metrology_object_model_contour訪問其XLD輪廓。要直觀檢查創建的測量區域,可以使用運算符get_metrology_object_measures訪問其XLD輪廓。
-
修改模型參數:如果已執行相機校準,則可以使用set_metrology_model_param,沒有就忽略(本示例沒有使用)。
-
修改對象參數:當將測量對象添加到測量模型時,可以設置許多參數,之後還可以使用運算符set_metrology_object_param修改其中的一些(本示例是在添加時設置的參數,所以沒有此步驟)。
-
調整測量模型:在執行下一次測量之前平移和旋轉測量模型,可以使用操作員align_metrology_model。通常使用基於形狀的匹配來獲得對準參數,相當於測量前的位置就糾偏(本示例比較簡單沒有此步驟)。
-
應用測量:使用apply_metrology_model執行測量過程。
-
訪問結果:測量後,可以使用get_metrology_object_result訪問結果,也可以使用get_metrology_object_measures獲取定位邊的行坐標和列坐標再進一步處理(本示例使用前者)。
代碼實現
抓變演算法的C#代碼如下:
private void button_FindEdge_Click(object sender, EventArgs e)
{
//創建測量對象
HMetrologyModel hMetrologyModely = new HMetrologyModel();
//設置圖片大小
image.GetImageSize(out int width, out int height);
hMetrologyModely.SetMetrologyModelImageSize(width, height);
//添加直線測量
double measureLength1= 30, measureLength2=30, measureSigma=1, measureThreshold=30;
HTuple genParamName = new HTuple(), genParamValue = new HTuple();
hMetrologyModely.AddMetrologyObjectLineMeasure(line.Row1, line.Column1,line.Row2, line.Column2, measureLength1, measureLength2, measureSigma, measureThreshold, genParamName, genParamValue);
//執行並獲取結果
hMetrologyModely.ApplyMetrologyModel(image);
//獲取測量區域
HTuple mRow = new HTuple(), mCol = new HTuple();
HXLDCont mContours = hMetrologyModely.GetMetrologyObjectMeasures("all", "all", out mRow, out mCol); //檢測區域輪廓
HXLDCont mmContours = hMetrologyModely.GetMetrologyObjectModelContour("all", 1); //測量對象輪廓
//參數順序 ["row_begin", "column_begin", "row_end", "column_end"]
HTuple lineRet =hMetrologyModely.GetMetrologyObjectResult("all", "all", "result_type", "all_param");
double[] retAry = lineRet.DArr;
//列印結果
hwindow.SetLineWidth(2);
hwindow.SetColor("green");
hwindow.DispLine(retAry[0], retAry[1], retAry[2], retAry[3]);
hwindow.SetColor("blue");
hwindow.DispXld(mContours);
hwindow.SetColor("yellow");
hwindow.DispXld(mmContours);
//清空測量對象
hMetrologyModely.ClearMetrologyModel();
//清理對象
hMetrologyModely?.Dispose();
genParamName?.Dispose();
genParamValue?.Dispose();
mRow.Dispose();
mCol.Dispose();
mContours.Dispose();
mmContours.Dispose();
}
Halcon的代碼如下:
*讀取圖片
read_image (Image, 'D:/test.bmp')
dev_get_window (WindowHandle)
*畫線
Row1:=1218.79
Column1:=1002.95
Row2:=1242.07
Column2:=2786.18
*draw_line (WindowHandle, Row1, Column1, Row2, Column2)
*gen_region_line (RegionLines, Row1, Column1, Row2, Column2)
*創建測量幾何形狀所需的數據結構
create_metrology_model (MetrologyHandle)
get_image_size (Image, Width, Height)
set_metrology_model_image_size (MetrologyHandle, Width, Height)
add_metrology_object_line_measure (MetrologyHandle, Row1, Column1, Row2, Column2, 100, 50, 1, 30, [], [], Index)
apply_metrology_model (Image, MetrologyHandle)
get_metrology_object_result (MetrologyHandle, 'all', 'all', 'result_type','all_param', Parameter)
get_metrology_object_measures(Contours, MetrologyHandle, 'all', 'all', Row, Column)
get_metrology_object_model_contour (Contour, MetrologyHandle, 0, 1.5)
*清空測量對象,否則會導致記憶體泄露
clear_metrology_model (MetrologyHandle)
*可視化
dev_clear_window ()
dev_display(Image)
dev_set_color('green')
dev_set_line_width(1)
disp_line (WindowHandle, Parameter[0], Parameter[1], Parameter[2], Parameter[3])
dev_display (Contours)
dev_display (Contour)
使用方法
直接在界面上點擊“打開圖片”->“畫線ROI”(預設位置我都調好了,你也可以自己調整大小、位置)->“抓邊”,過程如下:
測寬演算法
測寬演算法使用一維測量中的measure_pairs運算元提取直邊對,然後計算兩個直邊的距離。代碼太長這裡就不貼了,完整的項目源碼會在文章末尾給出。
需要註意,measure_pairs運算元的搜索框必須和目標邊緣完全垂直,否則寬度數據會不准確,運算元原理如下:
直接在界面上點擊“打開圖片”->“畫框ROI”(預設位置我都調好了,你也可以自己調整大小、位置)->“測寬”,過程如下:
上面的箭頭就是框的方向,測量邊必須與框的方向接近垂直否則會運算失敗,實際項目中還是建議用2D測量單獨抓兩個邊來測寬度。
源碼裡面顯示邊緣的DispEdgeMarker方法,是直接從measure_pairs運算元示例裡面導出轉C#的,所以風格會比較奇怪。