feat(viewport): 黑底检测与白/黑底结果随清除测量一并清除
- 新增黑底检测 Prism 事件与 MainViewModel 中 Otsu 二值化(Binary)流程,与白底(BinaryInv)对称。 - Viewport 统一 ROI 绘制与结果渲染;右键「清除所有测量」同时移除底色检测叠加层并复位 ROI 状态。
This commit is contained in:
@@ -142,21 +142,29 @@ namespace XplorePlane.Views
|
||||
// 白底检测:进入ROI绘制模式
|
||||
ea2?.GetEvent<WhiteBackgroundDetectionEvent>().Subscribe(() =>
|
||||
{
|
||||
_whiteDetectDrawing = false;
|
||||
_whiteDetectMode = true;
|
||||
// 注册鼠标事件(只注册一次)
|
||||
RoiCanvas.PreviewMouseLeftButtonDown -= OnMainCanvasPreviewMouseDown;
|
||||
RoiCanvas.PreviewMouseLeftButtonDown += OnMainCanvasPreviewMouseDown;
|
||||
RoiCanvas.PreviewMouseMove -= OnMainCanvasPreviewMouseMove;
|
||||
RoiCanvas.PreviewMouseMove += OnMainCanvasPreviewMouseMove;
|
||||
RoiCanvas.PreviewMouseLeftButtonUp -= OnMainCanvasPreviewMouseUp;
|
||||
RoiCanvas.PreviewMouseLeftButtonUp += OnMainCanvasPreviewMouseUp;
|
||||
_bgDefectDrawing = false;
|
||||
_bgDefectRoiMode = BackgroundDefectRoiMode.WhiteBackground;
|
||||
RegisterBackgroundDefectRoiMouseHandlers();
|
||||
}, Prism.Events.ThreadOption.UIThread);
|
||||
|
||||
// 黑底检测:进入ROI绘制模式
|
||||
ea2?.GetEvent<BlackBackgroundDetectionEvent>().Subscribe(() =>
|
||||
{
|
||||
_bgDefectDrawing = false;
|
||||
_bgDefectRoiMode = BackgroundDefectRoiMode.BlackBackground;
|
||||
RegisterBackgroundDefectRoiMouseHandlers();
|
||||
}, Prism.Events.ThreadOption.UIThread);
|
||||
|
||||
// 白底检测:渲染结果
|
||||
ea2?.GetEvent<WhiteBackgroundResultEvent>().Subscribe(payload =>
|
||||
{
|
||||
RenderWhiteBackgroundResult(payload);
|
||||
RenderBackgroundDefectResult(payload.RoiRect, payload.Detections);
|
||||
}, Prism.Events.ThreadOption.UIThread);
|
||||
|
||||
// 黑底检测:渲染结果
|
||||
ea2?.GetEvent<BlackBackgroundResultEvent>().Subscribe(payload =>
|
||||
{
|
||||
RenderBackgroundDefectResult(payload.RoiRect, payload.Detections);
|
||||
}, Prism.Events.ThreadOption.UIThread);
|
||||
}
|
||||
catch { }
|
||||
@@ -347,7 +355,25 @@ namespace XplorePlane.Views
|
||||
RoiCanvas.SelectedROI = null;
|
||||
if (DataContext is ViewportPanelViewModel vm)
|
||||
vm.ResetMeasurementState();
|
||||
SetStatus("已清除所有测量");
|
||||
|
||||
var canvas = FindChildByName<System.Windows.Controls.Canvas>(RoiCanvas, "mainCanvas");
|
||||
if (canvas != null)
|
||||
{
|
||||
if (_bgDefectPreview != null)
|
||||
{
|
||||
canvas.Children.Remove(_bgDefectPreview);
|
||||
_bgDefectPreview = null;
|
||||
}
|
||||
ClearBackgroundDefectOverlays(canvas);
|
||||
}
|
||||
else
|
||||
_bgDefectOverlays.Clear();
|
||||
|
||||
_bgDefectDrawing = false;
|
||||
_bgDefectRoiMode = BackgroundDefectRoiMode.None;
|
||||
try { RoiCanvas.ReleaseMouseCapture(); } catch { /* 未捕获则无影响 */ }
|
||||
|
||||
SetStatus("已清除所有测量及白底/黑底检测结果");
|
||||
}
|
||||
|
||||
private void SaveOriginalImage_Click(object sender, RoutedEventArgs e)
|
||||
@@ -400,37 +426,54 @@ namespace XplorePlane.Views
|
||||
|
||||
#endregion
|
||||
|
||||
#region 白底检测
|
||||
#region 白底/黑底检测
|
||||
|
||||
private bool _whiteDetectMode;
|
||||
private bool _whiteDetectDrawing;
|
||||
private System.Windows.Point _whiteDetectStart;
|
||||
private System.Windows.Shapes.Rectangle _whiteDetectPreview;
|
||||
private readonly System.Collections.Generic.List<System.Windows.UIElement> _whiteDetectOverlays = new();
|
||||
private enum BackgroundDefectRoiMode
|
||||
{
|
||||
None,
|
||||
WhiteBackground,
|
||||
BlackBackground
|
||||
}
|
||||
|
||||
private BackgroundDefectRoiMode _bgDefectRoiMode;
|
||||
private bool _bgDefectDrawing;
|
||||
private System.Windows.Point _bgDefectStart;
|
||||
private System.Windows.Shapes.Rectangle _bgDefectPreview;
|
||||
private readonly System.Collections.Generic.List<System.Windows.UIElement> _bgDefectOverlays = new();
|
||||
private bool _bgDefectMouseHandlersRegistered;
|
||||
|
||||
private void RegisterBackgroundDefectRoiMouseHandlers()
|
||||
{
|
||||
if (_bgDefectMouseHandlersRegistered) return;
|
||||
RoiCanvas.PreviewMouseLeftButtonDown += OnMainCanvasPreviewMouseDown;
|
||||
RoiCanvas.PreviewMouseMove += OnMainCanvasPreviewMouseMove;
|
||||
RoiCanvas.PreviewMouseLeftButtonUp += OnMainCanvasPreviewMouseUp;
|
||||
_bgDefectMouseHandlersRegistered = true;
|
||||
}
|
||||
|
||||
// 需要在 mainCanvas 的 MouseDown/Move/Up 中处理
|
||||
// 由于 PolygonRoiCanvas 内部已经处理了鼠标事件,我们通过 PreviewMouse 事件来拦截
|
||||
|
||||
private void OnMainCanvasPreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
if (!_whiteDetectMode || e.LeftButton != System.Windows.Input.MouseButtonState.Pressed) return;
|
||||
if (_bgDefectRoiMode == BackgroundDefectRoiMode.None || e.LeftButton != System.Windows.Input.MouseButtonState.Pressed) return;
|
||||
|
||||
var canvas = FindChildByName<System.Windows.Controls.Canvas>(RoiCanvas, "mainCanvas");
|
||||
if (canvas == null) return;
|
||||
|
||||
_whiteDetectStart = e.GetPosition(canvas);
|
||||
_whiteDetectDrawing = true;
|
||||
_bgDefectStart = e.GetPosition(canvas);
|
||||
_bgDefectDrawing = true;
|
||||
|
||||
// 创建预览矩形(不清除之前的检测结果)
|
||||
_whiteDetectPreview = new System.Windows.Shapes.Rectangle
|
||||
_bgDefectPreview = new System.Windows.Shapes.Rectangle
|
||||
{
|
||||
Stroke = System.Windows.Media.Brushes.Blue,
|
||||
StrokeThickness = 1,
|
||||
StrokeDashArray = new System.Windows.Media.DoubleCollection { 4, 2 }
|
||||
};
|
||||
System.Windows.Controls.Canvas.SetLeft(_whiteDetectPreview, _whiteDetectStart.X);
|
||||
System.Windows.Controls.Canvas.SetTop(_whiteDetectPreview, _whiteDetectStart.Y);
|
||||
canvas.Children.Add(_whiteDetectPreview);
|
||||
System.Windows.Controls.Canvas.SetLeft(_bgDefectPreview, _bgDefectStart.X);
|
||||
System.Windows.Controls.Canvas.SetTop(_bgDefectPreview, _bgDefectStart.Y);
|
||||
canvas.Children.Add(_bgDefectPreview);
|
||||
|
||||
RoiCanvas.CaptureMouse();
|
||||
e.Handled = true;
|
||||
@@ -438,45 +481,46 @@ namespace XplorePlane.Views
|
||||
|
||||
private void OnMainCanvasPreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
|
||||
{
|
||||
if (!_whiteDetectDrawing || _whiteDetectPreview == null) return;
|
||||
if (!_bgDefectDrawing || _bgDefectPreview == null) return;
|
||||
|
||||
var canvas = FindChildByName<System.Windows.Controls.Canvas>(RoiCanvas, "mainCanvas");
|
||||
if (canvas == null) return;
|
||||
|
||||
var current = e.GetPosition(canvas);
|
||||
double x = Math.Min(_whiteDetectStart.X, current.X);
|
||||
double y = Math.Min(_whiteDetectStart.Y, current.Y);
|
||||
double w = Math.Abs(current.X - _whiteDetectStart.X);
|
||||
double h = Math.Abs(current.Y - _whiteDetectStart.Y);
|
||||
double x = Math.Min(_bgDefectStart.X, current.X);
|
||||
double y = Math.Min(_bgDefectStart.Y, current.Y);
|
||||
double w = Math.Abs(current.X - _bgDefectStart.X);
|
||||
double h = Math.Abs(current.Y - _bgDefectStart.Y);
|
||||
|
||||
System.Windows.Controls.Canvas.SetLeft(_whiteDetectPreview, x);
|
||||
System.Windows.Controls.Canvas.SetTop(_whiteDetectPreview, y);
|
||||
_whiteDetectPreview.Width = Math.Max(1, w);
|
||||
_whiteDetectPreview.Height = Math.Max(1, h);
|
||||
System.Windows.Controls.Canvas.SetLeft(_bgDefectPreview, x);
|
||||
System.Windows.Controls.Canvas.SetTop(_bgDefectPreview, y);
|
||||
_bgDefectPreview.Width = Math.Max(1, w);
|
||||
_bgDefectPreview.Height = Math.Max(1, h);
|
||||
}
|
||||
|
||||
private void OnMainCanvasPreviewMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
if (!_whiteDetectDrawing) return;
|
||||
if (!_bgDefectDrawing) return;
|
||||
|
||||
_whiteDetectDrawing = false;
|
||||
_whiteDetectMode = false;
|
||||
_bgDefectDrawing = false;
|
||||
var completedMode = _bgDefectRoiMode;
|
||||
_bgDefectRoiMode = BackgroundDefectRoiMode.None;
|
||||
RoiCanvas.ReleaseMouseCapture();
|
||||
|
||||
var canvas = FindChildByName<System.Windows.Controls.Canvas>(RoiCanvas, "mainCanvas");
|
||||
if (canvas == null) return;
|
||||
|
||||
var end = e.GetPosition(canvas);
|
||||
int x = (int)Math.Min(_whiteDetectStart.X, end.X);
|
||||
int y = (int)Math.Min(_whiteDetectStart.Y, end.Y);
|
||||
int w = (int)Math.Abs(end.X - _whiteDetectStart.X);
|
||||
int h = (int)Math.Abs(end.Y - _whiteDetectStart.Y);
|
||||
int x = (int)Math.Min(_bgDefectStart.X, end.X);
|
||||
int y = (int)Math.Min(_bgDefectStart.Y, end.Y);
|
||||
int w = (int)Math.Abs(end.X - _bgDefectStart.X);
|
||||
int h = (int)Math.Abs(end.Y - _bgDefectStart.Y);
|
||||
|
||||
// 移除预览矩形
|
||||
if (_whiteDetectPreview != null)
|
||||
if (_bgDefectPreview != null)
|
||||
{
|
||||
canvas.Children.Remove(_whiteDetectPreview);
|
||||
_whiteDetectPreview = null;
|
||||
canvas.Children.Remove(_bgDefectPreview);
|
||||
_bgDefectPreview = null;
|
||||
}
|
||||
|
||||
if (w < 10 || h < 10) return; // 太小忽略
|
||||
@@ -485,34 +529,40 @@ namespace XplorePlane.Views
|
||||
try
|
||||
{
|
||||
var ea = ContainerLocator.Current?.Resolve<Prism.Events.IEventAggregator>();
|
||||
ea?.GetEvent<WhiteBackgroundRoiDrawnEvent>().Publish(new System.Windows.Int32Rect(x, y, w, h));
|
||||
var rect = new System.Windows.Int32Rect(x, y, w, h);
|
||||
if (completedMode == BackgroundDefectRoiMode.WhiteBackground)
|
||||
ea?.GetEvent<WhiteBackgroundRoiDrawnEvent>().Publish(rect);
|
||||
else if (completedMode == BackgroundDefectRoiMode.BlackBackground)
|
||||
ea?.GetEvent<BlackBackgroundRoiDrawnEvent>().Publish(rect);
|
||||
}
|
||||
catch { }
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void RenderWhiteBackgroundResult(WhiteBackgroundResultPayload payload)
|
||||
private void RenderBackgroundDefectResult(
|
||||
System.Drawing.Rectangle roiRect,
|
||||
System.Collections.Generic.List<(System.Drawing.Point center, int radius, double sizeMm)> detections)
|
||||
{
|
||||
var canvas = FindChildByName<System.Windows.Controls.Canvas>(RoiCanvas, "mainCanvas");
|
||||
if (canvas == null || payload?.Detections == null) return;
|
||||
if (canvas == null || detections == null) return;
|
||||
|
||||
// 绘制ROI矩形(蓝色实线)
|
||||
var roiRect = new System.Windows.Shapes.Rectangle
|
||||
var roiShape = new System.Windows.Shapes.Rectangle
|
||||
{
|
||||
Stroke = System.Windows.Media.Brushes.Blue,
|
||||
StrokeThickness = 1,
|
||||
Width = payload.RoiRect.Width,
|
||||
Height = payload.RoiRect.Height,
|
||||
Width = roiRect.Width,
|
||||
Height = roiRect.Height,
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
System.Windows.Controls.Canvas.SetLeft(roiRect, payload.RoiRect.X);
|
||||
System.Windows.Controls.Canvas.SetTop(roiRect, payload.RoiRect.Y);
|
||||
canvas.Children.Add(roiRect);
|
||||
_whiteDetectOverlays.Add(roiRect);
|
||||
System.Windows.Controls.Canvas.SetLeft(roiShape, roiRect.X);
|
||||
System.Windows.Controls.Canvas.SetTop(roiShape, roiRect.Y);
|
||||
canvas.Children.Add(roiShape);
|
||||
_bgDefectOverlays.Add(roiShape);
|
||||
|
||||
// 绘制每个检测到的黑色区域
|
||||
foreach (var (center, radius, sizeMm) in payload.Detections)
|
||||
// 绘制每个检测区域(白底为暗区、黑底为亮区,可视化相同)
|
||||
foreach (var (center, radius, sizeMm) in detections)
|
||||
{
|
||||
// 红色虚线圆
|
||||
var circle = new System.Windows.Shapes.Ellipse
|
||||
@@ -527,7 +577,7 @@ namespace XplorePlane.Views
|
||||
System.Windows.Controls.Canvas.SetLeft(circle, center.X - radius);
|
||||
System.Windows.Controls.Canvas.SetTop(circle, center.Y - radius);
|
||||
canvas.Children.Add(circle);
|
||||
_whiteDetectOverlays.Add(circle);
|
||||
_bgDefectOverlays.Add(circle);
|
||||
|
||||
// 45°直径标注线(从圆心向左上到右下)
|
||||
double offset = radius * 0.707; // cos(45°) * radius
|
||||
@@ -542,7 +592,7 @@ namespace XplorePlane.Views
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
canvas.Children.Add(diamLine);
|
||||
_whiteDetectOverlays.Add(diamLine);
|
||||
_bgDefectOverlays.Add(diamLine);
|
||||
|
||||
// 尺寸标注(在斜线右上方)
|
||||
string label = sizeMm >= 1000 ? $"{sizeMm / 1000:F2} mm" : $"{sizeMm:F0} μm";
|
||||
@@ -556,15 +606,15 @@ namespace XplorePlane.Views
|
||||
System.Windows.Controls.Canvas.SetLeft(text, center.X + offset + 3);
|
||||
System.Windows.Controls.Canvas.SetTop(text, center.Y - offset - 14);
|
||||
canvas.Children.Add(text);
|
||||
_whiteDetectOverlays.Add(text);
|
||||
_bgDefectOverlays.Add(text);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearWhiteDetectOverlays(System.Windows.Controls.Canvas canvas)
|
||||
private void ClearBackgroundDefectOverlays(System.Windows.Controls.Canvas canvas)
|
||||
{
|
||||
foreach (var el in _whiteDetectOverlays)
|
||||
foreach (var el in _bgDefectOverlays)
|
||||
canvas.Children.Remove(el);
|
||||
_whiteDetectOverlays.Clear();
|
||||
_bgDefectOverlays.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
Reference in New Issue
Block a user