自适应分区结合LPF的贴片电阻空洞检测算法

发布时间:2026/9/19 17:51:41
自适应分区结合LPF的贴片电阻空洞检测算法 简介面向电子制造质量控制与X射线无损检测领域的一份技术文档围绕贴片电阻焊点内部空洞缺陷检测难题系统提出一种融合局部预拟合活动轮廓模型LPF与自适应圆形卷积核的自适应检测方案。文档先梳理回流焊空洞成因及对可靠性的影响再对比BGA焊球与贴片电阻焊点在2D X-Ray成像背景上的差异指出现有全局操作和深度学习方法在背景自适应上的不足进而详细展开自适应分区、空洞粗检测、空洞精细检测三阶段算法框架并给出形状因子与平均灰度剔除误检测等后处理细节。资源为1个docx文档压缩包约334KB内容以文字、公式和示意图形式呈现便于检索、打印与标注适合机器视觉及缺陷检测算法研究人员、PCB/电子制造工程师、相关专业学生作为技术参考也可作为相关课题论文的参考资料。目前已有94人学习。1. 从 BGA 迁移到贴片电阻空洞检测为什么必须换思路回流焊工艺里焊料冷却时气体没来得及逃逸就会在焊点内部形成空洞。空洞直接拉低导热和导电性能导致 PCB 机械强度下降、热阻增大严重时器件直接失效。行业内对空洞的判定标准很明确——空洞面积占比超过 10% 即判不合格所以检测焊点内部空洞、计算空洞占比率是产线上绕不开的质检环节。2D X-Ray 是产线上最常用的成像方案成本低、效率高。BGA 焊球的空洞检测已经比较成熟核心思路是模板匹配、LoG 算子、Blob 分析或基于稀疏分解的方法。但这些方法都有一个隐含前提——图像背景相对单一全局操作就能把焊球区域和背景区分开。贴片电阻的情况完全不同。焊点表面呈凹形曲线锡膏厚的地方 X 射线衰减大、成像暗薄的地方成像亮。采集到的图像背景差异极为明显亮暗区域分布不规则空洞的灰度级表征也跟着变。你把 BGA 那套全局检测方法直接搬过来效果会很差。实际测试中DRLSE 在贴片电阻图像上的 Dice 系数只有 0.5630几乎不可用。本文方法的核心思路是先把图像分成亮暗两个区域再分别用不同的检测策略。较暗区域对比度低、空洞少且偏大用 LPF局部预拟合活动轮廓模型较亮区域对比度高、空洞密集且偏小用自适应圆形卷积核。最后用形状因子和平均灰度策略做精细过滤。实测 Acc 达到 95.52%Dice 系数 0.8846比 U-Net 的 0.7650 高出不少。这篇博文会完整拆解这个方案从分区策略到 LPF 参数整定再到后处理每一步都给出可复现的细节。2. 自适应分区策略——把全局问题拆成局部问题贴片电阻焊点的 X-Ray 图像最突出的特征是背景灰度分布极不均匀。焊料厚的地方暗、薄的地方亮而且较暗和较亮区域的分界线大致与贴片电阻边缘重合。这意味着可以用一条类直线把图像切割成两个区域分别处理。2.1 分区问题的数学表达分区本质上是一个寻优问题——找到一条竖直分界线 i使得左右两个区域的平均灰度差异最大。原论文给出的目标函数是i_hat arg max | I_il(x) * coll(i) - I_ir(x) * colr(i) | 约束coll(i) colr(i) N/2N/4 ≤ i ≤ 3N/4其中I_il(x)和I_ir(x)表示分界线左侧和右侧区域的灰度值除以行数coll(i)和colr(i)是对应区域的像素列数N是图像总列数。约束条件把搜索范围限制在中间 50% 区域内避免分界线跑到图像边缘去。这一步的作用就是把后续算法需要面对的复杂图像空间简化成两个相对均匀的子区域。分界线在电阻边缘附近左边是比较亮的区域右边是比较暗的区域——或者反过来取决于具体的成像方向和器件摆放。2.2 求解实现——匀速迭代遍历这个目标函数不需要复杂的优化器遍历就能解决。常见的做法是设定步长为 5从N/4开始扫描到3N/4每次计算当前分界线两侧的平均灰度差值记录最大值。完整代码如下import numpy as np def adaptive_split(img, step5): 自适应分区寻找最优竖直分界线将图像分为亮/暗两个区域 参数 img: 2D numpy数组灰度图 step: 迭代步长默认5论文经验值 返回 bright_region, dark_region, best_i h, w img.shape half_w w // 2 search_start w // 4 search_end 3 * w // 4 best_score -1.0 best_i search_start for i in range(search_start, search_end 1, step): left img[:, :i] right img[:, i:] # 计算左右区域的平均灰度除以行数 mean_left np.mean(left) / h mean_right np.mean(right) / h # 加权平均灰度差 score abs(mean_left * left.shape[1] - mean_right * right.shape[1]) if score best_score: best_score score best_i i # 划分亮暗区域灰度均值大的为亮区 if np.mean(img[:, :best_i]) np.mean(img[:, best_i:]): bright_region img[:, :best_i] dark_region img[:, best_i:] else: bright_region img[:, best_i:] dark_region img[:, :best_i] return bright_region, dark_region, best_i这个实现有几个关键参数需要说明。step5是论文里经验性设置的迭代步长步长越小分区越精细但计算量线性增加实测在190×210左右的图像上步长 5 已经足够。mean(img) / h中的除以行数是为了消除图像高度对灰度均值的影响保证不同尺寸图像的分区结果可比。2.3 分区失败的诊断方法分区策略不是万能的。实际应用中如果分界线找偏了后续的检测精度会直接崩溃。怎么判断分区是否成功一个简单的验证方法是对分区后的两个区域分别计算灰度直方图如果两边的灰度分布有明显的双峰特征说明分区合理如果直方图接近重叠说明分界线位置不对。调试时我一般会写一段可视化代码把分界线画在原图上叠加显示检查是否与电阻边缘大致重合。另外要注意这个分区策略假设分界线是竖直的。如果产线上相机角度有偏差或者元件摆放不水平需要先做图像旋转校正再分区。实际项目中我用cv2.minAreaRect检测电阻主体方向把图像摆正后再进分区流程效果稳定很多。3. 较亮区域检测——自适应圆形卷积核的设计与实现分区之后较亮区域的典型特征是空洞密集、面积偏小、与背景的对比度相对较高。原始图像中空洞呈现为较暗的圆形/类圆形区域。如果直接用固定尺寸的高斯拉普拉斯或固定尺寸的圆形卷积核很难同时兼顾大大小小的空洞——小核漏检大空洞大核吞掉小空洞。3.1 核尺寸的计算逻辑论文的核心思路每张贴片电阻图像根据其较亮区域中最大空洞的面积自适应地设计圆形卷积核尺寸。公式是W_j 2 * int(sqrt(S_j / pi)) 1其中S_j是第 j 张图像较亮区域中最大空洞的面积像素数。这个公式的本质是把最大空洞近似为圆算出直径再加 1 保证覆盖完整边缘。要得到S_j需要先对较亮区域做一次预检测。常见做法是自适应阈值分割加形态学处理找到候选连通域取其中面积最大的作为基准。代码如下import cv2 import numpy as np def design_circular_kernel(bright_region): 根据较亮区域中最大空洞面积设计自适应圆形卷积核 返回 kernel: 二维numpy数组元素之和为0的圆形卷积核 W: 核尺寸边长 # 1. 自适应阈值分割初步提取暗色空洞候选区域 thresh cv2.adaptiveThreshold( bright_region, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, blockSize15, C5 ) # 2. 形态学开运算剔除小噪点闭运算填补空洞内部裂缝 kernel_size 3 kernel np.ones((kernel_size, kernel_size), np.uint8) morph cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations1) morph cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel, iterations2) # 3. 找连通域取最大面积 num_labels, labels, stats, _ cv2.connectedComponentsWithStats(morph) if num_labels 2: return None, None max_area 0 for i in range(1, num_labels): area stats[i, cv2.CC_STAT_AREA] if area max_area: max_area area # 4. 计算卷积核尺寸 W 2 * int(np.sqrt(max_area / np.pi)) 1 W max(W, 5) # 保证最小核尺寸不小于5 if W % 2 0: W 1 # 确保为奇数 # 5. 构造圆形卷积核圆形区域内中央为负、四周为正元素和为0 kernel np.zeros((W, W), dtypenp.float32) center (W - 1) / 2 radius (W - 1) / 2 for y in range(W): for x in range(W): dist np.sqrt((x - center) ** 2 (y - center) ** 2) if dist radius: kernel[y, x] -1 else: kernel[y, x] 1 # 归一化使元素之和为0 kernel_sum np.sum(kernel) kernel kernel - kernel_sum / (W * W) return kernel, W3.2 圆形卷积核为什么要元素之和为零元素之和为零意味着卷积核在平坦区域的响应为零。空洞是暗色圆形中央区域和周围背景的灰度差会在卷积后产生强烈的负响应而平坦背景区域不产生响应。等效于一个带通滤波器聚焦圆形暗斑。以论文中W5的核为例结构是中央一个 -1 的圆斑实际是中心 5 个 -1 形成的近似圆形四周是正值。卷积操作后空洞区域的像素值会被显著拉低然后 Otsu 阈值分割就能把这些响应极强的区域切出来。不需要用大卷积核的原因在于小核只捕捉局部邻域的对比差异而空洞检测关注的是局部暗斑不是大尺度结构。相同尺寸的卷积操作卷积的时间复杂度是 O(W²)核从 9×9 涨到 99×99计算量增加约 121 倍但检测精度在论文的对比实验里却从 0.44 提升到 0.77远不如自适应核的 0.8453。所以自适应设计的核心意义是用合理计算量获得最大提升。3.3 卷积 导向滤波 Otsu 的完整流程拿到自适应圆形卷积核之后检测较亮区域空洞的完整流程分三步。第一步用卷积核对原图做卷积得到强响应图第二步用导向滤波平滑响应图保边降噪第三步Otsu 全局阈值分割提取空洞。import cv2 import numpy as np def detect_bright_region_holes(bright_region, W): 基于自适应圆形卷积核的空洞检测流程 参数 bright_region: 较亮区域的灰度图numpy数组 W: 卷积核尺寸从design_circular_kernel中获取 返回 mask: 二值掩膜1表示空洞区域 # 1. 构造自适应圆形卷积核 kernel, _ design_circular_kernel(bright_region) if kernel is None: kernel np.zeros((W, W), dtypenp.float32) center (W - 1) / 2 for y in range(W): for x in range(W): if np.sqrt((x - center)**2 (y - center)**2) center: kernel[y, x] -1 else: kernel[y, x] 1 kernel kernel - np.sum(kernel) / (W * W) # 2. 卷积操作 response cv2.filter2D(bright_region, cv2.CV_32F, kernel) # 3. 导向滤波保边平滑 guided cv2.ximgproc.guidedFilter( sourcebright_region.astype(np.float32), guideresponse, radius5, eps0.01 ) # 4. Otsu阈值分割 guided_norm cv2.normalize(guided, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8) _, mask cv2.threshold(guided_norm, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) return mask这段代码里cv2.filter2D完成卷积cv2.ximgproc.guidedFilter是导向滤波。两个参数需要注意radius5控制滤波窗口比卷积核尺寸略大即可eps0.01是正则化系数值越小边缘保留越强但对噪声也更敏感。Otsu在这里不是直接作用于原图而是作用于导向滤波后的归一化响应图这样能避免原图亮暗不均带来的误分割。4. 较暗区域检测——LPF 模型的原理、参数与调优较暗区域的情况和较亮区域完全相反——空洞与背景的对比度很低空洞分布稀疏但面积偏大。圆形卷积核在这个区域内效果极差论文消融实验里 Dice 只有 0.3976基本等于乱检。问题在于卷积核对低对比度的暗斑响应很弱空洞信号被背景噪声淹没。这就要换武器——活动轮廓模型。4.1 LPF 能量泛函拆解LPFLocal Pre-Fitting模型的核心思想是在水平集函数演化之前先通过一个局部窗口计算两个预拟合函数e1(x)和e2(x)分别表示局部区域内低于和高于平均灰度值的部分的灰度均值。这样把原本需要迭代才能确定的区域统计信息提前算好显著降低计算量。总能量泛函有四项公式 4E_LPF(φ, e1, e2) ∫Ω (∫Ω Kσ(x-y) |I_dark(y) - e1(x)|² · H(φ(y)) dy) dx # 轮廓内拟合项 ∫Ω (∫Ω Kσ(x-y) |I_dark(y) - e2(x)|² · (1-H(φ(y))) dy) dx # 轮廓外拟合项 υ · ∫Ω δ(φ) |∇φ| dx # 长度正则项 u · ∫Ω 1/2(|∇φ| - 1)² dx # 距离正则项第一、二项是数据项衡量当前水平集轮廓内外像素与实际灰度值的偏差。第三项是长度惩罚项防止轮廓过度弯曲。第四项是距离正则化项避免水平集函数在演化过程中退化。严格来说υ0.02×255×255和u2是论文实验给出的经验值。0.02×255×255之所以要乘255×255是因为灰度值范围为 0-255长度项的像素距离乘以灰度差后量级和拟合项不同。论文原始代码中乘法的常数就保持这样固定跨数据集时一般只需要调u和迭代次数υ基本不用改。4.2 高斯核和局域窗口怎么选LPF 模型里有三个关键参数高斯核Kσ的尺寸、标准差σ、预拟合函数的局部窗口大小M。论文实验给出的最优配置是核尺寸 9×9、σ3这个配置在两个指标上平衡得最好——Dice 系数 0.8910 是峰值处理时间 10.6 秒也还在可接受范围内。核尺寸的影响可以从论文表 2 看清楚3×3 时 Dice 只有 0.8562因为窗口太小局部区域的统计量不稳定预拟合函数对噪声太敏感13×13 时 Dice 掉到 0.8881因为窗口过大局部区域包含了过多背景像素预拟合函数被背景灰度拉偏导致小空洞或对比度极低的空洞被漏检。4.3 迭代次数和步长的工程取舍LPF 的梯度下降流用最速下降法求解公式 7然后通过公式 9 迭代更新水平集函数。论文里 Δt0.2迭代 25 次。实际调试时我一般用这样的策略先跑 25 次看收敛情况如果轮廓还在明显变化就加迭代如果轮廓在 15 次前已经稳定说明 Δt 太大或迭代疑似溢出。还有一个常见的坑是Hε(x)和δε(x)里的 ε 参数。论文里的公式公式 6用的是 arctan 正则化ε 不能设太大否则边缘会模糊考虑到空洞边缘普遍只有几个像素宽ε1 是比较稳的起点。import numpy as np from skimage import filters, morphology def detect_dark_region_holes_lpf(dark_region, kernel_sigma3, kernel_size9, dt0.2, num_iter25, nu0.02*255*255, mu2): LPF局部预拟合活动轮廓模型检测较暗区域空洞 参数 dark_region: 较暗区域的灰度图 kernel_sigma: 高斯核标准差 kernel_size: 高斯核尺寸 dt: 时间步长迭代步长 num_iter: 迭代次数 nu: 长度正则项权重 mu: 距离正则项权重 返回 seg: 二值分割掩膜 # 高斯核 K np.zeros((kernel_size, kernel_size)) center kernel_size // 2 for y in range(kernel_size): for x in range(kernel_size): K[y, x] np.exp(-((x-center)**2 (y-center)**2) / (2 * kernel_sigma**2)) K K / np.sum(K) # 初始化水平集为符号距离函数 phi np.ones_like(dark_region, dtypenp.float32) * 2 phi[center:-center, center:-center] -2 # 预拟合函数 def pre_fitting(I, K): e np.zeros((2,) I.shape, dtypenp.float32) h, w I.shape radius kernel_size // 2 # 对每个像素计算局部均值 local_mean filters.rank.mean(I, morphology.square(kernel_size)) \ if kernel_size % 2 1 else filters.rank.mean(I, morphology.square(kernel_size1)) # 小于邻域均值的像素参与 e1 e1 np.array(local_mean) e2 np.array(local_mean) # 实际实现中推荐用局部卷积代替逐像素循环 I_float I.astype(np.float32) local_mean_conv filters.gaussian(I_float, sigmakernel_sigma) # 近似 mask_low I_float local_mean_conv mask_high I_float local_mean_conv e[0] local_mean_conv # e1 初始化 e[1] local_mean_conv # e2 初始化 # 细化分别计算满足条件像素的局部均值这里用简化近似 e[0][mask_low] I_float[mask_low] e[1][mask_high] I_float[mask_high] return e # 主迭代 for _ in range(num_iter): e pre_fitting(dark_region, K) # 计算数据项 I dark_region.astype(np.float32) E1 (I - e[0]) ** 2 E2 (I - e[1]) ** 2 # 正则化 eps 1.0 H 0.5 * (1 2/np.pi * np.arctan(phi / eps)) delta eps / (np.pi * (eps**2 phi**2)) # 轮廓内外拟合项的差值驱动演化 spf E1 - E2 # 长度项曲率 grad_y, grad_x np.gradient(phi) grad_mag np.sqrt(grad_x**2 grad_y**2 1e-8) div np.gradient(grad_x / grad_mag)[0] np.gradient(grad_y / grad_mag)[1] # 距离正则项 lap np.gradient(np.gradient(phi)[0])[0] np.gradient(np.gradient(phi)[1])[1] # 更新水平集 phi phi dt * ( delta * spf nu * delta * div mu * (lap - div) ) seg phi 0 return seg4.4 为什么 LPF 优于 DRLSE 和 RSFLoG对比实验里 DRLSE 的表现极其拉胯Dice 系数 0.5630这是有深层原因的。DRLSE 是边缘驱动型模型依赖图像梯度把水平集吸引到目标边界。但较暗区域中空洞边缘弱、梯度小水平集在演化过程中直接穿透空洞边界或者根本没被吸引到目标上。RSFLoG 稍好因为 LoG 增强了边缘响应但还是困于同样的梯度依赖问题。LPF 是区域驱动型的不需要边缘响应它靠的是局部区域灰度均值统计来区分前景背景。空洞内部的灰度虽然与背景差异小但在局部窗口中仍有统计意义上的区分。这就是它在较暗区域胜出的根本原因。LPF 的时间也值得注意25 次迭代需要 10.6 秒——这是用skimage/Python 实现 CPU 平台的参考数字C 实现能快一个数量级左右。不能在对比实验里用 Python 的 10 秒直接对比 C 的 0.3 秒需要做同平台对比。5. 精细检测——形状因子和平均灰度策略如何剔除误检粗检测合并亮暗区域的候选空洞后结果里混入了两类典型的假目标背景痕迹background trace和过锡孔vias。背景痕迹形状不规则好剔除过锡孔是圆形或类圆形只用形状特征极难区分。这需要两级过滤。5.1 形状因子——滤掉非类圆干扰形状因子的定义很简单SF 4πA / L²其中 A 是连通域面积L 是周长。圆形的形状因子最接近 1形状越不规则 SF 越小。对于长条形的背景痕迹SF 明显偏低直接阈值剔除。关键是阈值的设定。论文给出的是T1分类设定面积大于 60 像素的大空洞阈值 0.6面积小于 60 像素的小空洞阈值 0.8。为什么小的要更严因为小空洞的形状变化更剧烈检测像素级偏差对形状因子的影响也更敏感必须用更高的阈值才能把不规则伪目标滤干净。import cv2 import numpy as np def shape_filter(seg_mask, T1_large0.6, T1_small0.8, area_thresh60): 形状因子过滤剔除不规则连通域 返回 filtered_mask: 经过形状过滤的二值图 num_labels, labels, stats, _ cv2.connectedComponentsWithStats(seg_mask.astype(np.uint8), connectivity8) filtered np.zeros_like(seg_mask) for i in range(1, num_labels): area stats[i, cv2.CC_STAT_AREA] x, y, w, h (stats[i, cv2.CC_STAT_LEFT], stats[i, cv2.CC_STAT_TOP], stats[i, cv2.CC_STAT_WIDTH], stats[i, cv2.CC_STAT_HEIGHT]) if area 10: # 过小区域直接丢弃 continue # 提取联通域轮廓 contour_mask (labels i).astype(np.uint8) contours, _ cv2.findContours(contour_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) length cv2.arcLength(contours[0], closedTrue) # 计算形状因子 sf 4 * np.pi * area / (length * length) if length 0 else 0 # 自适应阈值选择 t T1_small if area area_thresh else T1_large if sf t: filtered[labels i] 1 return filtered5.2 平均灰度策略——解决过锡孔的伪圆形伪装过锡孔在 X-Ray 图像中也呈现为圆形暗斑形状因子这一关直接放过去了。但过锡孔的中心通常是空的平均灰度极低而空洞内部是焊料平均灰度比周围背景略低但没到极低水平。基于这个差异论文提出公式 12mean(R_sl) - mean(R_ecl - R_sl) T2其中R_sl是候选连通域在原图上对应的区域R_ecl是它的最小外接矩形区域。不等式成立条件意味着候选区域的平均灰度大于周围一圈外接矩形去掉候选区域的平均灰度加阈值 T2。过锡孔是贯通孔中心区域几乎不吸收 X 射线所以 mean(R_sl) 明显低于周围不等式不成立被剔除。代码实现如下def avg_gray_filter(seg_mask, origin_img, T20.01): 平均灰度判定策略剔除过锡孔等伪目标 参数 seg_mask: 经过形状因子过滤后的二值图 origin_img: 原始灰度图 T2: 灰度差阈值论文默认0.01 返回 final_mask: 最终精细检测结果 num_labels, labels, stats, _ cv2.connectedComponentsWithStats(seg_mask.astype(np.uint8), connectivity8) final_mask np.zeros_like(seg_mask) for i in range(1, num_labels): x, y, w, h (stats[i, cv2.CC_STAT_LEFT], stats[i, cv2.CC_STAT_TOP], stats[i, cv2.CC_STAT_WIDTH], stats[i, cv2.CC_STAT_HEIGHT]) roi labels i rect np.zeros_like(seg_mask) rect[y:yh, x:xw] 1 rect_area rect[rect 1] # 候选区域在原图上对应的区域 orig_roi origin_img[roi] # 外接矩形去掉候选区域 ring (rect.astype(bool) ~roi) ring_roi origin_img[ring] if len(orig_roi) 0 or len(ring_roi) 0: continue mean_obj np.mean(orig_roi) mean_ring np.mean(ring_roi) if mean_obj - mean_ring T2: final_mask[roi] 1 return final_mask5.3 两级过滤的顺序为什么不能换形状因子过滤必须在前平均灰度过滤在后。因为平均灰度策略的外接矩形区域计算依赖于连通域的完整程度如果先做灰度过滤残留的不规则伪目标会干扰外接矩形的计算导致正常空洞被误删。同时形状过滤能先把面积差异较大的噪声区域清掉减少后续灰度统计的干扰。两级过滤之后输出的是干净的二值空洞掩膜这一步得到的二值图直接叠加到原图上计算空洞占比率。需要注意的是空洞占比率的分母是焊点区域总面积不是整图面积。这个细节在实际工程里容易踩坑。6. 把方法搬进产线——参数表、阈值调优和复现路线前面几章把算法原理和核心代码拆完这章说工程落地。论文的实验数据是 134 张图像、6 核 i5-10600KF CPU、Pycharm 环境。你把这套方法接进产线要关注的不是算法本身的效果——论文已经把效果验证过了——而是怎么在不同数据上快速复现和调优。6.1 一张参数速查表模块参数论文取值调优建议自适应分区step 迭代步长5图像尺寸放大时适当调大如 700×800 用 9圆形卷积核W 核尺寸自适应最小 5必须为奇数导向滤波radius5随 W 增大适当增加导向滤波eps0.010.005~0.05噪声大时调大LPF高斯核尺寸9×9空洞较小用 7×7对比度低用 11×11LPFσ 标准差3核尺寸的 1/3LPFΔt 时间步长0.2大于 0.5 容易震荡不收敛LPF迭代次数25观察是否收敛最多不超过 50LPFν 长度正则项0.02×255²一般不动除非空洞边缘过度平滑LPFu 距离正则项20.5~5 之间试验形状因子T1大空洞 60px0.6误检多时提高到 0.7形状因子T1小空洞 ≤60px0.8漏检多时降到 0.7平均灰度T20.01过锡孔误检严重时降低到 -0.026.2 调优的顺序和方法调参不要一上来就动 LPF 内部参数。先跑通整体流程用一个已知合格/不合格的样本集做基准记录 Acc、F1、Dice 三个指标。然后按以下顺序排查先看分区结果对不对。在原始图上画分界线如果分界线和电阻边缘明显不重合问题出在分区解决它比调后续任何参数都有用。再看较亮区域的粗检测结果——把卷积响应图和 Otsu 结果叠在图上如果空洞被漏检多半是最大空洞面积估算不准需要查看预检测的形态学核大小是否合适。较暗区域则重点关注 LPF 的迭代过程输出中间水平集曲线叠加到原图上观察是否有断裂、有无误收入背景区域。最后看精细检测。如果留下太多非圆形干扰调低 T1如果圆形过锡孔还在调低 T2 的数值。6.3 一个值得关注的边界情况论文最后提到 LPF 的局部窗口大小固定对灰度强度的泛化能力较差且 OTSU 是全局阈值处理不完全合适。实际工程中如果待测产品换了一款、厚度或焊料成分不同图像的灰度分布会整体偏向此时直接复现论文参数效果可能不理想。一个实用的妥协方案是对较暗区域只调核尺寸和迭代次数不动 ν 和 u对较亮区域的 Otsu 做一步改进用带mask参数的局部 Otsu 替代全局 Otsu——较亮区域只需在分区掩膜上计算直方图即可。这一步改进成本极低对灰度分布漂移的鲁棒性提升却很明显。另一个更省力的做法是引入形状先验。很多产线的空洞数据有明显规律——椭圆度有限、面积范围已知。可以在形状因子过滤之后再加一个连通域面积范围检查把超出[min_area, max_area]的目标直接删除。实测跟在 T1 阈值后面做能再压掉一部分误检。这篇方法的价值在于自适应分区和双路检测策略——它不假设产线图像是干净的、均匀的而是主动把复杂背景拆成两个相对可控的子问题处理。在柔性生产换线频繁的场景里这套思路比训练深度学习模型更省事、也更可解释。本文还有配套的精品资源点击获取