| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Threading.Tasks;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Input;
- using System.Windows.Media;
- using HalconDotNet;
- namespace Plugins.Halcon.Views
- {
- /// <summary>HALCON 画面框的显示结果类型(OK/NG/无)。</summary>
- public enum HalconResultType { Null, OK, NG }
- /// <summary>双击放大回调(与 VppWindow.MouseDoubleDelegate 对等)。</summary>
- public delegate void HalconMouseDoubleDelegate(HalconWindow window, int number);
- /// <summary>
- /// 单个 HALCON 画面框(镜像 VppWindow/VmWindow 的角色):标题、状态圆点、图像区。
- /// 图像区用 HALCON 原生 WPF 控件 <see cref="HSmartWindowControlWPF"/>(内置缩放/平移):
- /// - RefreshImage(image, overlay):经典模式——单底图 + 单叠加(主页画面框用);
- /// - ShowLayers(layers):图层模式——底图 + 多彩色叠加(编辑器大显示窗按图层筛选渲染);
- /// - 鼠标移动实时读取 行/列/灰度;支持拖入图片文件、右键保存图片、适应窗体;
- /// - 右键菜单可交互绘制 矩形/旋转矩形/圆形 ROI(HDrawingObject),结果存 <see cref="LastRoi"/>。
- /// 所有 HALCON 调用集中在 <see cref="HalconRuntime"/> 或本类;显示操作统一切到 UI 线程。
- /// 所有权规则:外部传入的 HObject 只引用不释放;由本窗体从 GenImage 转换出的副本(owned)在本窗体负责释放。
- /// </summary>
- public partial class HalconWindow : UserControl
- {
- /// <summary>双击放大/还原回调(由 UCDispHalcon 挂接)。</summary>
- public HalconMouseDoubleDelegate MouseDouble { get; set; }
- /// <summary>画面框编号(1 起)。</summary>
- public int Number { get; }
- // HALCON 显示面与右键菜单改为 code-behind 防御式创建(见 InitHalconSurface):
- // 机器缺 halcon.dll 等原生运行库时,XAML 声明式创建 + WPF 渲染期会抛出
- // DllNotFoundException(首次可被吞掉,二次渲染异常直接致命闪退)。
- private HSmartWindowControlWPF MyHwindow;
- private ContextMenu MyMenu;
- // —— 经典模式状态(单图 + 单叠加) ——
- private HObject _image;
- private bool _imageOwned;
- private HObject _overlay;
- private bool _overlayOwned;
- private int _imgW, _imgH;
- private bool _hasImage;
- // —— 图层模式状态(底图 + 多叠加) ——
- private readonly List<HObject> _layerOwnedCopies = new List<HObject>(); // 本窗体自有的转换副本(释放责任在本窗体)
- private HObject _layerBaseImage;
- private bool _layerBaseOwned;
- private readonly List<KeyValuePair<HObject, string>> _layerOverlays = new List<KeyValuePair<HObject, string>>();
- private int _layerImgW, _layerImgH;
- private bool _layerMode;
- /// <summary>图层缺省配色(按叠加顺序取色,可用 LayerItem.Color 覆盖)。</summary>
- private static readonly string[] LayerPalette = { "red", "green", "blue", "yellow", "cyan", "magenta", "orange" };
- // ROI 交互绘制
- private HDrawingObject _drawing;
- private bool _drawingBusy;
- /// <summary>最近一次交互绘制得到的 ROI(供节点读取;未绘制时为 null)。</summary>
- public Models.HalconRoi LastRoi { get; private set; }
- public HalconWindow() : this(1) { }
- public HalconWindow(int number)
- {
- Number = number;
- InitializeComponent();
- CaptionText.Text = $"画面 {number}";
- InitHalconSurface();
- SizeChanged += (s, e) => { if (e.NewSize.Width > 1) { FitCurrent(); } };
- }
- /// <summary>
- /// 防御式创建 HALCON 显示面:先做一次原生调用探测(缺 halcon.dll 等运行库时立即失败),
- /// 可用才创建 HSmartWindowControlWPF 并挂事件/右键菜单;不可用则显示错误占位。
- /// 保证 Halcon 流程编辑器等宿主在无原生运行库的机器上照常打开,不闪退。
- /// </summary>
- private void InitHalconSurface()
- {
- if (!TryProbeHalconNative(out var probeError))
- {
- NativeErrorText.Text = "HALCON 原生运行库不可用\n\n" +
- $"原因:{probeError}\n\n" +
- "请检查 Runtime\\dll\\x64 下的 halcon.dll 及配套运行库是否完整," +
- "或安装 HALCON 运行时。流程画布与属性编辑不受影响。";
- NativeErrorText.Visibility = Visibility.Visible;
- return;
- }
- try
- {
- var hw = new HSmartWindowControlWPF
- {
- HDoubleClickToFitContent = false,
- AllowDrop = true,
- Cursor = Cursors.Cross
- };
- hw.HMouseMove += MyHwindow_HMouseMove;
- hw.MouseLeave += MyHwindow_MouseLeave;
- hw.MouseRightButtonDown += MyHwindow_MouseRightButtonDown;
- hw.Drop += MyHwindow_Drop;
- hw.DragEnter += MyHwindow_DragEnter;
- MyMenu = BuildContextMenu();
- hw.ContextMenu = MyMenu;
- MyHwindow = hw;
- HwindowHost.Content = hw;
- }
- catch (Exception ex)
- {
- MyHwindow = null;
- NativeErrorText.Text = $"HALCON 显示控件创建失败:{ex.Message}";
- NativeErrorText.Visibility = Visibility.Visible;
- }
- }
- /// <summary>原生运行库探测:一次无副作用的 HALCON 调用,失败=缺原生 DLL。</summary>
- private static bool TryProbeHalconNative(out string error)
- {
- try
- {
- HOperatorSet.GetSystem("version", out HTuple version);
- error = null;
- return true;
- }
- catch (Exception ex)
- {
- error = ex.Message;
- return false;
- }
- }
- /// <summary>构建显示区右键菜单(缩放/适应/保存/ROI 绘制)。</summary>
- private ContextMenu BuildContextMenu()
- {
- var menu = new ContextMenu();
- void AddHandler(string header, RoutedEventHandler onClick)
- {
- var mi = new MenuItem { Header = header };
- mi.Click += onClick;
- menu.Items.Add(mi);
- }
- AddHandler("放大", MenuZoomIn_Click);
- AddHandler("缩小", MenuZoomOut_Click);
- AddHandler("适应窗体", MenuFit_Click);
- AddHandler("保存图片", MenuSave_Click);
- menu.Items.Add(new Separator());
- AddHandler("绘制矩形ROI", MenuRoiRect1_Click);
- AddHandler("绘制旋转矩形ROI", MenuRoiRect2_Click);
- AddHandler("绘制圆形ROI", MenuRoiCircle_Click);
- AddHandler("清除ROI", MenuRoiClear_Click);
- return menu;
- }
- /// <summary>HALCON 窗口句柄(控件初始化后才有效;用前判空)。</summary>
- private HWindow Hwn
- {
- get { try { return MyHwindow?.HalconWindow; } catch { return null; } }
- }
- #region 引擎/显示插件调用的公共接口(与 VmWindow 对齐)
- public void SetCaption(string caption)
- {
- if (!Dispatcher.CheckAccess()) { Dispatcher.Invoke(() => SetCaption(caption)); return; }
- CaptionText.Text = string.IsNullOrWhiteSpace(caption) ? $"画面 {Number}" : caption;
- }
- public void SetStatusDotVisible(bool visible)
- {
- if (!Dispatcher.CheckAccess()) { Dispatcher.Invoke(() => SetStatusDotVisible(visible)); return; }
- StatusDot.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
- }
- /// <summary>按 OK/NG 刷状态圆点颜色(并置为可见)。</summary>
- public void SetLable(HalconResultType type)
- {
- if (!Dispatcher.CheckAccess()) { Dispatcher.Invoke(() => SetLable(type)); return; }
- StatusDot.Visibility = Visibility.Visible;
- switch (type)
- {
- case HalconResultType.OK: StatusDot.Fill = new SolidColorBrush(Color.FromRgb(0x4C, 0xAF, 0x50)); break;
- case HalconResultType.NG: StatusDot.Fill = new SolidColorBrush(Color.FromRgb(0xF4, 0x43, 0x36)); break;
- default: StatusDot.Fill = new SolidColorBrush(Color.FromRgb(0x88, 0x88, 0x88)); break;
- }
- }
- /// <summary>
- /// 【经典模式】刷新显示:image=图像(HObject/GenImage/Bitmap),overlay=可选叠加对象(区域/XLD,红色边界绘制)。
- /// 从流程传入的 HObject 只引用不释放;从 GenImage 转换的副本由本窗体负责释放。
- /// </summary>
- public void RefreshImage(object image, object overlay = null)
- {
- if (!Dispatcher.CheckAccess()) { Dispatcher.Invoke(() => RefreshImage(image, overlay)); return; }
- // 经典调用接管显示:退出图层模式
- ExitLayerMode();
- var hw = Hwn;
- if (hw == null) return;
- // image 为 null:仅更新叠加层(保留当前底图),供只产出区域的节点使用
- if (image == null)
- {
- if (overlay == null) return;
- try
- {
- bool ovOwned = !(overlay is HObject);
- var hover = HalconRuntime.ToHObject(overlay);
- if (_overlayOwned) HalconRuntime.Dispose(_overlay);
- _overlay = hover; _overlayOwned = ovOwned;
- Redraw();
- }
- catch { }
- return;
- }
- try
- {
- bool imgOwned = !(image is HObject);
- var himg = HalconRuntime.ToHObject(image);
- HObject hover = null;
- bool overOwned = false;
- if (overlay != null)
- {
- overOwned = !(overlay is HObject);
- try { hover = HalconRuntime.ToHObject(overlay); } catch { hover = null; }
- }
- HOperatorSet.GetImageSize(himg, out HTuple wT, out HTuple hT);
- int w = wT.I, h = hT.I;
- // 释放上一帧「本窗体自有」的副本(流程对象不动)
- if (_imageOwned) HalconRuntime.Dispose(_image);
- if (_overlayOwned) HalconRuntime.Dispose(_overlay);
- _image = himg; _imageOwned = imgOwned;
- _overlay = hover; _overlayOwned = overOwned;
- _hasImage = true;
- bool needFit = w != _imgW || h != _imgH;
- _imgW = w; _imgH = h;
- if (needFit) FitPart();
- Redraw();
- }
- catch { /* 显示失败不影响流程 */ }
- }
- /// <summary>清空画面显示。</summary>
- public void ClearImage()
- {
- if (!Dispatcher.CheckAccess()) { Dispatcher.Invoke(new Action(ClearImage)); return; }
- try
- {
- ExitLayerMode();
- if (_imageOwned) HalconRuntime.Dispose(_image);
- if (_overlayOwned) HalconRuntime.Dispose(_overlay);
- _image = null; _overlay = null; _hasImage = false; _imgW = _imgH = 0;
- Hwn?.ClearWindow();
- }
- catch { }
- }
- #endregion
- #region 图层显示(编辑器大显示窗 / 图层筛选)
- /// <summary>
- /// 【图层模式】按图层集渲染:首个 Image 图层作底图,其余按 LayerItem.Color(缺省调色板)叠加。
- /// 图层对象的「注册表自有」部分只引用不释放;本窗体从 GenImage 转换出的副本在下次刷新/清理时释放。
- /// </summary>
- public void ShowLayers(IReadOnlyList<TeamAAS.Vision.LayerItem> layers)
- {
- if (!Dispatcher.CheckAccess()) { Dispatcher.Invoke(() => ShowLayers(layers)); return; }
- ExitLayerMode();
- var hw = Hwn;
- if (hw == null || layers == null || layers.Count == 0) return;
- try
- {
- foreach (var layer in layers)
- {
- if (layer == null || layer.Content == null) continue;
- if (layer.Kind == TeamAAS.Vision.LayerKind.Image)
- {
- bool owned = !(layer.Content is HObject);
- var himg = HalconRuntime.ToHObject(layer.Content);
- if (owned) _layerOwnedCopies.Add(himg);
- if (_layerBaseOwned && _layerBaseImage != null) HalconRuntime.Dispose(_layerBaseImage);
- _layerBaseImage = himg;
- _layerBaseOwned = owned;
- }
- else
- {
- bool owned = !(layer.Content is HObject);
- var hobj = HalconRuntime.ToHObject(layer.Content);
- if (owned) _layerOwnedCopies.Add(hobj);
- var color = string.IsNullOrWhiteSpace(layer.Color)
- ? LayerPalette[_layerOverlays.Count % LayerPalette.Length]
- : layer.Color;
- _layerOverlays.Add(new KeyValuePair<HObject, string>(hobj, color));
- }
- }
- if (_layerBaseImage != null && _layerBaseImage.IsInitialized())
- {
- HOperatorSet.GetImageSize(_layerBaseImage, out HTuple wT, out HTuple hT);
- int w = wT.I, h = hT.I;
- bool needFit = w != _layerImgW || h != _layerImgH;
- _layerImgW = w; _layerImgH = h;
- if (needFit) FitPart();
- }
- _layerMode = true;
- Redraw();
- }
- catch
- {
- ExitLayerMode();
- }
- }
- /// <summary>退出图层模式:释放本窗体自有的图层副本,恢复经典状态。</summary>
- private void ExitLayerMode()
- {
- _layerMode = false;
- foreach (var copy in _layerOwnedCopies)
- HalconRuntime.Dispose(copy);
- _layerOwnedCopies.Clear();
- if (_layerBaseOwned) HalconRuntime.Dispose(_layerBaseImage);
- _layerBaseImage = null; _layerBaseOwned = false;
- _layerOverlays.Clear();
- _layerImgW = _layerImgH = 0;
- }
- /// <summary>图层模式重绘:底图 + 多彩叠加。</summary>
- private void RedrawLayers()
- {
- var hw = Hwn;
- if (hw == null) return;
- try
- {
- HOperatorSet.SetSystem("flush_graphic", "false");
- hw.ClearWindow();
- bool hasBase = _layerBaseImage != null && _layerBaseImage.IsInitialized();
- HObject dispBase = hasBase ? ToDisplayImage(_layerBaseImage) : null; // 位深归一化显示
- if (hasBase) { hw.SetColor("default"); hw.SetDraw("fill"); hw.DispObj(dispBase); }
- foreach (var kv in _layerOverlays)
- {
- if (kv.Key == null || !kv.Key.IsInitialized()) continue;
- hw.SetColor(string.IsNullOrWhiteSpace(kv.Value) ? "red" : kv.Value);
- hw.SetDraw("margin");
- hw.SetLineWidth(2.0);
- hw.DispObj(kv.Key);
- }
- HOperatorSet.SetSystem("flush_graphic", "true");
- if (dispBase != null && !ReferenceEquals(dispBase, _layerBaseImage)) dispBase.Dispose();
- }
- catch { }
- }
- #endregion
- #region 显示绘制
- /// <summary>按当前模式重绘(保持当前缩放/平移的 part)。</summary>
- private void Redraw()
- {
- if (_layerMode) { RedrawLayers(); return; }
- var hw = Hwn;
- if (hw == null) return;
- bool hasImg = _image != null && _image.IsInitialized();
- bool hasOv = _overlay != null && _overlay.IsInitialized();
- if (!hasImg && !hasOv) return;
- try
- {
- HOperatorSet.SetSystem("flush_graphic", "false");
- hw.ClearWindow();
- HObject dispImg = hasImg ? ToDisplayImage(_image) : null; // 位深归一化显示
- if (hasImg) { hw.SetDraw("fill"); hw.DispObj(dispImg); }
- if (hasOv)
- {
- hw.SetColor("red");
- hw.SetDraw("margin");
- hw.SetLineWidth(2.0);
- hw.DispObj(_overlay);
- }
- HOperatorSet.SetSystem("flush_graphic", "true");
- if (dispImg != null && !ReferenceEquals(dispImg, _image)) dispImg.Dispose();
- }
- catch { }
- }
- /// <summary>按当前模式的图像尺寸做等比适配。</summary>
- private void FitCurrent()
- {
- if (_layerMode) { FitImage(_layerImgW, _layerImgH); return; }
- FitImage(_imgW, _imgH);
- }
- /// <summary>按图像尺寸与控件尺寸做等比适配(set_part),不拉伸变形。</summary>
- private void FitPart()
- {
- FitImage(_layerMode ? _layerImgW : _imgW, _layerMode ? _layerImgH : _imgH);
- }
- // 首次显示时控件可能尚未完成布局(ActualWidth=0),此时 Fit 只能挂起,
- // 等布局完成补做 —— 否则显示区域(part)停在默认值,大图只露出某个角落,肉眼上"全黑"
- private bool _pendingFit;
- private void FitImage(int imgW, int imgH)
- {
- var hw = Hwn;
- if (hw == null || imgW <= 0 || imgH <= 0) return;
- double W = MyHwindow?.ActualWidth ?? 0, H = MyHwindow?.ActualHeight ?? 0;
- if (W <= 1 || H <= 1)
- {
- if (!_pendingFit)
- {
- _pendingFit = true;
- MyHwindow.LayoutUpdated += OnLayoutPendingFit;
- }
- return;
- }
- _pendingFit = false;
- try
- {
- double winAspect = W / H;
- double imgAspect = (double)imgW / imgH;
- double partW, partH;
- if (imgAspect > winAspect) { partW = imgW; partH = imgW / winAspect; }
- else { partH = imgH; partW = imgH * winAspect; }
- double r1 = -(partH - imgH) / 2.0;
- double c1 = -(partW - imgW) / 2.0;
- hw.SetPart(r1, c1, r1 + partH - 1, c1 + partW - 1);
- }
- catch { }
- }
- /// <summary>布局完成后补做挂起的适配(回到正确显示区域)。</summary>
- private void OnLayoutPendingFit(object sender, EventArgs e)
- {
- if (!_pendingFit) { MyHwindow.LayoutUpdated -= OnLayoutPendingFit; return; }
- int imgW = _layerMode ? _layerImgW : _imgW;
- int imgH = _layerMode ? _layerImgH : _imgH;
- if (imgW <= 0 || MyHwindow == null || MyHwindow.ActualWidth <= 1) return;
- _pendingFit = false;
- MyHwindow.LayoutUpdated -= OnLayoutPendingFit;
- FitCurrent();
- Redraw();
- }
- /// <summary>
- /// 显示用规范化:uint2/int2/int4/real 图像按实际灰度范围拉伸到 0-255 再转 byte。
- /// 背景:HALCON 显示 uint2 时灰度 100 只相当于位深的 0.15%,肉眼上"整幅是黑的",
- /// 而灰度读数(直接取像素值)完全正常 —— 16 位相机图像黑屏即此原因。byte 原样返回。
- /// </summary>
- private static HObject ToDisplayImage(HObject img)
- {
- try
- {
- HOperatorSet.GetImageType(img, out HTuple t);
- var type = t.S;
- if (type == "byte" || type == "complex" || type == "vector_field") return img;
- HOperatorSet.MinMaxGray(img, img, 0, out HTuple mn, out HTuple mx, out HTuple _);
- double min = mn.D, range = mx.D - min;
- if (range < 1) range = 1;
- double scale = 255.0 / range;
- HOperatorSet.ScaleImage(img, out HObject scaled, scale, -min * scale);
- HOperatorSet.ConvertImageType(scaled, out HObject byteImg, "byte");
- scaled.Dispose();
- return byteImg;
- }
- catch { return img; }
- }
- #endregion
- #region 鼠标读取 / 拖放 / 双击
- private void MyHwindow_HMouseMove(object sender, HSmartWindowControlWPF.HMouseEventArgsWPF e)
- {
- try
- {
- int row = (int)e.Row, col = (int)e.Column;
- string txt = $"行:{row} 列:{col}";
- var img = _layerMode ? _layerBaseImage : _image;
- int imgH = _layerMode ? _layerImgH : _imgH;
- int imgW = _layerMode ? _layerImgW : _imgW;
- if (img != null && img.IsInitialized() &&
- row >= 0 && col >= 0 && row < imgH && col < imgW)
- {
- try
- {
- HOperatorSet.GetGrayval(img, row, col, out HTuple gray);
- txt += $" 灰度:{gray.D:F0}";
- }
- catch { }
- }
- CoordText.Text = txt;
- CoordText.Visibility = Visibility.Visible;
- }
- catch { }
- }
- private void MyHwindow_MouseLeave(object sender, MouseEventArgs e) => CoordText.Visibility = Visibility.Collapsed;
- private void MyHwindow_DragEnter(object sender, DragEventArgs e)
- => e.Effects = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None;
- private void MyHwindow_Drop(object sender, DragEventArgs e)
- {
- try
- {
- if (e.Data.GetData(DataFormats.FileDrop) is string[] files && files.Length > 0)
- {
- HOperatorSet.ReadImage(out HObject img, files[0]);
- RefreshImage(img); // 拖入的图像由本窗体持有
- _imageOwned = true;
- }
- }
- catch { }
- }
- private void UserControl_MouseDoubleClick(object sender, MouseButtonEventArgs e)
- => MouseDouble?.Invoke(this, Number);
- // 绘制 ROI 时右键用于「确认」,此时抑制右键菜单;非绘制态恢复菜单
- private void MyHwindow_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
- {
- if (_drawingBusy) MyHwindow.ContextMenu = null;
- else MyHwindow.ContextMenu = MyMenu;
- }
- #endregion
- #region 右键菜单:适应 / 保存
- private void MenuFit_Click(object sender, RoutedEventArgs e) { FitCurrent(); Redraw(); }
- private void MenuZoomIn_Click(object sender, RoutedEventArgs e) => ZoomBy(0.8); // part 变小 = 放大
- private void MenuZoomOut_Click(object sender, RoutedEventArgs e) => ZoomBy(1.25); // part 变大 = 缩小
- /// <summary>
- /// 以画面中心为基准缩放:factor<1 放大、>1 缩小。读写窗口 part(GetPart/SetPart),
- /// 与 HSmartWindowControlWPF 内置的鼠标滚轮缩放共用同一 part 状态,不冲突。
- /// </summary>
- public void ZoomBy(double factor)
- {
- var hw = Hwn;
- int refH = _layerMode ? _layerImgH : _imgH;
- int refW = _layerMode ? _layerImgW : _imgW;
- bool hasAny = _layerMode ? (_layerBaseImage != null && _layerBaseImage.IsInitialized()) : _hasImage;
- if (hw == null || !hasAny) return;
- try
- {
- hw.GetPart(out int r1, out int c1, out int r2, out int c2);
- double h = r2 - r1, w = c2 - c1;
- double nh = h * factor, nw = w * factor;
- if (nh < 4 || nw < 4) return; // 放大上限保护
- if (nh > refH * 20 || nw > refW * 20) return; // 缩小上限保护
- double cr = (r1 + r2) / 2.0, cc = (c1 + c2) / 2.0;
- hw.SetPart(cr - nh / 2, cc - nw / 2, cr + nh / 2, cc + nw / 2);
- Redraw();
- }
- catch { }
- }
- private void MenuSave_Click(object sender, RoutedEventArgs e)
- {
- var img = _layerMode ? _layerBaseImage : _image;
- if (img == null || !img.IsInitialized()) return;
- var sfd = new Microsoft.Win32.SaveFileDialog
- {
- Filter = "PNG 图像|*.png|BMP 图像|*.bmp|TIFF 图像|*.tif|JPEG 图像|*.jpg",
- FileName = "halcon_image"
- };
- if (sfd.ShowDialog() != true) return;
- try
- {
- string ext = Path.GetExtension(sfd.FileName).ToLowerInvariant();
- string fmt = ext == ".bmp" ? "bmp" : ext == ".tif" || ext == ".tiff" ? "tiff" : ext == ".jpg" || ext == ".jpeg" ? "jpeg" : "png";
- string noExt = Path.Combine(Path.GetDirectoryName(sfd.FileName) ?? "", Path.GetFileNameWithoutExtension(sfd.FileName));
- HOperatorSet.WriteImage(img, fmt, 0, noExt);
- }
- catch (Exception ex) { MessageBox.Show($"保存失败:{ex.Message}"); }
- }
- #endregion
- #region 交互 ROI 绘制(HDrawingObject:左键拖动调整,右键确认)
- private void MenuRoiRect1_Click(object sender, RoutedEventArgs e) => _ = DrawAsync(Models.HalconRoiType.Rectangle1);
- private void MenuRoiRect2_Click(object sender, RoutedEventArgs e) => _ = DrawAsync(Models.HalconRoiType.Rectangle2);
- private void MenuRoiCircle_Click(object sender, RoutedEventArgs e) => _ = DrawAsync(Models.HalconRoiType.Circle);
- private void MenuRoiClear_Click(object sender, RoutedEventArgs e)
- {
- LastRoi = null;
- DetachDrawing();
- Redraw();
- }
- private async Task DrawAsync(Models.HalconRoiType type)
- {
- var hw = Hwn;
- var baseImg = _layerMode ? _layerBaseImage : _image;
- if (_drawingBusy || hw == null || baseImg == null || !baseImg.IsInitialized()) return;
- _drawingBusy = true;
- MyHwindow.ContextMenu = null;
- try
- {
- DetachDrawing();
- _drawing = new HDrawingObject();
- double h = _layerMode ? _layerImgH : _imgH;
- double w = _layerMode ? _layerImgW : _imgW;
- switch (type)
- {
- case Models.HalconRoiType.Rectangle1:
- _drawing.CreateDrawingObjectRectangle1(h * 0.25, w * 0.25, h * 0.75, w * 0.75);
- break;
- case Models.HalconRoiType.Rectangle2:
- _drawing.CreateDrawingObjectRectangle2(h * 0.5, w * 0.5, 0, Math.Min(w, h) * 0.25, Math.Min(w, h) * 0.15);
- break;
- case Models.HalconRoiType.Circle:
- _drawing.CreateDrawingObjectCircle(h * 0.5, w * 0.5, Math.Min(w, h) * 0.25);
- break;
- }
- _drawing.SetDrawingObjectParams("line_width", 2.5);
- _drawing.SetDrawingObjectParams("color", "green");
- hw.AttachDrawingObjectToWindow(_drawing);
- // 后台等待右键确认(左键用于拖动 ROI 的控制点)
- await Task.Run(() =>
- {
- int button = 0;
- while (true)
- {
- hw.GetMbutton(out int r, out int c, out button);
- if (button == 1) { while (button == 1) hw.GetMposition(out r, out c, out button); }
- else break;
- }
- });
- LastRoi = ReadRoi(type);
- }
- catch { }
- finally
- {
- DetachDrawing();
- _drawingBusy = false;
- MyHwindow.ContextMenu = MyMenu;
- Redraw();
- }
- }
- private Models.HalconRoi ReadRoi(Models.HalconRoiType type)
- {
- if (_drawing == null) return null;
- var roi = new Models.HalconRoi { Type = type };
- switch (type)
- {
- case Models.HalconRoiType.Rectangle1:
- roi.Row1 = _drawing.GetDrawingObjectParams("row1").D;
- roi.Col1 = _drawing.GetDrawingObjectParams("column1").D;
- roi.Row2 = _drawing.GetDrawingObjectParams("row2").D;
- roi.Col2 = _drawing.GetDrawingObjectParams("column2").D;
- break;
- case Models.HalconRoiType.Rectangle2:
- roi.Row = _drawing.GetDrawingObjectParams("row").D;
- roi.Col = _drawing.GetDrawingObjectParams("column").D;
- roi.Phi = _drawing.GetDrawingObjectParams("phi").D;
- roi.Length1 = _drawing.GetDrawingObjectParams("length1").D;
- roi.Length2 = _drawing.GetDrawingObjectParams("length2").D;
- break;
- case Models.HalconRoiType.Circle:
- roi.Row = _drawing.GetDrawingObjectParams("row").D;
- roi.Col = _drawing.GetDrawingObjectParams("column").D;
- roi.Radius = _drawing.GetDrawingObjectParams("radius").D;
- break;
- }
- return roi;
- }
- private void DetachDrawing()
- {
- try
- {
- if (_drawing != null)
- {
- Hwn?.DetachDrawingObjectFromWindow(_drawing);
- _drawing.Dispose();
- }
- }
- catch { }
- finally { _drawing = null; }
- }
- #endregion
- }
- }
|