Files
XplorePlane/XP.ImageProcessing.Processors/Core/ImageProcessorBase.cs
T
XplorePlane Developer 957157ae80 fix: 修复编译错误、BGA向导预览、算子重复插入及状态栏消息
## Compilation fixes
- Fix XAML namespace reference XplorePlane.Converters → XplorePlane.Helpers (EventLogView, StateDisplayView)
- Add missing using for ISampleTypeRepository / JsonSampleTypeRepository in App.xaml.cs
- Fix ILoggerService.Warn() calls using Exception as first arg instead of message string
- Serialize CncProgram to JSON when passing to MatrixLayout (expects string, not CncProgram object)
- Suppress CS8632 nullable annotation warnings project-wide

## BGA wizard fixes
- Fix preview not showing on step 3: force RefreshBgaPreview() when entering final step
- Add step validation in CanNext() to prevent skipping to preview with invalid inputs
- Notify NextCommand.CanExecuteChanged when inputs change
- Fix RunProgress runtime binding error: set Mode=OneWay for ProgressBar.Value

## Operator toolbox: prevent duplicate insertion
- Add static OperatorTarget property for direct dispatch to active pipeline
- PipelineEditorView registers as toolbox target on load (floating window only)
- CNC page sets initial target; PipelineEditorWindow resets to CNC pipeline on close
- '+' button inserts only to the active pipeline instead of broadcasting to all subscribers

## Pipeline editor status bar messages
- Add StatusBarMessageEvent / StatusBarMessagePayload to CommonEvents.cs
- PipelineEditorViewModel publishes status on all ops (add/remove/reorder/save/execute)
- MainViewModel subscribes and displays on main window bottom status bar
- Auto-clear after timeout (3s info, 5s error)

## docs
- Merge three architecture docs into consolidated XplorePlane-架构与结构说明.md
2026-07-24 15:59:16 +08:00

196 lines
7.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件名: ImageProcessorBase.cs
// 描述: 图像处理算子泛型基类,支持 8 位(byte)和 16 位(ushort)灰度图像
// 功能:
// - 定义算子的基本属性(名称、描述)
// - 参数管理(设置、获取、验证)
// - ROI(感兴趣区域)处理支持
// - 输出数据管理(用于传递额外信息如轮廓等)
// - 为所有图像处理算子提供统一的基础框架
// 设计模式: 模板方法模式
// 作者: 李伟 wei.lw.li@hexagon.com
// ============================================================================
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using System.Globalization;
namespace XP.ImageProcessing.Core;
/// <summary>
/// 图像处理算子泛型基类。
/// <typeparam name="TDepth">像素深度类型:<see cref="byte"/>8位)或 <see cref="ushort"/>16位)。</typeparam>
/// </summary>
public abstract class ImageProcessorBase<TDepth> where TDepth : struct, IComparable
{
/// <summary>算子名称</summary>
public string Name { get; protected set; } = string.Empty;
/// <summary>算子描述</summary>
public string Description { get; protected set; } = string.Empty;
/// <summary>参数字典</summary>
protected Dictionary<string, ProcessorParameter> Parameters { get; set; }
/// <summary>输出数据(用于传递额外信息如轮廓等)</summary>
public Dictionary<string, object> OutputData { get; protected set; }
/// <summary>ROI区域</summary>
public System.Drawing.Rectangle? ROI { get; set; }
/// <summary>多边形ROI点集</summary>
public System.Drawing.Point[]? PolygonROIPoints { get; set; }
/// <summary>当前图像位深的最大像素值(byte=255ushort=65535</summary>
protected int MaxPixelValue => typeof(TDepth) == typeof(ushort) ? 65535 : 255;
protected ImageProcessorBase()
{
Parameters = new Dictionary<string, ProcessorParameter>();
OutputData = new Dictionary<string, object>();
InitializeParameters();
}
/// <summary>
/// 初始化算子参数(子类实现)
/// </summary>
protected abstract void InitializeParameters();
/// <summary>
/// 执行图像处理(子类实现)
/// </summary>
public abstract Image<Gray, TDepth> Process(Image<Gray, TDepth> inputImage);
/// <summary>
/// 执行图像处理(带矩形ROI支持)
/// </summary>
public Image<Gray, TDepth> ProcessWithROI(Image<Gray, TDepth> inputImage)
{
if (ROI.HasValue && ROI.Value != System.Drawing.Rectangle.Empty)
{
inputImage.ROI = ROI.Value;
var roiImage = inputImage.Copy();
inputImage.ROI = System.Drawing.Rectangle.Empty;
var processedROI = Process(roiImage);
OutputData["ROIOffset"] = new System.Drawing.Point(ROI.Value.X, ROI.Value.Y);
var result = inputImage.Clone();
result.ROI = ROI.Value;
processedROI.CopyTo(result);
result.ROI = System.Drawing.Rectangle.Empty;
roiImage.Dispose();
processedROI.Dispose();
return result;
}
return Process(inputImage);
}
/// <summary>
/// 执行图像处理(带多边形ROI掩码支持)
/// </summary>
public Image<Gray, TDepth> ProcessWithPolygonROI(Image<Gray, TDepth> inputImage)
{
if (PolygonROIPoints == null || PolygonROIPoints.Length < 3)
return Process(inputImage);
var mask = new Image<Gray, byte>(inputImage.Width, inputImage.Height);
mask.SetValue(new Gray(0));
using (var vop = new VectorOfPoint(PolygonROIPoints))
using (var vvop = new VectorOfVectorOfPoint(vop))
{
CvInvoke.DrawContours(mask, vvop, 0, new MCvScalar(255), -1);
}
var processedImage = Process(inputImage);
var result = inputImage.Clone();
for (int y = 0; y < inputImage.Height; y++)
{
for (int x = 0; x < inputImage.Width; x++)
{
if (mask.Data[y, x, 0] > 0)
result.Data[y, x, 0] = processedImage.Data[y, x, 0];
}
}
OutputData["ROIMask"] = mask;
OutputData["PolygonPoints"] = PolygonROIPoints;
OutputData["ROIOffset"] = System.Drawing.Point.Empty;
processedImage.Dispose();
return result;
}
/// <summary>获取所有参数列表</summary>
public List<ProcessorParameter> GetParameters()
=> new List<ProcessorParameter>(Parameters.Values);
/// <summary>获取参数字典引用(供适配器共享参数状态)</summary>
public Dictionary<string, ProcessorParameter> GetParametersDictionary() => Parameters;
/// <summary>设置参数值</summary>
public void SetParameter(string name, object value)
{
if (Parameters.ContainsKey(name))
Parameters[name].Value = value;
else
throw new ArgumentException($"参数 {name} 不存在");
}
/// <summary>获取参数值</summary>
public T GetParameter<T>(string name)
{
if (!Parameters.ContainsKey(name))
throw new ArgumentException($"参数 {name} 不存在");
var parameter = Parameters[name];
try
{
if (parameter.Value is T typedValue)
return typedValue;
if (parameter.Value is string textValue)
{
var normalizedText = NormalizeText(textValue);
if (typeof(T) == typeof(string))
return (T)(object)textValue;
if (typeof(T) == typeof(int) && int.TryParse(normalizedText, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
return (T)(object)intValue;
if (typeof(T) == typeof(double) && double.TryParse(normalizedText, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue))
return (T)(object)doubleValue;
if (typeof(T) == typeof(bool) && bool.TryParse(normalizedText, out var boolValue))
return (T)(object)boolValue;
}
return (T)Convert.ChangeType(parameter.Value, typeof(T), CultureInfo.InvariantCulture)!;
}
catch (Exception ex)
{
throw new ArgumentException(
$"参数 {name} 的值 '{parameter.Value}' 无法转换为 {typeof(T).Name}", ex);
}
}
/// <summary>获取单个参数信息</summary>
public ProcessorParameter? GetParameterInfo(string name)
=> Parameters.ContainsKey(name) ? Parameters[name] : null;
private static string NormalizeText(string value)
=> value.Trim().TrimEnd('、', '', ',', '。', '.', ';', '', ':', '');
}
/// <summary>
/// 8 位灰度图像算子基类别名(向后兼容)
/// </summary>
public abstract class ImageProcessorBase : ImageProcessorBase<byte> { }