共计 3605 个字符,预计需要花费 10 分钟才能阅读完成。
背景痛点:为什么 C# 调用 AI 模型这么难?
在.NET 生态中调用 Python 训练的 AI 模型时,开发者常遇到三个典型问题:

- 环境隔离问题 :Python 的虚拟环境和.NET 的运行环境天然隔离,依赖管理复杂
- 数据格式转换 :Python 的 NumPy 数组与 C# 的矩阵数据结构差异大,转换效率低
- 性能损耗 :跨语言调用会产生额外序列化 / 反序列化开销,特别是实时视频流场景
技术选型:ONNX Runtime 为什么胜出?
对比两种主流方案:
- ONNX Runtime
- 优点:跨平台支持好、性能优化充分(支持 CUDA/OpenVINO 等 EP)、内存占用低
-
缺点:模型转换需要额外步骤
-
TorchSharp
- 优点:直接加载 PyTorch 模型
- 缺点:依赖 Python 运行时、GPU 支持不稳定
决策关键 :ONNX 格式已成为行业标准,微软官方维护的 ONNX Runtime 对.NET 支持最完善,且 YOLOv11 官方已提供 ONNX 导出工具。
核心实现步骤
1. 环境准备
安装必要 NuGet 包:
// 包引用示例
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.15.1" />
<PackageReference Include="OpenCvSharp4" Version="4.7.0.20230115" />
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.7.0.20230115" />
2. 图像预处理(OpenCVSharp)
// 输入图像预处理
using OpenCvSharp;
Mat Preprocess(Mat image, int targetSize = 640)
{
// 保持宽高比 resize
float scale = Math.Min(targetSize / (float)image.Width, targetSize / (float)image.Height);
Size newSize = new Size((int)(image.Width * scale), (int)(image.Height * scale));
Mat resized = new Mat();
Cv2.Resize(image, resized, newSize);
// 填充到正方形
Mat padded = new Mat(targetSize, targetSize, MatType.CV_8UC3, new Scalar(114, 114, 114));
resized.CopyTo(padded[new Rect(0, 0, resized.Width, resized.Height)]);
// 转换为 RGB 并归一化
Mat normalized = new Mat();
padded.ConvertTo(normalized, MatType.CV_32FC3, 1.0 / 255);
Cv2.CvtColor(normalized, normalized, ColorConversionCodes.BGR2RGB);
return normalized;
}
3. ONNX Runtime 推理
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
float[] RunInference(Mat processedImage)
{
// 创建输入 Tensor
var input = new DenseTensor<float>(new[] {1, 3, 640, 640});
for (int y = 0; y < 640; y++)
for (int x = 0; x < 640; x++)
for (int c = 0; c < 3; c++)
input[0, c, y, x] = processedImage.At<Vec3f>(y, x)[c];
// 创建推理会话
using var session = new InferenceSession("yolov11.onnx");
using var inputs = new List<NamedOnnxValue>
{NamedOnnxValue.CreateFromTensor("images", input)
};
// 执行推理
try
{using var results = session.Run(inputs);
return results.First().AsTensor<float>().ToArray();}
catch (Exception ex)
{Console.WriteLine($"推理失败: {ex.Message}");
return Array.Empty<float>();}
}
4. 后处理(含 NMS 实现)
// 非极大值抑制 (Non-Maximum Suppression)
List<Rect> NMS(List<Detection> detections, float iouThreshold = 0.5f)
{
// 按置信度降序排序
detections.Sort((a, b) => b.Confidence.CompareTo(a.Confidence));
var results = new List<Rect>();
while (detections.Count > 0)
{
// 取当前最高分检测框
var current = detections[0];
results.Add(current.Box);
detections.RemoveAt(0);
// 计算与其他框的 IoU
for (int i = detections.Count - 1; i >= 0; i--)
{float iou = CalculateIoU(current.Box, detections[i].Box);
if (iou > iouThreshold)
detections.RemoveAt(i);
}
}
return results;
}
float CalculateIoU(Rect a, Rect b)
{// 计算交并比 (Intersection over Union)
Rect intersection = a & b;
float intersectArea = intersection.Width * intersection.Height;
float unionArea = a.Width * a.Height + b.Width * b.Height - intersectArea;
return intersectArea / unionArea;
}
性能优化技巧
1. 启用 CUDA 加速
var options = SessionOptions.MakeSessionOptionWithCudaProvider();
using var session = new InferenceSession("yolov11.onnx", options);
2. 内存池复用
// 全局初始化
static readonly MemoryInfo memoryInfo = OrtMemoryInfo.DefaultInstance;
// 每次推理复用内存
var inputOrtValue = OrtValue.CreateTensorValueFromMemory(memoryInfo, input.Buffer, input.Dimensions);
3. 批处理优化
// 修改输入维度为支持 batch
var input = new DenseTensor<float>(new[] {batchSize, 3, 640, 640});
避坑指南
- 输入尺寸问题 :
- YOLOv11 默认训练尺寸为 640×640
-
如果修改输入尺寸,需要同时修改模型最后的检测头
-
颜色通道顺序 :
- OpenCV 默认 BGR,ONNX 模型通常需要 RGB
-
预处理时务必转换通道顺序
-
多线程安全 :
- ONNX Session 不是线程安全的
- 推荐方案:每个线程创建独立 Session,或使用 Session 池
代码规范建议
-
所有公开方法添加 XML 注释
/// <summary> /// 执行非极大值抑制过滤 /// </summary> /// <param name="detections"> 检测结果列表 </param> /// <param name="iouThreshold"> 交并比阈值 </param> /// <returns> 过滤后的检测框 </returns> -
使用 C# 命名规范:
- 方法名:PascalCase
- 局部变量:camelCase
- 常量:ALL_CAPS
延伸思考
如何动态调整检测置信度阈值?
可以考虑以下实现方案:
1. 将 NMS 方法中的置信度阈值参数化
2. 使用滑动条实时调节(适用于 GUI 应用)
3. 根据场景复杂度自动调整(如检测目标数量少时降低阈值)
4. 将阈值保存在配置文件中支持热更新
完整示例项目已开源在 GitHub:https://github.com/example/yolov11-csharp-demo
希望这篇指南能帮助你顺利集成 YOLOv11 到 C# 项目中。如果遇到问题,欢迎在评论区交流讨论!
正文完
