优化高级模块CNC执行的可视化
CNC执行 → PipelineExecutionService(返回 LastStepOutputData)
→ CncExecutionService(调用 PushDetectionOverlay)
→ MainViewportService(触发 DetectionOverlayUpdated 事件)
→ ViewportPanelView(订阅事件,调用 DetectionOverlayRenderer)
→ PolygonRoiCanvas.SetDetectionOverlayCanvas(插入叠加层 Canvas)
This commit is contained in:
@@ -216,9 +216,6 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
OutputData["Thickness"] = thickness;
|
||||
OutputData["ResultText"] = $"Void: {overallVoidRate:F1}% | {classification} | BGA×{bgaResults.Count}";
|
||||
|
||||
// 渲染带标注的彩色结果图像
|
||||
OutputData["RenderedResultImage"] = RenderAnnotatedResult(inputImage, bgaResults, voidLimit, thickness);
|
||||
|
||||
roiMask?.Dispose();
|
||||
return inputImage.Clone();
|
||||
}
|
||||
@@ -375,67 +372,6 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
mask.Dispose();
|
||||
voidImg.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 渲染带标注的彩色结果图像(轮廓、编号、气泡填充、总览信息)
|
||||
/// </summary>
|
||||
private Image<Bgr, byte> RenderAnnotatedResult(Image<Gray, byte> grayImage, List<BgaBallInfo> bgaResults, double voidLimit, int thickness)
|
||||
{
|
||||
var colorImage = new Image<Bgr, byte>(grayImage.Width, grayImage.Height);
|
||||
CvInvoke.CvtColor(grayImage, colorImage, ColorConversion.Gray2Bgr);
|
||||
|
||||
if (bgaResults.Count == 0)
|
||||
return colorImage;
|
||||
|
||||
// 半透明气泡填充
|
||||
var overlay = colorImage.Clone();
|
||||
foreach (var bga in bgaResults)
|
||||
{
|
||||
var fillColor = new MCvScalar(0, 200, 255);
|
||||
foreach (var v in bga.Voids)
|
||||
{
|
||||
if (v.ContourPoints.Length > 0)
|
||||
{
|
||||
using var vop = new VectorOfPoint(v.ContourPoints);
|
||||
using var vvop = new VectorOfVectorOfPoint(vop);
|
||||
CvInvoke.DrawContours(overlay, vvop, 0, fillColor, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
CvInvoke.AddWeighted(overlay, 0.4, colorImage, 0.6, 0, colorImage);
|
||||
overlay.Dispose();
|
||||
|
||||
// 绘制焊球轮廓 + 编号
|
||||
int ngCount = 0;
|
||||
foreach (var bga in bgaResults)
|
||||
{
|
||||
var bgaColor = bga.Classification == "PASS"
|
||||
? new MCvScalar(0, 255, 0) : new MCvScalar(0, 0, 255);
|
||||
if (bga.Classification != "PASS") ngCount++;
|
||||
|
||||
if (bga.ContourPoints.Length > 0)
|
||||
{
|
||||
using var vop = new VectorOfPoint(bga.ContourPoints);
|
||||
using var vvop = new VectorOfVectorOfPoint(vop);
|
||||
CvInvoke.DrawContours(colorImage, vvop, 0, bgaColor, thickness);
|
||||
}
|
||||
|
||||
var bbox = CvInvoke.BoundingRectangle(new VectorOfPoint(bga.ContourPoints));
|
||||
CvInvoke.PutText(colorImage, $"#{bga.Index}",
|
||||
new Point(bbox.X + bbox.Width / 2 - 10, bbox.Bottom + 16),
|
||||
FontFace.HersheySimplex, 0.45, new MCvScalar(255, 100, 0), 2);
|
||||
}
|
||||
|
||||
// 左上角总览
|
||||
int okCount = bgaResults.Count - ngCount;
|
||||
var overallColor = ngCount > 0 ? new MCvScalar(0, 0, 255) : new MCvScalar(0, 255, 0);
|
||||
CvInvoke.PutText(colorImage,
|
||||
$"Total: {bgaResults.Count} | OK: {okCount} | NG: {ngCount}",
|
||||
new Point(10, 25),
|
||||
FontFace.HersheySimplex, 0.55, overallColor, 2);
|
||||
|
||||
return colorImage;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -207,65 +207,12 @@ public class VoidMeasurementProcessor : ImageProcessorBase
|
||||
OutputData["Voids"] = voids;
|
||||
OutputData["ResultText"] = $"Void: {voidRate:F1}% | {classification} | {voids.Count} voids | ROI: {roiArea}px";
|
||||
|
||||
// 渲染带标注的彩色结果图像
|
||||
OutputData["RenderedResultImage"] = RenderAnnotatedResult(inputImage, voids, voidRate, voidLimit, classification);
|
||||
|
||||
blurred.Dispose();
|
||||
voidImg.Dispose();
|
||||
roiMask.Dispose();
|
||||
|
||||
return inputImage.Clone();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 渲染带标注的彩色结果图像(轮廓、编号、半透明填充、总览信息)
|
||||
/// </summary>
|
||||
private Image<Bgr, byte> RenderAnnotatedResult(Image<Gray, byte> grayImage, List<VoidRegionInfo> voids, double voidRate, double voidLimit, string classification)
|
||||
{
|
||||
var colorImage = new Image<Bgr, byte>(grayImage.Width, grayImage.Height);
|
||||
CvInvoke.CvtColor(grayImage, colorImage, ColorConversion.Gray2Bgr);
|
||||
|
||||
if (voids.Count == 0)
|
||||
return colorImage;
|
||||
|
||||
// 半透明气泡填充
|
||||
var overlay = colorImage.Clone();
|
||||
foreach (var v in voids)
|
||||
{
|
||||
if (v.ContourPoints.Length > 0)
|
||||
{
|
||||
using var vop = new VectorOfPoint(v.ContourPoints);
|
||||
using var vvop = new VectorOfVectorOfPoint(vop);
|
||||
CvInvoke.DrawContours(overlay, vvop, 0, new MCvScalar(0, 200, 255), -1);
|
||||
}
|
||||
}
|
||||
CvInvoke.AddWeighted(overlay, 0.4, colorImage, 0.6, 0, colorImage);
|
||||
overlay.Dispose();
|
||||
|
||||
// 绘制轮廓 + 编号
|
||||
foreach (var v in voids)
|
||||
{
|
||||
if (v.ContourPoints.Length > 0)
|
||||
{
|
||||
using var vop = new VectorOfPoint(v.ContourPoints);
|
||||
using var vvop = new VectorOfVectorOfPoint(vop);
|
||||
CvInvoke.DrawContours(colorImage, vvop, 0, new MCvScalar(0, 255, 255), 1);
|
||||
}
|
||||
CvInvoke.PutText(colorImage, $"#{v.Index}",
|
||||
new Point((int)v.CenterX - 8, (int)v.CenterY + 5),
|
||||
FontFace.HersheySimplex, 0.35, new MCvScalar(255, 100, 0), 1);
|
||||
}
|
||||
|
||||
// 左上角总览
|
||||
var overallColor = classification == "PASS"
|
||||
? new MCvScalar(0, 255, 0) : new MCvScalar(0, 0, 255);
|
||||
CvInvoke.PutText(colorImage,
|
||||
$"Void: {voidRate:F1}% | Limit: {voidLimit:F0}% | {voids.Count} voids | {classification}",
|
||||
new Point(10, 25),
|
||||
FontFace.HersheySimplex, 0.5, overallColor, 2);
|
||||
|
||||
return colorImage;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -671,6 +671,38 @@ namespace XP.ImageProcessing.RoiControl.Controls
|
||||
if (element != null && mainCanvas.Children.Contains(element))
|
||||
mainCanvas.Children.Remove(element);
|
||||
}
|
||||
|
||||
// ── 检测结果叠加层 ──
|
||||
private Canvas _detectionOverlay;
|
||||
|
||||
/// <summary>
|
||||
/// 设置检测结果叠加层 Canvas(由外部构建好后传入)。
|
||||
/// </summary>
|
||||
public void SetDetectionOverlayCanvas(Canvas overlayCanvas)
|
||||
{
|
||||
ClearDetectionOverlay();
|
||||
|
||||
if (overlayCanvas == null) return;
|
||||
|
||||
_detectionOverlay = overlayCanvas;
|
||||
_detectionOverlay.IsHitTestVisible = false;
|
||||
_detectionOverlay.SetBinding(Canvas.WidthProperty, new System.Windows.Data.Binding("CanvasWidth") { Source = this });
|
||||
_detectionOverlay.SetBinding(Canvas.HeightProperty, new System.Windows.Data.Binding("CanvasHeight") { Source = this });
|
||||
|
||||
// 插入到 backgroundImage 之后(索引1),在 ROI 和测量层之下
|
||||
int insertIndex = System.Math.Min(1, mainCanvas.Children.Count);
|
||||
mainCanvas.Children.Insert(insertIndex, _detectionOverlay);
|
||||
}
|
||||
|
||||
/// <summary>清除检测结果叠加层</summary>
|
||||
public void ClearDetectionOverlay()
|
||||
{
|
||||
if (_detectionOverlay != null)
|
||||
{
|
||||
mainCanvas.Children.Remove(_detectionOverlay);
|
||||
_detectionOverlay = null;
|
||||
}
|
||||
}
|
||||
public int MeasureCount => _ppGroups.Count + _ptlGroups.Count + _angleGroups.Count + _frGroups.Count + _bgaGroups.Count;
|
||||
|
||||
// ── 点击分发 ──
|
||||
|
||||
@@ -718,6 +718,16 @@ namespace XplorePlane.Services.Cnc
|
||||
nodeResult.Status = InspectionNodeStatus.Succeeded;
|
||||
_mainViewportService?.SetCncResultImage(resultImage, $"CNC \u8282\u70b9\u7ed3\u679c\uff1a{inspectionNode.Name}");
|
||||
}
|
||||
|
||||
// 推送检测结果叠加层数据(轮廓、标注信息)供 UI 分层绘制
|
||||
if (execResult.LastStepOutputData != null)
|
||||
{
|
||||
var lastOperatorKey = inspectionNode.Pipeline.Nodes
|
||||
.Where(n => n.IsEnabled)
|
||||
.OrderBy(n => n.Order)
|
||||
.LastOrDefault()?.OperatorKey ?? string.Empty;
|
||||
_mainViewportService?.PushDetectionOverlay(execResult.LastStepOutputData, lastOperatorKey);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
using XP.ImageProcessing.Processors;
|
||||
|
||||
namespace XplorePlane.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 检测结果叠加层渲染器:根据算子输出数据构建 WPF Canvas 叠加层。
|
||||
/// 用于在 PolygonRoiCanvas 上分层绘制检测结果(轮廓、标注、半透明填充)。
|
||||
/// </summary>
|
||||
public static class DetectionOverlayRenderer
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据算子输出数据构建叠加层 Canvas。
|
||||
/// </summary>
|
||||
public static Canvas BuildOverlay(IReadOnlyDictionary<string, object> outputData, string operatorKey)
|
||||
{
|
||||
if (outputData == null) return null;
|
||||
|
||||
var canvas = new Canvas
|
||||
{
|
||||
IsHitTestVisible = false,
|
||||
Background = Brushes.Transparent
|
||||
};
|
||||
|
||||
if (string.Equals(operatorKey, "BgaVoidRate", StringComparison.OrdinalIgnoreCase))
|
||||
RenderBgaOverlay(canvas, outputData);
|
||||
else if (string.Equals(operatorKey, "VoidMeasurement", StringComparison.OrdinalIgnoreCase))
|
||||
RenderVoidOverlay(canvas, outputData);
|
||||
|
||||
return canvas.Children.Count > 0 ? canvas : null;
|
||||
}
|
||||
|
||||
private static void RenderBgaOverlay(Canvas canvas, IReadOnlyDictionary<string, object> outputData)
|
||||
{
|
||||
if (!outputData.TryGetValue("BgaBalls", out var ballsObj)) return;
|
||||
if (ballsObj is not List<BgaBallInfo> bgaBalls) return;
|
||||
if (bgaBalls.Count == 0) return;
|
||||
|
||||
int ngCount = 0;
|
||||
foreach (var bga in bgaBalls)
|
||||
{
|
||||
bool isFail = bga.Classification != "PASS";
|
||||
if (isFail) ngCount++;
|
||||
|
||||
var contourBrush = isFail ? Brushes.Red : Brushes.Lime;
|
||||
|
||||
// 绘制焊球轮廓
|
||||
if (bga.ContourPoints != null && bga.ContourPoints.Length > 2)
|
||||
{
|
||||
var polygon = new Polygon
|
||||
{
|
||||
Stroke = contourBrush,
|
||||
StrokeThickness = 2,
|
||||
Fill = Brushes.Transparent,
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
var points = new PointCollection();
|
||||
foreach (var pt in bga.ContourPoints)
|
||||
points.Add(new Point(pt.X, pt.Y));
|
||||
polygon.Points = points;
|
||||
canvas.Children.Add(polygon);
|
||||
}
|
||||
|
||||
// 绘制气泡填充(半透明)
|
||||
foreach (var v in bga.Voids)
|
||||
{
|
||||
if (v.ContourPoints != null && v.ContourPoints.Length > 2)
|
||||
{
|
||||
var voidPoly = new Polygon
|
||||
{
|
||||
Stroke = Brushes.Orange,
|
||||
StrokeThickness = 1,
|
||||
Fill = new SolidColorBrush(Color.FromArgb(100, 255, 200, 0)),
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
var voidPoints = new PointCollection();
|
||||
foreach (var pt in v.ContourPoints)
|
||||
voidPoints.Add(new Point(pt.X, pt.Y));
|
||||
voidPoly.Points = voidPoints;
|
||||
canvas.Children.Add(voidPoly);
|
||||
}
|
||||
}
|
||||
|
||||
// 编号标注
|
||||
var label = new TextBlock
|
||||
{
|
||||
Text = $"#{bga.Index}",
|
||||
FontSize = 12,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Foreground = Brushes.Cyan,
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
Canvas.SetLeft(label, bga.CenterX - 10);
|
||||
Canvas.SetTop(label, bga.CenterY - 8);
|
||||
canvas.Children.Add(label);
|
||||
}
|
||||
|
||||
// 总览标注
|
||||
int okCount = bgaBalls.Count - ngCount;
|
||||
var summaryLabel = new TextBlock
|
||||
{
|
||||
Text = $"Total: {bgaBalls.Count} | OK: {okCount} | NG: {ngCount}",
|
||||
FontSize = 14,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Foreground = ngCount > 0 ? Brushes.Red : Brushes.Lime,
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
Canvas.SetLeft(summaryLabel, 10);
|
||||
Canvas.SetTop(summaryLabel, 10);
|
||||
canvas.Children.Add(summaryLabel);
|
||||
}
|
||||
|
||||
private static void RenderVoidOverlay(Canvas canvas, IReadOnlyDictionary<string, object> outputData)
|
||||
{
|
||||
if (!outputData.TryGetValue("Voids", out var voidsObj)) return;
|
||||
if (voidsObj is not List<VoidRegionInfo> voids) return;
|
||||
if (voids.Count == 0) return;
|
||||
|
||||
double voidRate = outputData.TryGetValue("VoidRate", out var vrObj) && vrObj is double vr ? vr : 0;
|
||||
double voidLimit = outputData.TryGetValue("VoidLimit", out var vlObj) && vlObj is double vl ? vl : 25.0;
|
||||
string classification = outputData.TryGetValue("Classification", out var clsObj) && clsObj is string cls ? cls : "N/A";
|
||||
|
||||
foreach (var v in voids)
|
||||
{
|
||||
// 绘制空隙轮廓(半透明填充)
|
||||
if (v.ContourPoints != null && v.ContourPoints.Length > 2)
|
||||
{
|
||||
var voidPoly = new Polygon
|
||||
{
|
||||
Stroke = Brushes.Yellow,
|
||||
StrokeThickness = 1,
|
||||
Fill = new SolidColorBrush(Color.FromArgb(100, 255, 200, 0)),
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
var points = new PointCollection();
|
||||
foreach (var pt in v.ContourPoints)
|
||||
points.Add(new Point(pt.X, pt.Y));
|
||||
voidPoly.Points = points;
|
||||
canvas.Children.Add(voidPoly);
|
||||
}
|
||||
|
||||
// 编号标注
|
||||
var label = new TextBlock
|
||||
{
|
||||
Text = $"#{v.Index}",
|
||||
FontSize = 10,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Foreground = Brushes.Cyan,
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
Canvas.SetLeft(label, v.CenterX - 8);
|
||||
Canvas.SetTop(label, v.CenterY - 6);
|
||||
canvas.Children.Add(label);
|
||||
}
|
||||
|
||||
// 总览标注
|
||||
var overallColor = classification == "PASS" ? Brushes.Lime : Brushes.Red;
|
||||
var summaryLabel = new TextBlock
|
||||
{
|
||||
Text = $"Void: {voidRate:F1}% | Limit: {voidLimit:F0}% | {voids.Count} voids | {classification}",
|
||||
FontSize = 14,
|
||||
FontWeight = FontWeights.Bold,
|
||||
Foreground = overallColor,
|
||||
IsHitTestVisible = false
|
||||
};
|
||||
Canvas.SetLeft(summaryLabel, 10);
|
||||
Canvas.SetTop(summaryLabel, 10);
|
||||
canvas.Children.Add(summaryLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,28 +258,15 @@ namespace XplorePlane.Services
|
||||
var processedEmgu = processor.Process(emguImage);
|
||||
progress?.Report(0.9);
|
||||
|
||||
BitmapSource result;
|
||||
|
||||
// 如果处理器输出了渲染后的彩色结果图像,优先使用它
|
||||
if (processor.OutputData.TryGetValue("RenderedResultImage", out var renderedObj)
|
||||
&& renderedObj is Emgu.CV.Image<Emgu.CV.Structure.Bgr, byte> renderedImage)
|
||||
{
|
||||
result = ImageConverter.ToBitmapSourceFromBgr(renderedImage);
|
||||
renderedImage.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ImageConverter.ToBitmapSource(processedEmgu);
|
||||
}
|
||||
|
||||
var result = ImageConverter.ToBitmapSource(processedEmgu);
|
||||
result.Freeze();
|
||||
progress?.Report(1.0);
|
||||
|
||||
var snapshot = new Dictionary<string, object>(processor.OutputData.Count);
|
||||
foreach (var kv in processor.OutputData)
|
||||
{
|
||||
// 不将大型图像对象序列化到快照中
|
||||
if (kv.Key == "RenderedResultImage") continue;
|
||||
// 不将大型 Emgu 图像对象序列化到快照中
|
||||
if (kv.Key == "RenderedResultImage" || kv.Key == "RoiMask") continue;
|
||||
snapshot[kv.Key] = kv.Value;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace XplorePlane.Services.MainViewport
|
||||
@@ -34,5 +35,28 @@ namespace XplorePlane.Services.MainViewport
|
||||
/// 与 <see cref="SetManualImage"/> 不同,此方法在 CNC 运行期间不会被阻断。
|
||||
/// </summary>
|
||||
void SetCncResultImage(ImageSource image, string label);
|
||||
|
||||
/// <summary>
|
||||
/// 推送检测结果叠加层数据(轮廓、标注等),由 UI 分层绘制。
|
||||
/// </summary>
|
||||
event EventHandler<DetectionOverlayEventArgs> DetectionOverlayUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// 由 CNC 执行引擎调用,将检测算子的输出数据推送给 UI 叠加层。
|
||||
/// </summary>
|
||||
void PushDetectionOverlay(IReadOnlyDictionary<string, object> outputData, string operatorKey);
|
||||
}
|
||||
|
||||
/// <summary>检测结果叠加层事件参数</summary>
|
||||
public class DetectionOverlayEventArgs : EventArgs
|
||||
{
|
||||
public IReadOnlyDictionary<string, object> OutputData { get; }
|
||||
public string OperatorKey { get; }
|
||||
|
||||
public DetectionOverlayEventArgs(IReadOnlyDictionary<string, object> outputData, string operatorKey)
|
||||
{
|
||||
OutputData = outputData;
|
||||
OperatorKey = operatorKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
@@ -208,6 +209,14 @@ namespace XplorePlane.Services.MainViewport
|
||||
RaiseStateChanged();
|
||||
}
|
||||
|
||||
public event EventHandler<DetectionOverlayEventArgs> DetectionOverlayUpdated;
|
||||
|
||||
public void PushDetectionOverlay(IReadOnlyDictionary<string, object> outputData, string operatorKey)
|
||||
{
|
||||
if (outputData == null) return;
|
||||
DetectionOverlayUpdated?.Invoke(this, new DetectionOverlayEventArgs(outputData, operatorKey));
|
||||
}
|
||||
|
||||
public void SetManualImage(ImageSource image, string filePath)
|
||||
{
|
||||
if (image == null)
|
||||
|
||||
@@ -11,9 +11,11 @@ namespace XplorePlane.Services
|
||||
|
||||
/// <param name="Image">流水线输出图像(始终为灰度预览路径下的结果)。</param>
|
||||
/// <param name="TemplateMatchOverlayData">当且仅当最后一步为旋转模板匹配且需绘制匹配框时,为 OutputData 的副本,供 UI 透明叠加层使用;否则为 null。</param>
|
||||
/// <param name="LastStepOutputData">最后一步算子的 OutputData 快照,供检测结果叠加层使用。</param>
|
||||
public sealed record PipelineExecutionResult(
|
||||
BitmapSource Image,
|
||||
IReadOnlyDictionary<string, object>? TemplateMatchOverlayData);
|
||||
IReadOnlyDictionary<string, object>? TemplateMatchOverlayData,
|
||||
IReadOnlyDictionary<string, object>? LastStepOutputData = null);
|
||||
|
||||
public interface IPipelineExecutionService
|
||||
{
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace XplorePlane.Services
|
||||
|
||||
var total = enabledNodes.Count;
|
||||
IReadOnlyDictionary<string, object>? templateOverlayData = null;
|
||||
IReadOnlyDictionary<string, object>? lastStepOutputData = null;
|
||||
|
||||
for (var step = 0; step < total; step++)
|
||||
{
|
||||
@@ -82,6 +83,7 @@ namespace XplorePlane.Services
|
||||
{
|
||||
templateOverlayData = TemplateMatchOverlayRenderer.TrySnapshotOutputForOverlay(
|
||||
node.OperatorKey, parameters, output);
|
||||
lastStepOutputData = output;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
@@ -107,7 +109,7 @@ namespace XplorePlane.Services
|
||||
if (!current.IsFrozen)
|
||||
current.Freeze();
|
||||
|
||||
return new PipelineExecutionResult(current, templateOverlayData);
|
||||
return new PipelineExecutionResult(current, templateOverlayData, lastStepOutputData);
|
||||
}
|
||||
|
||||
private static BitmapSource ScaleForPreview(BitmapSource source)
|
||||
|
||||
@@ -9,6 +9,7 @@ using Microsoft.Win32;
|
||||
using Prism.Ioc;
|
||||
using XP.ImageProcessing.RoiControl.Controls;
|
||||
using XplorePlane.Events;
|
||||
using XplorePlane.Services;
|
||||
using XplorePlane.ViewModels;
|
||||
|
||||
namespace XplorePlane.Views
|
||||
@@ -126,6 +127,24 @@ namespace XplorePlane.Views
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 订阅检测结果叠加层事件
|
||||
try
|
||||
{
|
||||
var viewportService = ContainerLocator.Current?.Resolve<Services.MainViewport.IMainViewportService>();
|
||||
if (viewportService != null)
|
||||
{
|
||||
viewportService.DetectionOverlayUpdated += (s, args) =>
|
||||
{
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
var overlay = DetectionOverlayRenderer.BuildOverlay(args.OutputData, args.OperatorKey);
|
||||
RoiCanvas.SetDetectionOverlayCanvas(overlay);
|
||||
}));
|
||||
};
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// 光标信息:从 RoiCanvas.CursorInfo 同步到 MainViewModel
|
||||
var cursorInfoDesc = System.ComponentModel.DependencyPropertyDescriptor.FromProperty(
|
||||
PolygonRoiCanvas.CursorInfoProperty, typeof(PolygonRoiCanvas));
|
||||
|
||||
Reference in New Issue
Block a user