C#集成YOLOv5 ONNX模型:从环境配置到WinForm应用实战
1. 环境准备搭建C#与YOLOv5的开发基础在开始之前我们需要确保开发环境配置正确。这里我推荐使用Visual Studio 2022作为C#开发工具因为它对.NET 6.0的支持最为完善。同时Python环境建议选择Anaconda来管理可以避免很多依赖冲突问题。对于硬件配置如果你打算使用GPU加速需要确保你的显卡支持CUDA 11.7。我实测过RTX 3060和RTX 3090都能完美运行。如果只有CPU也没关系只是推理速度会慢一些。以下是具体的环境配置清单Visual Studio 2022社区版即可安装时勾选.NET桌面开发工作负载Anaconda3建议最新版本创建Python 3.8环境YOLOv5官方推荐CUDA 11.7cuDNN 8.7.0仅GPU用户需要.NET 6.0 SDKVS2022安装时一般会自带安装完基础环境后我们需要配置YOLOv5的训练环境。这里有个小技巧先创建一个新的conda环境避免污染系统Python环境。打开Anaconda Prompt执行conda create -n yolov5 python3.8 conda activate yolov5 pip install torch1.13.0cu117 torchvision0.14.0cu117 --extra-index-url https://download.pytorch.org/whl/cu117 pip install opencv-python4.7.0注意如果你的显卡不支持CUDA或者不想用GPU可以安装CPU版本的PyTorch去掉cu117后缀即可。2. YOLOv5模型训练与ONNX转换2.1 训练自定义数据集YOLOv5的训练其实比想象中简单。我建议从官方仓库克隆最新代码git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt数据集准备有个小技巧使用LabelImg标注工具时保存为YOLO格式每个图像对应一个.txt文件。目录结构应该这样组织dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/然后在data目录下创建自定义的yaml文件例如train: ../dataset/images/train val: ../dataset/images/val nc: 3 # 类别数 names: [cat, dog, person] # 你的类别名称训练命令推荐使用python train.py --img 640 --batch 16 --epochs 100 --data data/custom.yaml --weights yolov5s.pt --cache实测发现--cache参数可以显著提升训练速度特别是当你的数据集较小时。如果遇到内存不足的问题可以减小batch size。2.2 导出ONNX模型训练完成后模型转换是关键一步。YOLOv5提供了方便的export.py脚本python export.py --weights runs/train/exp/weights/best.pt --include onnx --img 640 --simplify这里有几个重要参数需要注意--img 640必须与训练时保持一致--simplify启用ONNX的简化优化--dynamic如果需要动态输入尺寸可以加上转换完成后强烈建议用Netron工具打开生成的.onnx文件检查输入节点应为1x3x640x640假设你使用640x640输入输出节点名称通常是output0或output检查输出维度是否符合预期如1x25200x85其中854坐标1置信度80类别3. C#项目配置与库集成3.1 创建WinForm项目打开VS2022新建一个.NET 6.0的Windows窗体应用。然后通过NuGet安装以下关键包Install-Package Microsoft.ML.OnnxRuntime -Version 1.14.0 Install-Package OpenCvSharp4 -Version 4.7.0 Install-Package OpenCvSharp4.runtime.win -Version 4.7.0我建议直接从GitHub获取yolov5-net的源码进行修改而不是直接使用编译好的DLL这样更容易调试。克隆仓库后重点关注以下几个文件Yolov5Net.Scorer/YoloScorer.cs核心推理类Yolov5Net.Scorer/Models/YoloCocoP5Model.cs模型配置类3.2 适配自定义模型修改YoloCocoP5Model.cs的关键点public class CustomModel : YoloModel { public override int Width { get; set; } 640; public override int Height { get; set; } 640; public override int Depth { get; set; } 3; public override int Dimensions { get; set; } 85; // 4坐标1置信度80类别 public override float[] Strides { get; set; } new float[] { 8, 16, 32 }; public override float[][][] Anchors { get; set; } new float[][][] { new float[][] { new float[] { 010f, 13f }, ... }, ... }; public override int[] Shapes { get; set; } new int[] { 80, 40, 20 }; public override float Confidence { get; set; } 0.20f; public override float MulConfidence { get; set; } 0.25f; public override float Overlap { get; set; } 0.45f; public override string[] Outputs { get; set; } new[] { output0 }; public override ListYoloLabel Labels { get; set; } new ListYoloLabel() { new YoloLabel { Id 0, Name cat }, new YoloLabel { Id 1, Name dog }, // 你的类别... }; public override bool UseDetect { get; set; } true; }特别注意Anchors值必须与训练时的模型配置一致可以从你的yolov5模型.yaml文件中找到。4. WinForm应用实战开发4.1 界面设计与模型加载创建一个简单的WinForm界面包含PictureBox控件命名为picbox_DisplayButton控件命名为btn_SelectImageLabel控件用于显示推理耗时在Form的构造函数中加载模型private readonly YoloScorerCustomModel _scorer; public MainForm() { InitializeComponent(); // 模型文件应放在bin/Debug或bin/Release目录下 string modelPath Path.Combine(Application.StartupPath, best.onnx); _scorer new YoloScorerCustomModel(modelPath); }4.2 实现图像检测功能为按钮添加点击事件处理private void btn_SelectImage_Click(object sender, EventArgs e) { using var openFileDialog new OpenFileDialog { Filter Image Files|*.jpg;*.jpeg;*.png, Title Select an Image }; if (openFileDialog.ShowDialog() DialogResult.OK) { var image Image.FromFile(openFileDialog.FileName); var processedImage ProcessImage(image); picbox_Display.Image processedImage; } } private Bitmap ProcessImage(Image image) { var stopwatch Stopwatch.StartNew(); // 执行推理 var predictions _scorer.Predict(image); // 使用OpenCV绘制结果 using var mat OpenCvSharp.Extensions.BitmapConverter.ToMat(new Bitmap(image)); foreach (var prediction in predictions) { // 绘制矩形框 var rect new OpenCvSharp.Rect( (int)prediction.Rectangle.X, (int)prediction.Rectangle.Y, (int)prediction.Rectangle.Width, (int)prediction.Rectangle.Height); Cv2.Rectangle(mat, rect, Scalar.Red, 2); // 绘制标签和置信度 var text ${prediction.Label.Name} {prediction.Score:0.00}; Cv2.PutText(mat, text, new Point(rect.X, rect.Y - 5), HersheyFonts.HersheySimplex, 0.5, Scalar.Green, 1); } stopwatch.Stop(); lbl_Time.Text $Inference time: {stopwatch.ElapsedMilliseconds}ms; return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat); }4.3 性能优化技巧在实际使用中我发现几个提升性能的方法图像预处理优化// 调整图像大小匹配模型输入 var resizedImage new Bitmap(image, new Size(640, 640));启用CUDA加速如果可用var options SessionOptions.MakeSessionOptionWithCudaProvider(); _scorer new YoloScorerCustomModel(modelPath, options);批量处理如果需要处理多张图片可以改为批量推理模型量化将FP32模型转为INT8可以显著提升速度但会损失少量精度5. 常见问题与解决方案5.1 模型加载失败如果遇到Failed to load model错误检查ONNX模型文件路径是否正确模型是否完整尝试用Netron打开验证是否安装了正确版本的OnnxRuntime5.2 推理结果异常当检测框位置或类别不正确时确认模型输入尺寸Width/Height设置正确检查Labels列表是否与训练时完全一致验证Anchors和Strides值是否正确5.3 内存泄漏问题长时间运行后内存增长明显解决方法确保所有IDisposable对象如Mat、Bitmap都使用using语句定期调用GC.Collect()虽然不推荐但在WinForm中有时必要考虑使用对象池重用资源6. 进阶应用扩展6.1 实时摄像头检测通过OpenCV捕获摄像头视频流private void StartCameraCapture() { var capture new VideoCapture(0); // 0表示默认摄像头 var frame new Mat(); while (true) { capture.Read(frame); if (frame.Empty()) break; var predictions _scorer.Predict(frame.ToBitmap()); // 绘制检测结果... pictureBox.Image frame.ToBitmap(); Application.DoEvents(); // 允许UI更新 } }6.2 多线程处理为了避免界面卡顿应该使用后台线程处理检测任务private async void btn_Process_Click(object sender, EventArgs e) { btn_Process.Enabled false; await Task.Run(() { var image (Bitmap)picbox_Display.Image; var processedImage ProcessImage(image); this.Invoke((MethodInvoker)delegate { picbox_Display.Image processedImage; btn_Process.Enabled true; }); }); }6.3 模型热切换实现运行时动态切换不同模型private void LoadNewModel(string modelPath) { var newScorer new YoloScorerCustomModel(modelPath); var oldScorer Interlocked.Exchange(ref _scorer, newScorer); oldScorer?.Dispose(); }