news 2026/6/6 20:14:24

Halcon机器视觉项目实战:在WPF中用HSmartWindowControl+HDrawingObject搞定模板区域选取与保存

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Halcon机器视觉项目实战:在WPF中用HSmartWindowControl+HDrawingObject搞定模板区域选取与保存

Halcon机器视觉实战:WPF中HSmartWindowControl与HDrawingObject的高效ROI处理

在工业自动化检测项目中,ROI(Region of Interest)的精确选取往往是整个视觉系统的关键起点。想象一下这样的场景:您正在开发一个精密零件尺寸检测系统,摄像头拍摄的图像中可能包含传送带、背景噪声等多种干扰因素,而真正需要分析的只是零件上某个特定区域。传统的手动编码ROI坐标不仅效率低下,更无法应对产线上零件位置的自然波动。这正是HSmartWindowControl与HDrawingObject组合大显身手的时刻——它们让操作者能够像使用Photoshop一样,通过可视化交互轻松框选目标区域,同时保持工业级精度。

1. 环境搭建与基础配置

1.1 WPF项目初始化

首先创建一个标准的WPF项目,通过NuGet添加HalconDotNet包(当前最新版本为20.11)。在MainWindow.xaml中配置基础界面:

<Window x:Class="HalconROIDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:halcon="clr-namespace:HalconDotNet;assembly=halcondotnet" Title="智能ROI选取器" Height="600" Width="800"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="3*"/> <ColumnDefinition Width="1*"/> </Grid.ColumnDefinitions> <halcon:HSmartWindowControlWPF x:Name="hswViewer" HDoubleClickToFitContent="True" HZoomContent="WheelForward" Grid.Column="0"/> <StackPanel Grid.Column="1" Margin="10"> <Button Content="加载图像" Click="BtnLoadImage_Click" Margin="0,10"/> <Button Content="创建矩形ROI" Click="BtnCreateRect_Click"/> <Button Content="保存模板区域" Click="BtnSaveTemplate_Click"/> <ComboBox x:Name="cmbROIType" SelectedIndex="0" Margin="0,20"> <ComboBoxItem Content="矩形"/> <ComboBoxItem Content="旋转矩形"/> <ComboBoxItem Content="圆形"/> </ComboBox> </StackPanel> </Grid> </Window>

1.2 核心对象初始化

在代码后台声明关键变量并初始化:

private HImage _currentImage = new HImage(); private HDrawingObject _drawingObj; private HTuple _imageWidth, _imageHeight; // 窗口加载时初始化Halcon环境 private void Window_Loaded(object sender, RoutedEventArgs e) { HOperatorSet.SetSystem("do_low_error", "true"); hswViewer.HInitWindow(0, 0, 800, 600); }

提示:SetSystem("do_low_error", "true")配置能让Halcon在出错时抛出异常而非静默失败,对调试非常有用。

2. 动态ROI交互实现

2.1 智能图像加载与显示

实现图像加载功能时,需要特别注意分辨率适配:

private void BtnLoadImage_Click(object sender, RoutedEventArgs e) { var dialog = new Microsoft.Win32.OpenFileDialog() { Filter = "图像文件|*.bmp;*.png;*.jpg|所有文件|*.*" }; if (dialog.ShowDialog() == true) { _currentImage.ReadImage(dialog.FileName); _currentImage.GetImageSize(out _imageWidth, out _imageHeight); hswViewer.HalconWindow.ClearWindow(); hswViewer.HalconWindow.DispImage(_currentImage); // 自动调整视图适应图像尺寸 hswViewer.HalconWindow.SetPart(0, 0, _imageHeight-1, _imageWidth-1); hswViewer.FitImageToWindow(true); } }

2.2 多类型ROI创建

根据用户选择创建不同类型的ROI对象:

private void BtnCreateRect_Click(object sender, RoutedEventArgs e) { // 移除已有ROI if (_drawingObj != null && _drawingObj.IsValid()) { hswViewer.HalconWindow.DetachDrawingObjectFromWindow(_drawingObj); _drawingObj.Dispose(); } // 根据选择创建不同类型ROI switch (cmbROIType.SelectedIndex) { case 0: // 标准矩形 _drawingObj = HDrawingObject.CreateDrawingObject( HDrawingObject.HDrawingObjectType.RECTANGLE1, _imageHeight/4, _imageWidth/4, _imageHeight*0.75, _imageWidth*0.75); break; case 1: // 旋转矩形 _drawingObj = HDrawingObject.CreateDrawingObject( HDrawingObject.HDrawingObjectType.RECTANGLE2, _imageHeight/2, _imageWidth/2, Math.PI/6, _imageWidth/4, _imageHeight/4); break; case 2: // 圆形 _drawingObj = HDrawingObject.CreateDrawingObject( HDrawingObject.HDrawingObjectType.CIRCLE, _imageHeight/2, _imageWidth/2, Math.Min(_imageWidth, _imageHeight)/4); break; } // 绑定到窗口并设置回调 hswViewer.HalconWindow.AttachDrawingObjectToWindow(_drawingObj); _drawingObj.OnDrag(OnROIChanged); _drawingObj.OnResize(OnROIChanged); }

2.3 ROI变化实时响应

通过回调函数实现ROI参数变化的实时监控:

private void OnROIChanged(HDrawingObject sender, HWindow window, string type) { // 获取当前ROI参数 HTuple paramNames = new HTuple("row1", "column1", "row2", "column2"); HTuple parameters = sender.GetDrawingObjectParams(paramNames); // 实时显示ROI坐标(实际项目中可更新UI或进行其他处理) Console.WriteLine($"ROI位置: ({parameters[0]}, {parameters[1]}) - ({parameters[2]}, {parameters[3]})"); }

3. 模板保存与优化技巧

3.1 精确区域提取

实现模板保存功能时,需要考虑图像坐标系与ROI参数的对应关系:

private void BtnSaveTemplate_Click(object sender, RoutedEventArgs e) { if (_drawingObj == null || !_drawingObj.IsValid()) { MessageBox.Show("请先创建ROI"); return; } try { // 获取ROI参数(根据不同类型调整参数名) HTuple paramNames = GetROIParameterNames(); HTuple parameters = _drawingObj.GetDrawingObjectParams(paramNames); // 生成Halcon区域对象 HRegion roiRegion = CreateRegionFromParams(parameters); // 裁剪图像并保存 HImage templateImage = _currentImage.ReduceDomain(roiRegion); var saveDialog = new SaveFileDialog() { Filter = "PNG图像|*.png|TIFF图像|*.tiff" }; if (saveDialog.ShowDialog() == true) { string extension = Path.GetExtension(saveDialog.FileName).TrimStart('.'); templateImage.WriteImage(extension, 0, saveDialog.FileName); MessageBox.Show("模板保存成功!"); } } catch (HalconException hex) { MessageBox.Show($"Halcon错误: {hex.Message}"); } } private HTuple GetROIParameterNames() { switch (_drawingObj.GetDrawingObjectType()) { case "rectangle1": return new HTuple("row1", "column1", "row2", "column2"); case "rectangle2": return new HTuple("row", "column", "phi", "length1", "length2"); case "circle": return new HTuple("row", "column", "radius"); default: throw new NotSupportedException("不支持的ROI类型"); } } private HRegion CreateRegionFromParams(HTuple parameters) { switch (_drawingObj.GetDrawingObjectType()) { case "rectangle1": return new HRegion().GenRectangle1( parameters[0].D, parameters[1].D, parameters[2].D, parameters[3].D); case "rectangle2": return new HRegion().GenRectangle2( parameters[0].D, parameters[1].D, parameters[2].D, parameters[3].D, parameters[4].D); case "circle": return new HRegion().GenCircle( parameters[0].D, parameters[1].D, parameters[2].D); default: throw new NotSupportedException("不支持的ROI类型"); } }

3.2 高级功能扩展

在实际项目中,我们往往需要更多增强功能:

多ROI管理方案

private List<HDrawingObject> _roiList = new List<HDrawingObject>(); // 添加新ROI时 _roiList.Add(_drawingObj); hswViewer.HalconWindow.AttachDrawingObjectToWindow(_drawingObj); // 清除所有ROI时 foreach(var roi in _roiList) { if (roi.IsValid()) { hswViewer.HalconWindow.DetachDrawingObjectFromWindow(roi); roi.Dispose(); } } _roiList.Clear();

ROI持久化保存与加载

// 保存ROI到XML public void SaveROIToFile(string filePath) { using (var writer = XmlWriter.Create(filePath)) { writer.WriteStartElement("ROICollection"); foreach (var roi in _roiList) { writer.WriteStartElement("ROI"); writer.WriteAttributeString("Type", roi.GetDrawingObjectType()); HTuple paramNames = GetROIParameterNames(roi); HTuple parameters = roi.GetDrawingObjectParams(paramNames); for (int i = 0; i < paramNames.Length; i++) { writer.WriteAttributeString( paramNames[i].S, parameters[i].ToString()); } writer.WriteEndElement(); } writer.WriteEndElement(); } } // 从XML加载ROI public void LoadROIFromFile(string filePath, HWindow window) { ClearAllROIs(); var doc = XDocument.Load(filePath); foreach (var element in doc.Root.Elements("ROI")) { string type = element.Attribute("Type").Value; HDrawingObject newROI = null; switch (type) { case "rectangle1": newROI = HDrawingObject.CreateDrawingObject( HDrawingObject.HDrawingObjectType.RECTANGLE1, double.Parse(element.Attribute("row1").Value), double.Parse(element.Attribute("column1").Value), double.Parse(element.Attribute("row2").Value), double.Parse(element.Attribute("column2").Value)); break; // 其他类型处理... } if (newROI != null) { window.AttachDrawingObjectToWindow(newROI); _roiList.Add(newROI); } } }

4. 实战问题排查与性能优化

4.1 常见错误处理

错误1:ROI未正确附着到窗口

症状:ROI绘制后无法显示或无法交互。 解决方案:

// 确保调用顺序正确:先创建ROI,再附着到窗口 _drawingObj = HDrawingObject.CreateDrawingObject(...); hswViewer.HalconWindow.AttachDrawingObjectToWindow(_drawingObj); // 检查窗口句柄是否有效 if (!hswViewer.HalconWindow.IsHandleValid()) { hswViewer.HInitWindow(0, 0, (int)hswViewer.ActualWidth, (int)hswViewer.ActualHeight); }

错误2:坐标系统不一致

症状:保存的模板区域与预期不符。 解决方案:

// 在获取ROI参数前确保图像已显示 hswViewer.HalconWindow.DispImage(_currentImage); // 检查图像坐标系 HTuple partStartRow, partStartCol, partEndRow, partEndCol; hswViewer.HalconWindow.GetPart(out partStartRow, out partStartCol, out partEndRow, out partEndCol);

4.2 性能优化技巧

大图像处理优化

当处理高分辨率图像(如4K以上)时:

// 1. 使用金字塔图像显示 HImage pyramidImage = _currentImage.ZoomImageSize( (int)(_imageWidth/4), (int)(_imageHeight/4), "constant"); // 2. 异步加载和显示 await Task.Run(() => { _currentImage.ReadImage(filePath); _currentImage.GetImageSize(out _imageWidth, out _imageHeight); }); // 3. 限制ROI数量(特别是在低配置设备上) if (_roiList.Count > 10) { MessageBox.Show("ROI数量过多可能影响性能"); }

内存管理最佳实践

// 及时释放Halcon对象 _currentImage.Dispose(); _drawingObj.Dispose(); // 使用using语句确保资源释放 using (HImage tempImage = new HImage()) { tempImage.ReadImage("temp.bmp"); // 处理图像... } // 定期调用垃圾回收(谨慎使用) if (System.Environment.WorkingSet > 500 * 1024 * 1024) // 500MB { GC.Collect(); GC.WaitForPendingFinalizers(); }

交互体验优化

// 添加ROI选中高亮效果 _drawingObj.SetDrawingObjectParams("color", "green"); _drawingObj.SetDrawingObjectParams("line_width", 2); // 实现双击删除ROI _drawingObj.OnAttached(OnROIAttached); private void OnROIAttached(HDrawingObject sender) { sender.OnClicked((obj, win, info) => { if (info == "double_click") { win.DetachDrawingObjectFromWindow(obj); _roiList.Remove(obj); obj.Dispose(); } }); }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/6 20:14:20

MonkeyCode 性能优化实战:让AI编程平台快如闪电的7个技巧

MonkeyCode 性能优化实战&#xff1a;让AI编程平台快如闪电的7个技巧性能是用户体验的基础。一个AI编程平台如果"思考"5秒才响应&#xff0c;用户会觉得AI不聪明&#xff08;即使答案是对的&#xff09;。MonkeyCode 团队在性能优化上做了大量工作。本文分享7个最有价…

作者头像 李华
网站建设 2026/6/6 20:14:17

如何让PS3手柄在Windows上完美工作:DsHidMini虚拟HID驱动完整指南

如何让PS3手柄在Windows上完美工作&#xff1a;DsHidMini虚拟HID驱动完整指南 【免费下载链接】DsHidMini Virtual HID Mini-user-mode-driver for Sony DualShock 3 Controllers 项目地址: https://gitcode.com/gh_mirrors/ds/DsHidMini 你是否还在为Windows系统无法识…

作者头像 李华
网站建设 2026/6/6 20:11:55

(浏览.md版本) Python入门(1):从环境搭建到内置函数核心精讲

### 第一部分#### 1. 一句话核心总结 该篇讲解了Python入门核心知识&#xff0c;涵盖环境搭建、内置函数&#xff08;print/input&#xff09;、变量与对象的本质、以及三种格式化输出方法&#xff08;百分号格式化、format()函数、f-string表达式&#xff09;&#xff0c;并重…

作者头像 李华
网站建设 2026/6/6 20:11:54

杨雨潼111212

动物实验设备费用击败法军则是部分金额是

作者头像 李华
网站建设 2026/6/6 20:09:39

linux内核源码中module_init()定义的函数指针变量解引用

最近&#xff0c;我在看linux内核源码&#xff0c;读到了模块tty_io.c文件&#xff0c;其中有一个函数console_init()用于遍历访问加解引用访问了module_init()含参宏定义的函数指针变量。具体过程在这里捋一下。 首先&#xff0c;module_init()含参宏 module_init(x)-->_…

作者头像 李华