告别DrawRectangle1!在WPF中用HSmartWindowControl和HDrawingObject搞定Halcon ROI交互(附完整C#代码)
2026/6/6 3:14:09 网站建设 项目流程

从传统Halcon控件到HSmartWindowControl:ROI交互的现代化升级实践

在工业视觉开发领域,Halcon作为行业标杆工具,其窗口控件的交互方式直接影响开发效率。许多开发者已经习惯了使用DrawRectangle1这类简洁的API快速绘制ROI区域,但当项目升级到支持缩放拖拽的HSmartWindowControl时,这套熟悉的操作突然失效——这正是我去年接手某自动化检测系统改造时遇到的第一个拦路虎。

1. 新旧ROI交互机制深度对比

传统Halcon窗口控件与HSmartWindowControl在ROI处理上存在本质区别。前者通过类似DrawRectangle1的函数直接绘制,后者则引入了HDrawingObject这一面向对象的设计理念。这种转变不仅仅是API调用方式的改变,更反映了交互设计思想的进化。

核心差异点对比:

特性传统DrawRectangle1方式HSmartWindowControl+HDrawingObject
交互方式一次性绘制完成可反复调整的交互式对象
坐标获取通过返回值直接获取需要主动查询对象参数
可视化反馈静态显示实时动态调整
多ROI支持需要自行管理内置对象管理机制
与图像缩放兼容性固定像素坐标自动适应缩放变换
// 传统方式(HSmartWindowControl中已不可用) HOperatorSet.DrawRectangle1(hWindowControl.HalconID, out row1, out column1, out row2, out column2); // 现代方式 HDrawingObject drawingObject = HDrawingObject.CreateDrawingObject( HDrawingObject.HDrawingObjectType.RECTANGLE1, height/4, width/4, height*0.75, width*0.75); hswControl.HalconWindow.AttachDrawingObjectToWindow(drawingObject);

2. HDrawingObject的实战应用全流程

2.1 创建与窗口关联

创建HDrawingObject时,类型参数决定了ROI的形状。除了常见的RECTANGLE1,Halcon还支持:

  • RECTANGLE2:带旋转角度的矩形
  • CIRCLE:圆形区域
  • ELLIPSE:椭圆区域
  • POLYGON:多边形区域
private HDrawingObject drawingObject; private void CreateRectangleROI() { // 获取当前图像尺寸 image.GetImageSize(out int width, out int height); // 创建位于图像中央、占画面1/4大小的矩形 drawingObject = HDrawingObject.CreateDrawingObject( HDrawingObject.HDrawingObjectType.RECTANGLE1, height/4, width/4, height*0.75, width*0.75); // 关联到窗口并启用交互 hswControl.HalconWindow.AttachDrawingObjectToWindow(drawingObject); }

注意:同一个HDrawingObject不能同时附加到多个窗口,如果需要多视图同步显示,需要创建多个对象实例。

2.2 参数获取与事件处理

获取ROI参数的最佳实践是在用户完成调整后触发,通常通过按钮点击或特定事件。以下是获取矩形参数的典型代码:

private void GetROIParameters() { string[] paramNames = { "row1", "column1", "row2", "column2" }; HTuple parameters = drawingObject.GetDrawingObjectParams(new HTuple(paramNames)); double row1 = parameters.DArr[0]; double col1 = parameters.DArr[1]; double row2 = parameters.DArr[2]; double col2 = parameters.DArr[3]; // 生成Halcon区域对象 HRegion roiRegion = new HRegion(); roiRegion.GenRectangle1(row1, col1, row2, col2); return roiRegion; }

对于需要实时响应ROI变化的高级场景,可以注册绘图对象回调:

drawingObject.OnDrag(OnROIChanged); drawingObject.OnResize(OnROIChanged); private void OnROIChanged(HDrawingObject sender) { // 实时处理ROI变化 }

3. WPF集成的最佳实践

3.1 前端XAML布局优化

HSmartWindowControlWPF在XAML中的声明需要特别注意命名空间引用和交互属性设置:

<Window xmlns:halcon="clr-namespace:HalconDotNet;assembly=halcondotnet" x:Class="YourNamespace.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="智能ROI编辑器" Height="600" Width="800"> <Grid> <halcon:HSmartWindowControlWPF x:Name="hswControl" HDoubleClickToFitContent="True" HMoveContent="True" HZoomContent="MouseWheel" Margin="5"/> <StackPanel Orientation="Vertical" HorizontalAlignment="Right" Width="120"> <Button Content="创建ROI" Click="BtnCreateROI_Click" Margin="5"/> <Button Content="应用ROI" Click="BtnApplyROI_Click" Margin="5"/> <ComboBox x:Name="cbROIType" SelectedIndex="0" Margin="5"> <ComboBoxItem Content="矩形"/> <ComboBoxItem Content="旋转矩形"/> <ComboBoxItem Content="圆形"/> </ComboBox> </StackPanel> </Grid> </Window>

3.2 后台逻辑完整实现

完整的WPF后台代码需要处理图像加载、ROI生命周期和异常情况:

public partial class MainWindow : Window { private HImage currentImage; private HDrawingObject activeROI; public MainWindow() { InitializeComponent(); LoadSampleImage(); } private void LoadSampleImage() { try { currentImage = new HImage("pathto/your/image.png"); hswControl.HalconWindow.DispImage(currentImage); } catch (HalconException hex) { MessageBox.Show($"图像加载失败: {hex.Message}"); } } private void BtnCreateROI_Click(object sender, RoutedEventArgs e) { // 清除现有ROI activeROI?.Dispose(); currentImage.GetImageSize(out int width, out int height); // 根据下拉框选择创建不同类型ROI switch (cbROIType.SelectedIndex) { case 0: // 标准矩形 activeROI = HDrawingObject.CreateDrawingObject( HDrawingObject.HDrawingObjectType.RECTANGLE1, height*0.2, width*0.2, height*0.8, width*0.8); break; case 1: // 旋转矩形 activeROI = HDrawingObject.CreateDrawingObject( HDrawingObject.HDrawingObjectType.RECTANGLE2, height/2, width/2, 0, width/4, height/8); break; case 2: // 圆形 activeROI = HDrawingObject.CreateDrawingObject( HDrawingObject.HDrawingObjectType.CIRCLE, height/2, width/2, Math.Min(width,height)/4); break; } hswControl.HalconWindow.AttachDrawingObjectToWindow(activeROI); } private void BtnApplyROI_Click(object sender, RoutedEventArgs e) { if (activeROI == null) { MessageBox.Show("请先创建ROI"); return; } try { HRegion roiRegion = GetROIRegion(); HImage roiImage = currentImage.ReduceDomain(roiRegion); // 执行后续处理... ProcessROIImage(roiImage); } catch (HalconException hex) { MessageBox.Show($"ROI处理错误: {hex.Message}"); } } private HRegion GetROIRegion() { HTuple paramNames = new HTuple(); HDrawingObject.HDrawingObjectType type = activeROI.GetDrawingObjectType(); switch (type) { case HDrawingObject.HDrawingObjectType.RECTANGLE1: paramNames = new HTuple(new[] { "row1", "column1", "row2", "column2" }); HTuple rect1Params = activeROI.GetDrawingObjectParams(paramNames); HRegion rect1 = new HRegion(); rect1.GenRectangle1(rect1Params[0], rect1Params[1], rect1Params[2], rect1Params[3]); return rect1; case HDrawingObject.HDrawingObjectType.RECTANGLE2: paramNames = new HTuple(new[] { "row", "column", "phi", "length1", "length2" }); HTuple rect2Params = activeROI.GetDrawingObjectParams(paramNames); HRegion rect2 = new HRegion(); rect2.GenRectangle2(rect2Params[0], rect2Params[1], rect2Params[2], rect2Params[3], rect2Params[4]); return rect2; case HDrawingObject.HDrawingObjectType.CIRCLE: paramNames = new HTuple(new[] { "row", "column", "radius" }); HTuple circleParams = activeROI.GetDrawingObjectParams(paramNames); HRegion circle = new HRegion(); circle.GenCircle(circleParams[0], circleParams[1], circleParams[2]); return circle; default: throw new NotSupportedException("不支持的ROI类型"); } } }

4. 高级技巧与性能优化

4.1 多ROI管理策略

在实际项目中,经常需要同时处理多个ROI区域。以下是管理多个HDrawingObject的推荐模式:

private Dictionary<string, HDrawingObject> roiCollection = new Dictionary<string, HDrawingObject>(); private void AddROIToCollection(string roiName, HDrawingObject.HDrawingObjectType type) { if (roiCollection.ContainsKey(roiName)) { roiCollection[roiName].Dispose(); roiCollection.Remove(roiName); } currentImage.GetImageSize(out int width, out int height); HDrawingObject newROI = type switch { HDrawingObject.HDrawingObjectType.RECTANGLE1 => HDrawingObject.CreateDrawingObject(type, height*0.1, width*0.1, height*0.3, width*0.3), _ => throw new ArgumentException("Unsupported ROI type") }; hswControl.HalconWindow.AttachDrawingObjectToWindow(newROI); roiCollection.Add(roiName, newROI); } private void ClearAllROIs() { foreach (var roi in roiCollection.Values) { roi.Dispose(); } roiCollection.Clear(); }

4.2 序列化与持久化

保存和加载ROI配置是实际项目中的常见需求:

public class ROIConfig { public string Type { get; set; } public double[] Parameters { get; set; } } private void SaveROIConfig(string filePath) { var configs = new Dictionary<string, ROIConfig>(); foreach (var kvp in roiCollection) { var type = kvp.Value.GetDrawingObjectType(); var paramNames = GetParamNamesForType(type); var parameters = kvp.Value.GetDrawingObjectParams(new HTuple(paramNames)); configs.Add(kvp.Key, new ROIConfig { Type = type.ToString(), Parameters = parameters.ToDArr() }); } File.WriteAllText(filePath, JsonConvert.SerializeObject(configs, Formatting.Indented)); } private void LoadROIConfig(string filePath) { ClearAllROIs(); var configs = JsonConvert.DeserializeObject<Dictionary<string, ROIConfig>>( File.ReadAllText(filePath)); foreach (var kvp in configs) { var type = (HDrawingObject.HDrawingObjectType)Enum.Parse( typeof(HDrawingObject.HDrawingObjectType), kvp.Value.Type); HDrawingObject roi = HDrawingObject.CreateDrawingObject(type, kvp.Value.Parameters); hswControl.HalconWindow.AttachDrawingObjectToWindow(roi); roiCollection.Add(kvp.Key, roi); } } private string[] GetParamNamesForType(HDrawingObject.HDrawingObjectType type) { return type switch { HDrawingObject.HDrawingObjectType.RECTANGLE1 => new[] { "row1", "column1", "row2", "column2" }, HDrawingObject.HDrawingObjectType.RECTANGLE2 => new[] { "row", "column", "phi", "length1", "length2" }, HDrawingObject.HDrawingObjectType.CIRCLE => new[] { "row", "column", "radius" }, _ => throw new NotSupportedException() }; }

4.3 性能优化要点

当处理高分辨率图像或多ROI场景时,需要注意以下性能因素:

  1. 对象复用:避免频繁创建和销毁HDrawingObject,尽可能复用现有对象
  2. 事件去抖:对OnDrag/OnResize等频繁触发的事件进行延迟处理
  3. 渲染控制:在批量操作时临时禁用自动渲染
  4. 内存管理:确保及时释放不再使用的Halcon对象
private async void OnROIChanged(HDrawingObject sender) { // 实现去抖处理 if (debounceTimer != null) { debounceTimer.Stop(); debounceTimer.Dispose(); } debounceTimer = new System.Timers.Timer(300); debounceTimer.AutoReset = false; debounceTimer.Elapsed += (s, e) => { Dispatcher.Invoke(() => ProcessROIUpdate(sender)); }; debounceTimer.Start(); } private void ProcessROIUpdate(HDrawingObject roi) { try { // 临时禁用自动渲染 hswControl.HalconWindow.SetWindowParam("flush", "false"); // 获取当前ROI参数 HRegion region = GetROIRegion(roi); // 执行处理逻辑 // ... } finally { // 恢复渲染 hswControl.HalconWindow.SetWindowParam("flush", "true"); hswControl.HalconWindow.FlushBuffer(); } }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询