修复注释乱码

This commit is contained in:
李伟
2026-04-14 17:11:31 +08:00
parent b8bcefc84b
commit cd03e30bb8
58 changed files with 761 additions and 767 deletions
@@ -1,17 +1,17 @@
// ============================================================================
// 文件å? AngleMeasurementProcessor.cs
// æè¿°: 角度测é‡ç®—å­ â€?共端点的两æ¡ç›´çº¿å¤¹è§’
// 文件名: AngleMeasurementProcessor.cs
// 描述: 角度测量算子 — 共端点的两条直线夹角
// 功能:
// - 用户定义三个点:端点(顶点)ã€å°„çº?终点ã€å°„çº?终点
// - 计算两æ¡å°„线之间的夹角(0°~180°ï¼?
// - 用户定义三个点:端点(顶点)、射线1终点、射线2终点
// - 计算两条射线之间的夹角(0°~180°)
// - 在图像上绘制两条射线、角度弧线和标注
// ============================================================================
using Emgu.CV;
using Emgu.CV.Structure;
using XP.ImageProcessing.Core;
using Serilog;
using System.Drawing;
using XP.ImageProcessing.Core;
namespace XP.ImageProcessing.Processors;
@@ -27,7 +27,7 @@ public class AngleMeasurementProcessor : ImageProcessorBase
protected override void InitializeParameters()
{
// ä¸‰ä¸ªç‚¹åæ ‡ï¼ˆç”±äº¤äº’控件注入,使用 double é¿å…å–æ•´è¯¯å·®ï¼?
// 三个点坐标(由交互控件注入,使用 double 避免取整误差)
Parameters.Add("VX", new ProcessorParameter("VX", "VX", typeof(double), 250.0, null, null, "") { IsVisible = false });
Parameters.Add("VY", new ProcessorParameter("VY", "VY", typeof(double), 250.0, null, null, "") { IsVisible = false });
Parameters.Add("AX", new ProcessorParameter("AX", "AX", typeof(double), 100.0, null, null, "") { IsVisible = false });
@@ -44,7 +44,7 @@ public class AngleMeasurementProcessor : ImageProcessorBase
OutputData.Clear();
// å‘é‡ VA å’?VB
// 向量 VA VB
double vax = ax - vx, vay = ay - vy;
double vbx = bx - vx, vby = by - vy;
@@ -59,11 +59,11 @@ public class AngleMeasurementProcessor : ImageProcessorBase
angleDeg = Math.Acos(cosAngle) * 180.0 / Math.PI;
}
// 计算角度弧的起始角和扫过角(用于绘制弧线�
// 计算角度弧的起始角和扫过角(用于绘制弧线)
double angleA = Math.Atan2(vay, vax) * 180.0 / Math.PI;
double angleB = Math.Atan2(vby, vbx) * 180.0 / Math.PI;
// ç¡®ä¿ä»?angleA åˆ?angleB çš„æ‰«è¿‡æ–¹å‘æ˜¯è¾ƒå°çš„夹è§?
// 确保从 angleA angleB 的扫过方向是较小的夹角
double sweep = angleB - angleA;
if (sweep > 180) sweep -= 360;
if (sweep < -180) sweep += 360;
@@ -84,4 +84,4 @@ public class AngleMeasurementProcessor : ImageProcessorBase
return inputImage.Clone();
}
}
}
@@ -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>();
}
}
@@ -1,23 +1,23 @@
// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件å? ContourProcessor.cs
// 文件名: ContourProcessor.cs
// 描述: 轮廓查找算子,用于检测和分析图像中的轮廓
// 功能:
// - 检测图åƒä¸­çš„外部轮å»?
// - 检测图像中的外部轮廓
// - 根据面积范围过滤轮廓
// - 计算轮廓的几何特å¾ï¼ˆé¢ç§¯ã€å‘¨é•¿ã€ä¸­å¿ƒã€å¤–接矩形等ï¼?
// - 输出轮廓信æ¯ä¾›åŽç»­å¤„ç†ä½¿ç”?
// 算法: 基于OpenCV的轮廓检测算�
// 作è€? æŽä¼Ÿ wei.lw.li@hexagon.com
// - 计算轮廓的几何特征(面积、周长、中心、外接矩形等)
// - 输出轮廓信息供后续处理使用
// 算法: 基于OpenCV的轮廓检测算法
// 作者: 李伟 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;
@@ -117,7 +117,7 @@ public class ContourProcessor : ImageProcessorBase
OutputData.Clear();
// 创建输入图åƒçš„副本用于处ç?
// 创建输入图像的副本用于处理
Image<Gray, byte> processImage = inputImage.Clone();
// 步骤1:如果启用阈值分割,先进行二值化
@@ -128,18 +128,18 @@ public class ContourProcessor : ImageProcessorBase
if (useOtsu)
{
// 使用Otsu自动阈�
// 使用Otsu自动阈值
CvInvoke.Threshold(processImage, thresholdImage, 0, 255, ThresholdType.Otsu);
_logger.Debug("Applied Otsu threshold");
}
else
{
// 使用固定阈�
// 使用固定阈值
CvInvoke.Threshold(processImage, thresholdImage, thresholdValue, 255, ThresholdType.Binary);
_logger.Debug("Applied binary threshold with value {ThresholdValue}", thresholdValue);
}
// ä¿å­˜é˜ˆå€¼å¤„ç†åŽçš„图åƒç”¨äºŽè°ƒè¯?
// 保存阈值处理后的图像用于调试
try
{
string debugPath = Path.Combine("logs", $"contour_threshold_{DateTime.Now:yyyyMMdd_HHmmss}.png");
@@ -156,7 +156,7 @@ public class ContourProcessor : ImageProcessorBase
processImage = thresholdImage;
}
// 步骤2:如果目标是黑色区域,需è¦å转图åƒ?
// 步骤2:如果目标是黑色区域,需要反转图像
bool isBlackTarget = targetColor != null &&
(targetColor.Equals("Black", StringComparison.OrdinalIgnoreCase) ||
targetColor.Equals("黑色", StringComparison.OrdinalIgnoreCase));
@@ -180,7 +180,7 @@ public class ContourProcessor : ImageProcessorBase
}
}
// 步骤3:查找轮�
// 步骤3:查找轮廓
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
Mat hierarchy = new Mat();
@@ -1,64 +1,53 @@
// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件å? EllipseDetectionProcessor.cs
// æè¿°: 椭圆检测算å­ï¼ŒåŸºäºŽè½®å»“分æžå’Œæ¤­åœ†æ‹Ÿåˆæ£€æµ‹å›¾åƒä¸­çš„æ¤­åœ?
// 文件名: EllipseDetectionProcessor.cs
// 描述: 椭圆检测算子,基于轮廓分析和椭圆拟合检测图像中的椭圆
// 功能:
// - 阈值分å‰?+ 轮廓æå
// - 椭圆拟åˆï¼ˆFitEllipseï¼?
// - é¢ç§¯/è½´é•¿/离心çŽ?拟åˆè¯¯å·®å¤šç»´è¿‡æ»¤
// - 支æŒåŒé˜ˆå€¼åˆ†å‰²å’Œ Otsu 自动阈å€?
// 算法: 阈值分�+ OpenCV FitEllipse
// 作è€? æŽä¼Ÿ wei.lw.li@hexagon.com
// - 阈值分割 + 轮廓提取
// - 椭圆拟合(FitEllipse
// - 面积/轴长/离心率/拟合误差多维过滤
// - 支持双阈值分割和 Otsu 自动阈值
// 算法: 阈值分割 + OpenCV FitEllipse
// 作者: 李伟 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;
/// <summary>
/// 椭圆检测结�
/// 椭圆检测结果
/// </summary>
public class EllipseInfo
{
/// <summary>序号</summary>
public int Index { get; set; }
/// <summary>中心点X</summary>
public float CenterX { get; set; }
/// <summary>中心点Y</summary>
public float CenterY { get; set; }
/// <summary>长轴长度</summary>
public float MajorAxis { get; set; }
/// <summary>短轴长度</summary>
public float MinorAxis { get; set; }
/// <summary>旋转角度(度�/summary>
/// <summary>旋转角度(度)</summary>
public float Angle { get; set; }
/// <summary>面积</summary>
public double Area { get; set; }
/// <summary>周长</summary>
public double Perimeter { get; set; }
/// <summary>离心çŽ?(0=åœ? 接近1=æ‰æ¤­åœ?</summary>
/// <summary>离心率 (0=圆, 接近1=扁椭圆)</summary>
public double Eccentricity { get; set; }
/// <summary>拟合误差(像素)</summary>
public double FitError { get; set; }
/// <summary>轮廓点集</summary>
public Point[] ContourPoints { get; set; } = Array.Empty<Point>();
/// <summary>外接矩形</summary>
public Rectangle BoundingBox { get; set; }
}
@@ -81,7 +70,7 @@ public class EllipseDetector
public double MaxFitError { get; set; } = 5.0;
public int Thickness { get; set; } = 2;
/// <summary>执行椭圆检�/summary>
/// <summary>执行椭圆检测</summary>
public List<EllipseInfo> Detect(Image<Gray, byte> inputImage, Image<Gray, byte>? roiMask = null)
{
_logger.Debug("Ellipse detection started: UseOtsu={UseOtsu}, MinThreshold={Min}, MaxThreshold={Max}",
@@ -197,7 +186,7 @@ public class EllipseDetector
}
/// <summary>
/// 椭圆检测算�
/// 椭圆检测算子
/// </summary>
public class EllipseDetectionProcessor : ImageProcessorBase
{
@@ -211,7 +200,7 @@ public class EllipseDetectionProcessor : ImageProcessorBase
protected override void InitializeParameters()
{
// ── 多边形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++)
{
@@ -311,4 +300,4 @@ public class EllipseDetectionProcessor : ImageProcessorBase
_logger.Information("Ellipse detection completed: detected {Count} ellipses", ellipses.Count);
return inputImage.Clone();
}
}
}
@@ -1,26 +1,26 @@
// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件å? FillRateProcessor.cs
// 文件名: FillRateProcessor.cs
// 描述: 通孔填锡率测量算子(倾斜投影几何法),基于四椭圆ROI
// 功能:
// - æ ·å“倾斜çº?5°放置,利用投影ä½ç§»å…³ç³»è®¡ç®—填锡率
// - 四个椭圆定义�
// - 样品倾斜约45°放置,利用投影位移关系计算填锡率
// - 四个椭圆定义:
// E1 = 通孔底部轮廓
// E2 = 通孔顶部轮廓
// E3 = 填锡起点(与E1é‡åˆï¼Œä»£è¡?%填锡ï¼?
// E4 = 填锡终点(锡实际填充到的高度�
// - 填锡�= |E4中心 - E3中心| / |E2中心 - E1中心| × 100%
// - 纯几何方法,ä¸ä¾èµ–ç°åº¦åˆ†æž?
// - IPC-610 THT 分级判定(Class 1/2/3�
// E3 = 填锡起点(与E1重合,代表0%填锡)
// E4 = 填锡终点(锡实际填充到的高度)
// - 填锡率 = |E4中心 - E3中心| / |E2中心 - E1中心| × 100%
// - 纯几何方法,不依赖灰度分析
// - IPC-610 THT 分级判定(Class 1/2/3
// 算法: 倾斜投影位移比例
// 作è€? æŽä¼Ÿ wei.lw.li@hexagon.com
// 作者: 李伟 wei.lw.li@hexagon.com
// ============================================================================
using Emgu.CV;
using Emgu.CV.Structure;
using XP.ImageProcessing.Core;
using Serilog;
using System.Drawing;
using XP.ImageProcessing.Core;
namespace XP.ImageProcessing.Processors;
@@ -42,7 +42,7 @@ public class FillRateProcessor : ImageProcessorBase
// 四个椭圆(由交互控件注入,UI不可见)
AddEllipseParams("E1", 200, 250, 60, 50, 0); // 底部
AddEllipseParams("E2", 220, 180, 60, 50, 0); // 顶部
AddEllipseParams("E3", 200, 250, 60, 50, 0); // 填锡起点�E1�
AddEllipseParams("E3", 200, 250, 60, 50, 0); // 填锡起点(=E1
AddEllipseParams("E4", 210, 220, 55, 45, 0); // 填锡终点
Parameters.Add("THTLimit", new ProcessorParameter(
@@ -78,7 +78,7 @@ public class FillRateProcessor : ImageProcessorBase
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");
@@ -89,17 +89,17 @@ public class FillRateProcessor : ImageProcessorBase
OutputData.Clear();
// 计算通孔全高度的投影ä½ç§»ï¼ˆE1底部 â†?E2顶部ï¼?
// 计算通孔全高度的投影位移(E1底部 → E2顶部)
double fullDx = e2cx - e1cx;
double fullDy = e2cy - e1cy;
double fullDistance = Math.Sqrt(fullDx * fullDx + fullDy * fullDy);
// 计算填锡高度的投影ä½ç§»ï¼ˆE3起点 â†?E4终点ï¼?
// 计算填锡高度的投影位移(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);
@@ -130,4 +130,4 @@ public class FillRateProcessor : ImageProcessorBase
return inputImage.Clone();
}
}
}
@@ -1,27 +1,28 @@
// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件? LineMeasurementProcessor.cs
// 描述: 直线测量算子,用于测量图像中两点之间的距?
// 文件名: LineMeasurementProcessor.cs
// 描述: 直线测量算子,用于测量图像中两点之间的距
// 功能:
// - 用户指定两个点坐标(像素坐标?
// - 计算两点之间的欧氏距离(像素单位?
// - 支持像素尺寸标定,输出实际物理距?
// - 用户指定两个点坐标(像素坐标
// - 计算两点之间的欧氏距离(像素单位
// - 支持像素尺寸标定,输出实际物理距
// - 在图像上绘制测量线和标注
// - 输出测量结果供后续处理使?
// - 输出测量结果供后续处理使
// 算法: 欧氏距离计算
// 作? 李伟 wei.lw.li@hexagon.com
// 作者: 李伟 wei.lw.li@hexagon.com
// ============================================================================
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using XP.ImageProcessing.Core;
using Serilog;
using System.Drawing;
using XP.ImageProcessing.Core;
namespace XP.ImageProcessing.Processors;
/// <summary>
/// 直线测量算子 - 测量两点之间的距?
/// 直线测量算子 - 测量两点之间的距
/// </summary>
public class LineMeasurementProcessor : ImageProcessorBase
{
@@ -40,28 +41,28 @@ public class LineMeasurementProcessor : ImageProcessorBase
LocalizationHelper.GetString("LineMeasurementProcessor_X1"),
typeof(int), 100, null, null,
LocalizationHelper.GetString("LineMeasurementProcessor_X1_Desc"))
{ IsVisible = false });
{ IsVisible = false });
Parameters.Add("Y1", new ProcessorParameter(
"Y1",
LocalizationHelper.GetString("LineMeasurementProcessor_Y1"),
typeof(int), 100, null, null,
LocalizationHelper.GetString("LineMeasurementProcessor_Y1_Desc"))
{ IsVisible = false });
{ IsVisible = false });
Parameters.Add("X2", new ProcessorParameter(
"X2",
LocalizationHelper.GetString("LineMeasurementProcessor_X2"),
typeof(int), 400, null, null,
LocalizationHelper.GetString("LineMeasurementProcessor_X2_Desc"))
{ IsVisible = false });
{ IsVisible = false });
Parameters.Add("Y2", new ProcessorParameter(
"Y2",
LocalizationHelper.GetString("LineMeasurementProcessor_Y2"),
typeof(int), 400, null, null,
LocalizationHelper.GetString("LineMeasurementProcessor_Y2_Desc"))
{ IsVisible = false });
{ IsVisible = false });
Parameters.Add("PixelSize", new ProcessorParameter(
"PixelSize",
@@ -119,7 +120,7 @@ public class LineMeasurementProcessor : ImageProcessorBase
// 计算实际距离
double actualDistance = pixelDistance * pixelSize;
// 计算角度(相对于水平方向?
// 计算角度(相对于水平方向
double angleRad = Math.Atan2(dy, dx);
double angleDeg = angleRad * 180.0 / Math.PI;
@@ -146,4 +147,4 @@ public class LineMeasurementProcessor : ImageProcessorBase
return inputImage.Clone();
}
}
}
@@ -1,21 +1,22 @@
// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件å? PointToLineProcessor.cs
// 文件名: PointToLineProcessor.cs
// 描述: 点到直线距离测量算子
// 功能:
// - 用户定义一条直线(两个端点)和一个测量点
// - 计算测é‡ç‚¹åˆ°ç›´çº¿çš„垂直è·ç¦?
// - 计算测量点到直线的垂直距离
// - 支持像素尺寸标定输出物理距离
// - 在图像上绘制直线、测量点、垂足和距离标注
// 算法: 点到直线距离公式
// 作è€? æŽä¼Ÿ wei.lw.li@hexagon.com
// 作者: 李伟 wei.lw.li@hexagon.com
// ============================================================================
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using XP.ImageProcessing.Core;
using Serilog;
using System.Drawing;
using XP.ImageProcessing.Core;
namespace XP.ImageProcessing.Processors;
@@ -31,7 +32,7 @@ public class PointToLineProcessor : ImageProcessorBase
protected override void InitializeParameters()
{
// 直线两端ç‚?+ 测é‡ç‚¹ï¼ˆç”±äº¤äº’控件注入)
// 直线两端点 + 测量点(由交互控件注入)
Parameters.Add("L1X", new ProcessorParameter("L1X", "L1X", typeof(int), 100, null, null, "") { IsVisible = false });
Parameters.Add("L1Y", new ProcessorParameter("L1Y", "L1Y", typeof(int), 200, null, null, "") { IsVisible = false });
Parameters.Add("L2X", new ProcessorParameter("L2X", "L2X", typeof(int), 400, null, null, "") { IsVisible = false });
@@ -79,7 +80,7 @@ public class PointToLineProcessor : ImageProcessorBase
if (abLen > 0.001)
{
// å‰ç§¯æ±‚è·ç¦?
// 叉积求距离
double cross = Math.Abs(abx * (l1y - py) - aby * (l1x - px));
pixelDistance = cross / abLen;
@@ -112,4 +113,4 @@ public class PointToLineProcessor : ImageProcessorBase
return inputImage.Clone();
}
}
}
@@ -1,22 +1,22 @@
// ============================================================================
// 文件å? VoidMeasurementProcessor.cs
// 文件名: VoidMeasurementProcessor.cs
// 描述: 空隙测量算子
//
// 处理流程:
// 1. 构建多边形ROI掩码,计算ROI面积
// 2. 在ROI内进行åŒé˜ˆå€¼åˆ†å‰²æå–气泡区åŸ?
// 2. 在ROI内进行双阈值分割提取气泡区域
// 3. 形态学膨胀合并相邻气泡
// 4. 轮廓检测,计算每个气泡面积
// 5. 计算空隙çŽ?= 总气泡é¢ç§?/ ROIé¢ç§¯
// 5. 计算空隙率 = 总气泡面积 / ROI面积
// ============================================================================
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;
@@ -32,7 +32,7 @@ public class VoidMeasurementProcessor : ImageProcessorBase
protected override void InitializeParameters()
{
// ── 多边形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++)
{
@@ -40,7 +40,7 @@ public class VoidMeasurementProcessor : ImageProcessorBase
Parameters.Add($"PolyY{i}", new ProcessorParameter($"PolyY{i}", $"PolyY{i}", typeof(int), 0, null, null, "") { IsVisible = false });
}
// ── æ°”æ³¡æ£€æµ‹å‚æ•?──
// ── 气泡检测参数 ──
Parameters.Add("MinThreshold", new ProcessorParameter(
"MinThreshold",
LocalizationHelper.GetString("VoidMeasurementProcessor_MinThreshold"),
@@ -109,7 +109,7 @@ public class VoidMeasurementProcessor : ImageProcessorBase
}
else
{
// 无ROI时使用全�
// 无ROI时使用全图
roiMask = new Image<Gray, byte>(w, h);
roiMask.SetValue(new Gray(255));
}
@@ -152,7 +152,7 @@ public class VoidMeasurementProcessor : ImageProcessorBase
CvInvoke.BitwiseAnd(voidImg, roiMask, voidImg);
}
// ── 轮廓检�──
// ── 轮廓检测 ──
using var contours = new VectorOfVectorOfPoint();
using var hierarchy = new Mat();
CvInvoke.FindContours(voidImg, contours, hierarchy, RetrType.External, ChainApproxMethod.ChainApproxSimple);
@@ -183,7 +183,7 @@ public class VoidMeasurementProcessor : ImageProcessorBase
});
}
// 按é¢ç§¯ä»Žå¤§åˆ°å°æŽ’åº?
// 按面积从大到小排序
voids.Sort((a, b) => b.Area.CompareTo(a.Area));
for (int i = 0; i < voids.Count; i++) voids[i].Index = i + 1;
@@ -227,4 +227,4 @@ public class VoidRegionInfo
public double AreaPercent { get; set; }
public Rectangle BoundingBox { get; set; }
public Point[] ContourPoints { get; set; } = Array.Empty<Point>();
}
}