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

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

147 lines
5.4 KiB
C#

namespace XP.ImageProcessing.Core.Alignment;
/// <summary>
/// 示教 ROI + 基准/测量位姿 → 运行图多边形;供 <c>RoiAlignment</c> 算子与流水线胶水调用。
/// </summary>
public static class RoiAlignmentApplier
{
public static RoiAlignmentResult Apply(AlignmentRecipe recipe, Pose2D measuredPose)
{
if (recipe == null)
return Fail(default, measuredPose, "Alignment recipe is null.");
return Apply(recipe.ReferencePose, recipe.RoiPoints, measuredPose);
}
public static RoiAlignmentResult Apply(
Pose2D referencePose,
IReadOnlyList<Point2D> teachPoints,
Pose2D measuredPose)
{
if (teachPoints == null || teachPoints.Count < 3)
return Fail(referencePose, measuredPose, "Teach polygon must have at least 3 points.");
var transformed = RoiAlignment.TransformPolygon(teachPoints, referencePose, measuredPose);
var transformedInt = RoiAlignment.TransformPolygonToInt(teachPoints, referencePose, measuredPose);
return new RoiAlignmentResult
{
Success = true,
ReferencePose = referencePose,
MeasuredPose = measuredPose,
TransformedPoints = transformed,
TransformedPointsInt = transformedInt
};
}
public static RoiAlignmentResult ApplyFromTemplateMatchOutput(
AlignmentRecipe recipe,
IReadOnlyDictionary<string, object>? templateMatchOutput,
int matchIndex = 0)
{
if (recipe == null)
return Fail(default, default, "Alignment recipe is null.");
if (!TemplateMatchOutputReader.TryReadMeasuredPose(templateMatchOutput, out var measured, out var error, matchIndex))
return Fail(recipe.ReferencePose, default, error);
return Apply(recipe, measured);
}
public static IReadOnlyList<Point2D> ReadTeachPolygon(IReadOnlyDictionary<string, object> parameters)
{
if (parameters == null)
return Array.Empty<Point2D>();
if (!parameters.TryGetValue(RoiPolygonParameterNames.PolyCount, out var countObj))
return Array.Empty<Point2D>();
int count = Convert.ToInt32(countObj);
if (count < 3)
return Array.Empty<Point2D>();
count = Math.Min(count, RoiPolygonParameterNames.MaxPoints);
var points = new List<Point2D>(count);
for (int i = 0; i < count; i++)
{
if (!parameters.TryGetValue(RoiPolygonParameterNames.PolyX(i), out var xObj)
|| !parameters.TryGetValue(RoiPolygonParameterNames.PolyY(i), out var yObj))
continue;
points.Add(new Point2D(Convert.ToDouble(xObj), Convert.ToDouble(yObj)));
}
return points;
}
/// <summary>
/// 将变换结果写入参数字典(PolyCount、PolyX/PolyY、RoiMode),供下游 VoidMeasurement 等算子使用。
/// </summary>
public static void WriteToParameters(
IDictionary<string, object> parameters,
RoiAlignmentResult result,
bool setRoiModePolygon = true)
{
if (parameters == null)
throw new ArgumentNullException(nameof(parameters));
parameters[RoiAlignmentOutputKeys.Success] = result.Success;
if (!string.IsNullOrEmpty(result.ErrorMessage))
parameters[RoiAlignmentOutputKeys.Message] = result.ErrorMessage!;
if (!result.Success)
return;
int count = result.TransformedPointsInt.Count;
parameters[RoiPolygonParameterNames.PolyCount] = count;
for (int i = 0; i < RoiPolygonParameterNames.MaxPoints; i++)
{
if (i < count)
{
parameters[RoiPolygonParameterNames.PolyX(i)] = result.TransformedPointsInt[i].X;
parameters[RoiPolygonParameterNames.PolyY(i)] = result.TransformedPointsInt[i].Y;
}
else
{
parameters[RoiPolygonParameterNames.PolyX(i)] = 0;
parameters[RoiPolygonParameterNames.PolyY(i)] = 0;
}
}
if (setRoiModePolygon)
parameters[RoiPolygonParameterNames.RoiMode] = "Polygon";
}
/// <summary>
/// 将变换结果写入算子 OutputData(与 <see cref="WriteToParameters"/> 键名一致,便于流水线拷贝)。
/// </summary>
public static void WriteToOutputData(IDictionary<string, object> outputData, RoiAlignmentResult result)
{
if (outputData == null)
throw new ArgumentNullException(nameof(outputData));
WriteToParameters(outputData, result);
if (result.Success)
{
outputData["ReferenceCenterX"] = result.ReferencePose.X;
outputData["ReferenceCenterY"] = result.ReferencePose.Y;
outputData["ReferenceAngle"] = result.ReferencePose.AngleDegrees;
outputData["MeasuredCenterX"] = result.MeasuredPose.X;
outputData["MeasuredCenterY"] = result.MeasuredPose.Y;
outputData["MeasuredAngle"] = result.MeasuredPose.AngleDegrees;
outputData["TransformedPointCount"] = result.TransformedPointsInt.Count;
}
}
private static RoiAlignmentResult Fail(Pose2D reference, Pose2D measured, string? message)
=> new()
{
Success = false,
ErrorMessage = message,
ReferencePose = reference,
MeasuredPose = measured
};
}