diff --git a/README.md b/README.md index 84a38dc3..31e8ff44 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,11 @@ XplorePlane 系统用于控制平面 CT 设备的各个子系统(射线源、 ### 总体架构 - 客户端框架: WPF + Prism MVVM(目标框架 net8.0-windows) -- 图像处理内核: ImageProcessing.Core(算子基类)+ ImageProcessing.Processors(具体算子),基于 EmguCV +- 图像处理内核: `XP.ImageProcessing.Processors`,基于 Emgu.CV + - `Core/`:包含 `ImageProcessorBase`、`ProcessorParameter` 及 ROI/对齐等核心模型和算子基类 + - 各功能目录:包含图像变换、滤波、增强、边缘检测、检测分析和模板匹配等具体算子 + - `ImageProcessingService`:负责算子程序集扫描、反射发现、注册及执行 + - 统一使用 16 位图像处理通道,并通过适配器兼容 8 位算子 - 相机控制: XP.Camera(Basler pylon SDK 封装,支持软件触发、参数读写) - 硬件抽象: XP.Common + XP.Hardware.RaySource(射线源控制) - 日志: Serilog @@ -27,11 +31,15 @@ XplorePlane.sln ├── XP.Hardware.RaySource/ # 射线源控制模块 ├── XP.Hardware.RaySource.Comet.Host/ # Comet 射线源独立 Host 进程 ├── XP.Hardware.RaySource.Comet.Messages/# Comet Host 通信消息定义 -├── XP.ImageProcessing.Core/ # 图像处理算子基类与核心模型 -├── XP.ImageProcessing.Processors/ # 具体图像处理算子实现 -├── XP.ImageProcessing.CfgControl/ # 图像处理配置控件 -├── XP.ImageProcessing.RoiControl/ # ROI 绘制与测量控件 -├── XP.ImageProcessing.SmokeTest/ # 图像处理冒烟测试程序 +├── XP.ImageProcessing.Processors/ # 图像处理核心与具体算子实现 +│ ├── Core/ # 算子基类、参数模型、ROI/对齐模型 +│ ├── 图像变换/ # 灰度、旋转、镜像、阈值等 +│ ├── 滤波处理/ # 滤波算子 +│ ├── 图像增强/ # 增强算子 +│ ├── 边缘检测/ # 边缘检测算子 +│ ├── 检测分析/ # BGA、QFN、空洞率及测量算子 +│ └── 定位识别/ # 模板匹配和定位算子 +├── XP.ImageProcessing.RoiControl/ # ROI 绘制与测量控件 ├── XP.Scan/ # 扫描模式相关模块 ├── XP.Calibration/ # 校准模块 ├── XP.ReportEngine/ # 报告生成模块 diff --git a/XplorePlane/Services/Cnc/Execution/CncExecutionService.cs b/XplorePlane/Services/Cnc/Execution/CncExecutionService.cs index 1fcd2ed1..9d8a3045 100644 --- a/XplorePlane/Services/Cnc/Execution/CncExecutionService.cs +++ b/XplorePlane/Services/Cnc/Execution/CncExecutionService.cs @@ -779,10 +779,19 @@ namespace XplorePlane.Services.Cnc /// 注意:与 HomeAll()(机械回零,驱动轴到机械零点建立坐标系)不同,MoveToSafePosition 是移动到用户配置的坐标。 /// 运动服务不可用时优雅降级(仿真模式直接返回)。 /// - private BitmapSource GetInspectionMapOverviewImage(BitmapSource fallbackImage) - { - if (_navigationFrameProvider?.TryGetLatestBitmapSource(out var navigationOverview) == true) - { + private BitmapSource GetInspectionMapOverviewImage(BitmapSource fallbackImage) + { + if (_navigationFrameProvider?.TryCaptureCompositeBitmapSource(out var compositeOverview) == true) + { + _logger.ForModule().Info( + "检查标记图 overview 使用导航窗体合成截图:{0}x{1}", + compositeOverview.PixelWidth, + compositeOverview.PixelHeight); + return compositeOverview; + } + + if (_navigationFrameProvider?.TryGetLatestBitmapSource(out var navigationOverview) == true) + { _logger.ForModule().Info( "检查标记图 overview 使用导航相机大视野图:{0}x{1}", navigationOverview.PixelWidth, diff --git a/XplorePlane/Services/Hardware/Camera/NavigationCameraFrameProvider.cs b/XplorePlane/Services/Hardware/Camera/NavigationCameraFrameProvider.cs index d84e31d0..9b046e81 100644 --- a/XplorePlane/Services/Hardware/Camera/NavigationCameraFrameProvider.cs +++ b/XplorePlane/Services/Hardware/Camera/NavigationCameraFrameProvider.cs @@ -10,8 +10,14 @@ namespace XplorePlane.Services.Hardware.Camera void UpdateFrame(byte[] pixelData, int width, int height, string pixelFormat); void UpdateFrame(BitmapSource bitmap); - - bool TryGetLatestBitmapSource(out BitmapSource bitmap); + + /// 注册导航窗体的合成截图回调(相机图像 + 标记叠加层)。 + void SetCompositeCapture(Func capture); + + /// 获取导航窗体当前实际渲染的合成图像。 + bool TryCaptureCompositeBitmapSource(out BitmapSource bitmap); + + bool TryGetLatestBitmapSource(out BitmapSource bitmap); } /// 导航摄像头帧提供器 @@ -26,8 +32,37 @@ namespace XplorePlane.Services.Hardware.Camera private int _height; private string _pixelFormat = string.Empty; private BitmapSource _bitmap; + private Func _compositeCapture; - public DateTime? LatestCapturedAtUtc { get; private set; } + public DateTime? LatestCapturedAtUtc { get; private set; } + + public void SetCompositeCapture(Func capture) + { + lock (_syncRoot) + { + _compositeCapture = capture; + } + } + + public bool TryCaptureCompositeBitmapSource(out BitmapSource bitmap) + { + Func capture; + lock (_syncRoot) + { + capture = _compositeCapture; + } + + try + { + bitmap = capture?.Invoke(); + return bitmap != null && bitmap.PixelWidth > 0 && bitmap.PixelHeight > 0; + } + catch + { + bitmap = null; + return false; + } + } public void UpdateFrame(byte[] pixelData, int width, int height, string pixelFormat) { diff --git a/XplorePlane/Views/Main/NavigationPropertyPanelView.xaml.cs b/XplorePlane/Views/Main/NavigationPropertyPanelView.xaml.cs index 5ca134c2..042f019d 100644 --- a/XplorePlane/Views/Main/NavigationPropertyPanelView.xaml.cs +++ b/XplorePlane/Views/Main/NavigationPropertyPanelView.xaml.cs @@ -7,6 +7,7 @@ using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; +using XplorePlane.Services.Hardware.Camera; using XplorePlane.Services.Hardware.Coordinate; using XplorePlane.Views.Main.Overlays; @@ -54,6 +55,17 @@ namespace XplorePlane.Views.Main ResolveViewModel(); AttachMarkOverlay(); + // 将导航窗体实际渲染结果注册给 CNC 归档服务,包含标记叠加层。 + try + { + var frameProvider = AppBootstrapper.Instance?.Container.Resolve(); + frameProvider?.SetCompositeCapture(CaptureCompositeImage); + } + catch (Exception ex) + { + _diag.Warning(ex, "注册导航合成截图回调失败"); + } + // 订阅 ViewModel 属性变化,驱动红十字线位置更新 if (_viewModel != null) { @@ -71,6 +83,9 @@ namespace XplorePlane.Views.Main /// private BitmapSource? CaptureCompositeImage() { + if (!Dispatcher.CheckAccess()) + return Dispatcher.Invoke(CaptureCompositeImage); + var target = cameraDisplayHost; if (target == null) return null;