1874c4a5bb
统一补齐对齐核心工具类、RoiAlignment 算子、模板匹配对齐扩展和多语言资源,便于在检测前稳定完成示教 ROI 到运行图的变换。 Co-authored-by: Cursor <cursoragent@cursor.com>
147 lines
5.4 KiB
C#
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
|
|
};
|
|
}
|