namespace XP.ImageProcessing.Core.Alignment; /// /// 示教 ROI + 基准/测量位姿 → 运行图多边形;供 RoiAlignment 算子与流水线胶水调用。 /// 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 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? 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 ReadTeachPolygon(IReadOnlyDictionary parameters) { if (parameters == null) return Array.Empty(); if (!parameters.TryGetValue(RoiPolygonParameterNames.PolyCount, out var countObj)) return Array.Empty(); int count = Convert.ToInt32(countObj); if (count < 3) return Array.Empty(); count = Math.Min(count, RoiPolygonParameterNames.MaxPoints); var points = new List(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; } /// /// 将变换结果写入参数字典(PolyCount、PolyX/PolyY、RoiMode),供下游 VoidMeasurement 等算子使用。 /// public static void WriteToParameters( IDictionary 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"; } /// /// 将变换结果写入算子 OutputData(与 键名一致,便于流水线拷贝)。 /// public static void WriteToOutputData(IDictionary 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 }; }