Files
XplorePlane/XP.ImageProcessing.Processors/定位识别/RoiAlignmentProcessor.cs
T
李伟 1874c4a5bb 新增 ROI 对齐基础能力并打通到算子与 UI。
统一补齐对齐核心工具类、RoiAlignment 算子、模板匹配对齐扩展和多语言资源,便于在检测前稳定完成示教 ROI 到运行图的变换。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 17:04:32 +08:00

131 lines
5.7 KiB
C#

// ============================================================================
// 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;
/// <summary>
/// 将示教图上的多边形 ROI 变换到当前图(不修改图像,仅输出对齐后的 ROI 参数)。
/// </summary>
public class RoiAlignmentProcessor : ImageProcessorBase
{
private static readonly ILogger _logger = Log.ForContext<RoiAlignmentProcessor>();
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<Gray, byte> Process(Image<Gray, byte> inputImage)
{
OutputData.Clear();
var reference = new Pose2D(
GetParameter<double>("RefCenterX"),
GetParameter<double>("RefCenterY"),
GetParameter<double>("RefAngle"));
var measured = new Pose2D(
GetParameter<double>("MeasuredCenterX"),
GetParameter<double>("MeasuredCenterY"),
GetParameter<double>("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();
}
}