// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件名: RoiAlignmentProcessor.cs
// 描述: 示教多边形 ROI 刚体对齐(平移+旋转)
//
// 流水线约定:
// 输入: 示教 Poly* + ReferencePose(Ref*) + 上一步模板匹配的 MeasuredPose(Measured*)
// 输出: OutputData 中含变换后的 PolyCount/PolyX/PolyY/RoiMode,可拷贝到 VoidMeasurement 等算子
// 或: 流水线直接调用 XP.ImageProcessing.Core.Alignment.RoiAlignmentApplier
// ============================================================================
using Emgu.CV;
using Emgu.CV.Structure;
using XP.ImageProcessing.Core;
using XP.ImageProcessing.Core.Alignment;
using Serilog;
namespace XP.ImageProcessing.Processors;
///
/// 将示教图上的多边形 ROI 变换到当前图(不修改图像,仅输出对齐后的 ROI 参数)。
///
public class RoiAlignmentProcessor : ImageProcessorBase
{
private static readonly ILogger _logger = Log.ForContext();
public RoiAlignmentProcessor()
{
Name = LocalizationHelper.GetString("RoiAlignmentProcessor_Name");
Description = LocalizationHelper.GetString("RoiAlignmentProcessor_Description");
}
protected override void InitializeParameters()
{
Parameters.Add("RefCenterX", new ProcessorParameter(
"RefCenterX",
LocalizationHelper.GetString("RoiAlignmentProcessor_RefCenterX"),
typeof(double), 0.0, null, null,
LocalizationHelper.GetString("RoiAlignmentProcessor_RefCenterX_Desc")));
Parameters.Add("RefCenterY", new ProcessorParameter(
"RefCenterY",
LocalizationHelper.GetString("RoiAlignmentProcessor_RefCenterY"),
typeof(double), 0.0, null, null,
LocalizationHelper.GetString("RoiAlignmentProcessor_RefCenterY_Desc")));
Parameters.Add("RefAngle", new ProcessorParameter(
"RefAngle",
LocalizationHelper.GetString("RoiAlignmentProcessor_RefAngle"),
typeof(double), 0.0, -180.0, 180.0,
LocalizationHelper.GetString("RoiAlignmentProcessor_RefAngle_Desc")));
Parameters.Add("MeasuredCenterX", new ProcessorParameter(
"MeasuredCenterX",
LocalizationHelper.GetString("RoiAlignmentProcessor_MeasuredCenterX"),
typeof(double), 0.0, null, null,
LocalizationHelper.GetString("RoiAlignmentProcessor_MeasuredCenterX_Desc")));
Parameters.Add("MeasuredCenterY", new ProcessorParameter(
"MeasuredCenterY",
LocalizationHelper.GetString("RoiAlignmentProcessor_MeasuredCenterY"),
typeof(double), 0.0, null, null,
LocalizationHelper.GetString("RoiAlignmentProcessor_MeasuredCenterY_Desc")));
Parameters.Add("MeasuredAngle", new ProcessorParameter(
"MeasuredAngle",
LocalizationHelper.GetString("RoiAlignmentProcessor_MeasuredAngle"),
typeof(double), 0.0, -180.0, 180.0,
LocalizationHelper.GetString("RoiAlignmentProcessor_MeasuredAngle_Desc")));
Parameters.Add(RoiPolygonParameterNames.PolyCount, new ProcessorParameter(
RoiPolygonParameterNames.PolyCount,
RoiPolygonParameterNames.PolyCount,
typeof(int), 0, null, null,
"") { IsVisible = false });
for (int i = 0; i < RoiPolygonParameterNames.MaxPoints; i++)
{
Parameters.Add(RoiPolygonParameterNames.PolyX(i), new ProcessorParameter(
RoiPolygonParameterNames.PolyX(i),
RoiPolygonParameterNames.PolyX(i),
typeof(int), 0, null, null,
"") { IsVisible = false });
Parameters.Add(RoiPolygonParameterNames.PolyY(i), new ProcessorParameter(
RoiPolygonParameterNames.PolyY(i),
RoiPolygonParameterNames.PolyY(i),
typeof(int), 0, null, null,
"") { IsVisible = false });
}
}
public override Image Process(Image inputImage)
{
OutputData.Clear();
var reference = new Pose2D(
GetParameter("RefCenterX"),
GetParameter("RefCenterY"),
GetParameter("RefAngle"));
var measured = new Pose2D(
GetParameter("MeasuredCenterX"),
GetParameter("MeasuredCenterY"),
GetParameter("MeasuredAngle"));
var paramDict = Parameters.ToDictionary(p => p.Key, p => p.Value.Value);
var teachPoints = RoiAlignmentApplier.ReadTeachPolygon(paramDict);
var result = RoiAlignmentApplier.Apply(reference, teachPoints, measured);
RoiAlignmentApplier.WriteToOutputData(OutputData, result);
OutputData[RoiAlignmentOutputKeys.Success] = result.Success;
if (!string.IsNullOrEmpty(result.ErrorMessage))
OutputData[RoiAlignmentOutputKeys.Message] = result.ErrorMessage!;
OutputData["ResultText"] = OutputData[RoiAlignmentOutputKeys.ResultText] = result.Success
? $"ROI aligned: {result.TransformedPointsInt.Count} pts"
: $"ROI align failed: {result.ErrorMessage}";
if (result.Success)
_logger.Debug("RoiAlignment: {Count} points, ref=({Rx:F1},{Ry:F1},{Ra:F1}) meas=({Mx:F1},{My:F1},{Ma:F1})",
result.TransformedPointsInt.Count,
reference.X, reference.Y, reference.AngleDegrees,
measured.X, measured.Y, measured.AngleDegrees);
else
_logger.Warning("RoiAlignment failed: {Msg}", result.ErrorMessage);
return inputImage.Clone();
}
}