Halcon机器视觉实战WPF中HSmartWindowControl与HDrawingObject的高效ROI处理在工业自动化检测项目中ROIRegion of Interest的精确选取往往是整个视觉系统的关键起点。想象一下这样的场景您正在开发一个精密零件尺寸检测系统摄像头拍摄的图像中可能包含传送带、背景噪声等多种干扰因素而真正需要分析的只是零件上某个特定区域。传统的手动编码ROI坐标不仅效率低下更无法应对产线上零件位置的自然波动。这正是HSmartWindowControl与HDrawingObject组合大显身手的时刻——它们让操作者能够像使用Photoshop一样通过可视化交互轻松框选目标区域同时保持工业级精度。1. 环境搭建与基础配置1.1 WPF项目初始化首先创建一个标准的WPF项目通过NuGet添加HalconDotNet包当前最新版本为20.11。在MainWindow.xaml中配置基础界面Window x:ClassHalconROIDemo.MainWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml xmlns:halconclr-namespace:HalconDotNet;assemblyhalcondotnet Title智能ROI选取器 Height600 Width800 Grid Grid.ColumnDefinitions ColumnDefinition Width3*/ ColumnDefinition Width1*/ /Grid.ColumnDefinitions halcon:HSmartWindowControlWPF x:NamehswViewer HDoubleClickToFitContentTrue HZoomContentWheelForward Grid.Column0/ StackPanel Grid.Column1 Margin10 Button Content加载图像 ClickBtnLoadImage_Click Margin0,10/ Button Content创建矩形ROI ClickBtnCreateRect_Click/ Button Content保存模板区域 ClickBtnSaveTemplate_Click/ ComboBox x:NamecmbROIType SelectedIndex0 Margin0,20 ComboBoxItem Content矩形/ ComboBoxItem Content旋转矩形/ ComboBoxItem Content圆形/ /ComboBox /StackPanel /Grid /Window1.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 ListHDrawingObject _roiList new ListHDrawingObject(); // 添加新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 常见错误处理错误1ROI未正确附着到窗口症状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(); } }); }
Halcon机器视觉项目实战:在WPF中用HSmartWindowControl+HDrawingObject搞定模板区域选取与保存
Halcon机器视觉实战WPF中HSmartWindowControl与HDrawingObject的高效ROI处理在工业自动化检测项目中ROIRegion of Interest的精确选取往往是整个视觉系统的关键起点。想象一下这样的场景您正在开发一个精密零件尺寸检测系统摄像头拍摄的图像中可能包含传送带、背景噪声等多种干扰因素而真正需要分析的只是零件上某个特定区域。传统的手动编码ROI坐标不仅效率低下更无法应对产线上零件位置的自然波动。这正是HSmartWindowControl与HDrawingObject组合大显身手的时刻——它们让操作者能够像使用Photoshop一样通过可视化交互轻松框选目标区域同时保持工业级精度。1. 环境搭建与基础配置1.1 WPF项目初始化首先创建一个标准的WPF项目通过NuGet添加HalconDotNet包当前最新版本为20.11。在MainWindow.xaml中配置基础界面Window x:ClassHalconROIDemo.MainWindow xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentation xmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml xmlns:halconclr-namespace:HalconDotNet;assemblyhalcondotnet Title智能ROI选取器 Height600 Width800 Grid Grid.ColumnDefinitions ColumnDefinition Width3*/ ColumnDefinition Width1*/ /Grid.ColumnDefinitions halcon:HSmartWindowControlWPF x:NamehswViewer HDoubleClickToFitContentTrue HZoomContentWheelForward Grid.Column0/ StackPanel Grid.Column1 Margin10 Button Content加载图像 ClickBtnLoadImage_Click Margin0,10/ Button Content创建矩形ROI ClickBtnCreateRect_Click/ Button Content保存模板区域 ClickBtnSaveTemplate_Click/ ComboBox x:NamecmbROIType SelectedIndex0 Margin0,20 ComboBoxItem Content矩形/ ComboBoxItem Content旋转矩形/ ComboBoxItem Content圆形/ /ComboBox /StackPanel /Grid /Window1.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 ListHDrawingObject _roiList new ListHDrawingObject(); // 添加新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 常见错误处理错误1ROI未正确附着到窗口症状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(); } }); }