HALCON 20.11实战:5步搞定工业图像异常检测(附完整代码)

📅 发布时间:2026/7/4 1:53:52 👁️ 浏览次数:
HALCON 20.11实战:5步搞定工业图像异常检测(附完整代码)
HALCON 20.11实战5步搞定工业图像异常检测附完整代码最近和几个在汽车零部件厂做质检的朋友聊天他们最头疼的就是金属表面那些细微的划痕和印刷电路板上的微小缺陷。人工盯久了眼睛累漏检率还高老板天天催着上自动化。其实这类问题用HALCON的深度学习异常检测模块来搞定效率能提升好几个量级。今天我就结合一个实际的金属零件表面检测案例把从零搭建一个异常检测系统的完整流程拆解给你看重点是那些能直接复制粘贴的代码和调参时容易踩的坑。这套方法特别适合产线上只有“良品”图片但缺陷五花八门、难以穷举的场景。你不用费劲去收集各种缺陷样本模型自己就能学会“正常”应该长什么样然后把它没见过的、反常的东西给揪出来。下面这五个步骤我会一步步带你走通。1. 环境准备与数据预处理工欲善其事必先利其器。在开始写代码之前确保你的HALCON环境是20.11或更高版本并且已经正确安装了深度学习模块。数据是模型的粮食预处理这一步直接决定了模型最终的“消化吸收”能力。我手头有一批金属冲压件的表面图像大约500张都是产线上确认过的合格品。我们的目标是用这些“好”的图片训练模型让它能识别出划痕、凹坑、污渍等异常。首先我们需要创建一个HALCON能识别的数据集字典。* 步骤1.1: 读取图像文件并创建异常检测数据集 ImageDir : path/to/your/ok_images/ read_dl_dataset_anomaly (ImageDir, last_folder, [], [], DLDataset)这里‘last_folder’参数意味着HALCON会将每个图像所在文件夹的最后一层名称作为图像标签。由于我们只有“ok”良品类所有图片都应放在以“ok”命名的文件夹下。DLDataset这个字典就像是一个数据管家记录了所有图像路径和标签信息。接下来我们需要把数据集分成训练集和验证集。验证集用于在训练过程中监控模型是否“学偏了”。* 步骤1.2: 划分训练集和验证集 TrainingPercent : 80 split_dl_dataset (DLDataset, TrainingPercent, [], TrainDataset, ValidationDataset)我通常习惯用80%的数据做训练20%做验证。这个比例可以根据你的数据总量微调数据量越大验证集比例可以适当减小。然后我们需要读取预训练的异常检测模型并获取它对输入图像的要求。* 步骤1.3: 读取模型并获取预处理参数 ModelFile : anomaly_detection.hdl read_dl_model (ModelFile, DLModelHandle) get_dl_model_param (DLModelHandle, image_dimensions, ImageDimensions) get_dl_model_param (DLModelHandle, image_range_min, RangeMin) get_dl_model_param (DLModelHandle, image_range_max, RangeMax)ImageDimensions会告诉你模型需要的图像尺寸比如[256, 256]。RangeMin和RangeMax则指定了图像像素值的归一化范围通常是[0, 1]或[-1, 1]。知道这些要求后我们就可以创建预处理参数字典了。* 步骤1.4: 创建并设置预处理参数 create_dl_preprocess_param (anomaly_detection, model, DLModelHandle, [], [], DLPreprocessParam) set_dl_preprocess_param (DLPreprocessParam, image_width, ImageDimensions[0]) set_dl_preprocess_param (DLPreprocessParam, image_height, ImageDimensions[1]) set_dl_preprocess_param (DLPreprocessParam, normalization_type, constant_values) set_dl_preprocess_param (DLPreprocessParam, mean_values, [127.5, 127.5, 127.5]) set_dl_preprocess_param (DLPreprocessParam, dev_values, [127.5, 127.5, 127.5])这里有个关键点normalization_type。我常用‘constant_values’配合特定的均值和标准差将像素值从0-255线性映射到-1到1之间这对模型训练的稳定性很有帮助。最后执行预处理* 步骤1.5: 对数据集进行预处理 preprocess_dl_dataset (TrainDataset, DataDirectory, DLPreprocessParam, GenParam, PreprocessedTrainDataset) preprocess_dl_dataset (ValidationDataset, DataDirectory, DLPreprocessParam, GenParam, PreprocessedValidationDataset)预处理后的图像和标签信息会被保存到DataDirectory指定的目录中同时生成新的、包含了预处理后文件路径的数据集字典。强烈建议把DLPreprocessParam字典保存下来因为后续推理新图片时必须使用完全相同的预处理参数。* 步骤1.6: 保存预处理参数供推理时使用 write_dict (DLPreprocessParam, preprocess_param.hdict, [], [])2. 模型训练与参数调优数据准备好了接下来就是“炼丹”环节。训练一个异常检测模型核心是让模型学会“正常”的表征。我们不需要标注缺陷只需要告诉模型这些图片都是好的。首先配置训练参数。这部分参数就像烹饪的火候和时间对最终效果影响巨大。* 步骤2.1: 创建训练参数字典 create_dl_train_param (DLModelHandle, [], [], anomaly_detection, 0.0001, adam, [], TrainParam)这里0.0001是初始学习率‘adam’是优化器。对于异常检测我习惯从较小的学习率开始避免训练初期震荡太大。接下来我们可以在TrainParam字典里设置更多细节* 步骤2.2: 设置详细的训练参数 set_dl_train_param (TrainParam, learning_rate, 0.0001) set_dl_train_param (TrainParam, batch_size, 8) set_dl_train_param (TrainParam, num_epochs, 100) set_dl_train_param (TrainParam, display_inference, true) set_dl_train_param (TrainParam, eval_frequency, epoch)注意异常检测模型的训练通常不使用传统的“批次”batch概念而是每次使用整个数据集所以‘batch_size’参数可能被忽略。但设置它也无妨。‘display_inference’设为‘true’可以在训练过程中可视化一些中间结果方便你直观感受模型的学习进度。开始训练* 步骤2.3: 启动模型训练 train_dl_model (DLModelHandle, PreprocessedTrainDataset, PreprocessedValidationDataset, TrainParam, 0, TrainResults, EvaluationResults)训练过程会输出TrainResults和EvaluationResults两个字典。你需要重点关注验证集上的损失loss曲线。一个健康的训练过程训练损失和验证损失都应该稳步下降并逐渐趋于平缓。如果出现验证损失上升过拟合你可能需要增加数据量收集更多“正常”样本。使用数据增强在预处理参数中可以加入随机旋转、平移、缩放等模拟更多样的正常状态。降低模型复杂度或增加正则化不过HALCON的预训练模型通常已经过优化这一步调整空间有限。提前停止Early Stopping监控验证损失当其在连续多个周期如10个不再下降时自动停止训练。HALCON可以通过设置‘stop_training_condition’参数来实现。训练完成后别忘了保存辛苦“炼制”出的模型* 步骤2.4: 保存训练好的模型 write_dl_model (DLModelHandle, trained_anomaly_model.hdl)3. 模型评估与阈值选择模型训练好了但它到底有多“准”我们需要量化评估。更重要的是模型输出的是一个0到1之间的“异常分数”我们需要设定一个阈值来决定分数高于多少才算“异常”。首先我们可以用验证集或者一个单独的测试集进行定量评估。* 步骤3.1: 在测试集上评估模型 GenParamEval : [] evaluate_dl_model (DLModelHandle, PreprocessedTestDataset, anomaly_detection, GenParamEval, EvaluationResult)EvaluationResult字典里会包含一些评估指标。但对于异常检测更关键的一步是阈值计算。HALCON提供了一个非常实用的函数compute_dl_anomaly_threshold它能基于验证集或测试集的结果帮你计算出几个候选阈值。* 步骤3.2: 计算异常分数阈值 GenParamThreshold : [] compute_dl_anomaly_threshold (DLModelHandle, PreprocessedValidationDataset, GenParamThreshold, Thresholds)Thresholds是一个数组通常包含基于不同准则如最大化F1分数、固定误报率等计算出的多个阈值。你可以选择一个比如第一个索引0通常是一个平衡了查全率和查准率的推荐值。SelectedThreshold : Thresholds[0]但这个阈值不一定是最优的。我强烈建议你进行可视化检查。手动调整阈值观察模型在哪些图片上发出了警报以及警报区域是否精准。下面这个代码片段可以帮助你快速浏览不同阈值下的效果* 步骤3.3: 可视化不同阈值下的检测结果 for Index : 0 to |ImageFiles|-1 by 1 read_image (Image, ImageFiles[Index]) * 预处理单张图像需使用之前保存的DLPreprocessParam gen_dl_samples_from_images (Image, DLSample) read_dict (preprocess_param.hdict, [], [], DLPreprocessParam) preprocess_dl_samples (DLSample, DLPreprocessParam) * 推理 apply_dl_model (DLModelHandle, DLSample, [], DLResult) * 应用阈值并获取异常区域 threshold_dl_anomaly_results (DLResult, anomaly_image, SelectedThreshold, anomaly_region, ThresholdedResult) * 显示原图、异常热力图和阈值化后的区域 dev_display (Image) get_dict_tuple (ThresholdedResult, anomaly_image, AnomalyImage) get_dict_tuple (ThresholdedResult, anomaly_region, AnomalyRegion) * ... 此处添加显示代码将原图、热力图、区域叠加显示 ... endfor通过这种可视化你能直观地感受到阈值调高更严格漏检多和调低更宽松误报多的影响。最终选择一个在业务可接受的误报率下检出率最高的阈值。4. 推理部署与完整代码示例到了最激动人心的环节把训练好的模型用起来。下面是一个完整的、可以集成到产线视觉系统中的推理脚本。假设我们已经有了训练好的模型trained_anomaly_model.hdl和预处理参数preprocess_param.hdict。* 步骤4.1: 初始化——加载模型和参数 dev_update_off () read_dl_model (trained_anomaly_model.hdl, DLModelHandle) read_dict (preprocess_param.hdict, [], [], DLPreprocessParam) * 设置模型推理参数例如阈值 AnomalyThreshold : 0.05 * 这是之前步骤中确定的最佳阈值 set_dl_model_param (DLModelHandle, anomaly_threshold, AnomalyThreshold) * 步骤4.2: 单张图像推理函数 procedure infer_anomaly (InputImage, DLModelHandle, DLPreprocessParam, AnomalyScore, AnomalyRegion) * 生成样本字典 gen_dl_samples_from_images (InputImage, DLSample) * 预处理必须与训练时一致 preprocess_dl_samples (DLSample, DLPreprocessParam) * 模型推理 apply_dl_model (DLModelHandle, DLSample, [], DLResult) * 提取异常分数和区域 get_dict_tuple (DLResult, anomaly_score, AnomalyScore) threshold_dl_anomaly_results (DLResult, anomaly_image, AnomalyThreshold, anomaly_region, ThresholdedResult) get_dict_tuple (ThresholdedResult, anomaly_region, AnomalyRegion) return () endprocedure * 步骤4.3: 模拟产线连续检测 AcqHandle : open_framegrabber (DirectShow, 1, 1, 0, 0, 0, 0, default, -1, rgb, -1, false, default, 0, -1, -1, AcqHandle) while (true) * 采集图像 grab_image (Image, AcqHandle) * 调用推理函数 infer_anomaly (Image, DLModelHandle, DLPreprocessParam, Score, DefectRegion) * 判断与输出 if (Score AnomalyThreshold) dev_display (Image) dev_set_color (red) dev_display (DefectRegion) * 触发NG信号或保存结果 write_image (Image, png, 0, defect_image_ (time()$.3f) .png) * 可以同时保存异常热力图用于追溯 get_dict_tuple (DLResult, anomaly_image, Heatmap) write_image (Heatmap, png, 0, heatmap_ (time()$.3f) .png) disp_message (3600, Defect Detected! Score: Score$.4f, window, 12, 12, red, true) else disp_message (3600, OK. Score: Score$.4f, window, 12, 12, forest green, true) endif * 控制循环频率 wait_seconds (0.1) endwhile close_framegrabber (AcqHandle) clear_dl_model (DLModelHandle)这个脚本展示了从相机抓图、预处理、推理到结果判断和输出的完整闭环。AnomalyThreshold是关键它直接决定了系统的灵敏度。5. 实战技巧与避坑指南纸上得来终觉浅绝知此事要躬行。在实际项目中摸爬滚打我总结了几个能显著提升效果和稳定性的技巧。技巧一数据质量是天花板。你的“正常”训练集必须尽可能纯净。哪怕混入一张有轻微缺陷的图片模型也会把这种缺陷当作“正常”特征学习进去导致后续检测失灵。在上线前务必对训练集进行一轮人工复查。技巧二关注光照与背景一致性。工业现场的光照变化是模型的一大敌人。如果训练数据是在特定光照下采集的而产线环境光照有变化模型性能会急剧下降。解决方案在数据采集阶段就使用稳定的光源。如果条件允许可以在预处理中加入简单的光照归一化算法或者使用数据增强来模拟轻微的光照变化提升模型鲁棒性。代码片段简易光照均衡* 在预处理前对图像进行CLAHE限制对比度自适应直方图均衡化处理 equ_histo_image (Image, ImageEqu) * 然后将ImageEqu送入后续的预处理流程技巧三理解“异常分数”的含义。模型输出的anomaly_score是一个相对值不是绝对值。不同产品、不同材质、不同拍摄条件下“正常”的分数基线可能不同。因此阈值可能需要针对不同产线或产品进行微调不能一个阈值打天下。技巧四利用好“异常热力图”。anomaly_image这个输出非常宝贵它用灰度图直观显示了图像中每个像素的“异常可能性”。不仅可以用它来生成二值化的缺陷区域还可以辅助定位热力图能清晰指示缺陷的中心和大致形状。缺陷分类初级通过观察热力图的纹理和分布模式有经验的工程师可以大致判断缺陷类型如线状可能是划痕块状可能是污渍。过程监控统计一批产品热力图的平均亮度或方差可以监控生产过程的稳定性。如果平均异常分数在缓慢上升可能预示着设备即将出现故障。技巧五模型更新策略。生产环境不是一成不变的。当出现新的、未知的缺陷类型或者产品设计变更时模型需要更新。增量学习HALCON支持在已有模型基础上继续训练。你可以将新收集的正常样本确保无缺陷加入训练集用较低的学习率进行少量轮次的训练让模型适应新的“正常”状态。定期再训练建议每季度或每半年用积累的所有正常数据重新训练一次模型使其保持最佳状态。最后部署时记得做压力测试和长时间运行测试确保你的HALCON程序在工业PC上能7x24小时稳定运行内存不会缓慢增长直至溢出。这些细节往往才是项目成功上线的最后一道关卡。