diff --git a/.gitignore b/.gitignore index 4e446be..c0743c1 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ bld/ [Ll]ogs/ lib/ XP.ImageProcessing/ +XP.ImageProcessing.SmokeTest/ ImageProcessing.sln # 排除 Libs 目录中的 DLL 和 PDB 文件(但保留目录结构) diff --git a/XP.Camera/Calibration/Controls/CalibrationControl.xaml b/XP.Camera/Calibration/Controls/CalibrationControl.xaml index 9c12e48..a5b1d88 100644 --- a/XP.Camera/Calibration/Controls/CalibrationControl.xaml +++ b/XP.Camera/Calibration/Controls/CalibrationControl.xaml @@ -5,6 +5,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:cal="clr-namespace:XP.Camera.Calibration" xmlns:controls="clr-namespace:XP.Camera.Calibration.Controls" + xmlns:roi="clr-namespace:XP.ImageProcessing.RoiControl.Controls;assembly=XP.ImageProcessing.RoiControl" mc:Ignorable="d" d:DesignHeight="850" d:DesignWidth="1400"> @@ -88,6 +89,20 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/XplorePlane/Views/ImageProcessing/QfnLeadPadDetectionPanel.xaml.cs b/XplorePlane/Views/ImageProcessing/QfnLeadPadDetectionPanel.xaml.cs new file mode 100644 index 0000000..6931fe4 --- /dev/null +++ b/XplorePlane/Views/ImageProcessing/QfnLeadPadDetectionPanel.xaml.cs @@ -0,0 +1,59 @@ +using System.Windows; +using Prism.Ioc; +using XP.ImageProcessing.RoiControl.Controls; +using XplorePlane.Services.MainViewport; +using XplorePlane.ViewModels.Cnc; +using XplorePlane.ViewModels.ImageProcessing; + +namespace XplorePlane.Views.ImageProcessing +{ + public partial class QfnLeadPadDetectionPanel : Window + { + public QfnLeadPadDetectionPanel() + { + InitializeComponent(); + var viewportService = ContainerLocator.Current?.Resolve(); + DataContext = new QfnLeadPadDetectionViewModel(viewportService); + + Loaded += (s, e) => + { + var mainWin = Owner as MainWindow; + if (mainWin != null) + { + var canvas = FindChild(mainWin); + if (DataContext is QfnLeadPadDetectionViewModel vm) + vm.SetCanvas(canvas); + } + + if (DataContext is QfnLeadPadDetectionViewModel qfnVm && Owner?.DataContext is ViewModels.MainViewModel mainVm) + { + var cncEditorField = mainVm.GetType().GetField("_cncEditorViewModel", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + if (cncEditorField?.GetValue(mainVm) is CncEditorViewModel cncEditor) + qfnVm.SetCncEditorViewModel(cncEditor); + } + }; + + Closed += (s, e) => + { + if (DataContext is QfnLeadPadDetectionViewModel vm) + vm.RestoreContextMenu(); + }; + } + + private void Close_Click(object sender, RoutedEventArgs e) => Close(); + + private static T FindChild(DependencyObject parent) where T : DependencyObject + { + int count = System.Windows.Media.VisualTreeHelper.GetChildrenCount(parent); + for (int i = 0; i < count; i++) + { + var child = System.Windows.Media.VisualTreeHelper.GetChild(parent, i); + if (child is T t) return t; + var result = FindChild(child); + if (result != null) return result; + } + return null; + } + } +} diff --git a/XplorePlane/Views/Main/MainWindow.xaml b/XplorePlane/Views/Main/MainWindow.xaml index 9f4e372..818d709 100644 --- a/XplorePlane/Views/Main/MainWindow.xaml +++ b/XplorePlane/Views/Main/MainWindow.xaml @@ -181,11 +181,11 @@ + Text="线灰度" /> + diff --git a/XplorePlane/Views/Main/ViewportPanelView.xaml.cs b/XplorePlane/Views/Main/ViewportPanelView.xaml.cs index f0957da..80fb852 100644 --- a/XplorePlane/Views/Main/ViewportPanelView.xaml.cs +++ b/XplorePlane/Views/Main/ViewportPanelView.xaml.cs @@ -187,6 +187,14 @@ namespace XplorePlane.Views ToggleLineProfile(); }, Prism.Events.ThreadOption.UIThread); + // 图像变化时重绘线灰度 + var canvasWidthDesc = System.ComponentModel.DependencyPropertyDescriptor.FromProperty( + PolygonRoiCanvas.CanvasWidthProperty, typeof(PolygonRoiCanvas)); + canvasWidthDesc?.AddValueChanged(RoiCanvas, (s, e) => + { + if (_lineProfileEnabled) RedrawLineProfile(); + }); + // 白底检测:进入ROI绘制模式 ea2?.GetEvent().Subscribe(() => { @@ -256,8 +264,12 @@ namespace XplorePlane.Views // 参考线默认在图像中间 _profileLineY = RoiCanvas.CanvasHeight / 2; + // 根据图像分辨率自适应线条粗细 + double maxDim = Math.Max(RoiCanvas.CanvasWidth, RoiCanvas.CanvasHeight); + double lineThickness = Math.Max(1, Math.Round(maxDim / 1000.0)); + // 创建参考线(红色水平线,可拖动) - // 用透明粗线作为命中区域,叠加1px红线显示 + // 用透明粗线作为命中区域,叠加红线显示 _profileRefLine = new System.Windows.Shapes.Line { X1 = 0, @@ -265,7 +277,7 @@ namespace XplorePlane.Views X2 = RoiCanvas.CanvasWidth, Y2 = _profileLineY, Stroke = System.Windows.Media.Brushes.Transparent, - StrokeThickness = 7, // 上下3px命中区域 + StrokeThickness = lineThickness + 6, // 上下命中区域 IsHitTestVisible = true, Cursor = System.Windows.Input.Cursors.SizeNS }; @@ -276,7 +288,7 @@ namespace XplorePlane.Views X2 = RoiCanvas.CanvasWidth, Y2 = _profileLineY, Stroke = System.Windows.Media.Brushes.Red, - StrokeThickness = 1, + StrokeThickness = lineThickness, IsHitTestVisible = false }; _profileRefLine.MouseLeftButtonDown += ProfileLine_MouseDown; @@ -289,7 +301,7 @@ namespace XplorePlane.Views _profileCurve = new System.Windows.Shapes.Polyline { Stroke = System.Windows.Media.Brushes.Red, - StrokeThickness = 1, + StrokeThickness = lineThickness, IsHitTestVisible = false }; canvas.Children.Add(_profileCurve); @@ -393,6 +405,64 @@ namespace XplorePlane.Views SetStatus($"行灰度分布 | Y={row} | 均值={rowPixels.Select(b => (double)b).Average():F1} | 最大={rowPixels.Max()} | 最小={rowPixels.Min()}"); } + /// + /// 图像变化时重绘线灰度(移除旧元素,重新创建) + /// + private void RedrawLineProfile() + { + var canvas = FindChildByName(RoiCanvas, "mainCanvas"); + if (canvas == null) return; + + // 移除旧元素 + if (_profileRefLine != null) { canvas.Children.Remove(_profileRefLine); _profileRefLine = null; } + if (_profileRefLineVisible != null) { canvas.Children.Remove(_profileRefLineVisible); _profileRefLineVisible = null; } + if (_profileCurve != null) { canvas.Children.Remove(_profileCurve); _profileCurve = null; } + + // 重新计算参考线位置(保持相对比例或重置到中间) + _profileLineY = RoiCanvas.CanvasHeight / 2; + + // 根据新图像分辨率自适应线条粗细 + double maxDim = Math.Max(RoiCanvas.CanvasWidth, RoiCanvas.CanvasHeight); + double lineThickness = Math.Max(1, Math.Round(maxDim / 1000.0)); + + _profileRefLine = new System.Windows.Shapes.Line + { + X1 = 0, + Y1 = _profileLineY, + X2 = RoiCanvas.CanvasWidth, + Y2 = _profileLineY, + Stroke = System.Windows.Media.Brushes.Transparent, + StrokeThickness = lineThickness + 6, + IsHitTestVisible = true, + Cursor = System.Windows.Input.Cursors.SizeNS + }; + _profileRefLineVisible = new System.Windows.Shapes.Line + { + X1 = 0, + Y1 = _profileLineY, + X2 = RoiCanvas.CanvasWidth, + Y2 = _profileLineY, + Stroke = System.Windows.Media.Brushes.Red, + StrokeThickness = lineThickness, + IsHitTestVisible = false + }; + _profileRefLine.MouseLeftButtonDown += ProfileLine_MouseDown; + _profileRefLine.MouseMove += ProfileLine_MouseMove; + _profileRefLine.MouseLeftButtonUp += ProfileLine_MouseUp; + canvas.Children.Add(_profileRefLineVisible); + canvas.Children.Add(_profileRefLine); + + _profileCurve = new System.Windows.Shapes.Polyline + { + Stroke = System.Windows.Media.Brushes.Red, + StrokeThickness = lineThickness, + IsHitTestVisible = false + }; + canvas.Children.Add(_profileCurve); + + UpdateLineProfile(); + } + #endregion private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)