简介:这份资源面向具备一定C#基础、希望将深度学习落地到机器视觉场景的开发者,围绕Halcon 21.11与VS2019联合开发,完整演示物体识别与图像分割的标注、训练、验证全流程。压缩包共57个文件,约5.39MB,以cs源码、resx与resources资源、hobj模型对象、dll动态库、config配置及exe可执行文件为主,另含hdev脚本与sln解决方案,覆盖界面控件、几何绘制、键盘模拟等辅助模块,目录按UI、Method、Properties等分层组织,便于按模块阅读与二次开发。已有515人学习下载。读者可据此理解Halcon深度学习接口在C#中的调用方式,掌握从数据标注、模型训练到验证评估的完整链路,并参考现成的视图控件与工具类快速搭建自己的识别与分割应用,适合作为项目原型或学习范例。
1. Halcon 深度学习落地:从标注到验证,C# 上位机怎么把活干完
产线上来了一批新工件,表面有六种缺陷,客户要求三天内出一个能跑在工控机上的识别程序。你打开 Visual Studio,引用 halcondotnet.dll,准备用 C# 写上位机界面,但模型训练那一步卡住了——Halcon 的深度学习不是纯 C# 能闭环的,标注、训练、评估这三块得在 HDevelop 里先跑通,再把模型导出给 C# 调用。这个流程我走过不止一遍,翻车最多的地方不是代码写错,而是标注格式和数据集划分没对齐,导致训练 loss 正常但验证精度死活上不去。
这篇要讲的就是这条链路:用 Halcon 的标注工具做物体识别和图像分割的数据准备,在 HDevelop 里配置训练参数并跑通,导出模型后在 C# 上位机里加载推理,最后用一套可复现的验证方法确认模型能不能上线。适合两类人:一是做视觉上位机的 C# 工程师,需要把 Halcon 深度学习嵌进现有框架;二是刚接触 Halcon 深度学习的视觉工程师,想搞清楚从标注到部署每一步的实际参数和边界。下面按标注、训练、C# 集成、验证、避坑的顺序展开,每一步都给可抄的命令和参数。
2. 标注与数据集:Halcon 深度学习的第一道门槛
2.1 物体识别和图像分割的标注差异
Halcon 深度学习支持三类任务:分类、物体检测(Detection)、语义分割(Segmentation)。物体识别通常对应 Detection,标注的是矩形框加类别标签;图像分割对应 Segmentation,标注的是每个像素的类别,输出是分割掩膜。两者的标注文件结构完全不同,Detection 用 .hobj 或 COCO 格式的 JSON,Segmentation 用像素级标注图加类别映射表。
我一般先用 HDevelop 的deep_learning_annotation工具做标注。Detection 标注时,每个目标画一个矩形,类别名要和后续训练配置里的 class_names 完全一致,大小写都不能差。Segmentation 标注更费时间,但 Halcon 提供了半自动工具:先用传统算子做粗分割,再手动修边缘。比如用threshold加connection先分出大致区域,导出成标注初稿,再在标注工具里微调。
标注完成后,目录结构建议这样组织:
dataset/ train/ images/ img_001.png img_002.png annotations/ img_001.json img_002.json val/ images/ annotations/ test/ images/ annotations/train/val/test 的比例按 7:2:1 切,如果样本少于 500 张,至少保证 val 有 50 张以上,否则验证精度波动会很大。
2.2 用 HDevelop 脚本批量检查标注质量
标注完不要直接开训,先跑一遍检查脚本。下面这段 HDevelop 代码遍历标注文件,检查每个 JSON 里的类别名是否在预定义列表里,以及框的宽高是否小于 5 像素(太小的框训练时会被忽略):
* 检查标注文件中的类别名和框尺寸 ClassNames := ['scratch', 'dent', 'stain', 'crack', 'burr', 'normal'] MinWidth := 5 MinHeight := 5 AnnotationDir := 'dataset/train/annotations' list_files(AnnotationDir, ['files'], Files) for Index := 0 to |Files| - 1 by 1 read_dict(Files[Index], [], [], DictHandle) get_dict_tuple(DictHandle, 'categories', Categories) for CIndex := 0 to |Categories| - 1 by 1 if (sum(ClassNames == Categories[CIndex]) == 0) disp_message(3600, '未知类别: ' + Categories[CIndex] + ' 文件: ' + Files[Index], 'window', 12, 12, 'red', 'true') endif endfor get_dict_tuple(DictHandle, 'bbox', BBoxes) for BIndex := 0 to |BBoxes| - 1 by 1 W := BBoxes[BIndex][2] - BBoxes[BIndex][0] H := BBoxes[BIndex][3] - BBoxes[BIndex][1] if (W < MinWidth or H < MinHeight) disp_message(3600, '框过小: ' + Files[Index], 'window', 12, 12, 'yellow', 'true') endif endfor clear_dict(DictHandle) endfor逻辑说明:read_dict读取 Halcon 的字典格式标注文件,get_dict_tuple取出类别列表和边界框坐标。sum(ClassNames == Categories[CIndex])利用 Halcon 的元组比较做类别名匹配,返回 0 表示不在列表里。框宽高检查是为了过滤掉标注时误画的极小框,这类框在训练时会被 Halcon 自动忽略,但会在日志里刷警告,提前清掉省得干扰排查。
参数说明:MinWidth和MinHeight设 5 像素是经验值,如果你的图像分辨率低于 640×480,可以降到 3;高于 2000×2000 可以提到 10。ClassNames必须和训练配置里的完全一致,包括顺序,Halcon 按索引映射类别 ID。
2.3 数据增强参数的取舍
Halcon 深度学习训练时自带增强,但增强参数不能乱开。Detection 任务常用的增强有旋转、缩放、亮度变化、镜像。镜像要注意:如果缺陷有方向性(比如划痕方向),左右镜像会引入错误标签,这时候要关掉 mirror。Segmentation 任务对几何变换更敏感,旋转角度超过 15 度时,边缘像素的类别可能失真,我一般把旋转限制在 ±10 度。
在 HDevelop 的训练配置里,增强参数通过set_dl_model_param设置:
* 设置数据增强参数 set_dl_model_param(DLModelHandle, 'augmentation_rotate', 10) set_dl_model_param(DLModelHandle, 'augmentation_scale_min', 0.9) set_dl_model_param(DLModelHandle, 'augmentation_scale_max', 1.1) set_dl_model_param(DLModelHandle, 'augmentation_brightness', 0.1) set_dl_model_param(DLModelHandle, 'augmentation_mirror', 'false')逻辑说明:augmentation_rotate控制随机旋转角度范围,单位是度。augmentation_scale_min/max控制缩放比例,0.9 到 1.1 表示缩小 10% 到放大 10%。augmentation_brightness是亮度偏移比例,0.1 表示 ±10%。augmentation_mirror设 'false' 关闭镜像,有方向性缺陷时必须关。
参数说明:旋转角度超过 15 度时,Detection 的框可能超出图像边界被裁掉,导致训练样本丢失。缩放范围超过 0.8 到 1.2 时,小目标可能缩到 5 像素以下被忽略。亮度变化超过 0.2 时,暗缺陷可能被增强成过曝区域,反而干扰训练。
3. 训练配置与 HDevelop 实操:把模型跑起来
3.1 创建模型和加载预训练权重
Halcon 深度学习支持从头训练和迁移学习。样本少于 1000 张时,强烈建议用预训练模型做迁移。Halcon 自带几个预训练网络,Detection 常用pretrained_dl_classifier_compact.hdl和pretrained_dl_classifier_enhanced.hdl,前者速度快适合工控机,后者精度高但推理慢。
创建 Detection 模型的代码:
* 创建物体检测模型 read_dl_model('pretrained_dl_classifier_compact.hdl', DLModelHandle) set_dl_model_param(DLModelHandle, 'type', 'detection') set_dl_model_param(DLModelHandle, 'class_names', ['scratch', 'dent', 'stain', 'crack', 'burr', 'normal']) set_dl_model_param(DLModelHandle, 'image_width', 640) set_dl_model_param(DLModelHandle, 'image_height', 480) set_dl_model_param(DLModelHandle, 'image_num_channels', 3)逻辑说明:read_dl_model加载预训练模型文件,这个文件在 Halcon 安装目录的dl文件夹下。set_dl_model_param设置模型类型为 detection,类别名列表,以及输入图像尺寸。图像尺寸要和标注时的图像尺寸一致,否则训练时会自动缩放,小目标可能缩没了。
参数说明:image_width和image_height设 640×480 是平衡速度和精度的常用值。如果缺陷目标小于 20×20 像素,建议提到 1024×768。image_num_channels设 3 表示 RGB 三通道,如果输入是灰度图,设 1 可以省显存。
3.2 训练参数设置和迭代控制
训练参数里最关键的是学习率、batch size 和迭代次数。Halcon 的 Detection 训练用train_dl_model_batch逐批训练,学习率通过set_dl_model_param设置:
* 设置训练超参数 set_dl_model_param(DLModelHandle, 'learning_rate', 0.001) set_dl_model_param(DLModelHandle, 'momentum', 0.9) set_dl_model_param(DLModelHandle, 'weight_decay', 0.0005) set_dl_model_param(DLModelHandle, 'batch_size', 4) set_dl_model_param(DLModelHandle, 'max_epochs', 100) set_dl_model_param(DLModelHandle, 'lr_step', 30) set_dl_model_param(DLModelHandle, 'lr_factor', 0.1)逻辑说明:learning_rate初始学习率,0.001 是迁移学习的常用起点。momentum动量项,0.9 是标准值。weight_decay权重衰减防过拟合,0.0005 适合小数据集。batch_size批大小,4 是 8GB 显存下的安全值。max_epochs最大迭代轮数。lr_step和lr_factor控制学习率衰减:每 30 轮学习率乘以 0.1。
参数说明:学习率超过 0.01 时,迁移学习容易把预训练权重冲垮,loss 会震荡不降。batch size 设太大(比如 16)在 8GB 显存上会 OOM,设太小(比如 1)梯度噪声大,收敛慢。max_epochs设 100 是起点,如果验证精度在第 60 轮还在涨,可以加到 150;如果第 30 轮就平了,减到 60 省时间。
3.3 训练过程监控和中断恢复
训练时用train_dl_model_batch循环,每轮结束后在验证集上跑evaluate_dl_model看精度。下面这段代码展示训练循环和精度记录:
* 训练循环,每轮记录验证精度 for Epoch := 1 to MaxEpochs by 1 train_dl_model_batch(DLModelHandle, TrainDataloader, TrainResults) evaluate_dl_model(DLModelHandle, ValDataloader, ValResults) get_dl_model_param(DLModelHandle, 'learning_rate', CurrentLR) Precision := ValResults['precision'] Recall := ValResults['recall'] disp_message(3600, 'Epoch ' + Epoch + ' LR ' + CurrentLR + ' P ' + Precision + ' R ' + Recall, 'window', 12, 12, 'black', 'true') if (Precision > 0.95 and Recall > 0.90) break endif endfor逻辑说明:train_dl_model_batch跑一轮训练,evaluate_dl_model在验证集上算精度和召回。get_dl_model_param取当前学习率,确认衰减是否生效。精度和召回同时达标就提前停,省时间。
参数说明:精度阈值 0.95 和召回 0.90 是工业检测的常见要求,具体看客户标准。如果召回优先(漏检代价高),把召回阈值提到 0.95,精度可以降到 0.90。训练中断后恢复:Halcon 支持write_dl_model保存中间模型,下次用read_dl_model加载后继续训练,但优化器状态不保存,恢复后前几轮 loss 会跳一下,正常现象。
4. C# 联合 Halcon:模型部署到上位机
4.1 C# 项目引用 Halcon 库和环境配置
C# 调用 Halcon 深度学习模型,需要引用halcondotnet.dll,这个文件在 Halcon 安装目录的bin/dotnet35或bin/dotnetcore下。新建 C# WinForms 或 WPF 项目后,在解决方案里添加引用,然后把 Halcon 的运行时目录加到系统 PATH 或项目输出目录。
环境变量配置:
# 把 Halcon 的 bin 目录加到 PATH setx PATH "%PATH%;C:\Program Files\MVTec\HALCON-23.11\bin\x64-win64"逻辑说明:Halcon 的运行时依赖halcon.dll和hdevengine.dll等原生库,C# 通过 P/Invoke 调用。PATH 里没有这些库的目录时,运行时会报DllNotFoundException。
参数说明:Halcon 版本号按实际安装的改,23.11 是当前常用版本。如果项目是 64 位,用x64-win64目录;32 位用x86-win32。注意 Halcon 的 license 文件要放在bin目录下或者通过set_system('license_file', ...)指定路径,否则初始化会失败。
4.2 在 C# 中加载模型并推理
C# 里用HDevEngine或直接调HOperatorSet加载模型。推荐用HDevEngine把 HDevelop 的推理过程封装成外部过程,C# 调用过程名传参,这样逻辑改动不用重编译 C#。下面是加载模型并推理的 C# 代码:
using HalconDotNet; public class HalconInference { private HTuple modelHandle; private HDevEngine engine; public void LoadModel(string modelPath) { // 加载训练好的模型 HOperatorSet.ReadDlModel(modelPath, out modelHandle); // 设置推理时的 batch size HOperatorSet.SetDlModelParam(modelHandle, "batch_size", 1); } public HTuple Infer(HObject image) { HTuple result; // 调用推理算子 HOperatorSet.ApplyDlModel(modelHandle, image, "all", out result); return result; } }逻辑说明:ReadDlModel加载.hdl模型文件,SetDlModelParam设 batch size 为 1(推理时逐张处理)。ApplyDlModel执行推理,返回结果元组,包含类别 ID、置信度、边界框坐标。
参数说明:batch_size推理时设 1 延迟最低,设 4 吞吐高但延迟增加。ApplyDlModel的第三个参数"all"表示返回所有输出,也可以指定"segmentation"或"detection"只取需要的部分。返回的边界框坐标是归一化的,要乘以图像宽高还原像素坐标。
4.3 推理结果可视化和数据回传
推理完要把结果画到图像上,C# 里用 Halcon 的绘图算子:
public void DrawResults(HObject image, HTuple result, HWindow window) { // 获取边界框和类别 HTuple bbox = result.TupleSelectRange(0, 3); HTuple classIds = result.TupleSelect(4); HTuple confidences = result.TupleSelect(5); window.ClearWindow(); window.DispObj(image); for (int i = 0; i < classIds.Length; i++) { if (confidences[i].D < 0.5) continue; // 置信度过滤 double row1 = bbox[i * 4].D * imageHeight; double col1 = bbox[i * 4 + 1].D * imageWidth; double row2 = bbox[i * 4 + 2].D * imageHeight; double col2 = bbox[i * 4 + 3].D * imageWidth; window.SetColor("red"); window.DispRectangle1(row1, col1, row2, col2); window.DispText(classNames[classIds[i].I], "image", row1, col1, "red", "box", "false"); } }逻辑说明:TupleSelectRange取边界框的四个坐标,TupleSelect取类别 ID 和置信度。置信度低于 0.5 的框不画,减少误检干扰。坐标从归一化值乘以图像宽高还原。
参数说明:置信度阈值 0.5 是起点,误检多就提到 0.7,漏检多就降到 0.3。DispText的最后一个参数"false"表示不画文字背景框,画面更干净。
5. 验证与调优:怎么确认模型能上线
5.1 验证集评估指标解读
Halcon 的evaluate_dl_model返回 precision、recall、f1_score、mAP 等指标。Detection 任务重点看 mAP(mean Average Precision),Segmentation 任务看 IoU(Intersection over Union)。mAP 低于 0.7 时模型基本不可用,0.7 到 0.85 需要调优,0.85 以上可以考虑上线。
评估代码:
* 在测试集上评估模型 evaluate_dl_model(DLModelHandle, TestDataloader, EvalResults) get_dict_tuple(EvalResults, 'mAP', MAP) get_dict_tuple(EvalResults, 'precision', Precision) get_dict_tuple(EvalResults, 'recall', Recall) disp_message(3600, 'mAP: ' + MAP + ' P: ' + Precision + ' R: ' + Recall, 'window', 12, 12, 'black', 'true')逻辑说明:evaluate_dl_model在测试集上跑推理并算指标,结果存在字典里。get_dict_tuple取出 mAP、precision、recall。
参数说明:mAP 的计算依赖 IoU 阈值,Halcon 默认 0.5。如果客户要求更严格的定位精度,把 IoU 阈值提到 0.75,mAP 会下降 10% 到 20%,属正常。
5.2 误检和漏检的排查方法
误检多时,先看置信度分布。把测试集所有推理结果的置信度导出来,画直方图。如果误检的置信度集中在 0.5 到 0.6,说明阈值设低了,提到 0.7 能过滤大部分。如果误检置信度高于 0.8,说明模型把背景学成了目标,要检查标注里有没有漏标的目标被当成背景。
漏检多时,看召回率。召回低于 0.8 时,先检查训练集里该类别的样本数是否少于 100,少了就补样本。如果样本够,检查增强参数是否把目标转没了,把旋转角度和缩放范围调小试试。
5.3 模型导出和 C# 端性能测试
训练完用write_dl_model导出模型:
write_dl_model(DLModelHandle, 'defect_detection.hdl')导出后在 C# 端跑 100 张测试图,记录单张推理耗时。工控机上单张 640×480 图像的推理耗时应在 50ms 以内,超过 100ms 要考虑换 compact 模型或降分辨率。C# 端计时用Stopwatch:
Stopwatch sw = new Stopwatch(); sw.Start(); var result = Infer(image); sw.Stop(); Console.WriteLine($"推理耗时: {sw.ElapsedMilliseconds} ms");逻辑说明:Stopwatch测单次推理耗时,跑 100 次取平均。第一次推理会包含模型加载时间,从第二次开始计。
参数说明:耗时超过 100ms 时,先把image_width和image_height降到 480×360 试试,精度掉不超过 5% 的话可以接受。还不行就换pretrained_dl_classifier_compact.hdl从头训,compact 模型推理速度比 enhanced 快 2 到 3 倍。
6. 避坑与排查:标注训练验证里的血泪经验
6.1 标注类别名不一致导致训练报错
现象:训练启动时报Unknown class name或class_names mismatch。
原因:标注 JSON 里的类别名和set_dl_model_param里的class_names列表不一致,比如标注里写的是scratch,配置里写的是Scratch,大小写不同。
解决:用 2.2 节的检查脚本先跑一遍,把所有标注文件的类别名统一。Halcon 的类别名区分大小写,建议全用小写加下划线。
6.2 图像尺寸和标注尺寸不匹配
现象:训练 loss 正常下降,但验证精度始终低于 0.5,推理时框的位置偏移很大。
原因:标注时的图像尺寸和set_dl_model_param里的image_width/image_height不一致,Halcon 自动缩放图像时把标注框也缩放了,但缩放比例没对齐。
解决:标注前先确认图像尺寸,训练配置里的尺寸和标注尺寸保持一致。如果必须缩放,用scale_image先统一缩放图像和标注,再训练。
6.3 C# 调用时报 DllNotFoundException
现象:C# 程序在开发机上跑正常,部署到工控机上报DllNotFoundException: halcon.dll。
原因:工控机上没装 Halcon 运行时,或者 PATH 里没有 Halcon 的 bin 目录。
解决:把 Halcon 安装目录的bin/x64-win64整个文件夹拷到工控机,在 C# 程序启动时用SetDllDirectory指定路径,或者把 bin 目录加到系统 PATH。注意 license 文件也要一起拷,否则初始化会报 license 错误。
6.4 推理结果置信度普遍偏低
现象:所有推理结果的置信度都在 0.3 到 0.5 之间,没有高于 0.7 的。
原因:训练时学习率设太大,模型没收敛;或者训练轮数不够,模型欠拟合。
解决:把学习率从 0.001 降到 0.0001,训练轮数从 100 加到 200。如果还不行,检查训练集里正样本和负样本的比例,负样本(背景图)太多时,模型会偏向预测背景,置信度普遍低。负样本比例控制在 1:3 以内。
6.5 模型文件太大导致 C# 加载慢
现象:C# 端ReadDlModel耗时超过 3 秒,界面卡顿。
原因:模型文件超过 100MB,加载时要把权重全部读进内存。
解决:用set_dl_model_param把模型量化成 int8,文件大小能压到原来的 1/4,推理速度也能提升。量化代码:
set_dl_model_param(DLModelHandle, 'quantization', 'int8') write_dl_model(DLModelHandle, 'defect_detection_int8.hdl')逻辑说明:quantization设int8把浮点权重转成 8 位整数,精度损失通常在 1% 到 3%。
参数说明:量化后要在测试集上重新评估,mAP 掉超过 5% 就放弃量化,改用裁剪模型层数的方式压缩。
7. 把验证做成自动化脚本:一个省后悔药的习惯
训练完手动跑评估、手动记录指标、手动对比不同参数的效果,这套流程跑三次以上就会烦。我后来的习惯是写一个 HDevelop 脚本,把训练、评估、导出串成一条流水线,每次改参数只改脚本开头的配置区,跑完自动输出对比表格。这样调参时不会漏掉某次实验的记录,也不会因为手动操作引入不一致。
脚本结构大概是这样:配置区放数据集路径、类别名、学习率、batch size、增强参数;训练区跑训练循环;评估区在测试集上算 mAP、precision、recall;导出区把模型和指标写到一个 CSV 里。每次实验的 CSV 追加一行,跑十次后打开 CSV 就能看出哪个参数组合最好。
* 自动化训练评估流水线(配置区) ConfigFile := 'experiment_config.txt' read_tuple(ConfigFile, [], [], Config) LR := Config[0] BatchSize := Config[1] Epochs := Config[2] AugRotate := Config[3] * 训练区 set_dl_model_param(DLModelHandle, 'learning_rate', LR) set_dl_model_param(DLModelHandle, 'batch_size', BatchSize) set_dl_model_param(DLModelHandle, 'augmentation_rotate', AugRotate) for Epoch := 1 to Epochs by 1 train_dl_model_batch(DLModelHandle, TrainDataloader, TrainResults) endfor * 评估区 evaluate_dl_model(DLModelHandle, TestDataloader, EvalResults) get_dict_tuple(EvalResults, 'mAP', MAP) get_dict_tuple(EvalResults, 'precision', Precision) get_dict_tuple(EvalResults, 'recall', Recall) * 导出区:追加到 CSV open_file('experiment_log.csv', 'append', FileHandle) fwrite_string(FileHandle, LR + ',' + BatchSize + ',' + Epochs + ',' + AugRotate + ',' + MAP + ',' + Precision + ',' + Recall + '\n') close_file(FileHandle) write_dl_model(DLModelHandle, 'model_lr' + LR + '_bs' + BatchSize + '.hdl')逻辑说明:配置区从文本文件读参数,改参数不用改脚本。训练区按配置跑训练。评估区算指标。导出区把参数和指标追加到 CSV,同时按参数组合命名保存模型文件,方便回溯。
参数说明:experiment_config.txt每行一个参数,顺序和Config索引对应。CSV 用逗号分隔,Excel 直接打开就能排序筛选。模型文件名带参数组合,避免覆盖。
这个习惯帮我省了很多后悔药。有一次客户反馈模型在产线上漏检率突然升高,我翻出 CSV 发现是三天前改了一版增强参数,旋转角度从 10 度提到 20 度,把细长划痕转没了。如果没有 CSV 记录,根本想不到是增强参数的问题。把验证自动化,不是为了省事,是为了在出问题时能快速定位到是哪次改动引入的。希望帮到你。
本文还有配套的精品资源,点击获取