133 lines
6.4 KiB
C#
133 lines
6.4 KiB
C#
// ============================================================================
|
|
// 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;
|
|
|
|
/// <summary>
|
|
/// 通å”填锡率测é‡�ç®—å�ï¼ˆå€¾æ–œæŠ•å½±å‡ ä½•æ³•ï¼‰
|
|
/// </summary>
|
|
public class FillRateProcessor : ImageProcessorBase
|
|
{
|
|
private static readonly ILogger _logger = Log.ForContext<FillRateProcessor>();
|
|
|
|
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<Gray, byte> Process(Image<Gray, byte> inputImage)
|
|
{
|
|
double thtLimit = GetParameter<double>("THTLimit");
|
|
int thickness = GetParameter<int>("Thickness");
|
|
|
|
// 获å�–四个æ¤åœ†ä¸å¿ƒ
|
|
int e1cx = GetParameter<int>("E1_CX"), e1cy = GetParameter<int>("E1_CY");
|
|
int e2cx = GetParameter<int>("E2_CX"), e2cy = GetParameter<int>("E2_CY");
|
|
int e3cx = GetParameter<int>("E3_CX"), e3cy = GetParameter<int>("E3_CY");
|
|
int e4cx = GetParameter<int>("E4_CX"), e4cy = GetParameter<int>("E4_CY");
|
|
|
|
// 获å�–æ¤åœ†è½´å�‚数(用于绘制ï¼?
|
|
double e1a = GetParameter<double>("E1_A"), e1b = GetParameter<double>("E1_B"), e1ang = GetParameter<double>("E1_Angle");
|
|
double e2a = GetParameter<double>("E2_A"), e2b = GetParameter<double>("E2_B"), e2ang = GetParameter<double>("E2_Angle");
|
|
double e3a = GetParameter<double>("E3_A"), e3b = GetParameter<double>("E3_B"), e3ang = GetParameter<double>("E3_Angle");
|
|
double e4a = GetParameter<double>("E4_A"), e4b = GetParameter<double>("E4_B"), e4ang = GetParameter<double>("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();
|
|
}
|
|
} |