共计 2477 个字符,预计需要花费 7 分钟才能阅读完成。
痛点分析
在 C# 项目中直接使用 Python 实现的 YOLO 算法会面临几个典型问题:

- 进程间通信开销 :通过 Python 进程调用模型,每次推理都需要序列化 / 反序列化数据,导致延迟显著增加
- 依赖冲突 :Python 环境与.NET 运行时的库依赖可能产生冲突,特别是涉及 CUDA 等底层依赖时
- 部署复杂度 :需要同时维护 Python 和.NET 两套环境,增加运维成本
技术选型
对比主流方案:
- ONNX Runtime 优势
- 跨平台支持(Windows/Linux/macOS)
- 轻量级推理引擎(仅需 15MB 左右 DLL)
- 支持硬件加速(CUDA/DirectML)
-
模型优化工具链完善
-
TensorFlow.NET 劣势
- 依赖完整的 TensorFlow 运行时
- GPU 版本需要复杂的环境配置
- 对新型模型架构支持滞后
核心实现
1. 图像预处理
using OpenCvSharp;
Mat Preprocess(Mat image, int targetSize)
{
// 保持长宽比的缩放
var scale = Math.Min((float)targetSize / image.Width, (float)targetSize / image.Height);
var newWidth = (int)(image.Width * scale);
var newHeight = (int)(image.Height * scale);
using var resized = new Mat();
Cv2.Resize(image, resized, new Size(newWidth, newHeight));
// 填充到正方形
var padded = new Mat(targetSize, targetSize, MatType.CV_8UC3, Scalar.Black);
resized.CopyTo(padded[new Rect(0, 0, newWidth, newHeight)]);
// 归一化并转换通道顺序
padded.ConvertTo(padded, MatType.CV_32FC3, 1.0 / 255);
return padded;
}
2. ONNX 模型加载
using Microsoft.ML.OnnxRuntime;
class YoloModel : IDisposable
{
private InferenceSession _session;
public YoloModel(string modelPath)
{
var options = new SessionOptions
{
GraphOptimizationLevel = GraphOptimizationLevel.ORT_ENABLE_ALL,
EnableCpuMemArena = true
};
// GPU 加速配置(可选)if (OrtEnv.Instance.GetAvailableProviders().Contains("CUDA"))
{options.AppendExecutionProvider_CUDA();
}
_session = new InferenceSession(modelPath, options);
}
}
3. 后处理与 NMS
List<Detection> Postprocess(float[] outputs, float confThreshold = 0.5f, float iouThreshold = 0.4f)
{var detections = new List<Detection>();
// 解析输出维度(假设为 1x25200x85 格式)for (int i = 0; i < outputs.Length; i += 85)
{float confidence = outputs[i + 4];
if (confidence < confThreshold) continue;
// 解析类别概率
var classes = new float[80];
Array.Copy(outputs, i + 5, classes, 0, 80);
var maxScore = classes.Max();
if (maxScore < confThreshold) continue;
detections.Add(new Detection
{Box = ParseBoundingBox(outputs, i),
Confidence = confidence * maxScore,
ClassId = Array.IndexOf(classes, maxScore)
});
}
// 非极大值抑制
return ApplyNMS(detections, iouThreshold);
}
性能优化
量化模型测试数据
| 模型类型 | 推理速度 (FPS) | 内存占用 | mAP@0.5 |
|---|---|---|---|
| FP32 | 45 | 1.2GB | 0.68 |
| INT8 | 120 | 600MB | 0.65 |
内存池化实现
class TensorPool : IDisposable
{private ConcurrentBag<NamedOnnxValue> _pool = new();
public NamedOnnxValue Rent(DenseTensor<float> tensor, string inputName)
{if (!_pool.TryTake(out var item))
{item = NamedOnnxValue.CreateFromTensor(inputName, tensor);
}
return item;
}
public void Return(NamedOnnxValue value)
{_pool.Add(value);
}
}
避坑指南
- 模型版本兼容性 :
- ONNX opset 版本需与 Runtime 匹配
-
YOLOv5 官方导出时建议使用
--opset 12 -
长宽比保持技巧 :
- 预处理时计算缩放比例后,记录 padding 偏移量
-
后处理时将坐标转换回原始图像空间
-
置信度阈值策略 :
- 对不同类别设置独立阈值
- 使用动态阈值:
confThreshold = 0.5 - (0.1 * classId)
延伸思考
对于视频流处理场景建议:
- 使用生产者 - 消费者模式分离帧捕获和推理
- RTSP 流处理注意点:
- 设置合理的 TCP/UDP 传输模式
- 处理网络中断自动重连
- 使用帧缓存队列应对网络抖动
完整实现示例可参考 GitHub 仓库:[示例项目链接]
正文完
