// ============================================================================ // Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved. // 文件? FillRateProcessor.cs // 描述: 通孔填锡率测量算子(倾斜投影几何法),基于四椭圆ROI // 功能: // - 样品倾斜?5°放置,利用投影位移关系计算填锡率 // - 四个椭圆定义? // E1 = 通孔底部轮廓 // E2 = 通孔顶部轮廓 // E3 = 填锡起点(与E1重合,代?%填锡? // E4 = 填锡终点(锡实际填充到的高度? // - 填锡?= |E4中心 - E3中心| / |E2中心 - E1中心| × 100% // - 纯几何方法,不依赖灰度分? // - IPC-610 THT 分级判定(Class 1/2/3? // 算法: 倾斜投影位移比例 // 作? 李伟 wei.lw.li@hexagon.com // ============================================================================ using Emgu.CV; using Emgu.CV.Structure; using Serilog; using System.Drawing; using XP.ImageProcessing.Core; namespace XP.ImageProcessing.Processors; /// /// 通孔填锡率测量算子(倾斜投影几何法) /// public class FillRateProcessor : ImageProcessorBase { private static readonly ILogger _logger = Log.ForContext(); public FillRateProcessor() { Name = LocalizationHelper.GetString("FillRateProcessor_Name"); Description = LocalizationHelper.GetString("FillRateProcessor_Description"); } protected override void InitializeParameters() { // 四个椭圆(由交互控件注入,UI不可见) AddEllipseParams("E1", 200, 250, 60, 50, 0); // 底部 AddEllipseParams("E2", 220, 180, 60, 50, 0); // 顶部 AddEllipseParams("E3", 200, 250, 60, 50, 0); // 填锡起点?E1? AddEllipseParams("E4", 210, 220, 55, 45, 0); // 填锡终点 Parameters.Add("THTLimit", new ProcessorParameter( "THTLimit", LocalizationHelper.GetString("FillRateProcessor_THTLimit"), typeof(double), 75.0, 0.0, 100.0, LocalizationHelper.GetString("FillRateProcessor_THTLimit_Desc"))); Parameters.Add("Thickness", new ProcessorParameter( "Thickness", LocalizationHelper.GetString("FillRateProcessor_Thickness"), typeof(int), 2, 1, 10, LocalizationHelper.GetString("FillRateProcessor_Thickness_Desc"))); } private void AddEllipseParams(string prefix, int cx, int cy, double a, double b, double angle) { Parameters.Add($"{prefix}_CX", new ProcessorParameter($"{prefix}_CX", $"{prefix}_CX", typeof(int), cx, null, null, "") { IsVisible = false }); Parameters.Add($"{prefix}_CY", new ProcessorParameter($"{prefix}_CY", $"{prefix}_CY", typeof(int), cy, null, null, "") { IsVisible = false }); Parameters.Add($"{prefix}_A", new ProcessorParameter($"{prefix}_A", $"{prefix}_A", typeof(double), a, null, null, "") { IsVisible = false }); Parameters.Add($"{prefix}_B", new ProcessorParameter($"{prefix}_B", $"{prefix}_B", typeof(double), b, null, null, "") { IsVisible = false }); Parameters.Add($"{prefix}_Angle", new ProcessorParameter($"{prefix}_Angle", $"{prefix}_Angle", typeof(double), angle, null, null, "") { IsVisible = false }); } public override Image Process(Image inputImage) { double thtLimit = GetParameter("THTLimit"); int thickness = GetParameter("Thickness"); // 获取四个椭圆中心 int e1cx = GetParameter("E1_CX"), e1cy = GetParameter("E1_CY"); int e2cx = GetParameter("E2_CX"), e2cy = GetParameter("E2_CY"); int e3cx = GetParameter("E3_CX"), e3cy = GetParameter("E3_CY"); int e4cx = GetParameter("E4_CX"), e4cy = GetParameter("E4_CY"); // 获取椭圆轴参数(用于绘制? double e1a = GetParameter("E1_A"), e1b = GetParameter("E1_B"), e1ang = GetParameter("E1_Angle"); double e2a = GetParameter("E2_A"), e2b = GetParameter("E2_B"), e2ang = GetParameter("E2_Angle"); double e3a = GetParameter("E3_A"), e3b = GetParameter("E3_B"), e3ang = GetParameter("E3_Angle"); double e4a = GetParameter("E4_A"), e4b = GetParameter("E4_B"), e4ang = GetParameter("E4_Angle"); _logger.Debug("FillRate: E1=({E1X},{E1Y}), E2=({E2X},{E2Y}), E3=({E3X},{E3Y}), E4=({E4X},{E4Y})", e1cx, e1cy, e2cx, e2cy, e3cx, e3cy, e4cx, e4cy); OutputData.Clear(); // 计算通孔全高度的投影位移(E1底部 ?E2顶部? double fullDx = e2cx - e1cx; double fullDy = e2cy - e1cy; double fullDistance = Math.Sqrt(fullDx * fullDx + fullDy * fullDy); // 计算填锡高度的投影位移(E3起点 ?E4终点? double fillDx = e4cx - e3cx; double fillDy = e4cy - e3cy; double fillDistance = Math.Sqrt(fillDx * fillDx + fillDy * fillDy); // 填锡?= 填锡位移 / 全高度位? double fillRate = fullDistance > 0 ? (fillDistance / fullDistance) * 100.0 : 0; fillRate = Math.Clamp(fillRate, 0, 100); // 判定 string classification = fillRate >= thtLimit ? "PASS" : "FAIL"; // 存储结果 OutputData["FillRateResult"] = true; OutputData["FillRate"] = fillRate; OutputData["VoidRate"] = 100.0 - fillRate; OutputData["FullDistance"] = fullDistance; OutputData["FillDistance"] = fillDistance; OutputData["THTLimit"] = thtLimit; OutputData["Classification"] = classification; OutputData["Thickness"] = thickness; // 椭圆几何(用于绘制) OutputData["E1"] = (new Point(e1cx, e1cy), new Size((int)e1a, (int)e1b), e1ang); OutputData["E2"] = (new Point(e2cx, e2cy), new Size((int)e2a, (int)e2b), e2ang); OutputData["E3"] = (new Point(e3cx, e3cy), new Size((int)e3a, (int)e3b), e3ang); OutputData["E4"] = (new Point(e4cx, e4cy), new Size((int)e4a, (int)e4b), e4ang); string resultText = $"{fillRate:F1}% | {classification}"; OutputData["ResultText"] = resultText; _logger.Information("FillRate (geometric): {Rate}%, {Class}, FullDist={FD:F1}, FillDist={FiD:F1}", fillRate, classification, fullDistance, fillDistance); return inputImage.Clone(); } }