资讯动态

Halcon区域凸起缺陷检测:从Top-Hat到动态阈值的完整方案

发布时间:2026/10/17 11:43:19 来源:尧图企业网站定制
1. 区域凸起缺陷检测到底在检什么1.1 从产线痛点说起做过工业视觉的人大概都有这种体会表面缺陷检测里凹坑、划痕、脏污这类“减法型”缺陷相对好处理因为背景和缺陷的灰度差异通常比较明显阈值一卡、形态学一开基本就能把目标抠出来。真正让人头疼的是凸起类缺陷——它不改变区域的整体轮廓也不一定带来明显的灰度跳变但它在高度方向上“鼓”出来了。典型场景包括金属冲压件表面的微小凸包、注塑件表面的料花凸点、玻璃盖板上的异物凸起、锂电池极片涂层上的颗粒凸起、PCB焊盘上的锡珠等等。这类缺陷的麻烦在于如果你只用二维灰度图像去看它可能只是几个像素的亮度变化甚至和正常纹理混在一起阈值稍微松一点就漏检紧一点就误报。所以“区域凸起缺陷检测”这个题目的核心不是简单地做一次阈值分割而是要把“凸起”这个三维特征在二维图像里可靠地表达出来再配合区域约束把缺陷定位到具体位置。Halcon在这类任务里是一把好手它提供的算子组合足够灵活既能做基于灰度梯度的凸起增强也能做基于形态学的局部对比度提取还能结合区域特征做筛选。我下面就把整套思路拆开讲从原理到参数到踩坑尽量让你看完能直接上手改。1.2 适合谁来参考这篇内容适合三类人一是刚接触Halcon、手里有表面检测项目但不知道从哪下手的视觉工程师二是已经在用Halcon做缺陷检测、但凸起类缺陷漏检率居高不下、想找新思路的老手三是做自动化设备集成、需要评估视觉方案可行性的朋友。我不假设你精通Halcon所有算子但假设你会基本的图像读取、窗口显示和区域操作。如果你连read_image和dev_display都没用过建议先补一下基础再回来看这篇。另外说明一点凸起缺陷检测没有“一招鲜”的通用方案不同材质、不同光照、不同缺陷尺度参数差异很大。我给的是一套可复现的框架和调参逻辑你拿到自己的图上需要按我讲的思路去微调而不是照抄参数就完事。2. 整体方案设计与选型逻辑2.1 为什么不用深度学习一把梭现在一提到缺陷检测很多人第一反应是上深度学习。但在这个具体场景里我依然倾向于先用Halcon传统算子把方案跑通原因有三点。第一凸起缺陷的样本往往极少产线上可能一天就出几个你根本凑不齐训练集标注成本还高得离谱。第二传统算子的可解释性强客户问你这个缺陷为什么被判出来你能指着某一步说“这里梯度异常”深度学习你只能给个热力图沟通成本高。第三Halcon传统方案在CPU上跑节拍稳定不需要GPU设备成本低维护也简单。当然如果你的缺陷形态极其复杂、传统算子怎么调都压不住误报那再考虑深度学习也不迟。但在那之前把传统方案吃透往往能解决八成问题。2.2 核心思路把“凸起”转成“局部异常”凸起缺陷在二维图像里的本质是什么是局部区域的高度变化导致的光照反射差异。在明场照明下凸起的顶部通常比周围亮边缘可能偏暗在暗场照明下则相反。不管哪种它都表现为一种局部对比度异常——和周围邻域相比这个小区域的灰度分布“不太一样”。所以整套方案的核心逻辑就是构造一个能放大局部对比度的响应图然后在响应图上做阈值分割和区域筛选。具体来说我通常走这条链路图像预处理去噪、增强把干扰纹理压下去。局部对比度增强用形态学或频域方法把凸起响应提出来。阈值分割在响应图上分割出候选区域。区域筛选用面积、圆度、灰度均值等特征过滤掉误报。结果输出定位缺陷坐标输出给下游。这条链路里第2步是灵魂第4步是保命符。下面我逐步展开。2.3 光照方案对检测效果的决定性影响在讲算子之前我必须先强调光照。很多人算法调半天没效果最后发现是打光不对。凸起缺陷检测对光照极其敏感因为凸起本身就是靠反射差异体现的。我实测下来比较有效的两种打光方式一是低角度环形光光线掠射表面凸起会在背光侧形成阴影对比度拉满二是同轴光适合镜面材质凸起顶部会形成亮斑。如果你的现场只能用普通漫射光那凸起响应会很弱算法再强也白搭。所以如果你有权限改硬件先把光调好能省掉后面一半的调参时间。提示在算法调试阶段可以先用一张已知有凸起缺陷的图手动改变打光角度拍几张对比找到对比度最强的那个角度再固定下来。这一步花十分钟后面省十小时。3. 核心算子解析与参数调优3.1 局部对比度增强的三种武器Halcon里做局部对比度增强我常用的有三个方向各有适用场景。第一种形态学Top-Hat变换。这是最经典的凸起检测手段。top_hat的本质是原图减去开运算结果开运算会抹掉比结构元小的亮区域所以Top-Hat的结果就是那些“比结构元小、又比周围亮”的局部凸起。结构元的大小要略大于缺陷尺度比如你的凸起直径大概10像素结构元半径取15左右。代码大概长这样read_image (Image, part_surface.png) rgb1_to_gray (Image, GrayImage) gen_disk (StructElement, 15, byte) top_hat (GrayImage, TopHatImage, StructElement)Top-Hat的优点是快、简单、参数直观。缺点是如果背景本身有和大缺陷尺度相近的纹理会一起被提出来误报多。第二种局部均值差分。用一个大窗口的均值滤波结果减去原图或者原图减去均值得到局部偏差图。mean_image配合sub_image就能做。窗口大小同样要大于缺陷尺度。这个方法对缓慢变化的背景适应性好但边缘处容易产生响应。第三种频域带通滤波。把图像转到频域用带通滤波器保留缺陷对应的频率成分再转回来。这个方法对周期性纹理背景特别有效比如织物、网格状表面。但调参复杂实时性差我一般作为备选。实际项目里我八成情况用Top-Hat一成半用局部均值差分剩下半成才考虑频域。你可以先按这个优先级试。3.2 阈值分割动态阈值比固定阈值靠谱响应图出来之后下一步是分割。这里我强烈建议不要用固定阈值。为什么因为产线上光照会有波动工件批次之间表面状态也有差异固定阈值今天调好明天就飘了。我通常用dyn_threshold它拿原图和一张平滑后的图做比较逐像素判断偏差是否超过给定值。这个“给定值”是相对量对整体亮度变化不敏感。用法是mean_image (TopHatImage, MeanImage, 31, 31) dyn_threshold (TopHatImage, MeanImage, RegionDyn, 8, light)这里的8是偏移量意思是像素比局部均值亮8个灰度级以上才算候选。这个值需要根据你的响应图对比度来定一般取响应图里缺陷峰值和背景噪声峰值的中间偏上位置。如果你发现缺陷和噪声混在一起分不开说明响应图质量不够要回到上一步加强增强而不是在这里硬调阈值。3.3 区域筛选把误报挡在门外分割出来的候选区域里必然混着噪声、纹理、边缘响应。这时候就要靠区域特征筛选。我常用的特征和判断逻辑如下表特征算子典型用法注意事项面积area_center过滤太小噪声和太大背景的区域上下限都要设别只设下限圆度circularity凸起通常偏圆长条形多为划痕或纹理圆度阈值别设太高0.5左右即可灰度均值intensity凸起区域在响应图上均值应偏高结合响应图算不是原图矩形度rectangularity排除不规则纹理块对圆形凸起参考价值有限紧密度compactness辅助判断区域是否紧凑和圆度配合用筛选代码示例connection (RegionDyn, ConnectedRegions) select_shape (ConnectedRegions, SelectedRegions, area, and, 20, 500) select_shape (SelectedRegions, SelectedRegions2, circularity, and, 0.4, 1.0) area_center (SelectedRegions2, Area, Row, Column)这里面积下限20、上限500圆度下限0.4都是经验值你要按自己缺陷的实际像素尺度来定。定之前先用area_center把候选区域的面积打出来看看分布再决定卡在哪。注意筛选条件不要一次加太多容易把真缺陷也筛掉。我的做法是先只卡面积看剩下的误报长什么样再针对性地加特征。每加一个条件都回头确认真缺陷还在。4. 完整实操流程与现场记录4.1 从读图到输出的完整代码框架下面这套代码是我在一个金属表面凸起检测项目里实际用过的简化版你可以直接拿去改。假设图像是灰度图凸起缺陷直径在8到20像素之间。* 1. 读取与预处理 read_image (Image, metal_surface.png) get_image_size (Image, Width, Height) rgb1_to_gray (Image, GrayImage) * 2. 去噪保留边缘 median_image (GrayImage, MedianImage, circle, 3, mirrored) * 3. Top-Hat增强凸起响应 gen_disk (StructElement, 18, byte) top_hat (MedianImage, TopHatImage, StructElement) * 4. 动态阈值分割 mean_image (TopHatImage, MeanImage, 41, 41) dyn_threshold (TopHatImage, MeanImage, RegionDyn, 6, light) * 5. 区域筛选 connection (RegionDyn, ConnectedRegions) select_shape (ConnectedRegions, RegionArea, area, and, 15, 600) select_shape (RegionArea, RegionCirc, circularity, and, 0.35, 1.0) * 6. 结果输出 count_obj (RegionCirc, Number) if (Number 0) area_center (RegionCirc, Area, Row, Column) * 这里可以把坐标输出给下游或画框显示 endif这段代码里median_image的窗口我取3是为了去掉椒盐噪声但保留凸起边缘。如果你图像本身很干净这步可以省。gen_disk的半径18对应缺陷最大直径20像素左右结构元要略大于缺陷否则Top-Hat会把缺陷本身也当成背景抹掉。dyn_threshold的偏移量6是我在这批图上试出来的你的图可能要调。4.2 参数是怎么一步步定下来的很多人拿到代码直接跑发现效果不对就懵了。我讲讲我定参数的过程你照着做就能找到自己的值。第一步定结构元大小。先把top_hat的结果显示出来如果缺陷在响应图里是一个完整的亮斑说明结构元合适如果缺陷中间是暗的、只有边缘亮说明结构元太小把缺陷内部也开掉了如果响应图里缺陷很弱、背景纹理很强说明结构元太大把缺陷当背景了。我一般从缺陷最大直径的1.5倍开始试上下浮动。第二步定动态阈值偏移量。把TopHatImage的灰度直方图打出来你会看到背景噪声集中在一个低灰度区间缺陷响应在更高的区间。偏移量就取两个区间之间的谷底附近。如果两个区间重叠说明响应图对比度不够回第一步调结构元或者检查光照。第三步定面积范围。把ConnectedRegions里每个区域的面积打出来真缺陷的面积应该在一个相对集中的区间噪声面积通常很小背景误报面积通常很大。卡在中间就行。第四步定圆度。把候选区域叠加到原图上目视检查真凸起基本是圆的或椭圆的划痕、纹理是长条的。圆度卡0.35到0.4能滤掉大部分长条误报又不至于误杀椭圆凸起。这套流程走下来一般两三个小时能把一个稳定方案调出来。比盲目试参数快得多。4.3 一个真实项目的调参记录我之前做过一个注塑件表面凸点检测的项目工件是黑色塑料表面有细微的模具纹理。一开始用Top-Hat结构元半径12结果模具纹理全被提出来了误报上百个。后来分析发现模具纹理的尺度比凸点小凸点直径大概15像素纹理周期大概3像素。于是我把结构元半径加到20纹理被开运算抹掉了凸点保留下来。但新的问题是凸点响应变弱了因为结构元太大凸点边缘也被削掉一部分。解决办法是先做一次高斯平滑把纹理压下去再做Top-Hat。gauss_filter窗口取5纹理基本被抹平凸点因为尺度大还能保留。然后再Top-Hat结构元半径15效果就出来了。最终误报控制在个位数漏检为零。这个案例说明参数不是孤立的预处理和增强要配合着调。你如果遇到类似情况不妨试试先平滑再增强。5. 常见问题与排查速查5.1 漏检了怎么办漏检是凸起检测里最要命的问题因为客户往往对漏检零容忍。漏检的原因通常有三个响应太弱、阈值太高、筛选太狠。排查顺序我建议这样先把TopHatImage显示出来看缺陷位置有没有亮斑。如果没有亮斑说明增强不够要调结构元或换增强方法。如果有亮斑但没被分割出来说明dyn_threshold偏移量太大往小调。如果分割出来了但被筛掉了把筛选条件逐个去掉看是哪个条件杀的然后放宽那个条件。还有一种隐蔽的漏检缺陷在图像边缘。Top-Hat在图像边缘会有边界效应响应异常。解决办法是做一次gen_rectangle1把边缘区域排除或者对图像做镜像扩展再处理。5.2 误报太多怎么压误报多的原因和漏检相反响应图里噪声太多、阈值太低、筛选太松。但误报的排查更麻烦因为你要搞清楚每个误报是什么。我的做法是把误报区域一个个叠加到原图上分类。常见误报类型和对策如下表误报类型表现对策噪声点面积很小随机分布加大面积下限或预处理加中值滤波纹理块面积中等形状不规则调结构元大小或加圆度筛选边缘响应集中在工件轮廓附近用区域掩膜排除边缘反光斑面积大灰度很高加面积上限或换打光脏污形状随机灰度不均加灰度标准差筛选分类之后对症下药比盲目调参高效得多。5.3 光照波动导致方案不稳定这是传统视觉方案的通病。今天调好的阈值明天换一批工件就飘了。缓解办法有几个一是用dyn_threshold代替固定阈值它对整体亮度变化不敏感二是加自动曝光或光源控制器稳光三是在响应图上做归一化把响应图除以它的均值或最大值消除整体增益变化。如果波动实在太大可以考虑加一个参考工件每次检测前先拍参考件用参考件的响应做基准动态调整阈值。这个方案复杂一点但稳定性提升明显。5.4 检测节拍跟不上Halcon传统算子在CPU上跑单张图通常几十毫秒。如果你的节拍要求很高比如10毫秒以内那要优化。优化方向缩小ROI只处理可能有缺陷的区域降低图像分辨率如果缺陷尺度允许把median_image换成更快的mean_image减少不必要的区域筛选步骤。我实测下来一张500万像素的图上面那套流程跑下来大概40到60毫秒取决于结构元大小和筛选步骤。如果你要更快可以把Top-Hat的结构元换成矩形gen_rectangle1比gen_disk快不少代价是方向性稍差。6. 进阶技巧与方案扩展6.1 多尺度检测应对不同大小的凸起实际产线上凸起缺陷的大小往往不是单一的可能既有5像素的小凸点也有30像素的大凸包。单一结构元的Top-Hat很难同时覆盖。这时候可以用多尺度Top-Hat用几个不同大小的结构元分别做Top-Hat然后把响应图取最大值或求和。gen_disk (StructElement1, 8, byte) gen_disk (StructElement2, 15, byte) gen_disk (StructElement3, 25, byte) top_hat (MedianImage, TopHat1, StructElement1) top_hat (MedianImage, TopHat2, StructElement2) top_hat (MedianImage, TopHat3, StructElement3) max_image (TopHat1, TopHat2, MaxImage1) max_image (MaxImage1, TopHat3, MaxImage)这样小凸点被小结构元捕捉大凸包被大结构元捕捉响应图更全面。代价是耗时增加但换来的是漏检率下降值得。6.2 结合高度信息做三维凸起检测如果你的设备配了3D相机或激光轮廓仪那凸起检测就简单多了直接看高度图就行。但很多产线只有2D相机这时候可以用光度立体技术用多个不同方向的光源分别拍图通过光度立体算法恢复表面法向量再积分得到相对高度图。Halcon里有photometric_stereo算子支持这个。光度立体对凸起缺陷的检测效果比单张图好很多因为它直接拿到了高度信息凸起就是高度图上的局部极大值阈值分割非常干净。缺点是硬件成本高需要多光源和同步控制标定也麻烦。如果你的项目预算允许这是个值得考虑的升级方向。6.3 把检测结果可视化输出给下游检测出来只是第一步怎么把结果用好也重要。我通常会把缺陷区域画框、标号输出一张结果图给操作员看同时把缺陷坐标、面积、等级通过TCP或串口发给PLC。Halcon里用dev_display配合disp_message就能画生产环境里可以用write_image存图留档。如果要做缺陷分类比如区分凸点和脏污可以在筛选后对每个区域提取更多特征用classify_class_mlp做个简单的分类器。但样本少的时候规则分类往往比机器学习更稳。7. 我踩过的坑和给你的建议7.1 别忽视图像采集环节我见过太多项目算法调了一周没效果最后发现是相机没对焦、光源角度偏了、镜头有灰尘。凸起检测对成像质量要求极高因为你要捕捉的是微小的灰度差异。所以动手写代码之前先确认图像本身是清晰的、对比度足够的、没有过曝欠曝的。这一步做好了后面事半功倍。7.2 参数要留调试接口写Halcon程序的时候别把参数写死在代码里。把结构元大小、阈值偏移量、面积范围这些做成变量最好能通过界面或配置文件调整。产线换批次的时候操作员能自己微调不用每次都找你。我一般会做一个简单的参数面板用create_dialog或者直接读INI文件省去很多售后麻烦。7.3 建立缺陷样本库每次调试和运行中发现的缺陷图都存下来按类型分类。时间长了你就有了一个样本库一方面可以用来验证新方案另一方面如果以后要上深度学习这些就是现成的训练数据。我现在的习惯是每处理一个项目至少存50张真缺陷图和200张误报图后面受益无穷。7.4 关于Halcon版本的选择Halcon不同版本在算子性能和稳定性上有差异。做凸起检测我建议用18.11以后的版本dyn_threshold和top_hat的底层优化更好速度有提升。另外新版本的select_shape支持更多特征筛选更灵活。如果你还在用很老的版本遇到性能瓶颈可以考虑升级但要注意算子兼容性升级前先备份代码。最后分享一个小技巧如果你发现Top-Hat对某种缺陷效果不好不妨试试lines_gauss算子它对线状和脊状结构响应很好有时候凸起边缘会被它捕捉到作为补充手段挺有用。这个算子参数多调起来费劲但关键时刻能救场。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑