修复注释乱码
This commit is contained in:
@@ -1,25 +1,25 @@
|
||||
// ============================================================================
|
||||
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
|
||||
// 文件� BgaVoidRateProcessor.cs
|
||||
// æ��è¿°: BGA 空洞率检测算å�(两æ¥è‡ªåŠ¨æ£€æµ‹æ³•ï¼?
|
||||
// 文件名: BgaVoidRateProcessor.cs
|
||||
// 描述: BGA 空洞率检测算子(两步自动检测法)
|
||||
//
|
||||
// 处理流程:
|
||||
// 第一æ?â€?焊ç�ƒå®šä½�: 高斯模糊 â†?Otsuå��å�‘二值化 â†?é—è¿�ç®?â†?轮廓检æµ?â†?圆度过滤 â†?æ¤åœ†æ‹Ÿå�ˆ
|
||||
// 第二æ?â€?气泡检æµ? 焊ç�ƒè½®å»“掩ç � â†?å�Œé˜ˆå€¼åˆ†å‰?â†?轮廓检æµ?â†?é�¢ç§¯è¿‡æ»¤ â†?气泡率计ç®?
|
||||
// 第一步 — 焊球定位: 高斯模糊 → Otsu反向二值化 → 闭运算 → 轮廓检测 → 圆度过滤 → 椭圆拟合
|
||||
// 第二步 — 气泡检测: 焊球轮廓掩码 → 双阈值分割 → 轮廓检测 → 面积过滤 → 气泡率计算
|
||||
//
|
||||
// 支持多边形ROI限定检测区域,支持IPC-7095标准PASS/FAIL判定
|
||||
// æ£ç‰‡æ¨¡å¼�:焊ç�?暗区域,气泡=亮区åŸ?
|
||||
// 正片模式:焊球=暗区域,气泡=亮区域
|
||||
//
|
||||
// 作� �伟 wei.lw.li@hexagon.com
|
||||
// 作者: 李伟 wei.lw.li@hexagon.com
|
||||
// ============================================================================
|
||||
|
||||
using Emgu.CV;
|
||||
using Emgu.CV.CvEnum;
|
||||
using Emgu.CV.Structure;
|
||||
using Emgu.CV.Util;
|
||||
using XP.ImageProcessing.Core;
|
||||
using Serilog;
|
||||
using System.Drawing;
|
||||
using XP.ImageProcessing.Core;
|
||||
|
||||
namespace XP.ImageProcessing.Processors;
|
||||
|
||||
@@ -43,7 +43,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
LocalizationHelper.GetString("BgaVoidRateProcessor_RoiMode_Desc"),
|
||||
new string[] { "None", "Polygon" }));
|
||||
|
||||
// 多边形ROI点数和å��æ ‡ï¼ˆç”±UI注入,ä¸�å�¯è§�,最多支æŒ?2个点ï¼?
|
||||
// 多边形ROI点数和坐标(由UI注入,不可见,最多支持32个点)
|
||||
Parameters.Add("PolyCount", new ProcessorParameter("PolyCount", "PolyCount", typeof(int), 0, null, null, "") { IsVisible = false });
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
@@ -76,7 +76,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
typeof(double), 0.5, 0.0, 1.0,
|
||||
LocalizationHelper.GetString("BgaVoidRateProcessor_BgaCircularity_Desc")));
|
||||
|
||||
// ── 第二æ¥ï¼šæ°”泡检测å�‚æ•?──
|
||||
// ── 第二步:气泡检测参数 ──
|
||||
Parameters.Add("MinThreshold", new ProcessorParameter(
|
||||
"MinThreshold",
|
||||
LocalizationHelper.GetString("BgaVoidRateProcessor_MinThreshold"),
|
||||
@@ -148,7 +148,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
OutputData["RoiMode"] = roiMode;
|
||||
OutputData["RoiMask"] = roiMask;
|
||||
|
||||
_logger.Debug("BgaVoidRate ä¸¤æ¥æ³? BgaArea=[{Min},{Max}], Blur={Blur}, Circ={Circ}, Thresh=[{TMin},{TMax}]",
|
||||
_logger.Debug("BgaVoidRate 两步法: BgaArea=[{Min},{Max}], Blur={Blur}, Circ={Circ}, Thresh=[{TMin},{TMax}]",
|
||||
bgaMinArea, bgaMaxArea, bgaBlurSize, bgaCircularity, minThresh, maxThresh);
|
||||
|
||||
// ================================================================
|
||||
@@ -156,7 +156,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
// ================================================================
|
||||
var bgaResults = DetectBgaBalls(inputImage, bgaBlurSize, bgaMinArea, bgaMaxArea, bgaCircularity, roiMask);
|
||||
|
||||
_logger.Information("第一æ¥å®Œæˆ? 检测到 {Count} 个BGA焊ç�ƒ", bgaResults.Count);
|
||||
_logger.Information("第一步完成: 检测到 {Count} 个BGA焊球", bgaResults.Count);
|
||||
|
||||
if (bgaResults.Count == 0)
|
||||
{
|
||||
@@ -176,7 +176,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 第二æ¥ï¼šåœ¨æ¯�个焊ç�ƒåŒºåŸŸå†…检测气æ³?
|
||||
// 第二步:在每个焊球区域内检测气泡
|
||||
// ================================================================
|
||||
int totalBgaArea = 0;
|
||||
int totalVoidArea = 0;
|
||||
@@ -193,13 +193,13 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
double overallVoidRate = totalBgaArea > 0 ? (double)totalVoidArea / totalBgaArea * 100.0 : 0;
|
||||
string classification = overallVoidRate <= voidLimit ? "PASS" : "FAIL";
|
||||
|
||||
// 检查æ¯�个焊ç�ƒæ˜¯å�¦å�•独超æ ?
|
||||
// 检查每个焊球是否单独超标
|
||||
foreach (var bga in bgaResults)
|
||||
{
|
||||
bga.Classification = bga.VoidRate <= voidLimit ? "PASS" : "FAIL";
|
||||
}
|
||||
|
||||
_logger.Information("第二æ¥å®Œæˆ? 总气泡率={VoidRate:F1}%, 气泡æ•?{Count}, 判定={Class}",
|
||||
_logger.Information("第二步完成: 总气泡率={VoidRate:F1}%, 气泡数={Count}, 判定={Class}",
|
||||
overallVoidRate, totalVoidCount, classification);
|
||||
|
||||
// 输出数据
|
||||
@@ -222,7 +222,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
|
||||
/// <summary>
|
||||
/// 第一步:自动检测BGA焊球位置
|
||||
/// 使用Otsu二值化 + 轮廓检æµ?+ 圆度过滤 + æ¤åœ†æ‹Ÿå�ˆ
|
||||
/// 使用Otsu二值化 + 轮廓检测 + 圆度过滤 + 椭圆拟合
|
||||
/// </summary>
|
||||
private List<BgaBallInfo> DetectBgaBalls(Image<Gray, byte> input, int blurSize, int minArea, int maxArea, double minCircularity, Image<Gray, byte>? roiMask)
|
||||
{
|
||||
@@ -233,7 +233,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
var blurred = new Image<Gray, byte>(w, h);
|
||||
CvInvoke.GaussianBlur(input, blurred, new Size(blurSize, blurSize), 0);
|
||||
|
||||
// Otsu自动二值化(X-Rayæ£ç‰‡ï¼šç„Šç�?暗区域)
|
||||
// Otsu自动二值化(X-Ray正片:焊球=暗区域)
|
||||
var binary = new Image<Gray, byte>(w, h);
|
||||
CvInvoke.Threshold(blurred, binary, 0, 255, ThresholdType.Otsu | ThresholdType.BinaryInv);
|
||||
|
||||
@@ -264,7 +264,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
double circularity = 4.0 * Math.PI * area / (perimeter * perimeter);
|
||||
if (circularity < minCircularity) continue;
|
||||
|
||||
// 需è¦�至å°?个点æ‰�能拟å�ˆæ¤åœ†
|
||||
// 需要至少5个点才能拟合椭圆
|
||||
if (contours[i].Size < 5) continue;
|
||||
|
||||
var ellipse = CvInvoke.FitEllipse(contours[i]);
|
||||
@@ -284,7 +284,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
});
|
||||
}
|
||||
|
||||
// 按�积从大到�排�
|
||||
// 按面积从大到小排序
|
||||
results.Sort((a, b) => b.BgaArea.CompareTo(a.BgaArea));
|
||||
for (int i = 0; i < results.Count; i++) results[i].Index = i + 1;
|
||||
|
||||
@@ -296,8 +296,8 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 第二æ¥ï¼šåœ¨å�•个BGA焊ç�ƒåŒºåŸŸå†…检测气æ³?
|
||||
/// 使用焊ç�ƒè½®å»“作为掩ç �,å�Œé˜ˆå€¼åˆ†å‰²æ°”泡区åŸ?
|
||||
/// 第二步:在单个BGA焊球区域内检测气泡
|
||||
/// 使用焊球轮廓作为掩码,双阈值分割气泡区域
|
||||
/// </summary>
|
||||
private void DetectVoidsInBga(Image<Gray, byte> input, BgaBallInfo bga, int minThresh, int maxThresh, int minVoidArea)
|
||||
{
|
||||
@@ -314,7 +314,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
int bgaPixels = CvInvoke.CountNonZero(mask);
|
||||
bga.BgaArea = bgaPixels;
|
||||
|
||||
// å�Œé˜ˆå€¼åˆ†å‰²ï¼ˆæ£ç‰‡æ¨¡å¼�:气æ³?亮,ç�°åº¦åœ¨[minThresh, maxThresh]范围内判为气泡)
|
||||
// 双阈值分割(正片模式:气泡=亮,灰度在[minThresh, maxThresh]范围内判为气泡)
|
||||
var voidImg = new Image<Gray, byte>(w, h);
|
||||
byte[,,] srcData = input.Data;
|
||||
byte[,,] dstData = voidImg.Data;
|
||||
@@ -361,7 +361,7 @@ public class BgaVoidRateProcessor : ImageProcessorBase
|
||||
});
|
||||
}
|
||||
|
||||
// 按�积从大到�排�
|
||||
// 按面积从大到小排序
|
||||
bga.Voids.Sort((a, b) => b.Area.CompareTo(a.Area));
|
||||
for (int i = 0; i < bga.Voids.Count; i++) bga.Voids[i].Index = i + 1;
|
||||
|
||||
@@ -400,4 +400,4 @@ public class VoidInfo
|
||||
public double AreaPercent { get; set; }
|
||||
public Rectangle BoundingBox { get; set; }
|
||||
public Point[] ContourPoints { get; set; } = Array.Empty<Point>();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user