
在工业质检、安防监控等场景中快速准确地识别图像中的目标物体是核心需求。传统方法往往需要复杂的图像处理和大量的定制开发而基于深度学习的目标检测技术尤其是 YOLO 系列因其速度快、精度高而备受青睐。然而对于许多 C# 开发者特别是工业上位机、MES/WMS 系统开发者而言如何将前沿的 YOLO 模型无缝集成到熟悉的 .NET 生态中常常面临环境配置复杂、依赖项冲突、推理流程不清晰等挑战。本文旨在提供一个从零开始的、保姆级的实战教程手把手教你如何在 Visual Studio 中使用 C# 集成最新的 YOLOv8 模型并利用 ONNX Runtime 进行高效推理实现一个完整的工业目标检测应用。整个过程无需深厚的深度学习背景只要你有基础的 C# 开发经验就能在 30 分钟内跑通整个流程看到检测效果。1. 核心概念与准备工作在开始编码之前我们需要理解几个关键概念并准备好相应的“食材”。1.1 YOLOv8 与 ONNX 格式YOLOv8是 Ultralytics 公司发布的最新 YOLO 系列模型它在速度、精度和易用性上都有显著提升。它支持目标检测、实例分割、姿态估计等多种视觉任务。我们这里主要使用其目标检测功能。ONNX是一种开放的模型格式它允许你在不同的框架如 PyTorch, TensorFlow之间转换和运行模型。将 YOLOv8 模型导出为 ONNX 格式后我们就可以脱离 Python 环境在 C#、C 等环境中进行推理。ONNX Runtime是一个高性能的推理引擎专门用于运行 ONNX 格式的模型。它支持 CPU、GPU 等多种硬件加速并且为 .NET 提供了完美的支持Microsoft.ML.OnnxRuntime库。简单来说我们的技术路径是YOLOv8 (PyTorch) - ONNX 模型 - ONNX Runtime (C#) - 检测结果。1.2 环境与工具清单请确保你的开发环境中已安装以下工具这是后续所有步骤的基础Visual Studio 2022社区版即可。确保安装了“.NET 桌面开发”和“使用 C 的桌面开发”工作负载。.NET Framework 4.7.2 或 .NET 6/8本教程以 .NET 6 控制台应用为例它兼容性好且现代。Python 环境用于模型导出如果你已经有训练好的 YOLOv8 模型 (.pt文件)可以跳过此步。如果没有需要一个临时的 Python 环境来下载和导出官方预训练模型。安装 Python 3.8。使用 pip 安装ultralytics和onnx包pip install ultralytics onnx。1.3 项目最终效果预览我们将创建一个 C# 控制台应用程序。你只需要提供一张图片的路径程序就会加载 ONNX 格式的 YOLOv8 模型。对图片进行预处理缩放、归一化、转换维度。使用 ONNX Runtime 进行推理。解析推理输出得到边界框、类别和置信度。将检测结果绘制在原图上并保存。最终你会得到一个控制台输出检测信息以及一张画有检测框的结果图片。2. 第一步获取 ONNX 格式的 YOLOv8 模型这是整个流程的起点。你需要一个.onnx格式的模型文件。有两种方式2.1 方式一使用官方预训练模型推荐新手如果你没有自定义训练的需求可以直接导出 Ultralytics 官方的预训练模型。在准备好的 Python 环境中运行以下脚本# export_model.py from ultralytics import YOLO # 加载官方的 YOLOv8n 模型nano版本最小最快适合演示 model YOLO(yolov8n.pt) # 会自动下载 yolov8n.pt # 将模型导出为 ONNX 格式 # imgsz: 指定模型输入图片的尺寸必须是32的倍数如640 # simplify: 使用 onnx-simplifier 简化模型减少出错概率 # opset: ONNX 算子集版本12是一个稳定版本 success model.export(formatonnx, imgsz640, simplifyTrue, opset12) print(f导出成功: {success})运行后你会在当前目录下得到一个yolov8n.onnx文件。这就是我们 C# 程序需要的核心模型文件。2.2 方式二使用自己训练的模型如果你有自己的数据集并训练了模型例如best.pt导出方式类似from ultralytics import YOLO model YOLO(‘path/to/your/best.pt’) # 加载自定义模型 success model.export(format‘onnx’, imgsz640, simplifyTrue, opset12)关键点请记录下你导出模型时使用的imgsz参数例如 640后续 C# 代码中的输入尺寸必须与此严格一致。3. 第二步创建 C# 项目并配置环境打开 Visual Studio 2022开始创建我们的项目。3.1 创建新项目选择“创建新项目”。搜索“控制台”选择“控制台应用”如果是 .NET Framework选择“控制台应用(.NET Framework)”。项目名称可以定为YoloV8OnnxRuntimeDemo。选择目标框架建议选择.NET 6.0 (长期支持版)或更高版本以获得更好的性能和跨平台支持。3.2 安装必要的 NuGet 包项目创建后我们需要通过 NuGet 包管理器安装运行 ONNX 模型的核心依赖。右键点击项目 - “管理 NuGet 程序包”。在“浏览”选项卡中搜索并安装以下两个包Microsoft.ML.OnnxRuntime这是 ONNX Runtime 的 .NET 绑定库是我们进行模型推理的引擎。Microsoft.ML.OnnxRuntime.GPU可选如果你的电脑有 NVIDIA GPU 并且配置好了 CUDA/cuDNN 环境安装此包可以利用 GPU 加速推理速度会大幅提升。如果只安装第一个则默认使用 CPU 推理。安装完成后你的项目依赖项中应该能看到这两个包。3.3 准备模型和测试图片在项目根目录下或者任何你喜欢的位置创建一个文件夹例如叫Assets。将上一步导出的yolov8n.onnx文件复制到这个文件夹中。同时找一张包含常见物体如人、车、狗的图片例如test.jpg也放入Assets文件夹用于测试。为了让程序运行时能找到这些文件我们需要在 Visual Studio 中设置它们的属性。在“解决方案资源管理器”中右键点击Assets文件夹下的yolov8n.onnx和test.jpg文件选择“属性”。将“复制到输出目录”设置为“如果较新则复制”或“始终复制”。这样当你编译运行程序时这些文件会被自动复制到输出目录如bin\Debug\net6.0下。4. 第三步编写 C# 核心推理代码这是最核心的部分。我们将创建几个关键的类来组织代码。为了清晰我们新建几个类文件。4.1 定义模型输入输出和配置类首先创建一个YoloConfig.cs文件用于存放模型的配置信息。// YoloConfig.cs namespace YoloV8OnnxRuntimeDemo { public class YoloConfig { // 模型路径 (相对于可执行文件) public string ModelPath { get; set; } “Assets\yolov8n.onnx”; // 模型输入的图片尺寸必须与导出模型时的 imgsz 一致 public int InputWidth { get; set; } 640; public int InputHeight { get; set; } 640; // 置信度阈值低于此值的检测框将被过滤 public float ConfidenceThreshold { get; set; } 0.5f; // 非极大值抑制(NMS)的IOU阈值用于合并重叠框 public float NmsThreshold { get; set; } 0.5f; // COCO数据集的80个类别名称 (YOLOv8n预训练模型使用) public static readonly string[] CocoLabels new string[] { “person”, “bicycle”, “car”, “motorcycle”, “airplane”, “bus”, “train”, “truck”, “boat”, “traffic light”, “fire hydrant”, “stop sign”, “parking meter”, “bench”, “bird”, “cat”, “dog”, “horse”, “sheep”, “cow”, “elephant”, “bear”, “zebra”, “giraffe”, “backpack”, “umbrella”, “handbag”, “tie”, “suitcase”, “frisbee”, “skis”, “snowboard”, “sports ball”, “kite”, “baseball bat”, “baseball glove”, “skateboard”, “surfboard”, “tennis racket”, “bottle”, “wine glass”, “cup”, “fork”, “knife”, “spoon”, “bowl”, “banana”, “apple”, “sandwich”, “orange”, “broccoli”, “carrot”, “hot dog”, “pizza”, “donut”, “cake”, “chair”, “couch”, “potted plant”, “bed”, “dining table”, “toilet”, “tv”, “laptop”, “mouse”, “remote”, “keyboard”, “cell phone”, “microwave”, “oven”, “toaster”, “sink”, “refrigerator”, “book”, “clock”, “vase”, “scissors”, “teddy bear”, “hair drier”, “toothbrush” }; } }4.2 定义检测结果类创建一个Prediction.cs文件用于表示单个检测结果。// Prediction.cs using System.Drawing; namespace YoloV8OnnxRuntimeDemo { public class Prediction { // 边界框 (相对于原始图片的坐标) public RectangleF BoundingBox { get; set; } // 类别标签 public string Label { get; set; } // 置信度 public float Confidence { get; set; } public override string ToString() { return $“{Label} ({Confidence:P1}) at [{BoundingBox.X}, {BoundingBox.Y}, {BoundingBox.Width}, {BoundingBox.Height}]”; } } }4.3 核心推理器类这是重中之重创建YoloInference.cs文件。它将负责加载模型、预处理图片、运行推理和后处理结果。// YoloInference.cs using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using System.Drawing; using System.Drawing.Imaging; namespace YoloV8OnnxRuntimeDemo { public class YoloInference : IDisposable { private readonly InferenceSession _session; private readonly YoloConfig _config; public YoloInference(YoloConfig config) { _config config; // 创建推理会话加载模型 // SessionOptions 可以配置线程数、GPU等。这里使用默认CPU。 var options new SessionOptions(); // 如果需要使用GPU可以这样配置确保安装了GPU包 // options.AppendExecutionProvider_CUDA(0); // 使用第0块GPU _session new InferenceSession(_config.ModelPath, options); } // 主方法输入图片路径返回检测结果列表 public ListPrediction Predict(string imagePath) { // 1. 预处理将图片加载并处理成模型需要的张量 using var image Image.FromFile(imagePath); var inputTensor PreprocessImage(image); // 2. 准备模型输入 var inputs new ListNamedOnnxValue { NamedOnnxValue.CreateFromTensor(“images”, inputTensor) }; // 3. 运行推理 using IDisposableReadOnlyCollectionDisposableNamedOnnxValue results _session.Run(inputs); // 4. 获取输出数据 // YOLOv8 ONNX 模型输出名称为 “output0”是一个形状为 [1, 84, 8400] 的张量 var outputTensor results.First().AsTensorfloat(); var predictions ParseOutput(outputTensor, image.Width, image.Height); // 5. 应用非极大值抑制(NMS)过滤重叠框 var finalPredictions ApplyNms(predictions); return finalPredictions; } // 图片预处理缩放、归一化、转换通道、转为张量 private DenseTensorfloat PreprocessImage(Image image) { // 将图片缩放到模型输入尺寸 using var resizedImage ResizeImage(image, _config.InputWidth, _config.InputHeight); // 将 Bitmap 转换为 RGB 字节数组 var bitmap new Bitmap(resizedImage); var bitmapData bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); int bytesPerPixel 3; // RGB int tensorSize _config.InputWidth * _config.InputHeight * bytesPerPixel; var tensorData new float[tensorSize]; unsafe { byte* srcPtr (byte*)bitmapData.Scan0; int stride bitmapData.Stride; // 遍历每个像素将 BGR 数据转换为 RGB 并归一化到 [0,1] for (int y 0; y _config.InputHeight; y) { byte* row srcPtr (y * stride); for (int x 0; x _config.InputWidth; x) { // 注意Bitmap 数据是 BGR 顺序 float b row[x * bytesPerPixel] / 255.0f; // Blue float g row[x * bytesPerPixel 1] / 255.0f; // Green float r row[x * bytesPerPixel 2] / 255.0f; // Red // YOLOv8 官方预处理是除以255归一化通道顺序为RGB int baseIndex (y * _config.InputWidth x) * 3; tensorData[baseIndex] r; // R channel tensorData[baseIndex 1] g; // G channel tensorData[baseIndex 2] b; // B channel } } } bitmap.UnlockBits(bitmapData); // 创建张量形状为 [1, 3, Height, Width] (NCHW格式) var tensor new DenseTensorfloat(tensorData, new[] { 1, 3, _config.InputHeight, _config.InputWidth }); return tensor; } // 简单的图片缩放方法 private Image ResizeImage(Image image, int width, int height) { var destRect new Rectangle(0, 0, width, height); var destImage new Bitmap(width, height); destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution); using (var graphics Graphics.FromImage(destImage)) { graphics.CompositingMode System.Drawing.Drawing2D.CompositingMode.SourceCopy; graphics.CompositingQuality System.Drawing.Drawing2D.CompositingQuality.HighQuality; graphics.InterpolationMode System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; graphics.SmoothingMode System.Drawing.Drawing2D.SmoothingMode.HighQuality; graphics.PixelOffsetMode System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; using (var wrapMode new ImageAttributes()) { wrapMode.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY); graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode); } } return destImage; } // 解析模型输出张量 private ListPrediction ParseOutput(Tensorfloat output, int originalWidth, int originalHeight) { var predictions new ListPrediction(); // output 形状: [1, 84, 8400] // 84 4 (bbox cx, cy, w, h) 80 (COCO类别分数) // 8400 80*80 40*40 20*20 (三种尺度的特征图) int dimensions output.Dimensions[1]; // 84 int numProposals output.Dimensions[2]; // 8400 float widthScale (float)originalWidth / _config.InputWidth; float heightScale (float)originalHeight / _config.InputHeight; for (int i 0; i numProposals; i) { // 找到最大类别分数及其索引 float maxScore float.MinValue; int maxIndex -1; for (int j 4; j dimensions; j) { float score output[0, j, i]; if (score maxScore) { maxScore score; maxIndex j - 4; // 转换为类别索引 (0-79) } } // 如果最大分数超过置信度阈值则保留 if (maxScore _config.ConfidenceThreshold maxIndex 0) { // 获取边界框中心点和宽高 (相对于640x640) float cx output[0, 0, i]; float cy output[0, 1, i]; float w output[0, 2, i]; float h output[0, 3, i]; // 将中心点坐标转换为左上角坐标 float x1 cx - w / 2; float y1 cy - h / 2; // 将坐标映射回原始图片尺寸 x1 * widthScale; y1 * heightScale; w * widthScale; h * heightScale; // 确保坐标不超出图片范围 x1 Math.Max(0, x1); y1 Math.Max(0, y1); w Math.Min(w, originalWidth - x1); h Math.Min(h, originalHeight - y1); var bbox new RectangleF(x1, y1, w, h); var label YoloConfig.CocoLabels[maxIndex]; predictions.Add(new Prediction { BoundingBox bbox, Label label, Confidence maxScore }); } } return predictions; } // 非极大值抑制 (NMS) - 过滤重叠的检测框 private ListPrediction ApplyNms(ListPrediction predictions) { if (predictions.Count 0) return new ListPrediction(); // 按置信度降序排序 var sortedPredictions predictions.OrderByDescending(p p.Confidence).ToList(); var selected new ListPrediction(); while (sortedPredictions.Any()) { // 取出置信度最高的一个 var current sortedPredictions[0]; selected.Add(current); sortedPredictions.RemoveAt(0); // 计算当前框与剩余框的 IoU移除重叠度高的 for (int i sortedPredictions.Count - 1; i 0; i--) { var iou CalculateIoU(current.BoundingBox, sortedPredictions[i].BoundingBox); if (iou _config.NmsThreshold) { sortedPredictions.RemoveAt(i); } } } return selected; } // 计算两个矩形框的交并比 (IoU) private float CalculateIoU(RectangleF boxA, RectangleF boxB) { float x1 Math.Max(boxA.Left, boxB.Left); float y1 Math.Max(boxA.Top, boxB.Top); float x2 Math.Min(boxA.Right, boxB.Right); float y2 Math.Min(boxA.Bottom, boxB.Bottom); float intersectionArea Math.Max(0, x2 - x1) * Math.Max(0, y2 - y1); float areaA boxA.Width * boxA.Height; float areaB boxB.Width * boxB.Height; float unionArea areaA areaB - intersectionArea; return unionArea 0 ? intersectionArea / unionArea : 0; } public void Dispose() { _session?.Dispose(); } } }4.4 绘制结果与主程序入口最后我们修改Program.cs文件串联整个流程并绘制检测框。// Program.cs using System.Drawing; using System.Drawing.Imaging; namespace YoloV8OnnxRuntimeDemo { internal class Program { static void Main(string[] args) { Console.WriteLine(“ C# YOLOv8 ONNX Runtime 目标检测演示 ”); // 1. 初始化配置和推理器 var config new YoloConfig(); // 可以根据需要修改配置例如使用GPU或调整阈值 // config.ModelPath “Assets\yolov8s.onnx”; // 使用更大的模型 // config.ConfidenceThreshold 0.3f; using var yolo new YoloInference(config); Console.WriteLine($“模型加载成功: {config.ModelPath}”); // 2. 指定测试图片路径 string testImagePath “Assets\test.jpg”; // 请确保此图片存在 if (!File.Exists(testImagePath)) { Console.WriteLine($“错误测试图片不存在于 {testImagePath}”); Console.WriteLine(“请将一张名为 ‘test.jpg’ 的图片放入 Assets 文件夹。”); return; } // 3. 进行推理 Console.WriteLine($“正在检测图片: {testImagePath}”); var stopwatch System.Diagnostics.Stopwatch.StartNew(); ListPrediction results; try { results yolo.Predict(testImagePath); } catch (Exception ex) { Console.WriteLine($“推理过程中发生错误: {ex.Message}”); return; } stopwatch.Stop(); Console.WriteLine($“推理完成耗时: {stopwatch.ElapsedMilliseconds} ms”); Console.WriteLine($“检测到 {results.Count} 个目标:”); // 4. 打印检测结果 foreach (var pred in results) { Console.WriteLine($“ - {pred}”); } // 5. 在图片上绘制检测框并保存 string outputImagePath “Assets\output.jpg”; DrawPredictions(testImagePath, outputImagePath, results); Console.WriteLine($“结果图片已保存至: {outputImagePath}”); Console.WriteLine(“\n演示结束按任意键退出...”); Console.ReadKey(); } static void DrawPredictions(string inputPath, string outputPath, ListPrediction predictions) { using var image Image.FromFile(inputPath); using var graphics Graphics.FromImage(image); // 使用抗锯齿让线条更平滑 graphics.SmoothingMode System.Drawing.Drawing2D.SmoothingMode.AntiAlias; // 定义颜色和字体 var font new Font(“Arial”, 12, FontStyle.Bold); var brush new SolidBrush(Color.Red); var pen new Pen(Color.Red, 2); // 框线宽度为2 foreach (var pred in predictions) { var rect pred.BoundingBox; // 绘制矩形框 graphics.DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height); // 准备标签文本 string labelText $“{pred.Label} ({pred.Confidence:P0})”; // 测量文本大小 var textSize graphics.MeasureString(labelText, font); // 绘制文本背景提高可读性 graphics.FillRectangle(Brushes.Red, rect.X, rect.Y - textSize.Height, textSize.Width, textSize.Height); // 绘制文本 graphics.DrawString(labelText, font, Brushes.White, rect.X, rect.Y - textSize.Height); } // 保存图片 image.Save(outputPath, ImageFormat.Jpeg); Console.WriteLine(“检测框绘制完成。”); } } }5. 第四步运行与验证现在所有代码都已就绪。确保Assets文件夹下的yolov8n.onnx和test.jpg文件属性已设置为“复制到输出目录”。在 Visual Studio 中按F5或点击“开始调试”运行程序。如果一切顺利你将在控制台看到类似以下的输出 C# YOLOv8 ONNX Runtime 目标检测演示 模型加载成功: Assets\yolov8n.onnx 正在检测图片: Assets\test.jpg 推理完成耗时: 120 ms 检测到 3 个目标: - person (98%) at [245.3, 120.5, 150.2, 350.8] - car (95%) at [450.1, 300.7, 200.5, 150.3] - dog (87%) at [50.8, 400.2, 100.4, 120.9] 结果图片已保存至: Assets\output.jpg同时在Assets文件夹下会生成一张output.jpg打开它你就能看到画有红色检测框和标签的图片了恭喜你成功在 C# 中集成了 YOLOv8 目标检测6. 常见问题与排查思路在实际操作中你可能会遇到一些问题。以下是常见问题的排查指南问题现象可能原因解决思路运行时错误找不到模型文件1. 模型文件路径错误。2. 文件属性未设置为“复制到输出目录”。1. 检查YoloConfig中的ModelPath是否为相对路径并确保相对于bin/Debug/netX.0目录正确。2. 在解决方案资源管理器中右键点击模型文件 - 属性 - 复制到输出目录 - 选择“始终复制”。推理时抛出异常输入维度不匹配1. C# 代码中InputWidth/Height与导出模型时的imgsz参数不一致。2. 图片预处理后的张量形状错误。1.确保YoloConfig.InputWidth和InputHeight与 Python 导出命令中的imgsz完全一致通常是640。2. 调试PreprocessImage方法检查生成的张量形状是否为[1, 3, height, width]。检测结果为空或完全错误1. 图片预处理归一化、通道顺序与模型训练时不一致。2. 置信度阈值ConfidenceThreshold设置过高。3. 模型输出解析逻辑错误。1. YOLOv8 官方预处理是RGB通道顺序和除以255归一化。确保PreprocessImage中的通道交换BGR-RGB和归一化/255.0f正确。2. 尝试降低ConfidenceThreshold到 0.25 或 0.3。3. 打印outputTensor的维度 (outputTensor.Dimensions)确认是[1, 84, 8400]。检查ParseOutput中索引计算是否正确。程序运行非常慢1. 使用 CPU 进行推理。2. 图片分辨率过大。3. 未进行 NMS 过滤框太多。1. 如果有 NVIDIA GPU安装Microsoft.ML.OnnxRuntime.GPU包并在InferenceSession的SessionOptions中启用 CUDA 提供程序参见代码注释。2. 在推理前先将图片缩放到合理大小如 640x640。3. 确保 NMS 阈值NmsThreshold设置合理如0.5。内存泄漏或程序崩溃InferenceSession或Bitmap对象未正确释放。1. 确保YoloInference类实现了IDisposable并在Dispose方法中释放_session。2. 确保PreprocessImage方法中使用的Bitmap在LockBits后调用了UnlockBits。3. 使用using语句包裹YoloInference对象。绘制图片时抛出 GDI 异常1. 输出图片路径已存在且被占用。2. 对Image对象的多次不当操作。1. 在保存前检查文件是否存在可以先删除旧文件。2. 确保DrawPredictions方法中用于绘制的Graphics对象和Image对象在使用后被正确释放使用using。7. 工程化最佳实践与扩展思路将演示代码应用到实际工业项目中还需要考虑更多因素7.1 性能优化建议会话复用InferenceSession的创建开销较大。在实际服务中应将其设计为单例或池化对象在整个应用生命周期内复用。批量推理ONNX 模型支持批量输入。如果需要处理大量图片可以将多张图片预处理后组合成一个[BatchSize, 3, Height, Width]的张量进行批量推理能显著提升吞吐量。异步处理推理是计算密集型操作。在 GUI 应用如 WPF/WinForms或 Web API 中务必使用Task.Run或异步方法将推理操作放到后台线程避免阻塞UI或请求线程。输入尺寸优化不是所有场景都需要 640x640 的输入。对于小目标检测可能需要更大的imgsz如 1280。对于纯速度要求可以使用更小的imgsz如 320或更小的模型如yolov8n。需要在速度和精度间权衡。7.2 代码结构优化依赖注入在 ASP.NET Core 或大型桌面应用中通过依赖注入容器注册IYoloInference接口和其实现方便管理和测试。配置外部化将模型路径、阈值等配置移到appsettings.json文件中便于不同环境开发、测试、生产切换。日志记录集成ILogger接口记录模型加载时间、推理耗时、错误信息等便于监控和调试。单元测试为预处理、后处理NMS, IoU计算等纯逻辑函数编写单元测试确保核心算法正确性。7.3 功能扩展方向支持更多视觉任务YOLOv8 不仅支持检测还支持分割segment和姿态估计pose。这些模型的 ONNX 输出格式不同需要修改ParseOutput逻辑来解析关键点或掩码。集成到 GUI 应用将上述核心逻辑封装成一个类库然后轻松集成到 WPF、WinForms 或 Avalonia 等桌面应用中实现实时摄像头检测或图片批量处理功能。开发 RESTful API创建一个 ASP.NET Core Web API 项目提供图片上传接口返回 JSON 格式的检测结果方便与其他系统集成。使用 OpenCvSharp 替代 System.Drawing对于更专业的图像处理如摄像头采集、视频流处理System.Drawing功能有限且跨平台支持不佳。可以使用OpenCvSharp库它提供了更强大且跨平台的计算机视觉功能并且其Mat对象与 ONNX Runtime 的Tensor转换也更高效。通过这个从模型导出、环境搭建、代码编写到运行验证的完整流程你已经掌握了在 C# 生态中集成深度学习目标检测模型的关键技能。这套方法不仅适用于 YOLOv8其核心思路——通过 ONNX 格式桥接训练框架和推理引擎在 C# 中进行预处理、推理和后处理——可以迁移到许多其他视觉甚至 NLP 模型上。