217 lines
6.9 KiB
C#
217 lines
6.9 KiB
C#
// ============================================================================
|
||
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
|
||
// 文件名: ImageProcessorBase.cs
|
||
// 描述: 8位图像处理算子基类,定义图像处理算子的通用接口和行为
|
||
// 功能:
|
||
// - 定义算子的基本属性(名称、描述)
|
||
// - 参数管理(设置、获取、验证)
|
||
// - ROI(感兴趣区域)处理支持
|
||
// - 输出数据管理(用于传递额外信息如轮廓等)
|
||
// - 为所有8位图像处理算子提供统一的基础框架
|
||
// 设计模式: 模板方法模式
|
||
// 作者: 李伟 wei.lw.li@hexagon.com
|
||
// ============================================================================
|
||
|
||
using Emgu.CV;
|
||
using Emgu.CV.Structure;
|
||
using Emgu.CV.Util;
|
||
using System.Globalization;
|
||
|
||
namespace XP.ImageProcessing.Core;
|
||
|
||
/// <summary>
|
||
/// 图像处理算子基类
|
||
/// </summary>
|
||
public abstract class ImageProcessorBase
|
||
{
|
||
/// <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; }
|
||
|
||
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, byte> Process(Image<Gray, byte> inputImage);
|
||
|
||
/// <summary>
|
||
/// 执行图像处理(带矩形ROI支持)
|
||
/// </summary>
|
||
public Image<Gray, byte> ProcessWithROI(Image<Gray, byte> 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);
|
||
|
||
// 将 ROI 偏移量保存到输出数据中,供轮廓绘制等使用
|
||
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, byte> ProcessWithPolygonROI(Image<Gray, byte> 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));
|
||
|
||
// 绘制多边形掩码(白色表示ROI区域)
|
||
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();
|
||
|
||
// 使用掩码:ROI内使用处理后的像素,ROI外保持原始像素
|
||
for (int y = 0; y < inputImage.Height; y++)
|
||
{
|
||
for (int x = 0; x < inputImage.Width; x++)
|
||
{
|
||
if (mask.Data[y, x, 0] > 0) // 在ROI内
|
||
{
|
||
result.Data[y, x, 0] = processedImage.Data[y, x, 0];
|
||
}
|
||
}
|
||
}
|
||
|
||
// 保存ROI信息
|
||
OutputData["ROIMask"] = mask;
|
||
OutputData["PolygonPoints"] = PolygonROIPoints;
|
||
OutputData["ROIOffset"] = System.Drawing.Point.Empty;
|
||
|
||
processedImage.Dispose();
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有参数列表
|
||
/// </summary>
|
||
public List<ProcessorParameter> GetParameters()
|
||
{
|
||
return new List<ProcessorParameter>(Parameters.Values);
|
||
}
|
||
|
||
/// <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);
|
||
}
|
||
}
|
||
|
||
private static string NormalizeText(string value)
|
||
{
|
||
return value.Trim().TrimEnd('、', ',', ',', '。', '.', ';', ';', ':', ':');
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取单个参数
|
||
/// </summary>
|
||
public ProcessorParameter? GetParameterInfo(string name)
|
||
{
|
||
return Parameters.ContainsKey(name) ? Parameters[name] : null;
|
||
}
|
||
}
|