C#与YOLO算法实战:从零搭建目标检测系统

1次阅读
没有评论

共计 1981 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

为什么选择 YOLO 算法

YOLO(You Only Look Once)作为单阶段目标检测的标杆算法,在工业质检中能快速定位缺陷位置,安防监控场景下可实现实时多人脸追踪,其平衡速度和精度的特性尤其适合.NET 生态下的边缘计算部署。相比传统方法,YOLO 将检测任务转化为回归问题,单次前向传播即可输出所有目标的类别和位置信息。

C# 与 YOLO 算法实战:从零搭建目标检测系统

技术选型:三大方案对比

  1. TensorFlow.NET
  2. 优势:完整支持 TensorFlow 算子,可直接加载 PB 格式模型
  3. 劣势:文档较少,GPU 内存管理复杂
  4. 典型报错:TFException: Output tensors must be on CPU

  5. ML.NET

  6. 优势:微软官方维护,与 ASP.NET Core 深度集成
  7. 劣势:ONNX 支持有限,无法自定义预处理层
  8. 典型场景:已有训练好的 ONNX 模型时快速接入

  9. Python 服务调用

  10. 优势:可利用完整 Python 生态
  11. 劣势:需要维护跨进程通信,延迟增加 2 - 3 倍

推荐方案:生产环境首选 ML.NET+ONNX 组合,开发阶段可用 TensorFlow.NET 调试。

核心实现步骤

图像预处理

using OpenCvSharp;

// 归一化并转换通道顺序
Mat Preprocess(Mat src, Size targetSize)
{
    // BGR 转 RGB
    Cv2.CvtColor(src, src, ColorConversionCodes.BGR2RGB);

    // 归一化到 0 - 1 范围
    Mat normalized = new Mat();
    src.ConvertTo(normalized, MatType.CV_32FC3, 1.0/255);

    // 调整尺寸并中心裁剪
    Mat resized = new Mat();
    Cv2.Resize(normalized, resized, targetSize);
    return resized;
}

模型加载方式对比

文件直接加载

var session = new InferenceSession("yolov5s.onnx");

内存流加载(适合加密模型):

byte[] modelBytes = File.ReadAllBytes("yolov5s.onnx");
using var stream = new MemoryStream(modelBytes);
var session = new InferenceSession(stream);

NMS 实现关键点

List<Detection> ApplyNMS(IReadOnlyList<Detection> detections, 
    float iouThreshold = 0.5f)
{
    // 按置信度降序排序
    var sorted = detections.OrderByDescending(d => d.Confidence).ToList();

    for (int i = 0; i < sorted.Count; i++) {
        // 计算当前框与后续所有框的 IoU
        for (int j = i + 1; j < sorted.Count; j++) {float iou = CalculateIoU(sorted[i].Box, sorted[j].Box);

            // IoU 超过阈值则移除低置信度框
            if (iou > iouThreshold) {sorted.RemoveAt(j);
                j--;
            }
        }
    }
    return sorted;
}

// IoU 计算公式:$\frac{area(A \cap B)}{area(A \cup B)}$

性能优化实战

分辨率对 FPS 的影响

分辨率 平均 FPS (RTX 3060) 显存占用
416×416 45.2 1.8GB
640×640 28.7 3.2GB

多线程注意事项

  1. 每个线程创建独立的 InferenceSession 实例
  2. 使用 CUDAExecutionProvider 时设置gpu_mem_limit
  3. 监控 nvidia-smi 中的显存波动

常见避坑指南

ONNX 导出问题

  • 使用 opset_version=12 兼容 ML.NET
  • 验证算子支持列表:
    python -c "import onnxruntime as ort; print(ort.get_all_providers())"

显存泄漏排查

  1. finally 块中强制调用session.Dispose()
  2. 使用 GC.Collect() 后观察显存释放
  3. 通过 Process.GetCurrentProcess().PrivateMemorySize64 监控

进阶思考

尝试用 Span<T> 重构张量传输代码:

float[] tensorData = new float[3 * 640 * 640];
var span = new Span<float>(tensorData);
// 直接操作内存块替代逐像素赋值

通过本教程,我们完成了从图像预处理到模型部署的全流程实践。YOLO 在 C# 中的表现令人惊喜——在消费级显卡上能达到实时检测的要求。建议下一步尝试将模型封装为 gRPC 服务,或探索使用 TorchSharp 进行模型微调。

正文完
 0
评论(没有评论)