(六)海康工业相机与halcon+C#联合编程

HALCON是德国MVTec公司开发的一款综合性机器视觉标准软件,被广泛认为是该领域功能最全面、性能最强大的商业软件之一。集成开发环境HDevelop,并包含一个拥有超过2100个独立算子(函数)的图像处理库。其灵活的架构旨在帮助开发者缩短软件开发周期,并降低产品成本,是机器视觉领域的行业领头羊。官方网站:

授权分为三种:

开发许可证(开发狗):用于在HDevelop环境或通过C++、C#等编程语言进行程序开发。这是永久授权,是购买运行狗的前提。

运行许可证(运行狗):用于在客户端电脑上部署和运行已开发完成的应用程序。这也是永久授权。

评估/试用许可证:用于短期评估HALCON的全部功能。通常为一个月有效期的文件授权,无需加密狗即可使用。

一、Halcon 软件安装

1 、软件安装

(1)双击下载好的安装包,点击下一步

(2)阅读软件协议,将下拉滑到最下面,勾选我接受协议

(3)将检查更新的勾选去掉,点击下一步

(4)根据自己系统位数自行选择,点击下一步

(5)自行选择是否安装其他驱动程序,点击下一步

(6)选择文档语言,默认选择第一项英文

(7)选择软件安装位置

(8)需要选择许可证文件路径,这里先选择“Do not install a license file”

(9)安装完成,把勾点上,桌面才会有图标,点击Finish退出软件安装向导

(10)打开软件

二、Halcon 与C#联合编程

1 、WindForm

(1)创建工程

文件->新建->项目

选择所需要采用的框架版本,以4.8为例

将项目的编译配置切换为“Release”,并将目标平台设定为“x64”

运行成功显示新窗口

在窗体中添加显示控件

工具箱->PictureBox

属性在右下角,名称,大小,颜色,样式等

从文件中加载一张图片并显示到picturebox,点击按钮显示图片

private void button1_Click(object sender, EventArgs e) { LoadImage("./139.jpg"); }
// 从文件中读取图像在名为 pictureBox1 的 PictureBox 控件显示 private void LoadImage(string filePath) { try { // 从文件创建 Image 对象 Image img = Image.FromFile(filePath); // 将图像赋值给 PictureBox pictureBox1.Image = img; // 可选:调整 PictureBox 的显示模式 pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; // 按比例缩放适应控件 } catch (Exception ex) { MessageBox.Show($"加载图片失败:{ex.Message}"); } }

成功显示图片

(2)配置hlacon算子库到工程中

右键->打开文件所在位置

bin/.Net35文件夹内

复制halcondotnet.dll到工程目录下

打开vs2019工程的工程目录,右键引用,添加引用

将halcondotnet.dll添加到工程引用

(3)halcon算子转换C#

利用halcon读取一张彩色图像,转化成灰度图像后保存为png格式,运行如图。

保存的png格式图像如下

文件->导出

选择导出语言为C#

导出代码如下:

/ // File generated by HDevelop for HALCON/.NET (C#) Version 17.12 // // This file is intended to be used with the HDevelopTemplate or // HDevelopTemplateWPF projects located under %HALCONEXAMPLES%\c# using System; using HalconDotNet; public partial class HDevelopExport { public HTuple hv_ExpDefaultWinHandle; // Main procedure private void action() { // Local iconic variables HObject ho_Image139, ho_GrayImage; // Initialize local and output iconic variables HOperatorSet.GenEmptyObj(out ho_Image139); HOperatorSet.GenEmptyObj(out ho_GrayImage); ho_Image139.Dispose(); HOperatorSet.ReadImage(out ho_Image139, "C:/Users/**/Desktop/11111111/WindowsFormsApp1/bin/x64/Release/139.jpg"); ho_GrayImage.Dispose(); HOperatorSet.Rgb1ToGray(ho_Image139, out ho_GrayImage); HOperatorSet.WriteImage(ho_GrayImage, "png", 0, "./gray.png"); ho_Image139.Dispose(); ho_GrayImage.Dispose(); } public void InitHalcon() { // Default settings used in HDevelop HOperatorSet.SetSystem("width", 512); HOperatorSet.SetSystem("height", 512); } public void RunHalcon(HTuple Window) { hv_ExpDefaultWinHandle = Window; action(); } }

从导出的代码结构中可以看出,halcon库的头文件为

using HalconDotNet;

其中,action函数中的代码为执行的算法部分,复制到工程中

private void action() { // Local iconic variables HObject ho_Image139, ho_GrayImage; // Initialize local and output iconic variables HOperatorSet.GenEmptyObj(out ho_Image139); HOperatorSet.GenEmptyObj(out ho_GrayImage); ho_Image139.Dispose(); HOperatorSet.ReadImage(out ho_Image139, "./139.jpg"); ho_GrayImage.Dispose(); HOperatorSet.Rgb1ToGray(ho_Image139, out ho_GrayImage); HOperatorSet.WriteImage(ho_GrayImage, "png", 0, "./gray.png"); ho_Image139.Dispose(); ho_GrayImage.Dispose(); }

点击按钮执行action函数

private void button1_Click(object sender, EventArgs e) { action(); }

在根目录下生成的灰度图像,证明算法执行完毕

(4)halcon图像转化为C# WindForm框架下的BitMap格式在界面上显示

halcon图片转化为BitMap的代码

public static Bitmap HobjectToBitmap(HObject ho_image) { HTuple type, width, height; //创建交错格式图像 HOperatorSet.InterleaveChannels(ho_image, out HObject InterImage, "rgb", "match", 255); HOperatorSet.WriteImage(InterImage, "png", 0, "./gray1.png"); //获取交错格式图像指针 HOperatorSet.GetImagePointer1(InterImage, out HTuple Pointer, out type, out width, out height); IntPtr ptr = Pointer; //构建新24位Bitmap图像 Bitmap res24 = new Bitmap(width.I/3, height.I , width.I, System.Drawing.Imaging.PixelFormat.Format24bppRgb, ptr); Bitmap result = new Bitmap(res24); // 5. 清理资源(此时 tempBmp 不再依赖外部内存,但依然要释放;InterImage 可以安全释放) res24.Dispose(); InterImage.Dispose(); return result; }

更改算法执行的函数

private void action() { // Local iconic variables HObject ho_Image139, ho_GrayImage; // Initialize local and output iconic variables HOperatorSet.GenEmptyObj(out ho_Image139); HOperatorSet.GenEmptyObj(out ho_GrayImage); ho_Image139.Dispose(); HOperatorSet.ReadImage(out ho_Image139, "./139.jpg"); Bitmap show = HobjectToBitmap(ho_Image139); pictureBox1.Image = show; ho_GrayImage.Dispose(); HOperatorSet.Rgb1ToGray(ho_Image139, out ho_GrayImage); HOperatorSet.WriteImage(ho_GrayImage, "png", 0, "./gray.png"); ho_Image139.Dispose(); ho_GrayImage.Dispose(); }

点击按钮运行

同时生成了halcon算法得到的灰度图像

2 、从海康相机得到一张图片由halcon处理并显示

(1)首先将海康采集得到的bitmap格式转化为halcon格式

public static void Bitmap24ToHobject(Bitmap bmp, out HObject ho_img) { int height = bmp.Height; int width = bmp.Width; Rectangle imgRect = new Rectangle(0, 0, width, height); BitmapData bitData = bmp.LockBits(imgRect, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb); try { int stride = bitData.Stride; unsafe { int totalBytes = height * width * 3; byte[] data = new byte[totalBytes]; byte* bptr = (byte*)bitData.Scan0.ToPointer(); fixed (byte* pData = data) { for (int i = 0; i < height; i++) { byte* source = bptr + i * stride; byte* dest = pData + i * width * 3; // 使用 Buffer.MemoryCopy 替代逐字节复制,提升性能 Buffer.MemoryCopy(source, dest, width * 3, width * 3); } // 生成 Halcon 图像对象 HOperatorSet.GenImageInterleaved(out ho_img, new IntPtr(pData), "bgr", width, height, 0, "byte", width, height, 0, 0, -1, 0); } } } finally { // 确保释放 BitmapData 资源 bmp.UnlockBits(bitData); } }

(2)修改halcon算法更改输入输出

private void action(HObject ho_Image139) { // Local iconic variables HObject ho_GrayImage; HOperatorSet.GenEmptyObj(out ho_GrayImage); ho_GrayImage.Dispose(); HOperatorSet.Rgb1ToGray(ho_Image139, out ho_GrayImage); HOperatorSet.WriteImage(ho_GrayImage, "png", 0, "./gray.png"); ho_Image139.Dispose(); ho_GrayImage.Dispose(); }

(3)从相机采集图像并执行算法的函数

private void button1_Click(object sender, EventArgs e) { // LoadImage("./139.jpg"); //枚举设备 int nRet = DeviceEnumerator.EnumDevices(enumTLayerType, out deviceInfoList); if (nRet != MvError.MV_OK) { //ShowErrorMsg("Enumerate devices fail!", nRet); return; } // ch:获取选择的设备信息 | en:Get selected device information IDeviceInfo deviceInfo = deviceInfoList[0]; try { // ch:打开设备 | en:Open device device = DeviceFactory.CreateDevice(deviceInfo); } catch (Exception ex) { MessageBox.Show("Create Device fail!" + ex.Message); return; } int result = device.Open(); if (result != MvError.MV_OK) { //ShowErrorMsg("Open Device fail!", result); return; } //ch: 判断是否为gige设备 | en: Determine whether it is a GigE device if (device is IGigEDevice) { //ch: 转换为gigE设备 | en: Convert to Gige device IGigEDevice gigEDevice = device as IGigEDevice; // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera) int optionPacketSize; result = gigEDevice.GetOptimalPacketSize(out optionPacketSize); if (result != MvError.MV_OK) { // ShowErrorMsg("Warning: Get Packet Size failed!", result); } else { result = device.Parameters.SetIntValue("GevSCPSPacketSize", (long)optionPacketSize); if (result != MvError.MV_OK) { //ShowErrorMsg("Warning: Set Packet Size failed!", result); } } } // ch:设置采集连续模式 | en:Set Continues Aquisition Mode device.Parameters.SetEnumValueByString("AcquisitionMode", "Continuous"); device.Parameters.SetEnumValueByString("TriggerMode", "Off"); // ch:开始采集 | en:Start Grabbing result = device.StreamGrabber.StartGrabbing(); if (result != MvError.MV_OK) { //ShowErrorMsg("Start Grabbing Fail!", result); return; } IFrameOut frameOut; nRet = device.StreamGrabber.GetImageBuffer(1000, out frameOut); if (MvError.MV_OK == nRet) { if (pictureBox1.Image != null) { pictureBox1.Image.Dispose(); } // 创建新图并显示 Bitmap show = frameOut.Image.ToBitmap(); HObject ho_img; Bitmap24ToHobject(show, out ho_img); action(ho_img); pictureBox1.Image = show; ho_img.Dispose(); } else { return; } // ch:停止采集 | en:Stop Grabbing result = device.StreamGrabber.StopGrabbing(); if (result != MvError.MV_OK) { // ShowErrorMsg("Stop Grabbing Fail!", result); return; } }

(4)显示效果与算法运行得到的灰度图

(5)从相机采集图像在WPF设计的界面中显示时,可以将bitmap转化为BitImage显示

public static BitmapImage ConvertToBitmapImage(System.Drawing.Bitmap bitmap) { using (MemoryStream memory = new MemoryStream()) { // 将 System.Drawing.Bitmap 保存到内存流 bitmap.Save(memory, ImageFormat.Png); memory.Position = 0; BitmapImage bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.StreamSource = memory; bitmapImage.EndInit(); bitmapImage.Freeze(); // 跨线程使用时需要 Freeze return bitmapImage; } }
System.Drawing.Bitmap show = frameOut.Image.ToBitmap(); BitmapImage showBitmapImage = ConvertToBitmapImage(show); Original.Source = showBitmapImage;