在工业自动化、安防监控、缺陷检测等场景中目标检测是核心的视觉任务。传统方案往往依赖昂贵的专用硬件和复杂的算法部署让许多开发者望而却步。如今借助 YOLOv8 这一高效、易用的目标检测框架以及 C# 这一在工业上位机开发中广泛使用的语言我们可以构建一个轻量、高效且易于集成的检测系统。本文旨在为 C# 开发者特别是工业自动化、上位机软件方向的工程师提供一个从零开始的实战指南。你将学习如何将训练好的 YOLOv8 模型通过 ONNX Runtime 集成到 C# 项目中完成一个可运行的检测程序并理解其中的关键步骤和常见陷阱。整个过程无需深入掌握 Python 或复杂的深度学习框架专注于 C# 端的工程化实现。1. 理解 YOLOv8 与 ONNX Runtime 的集成链路在开始编码之前必须理清从模型训练到 C# 应用调用的完整技术链路。这有助于在后续步骤中定位问题。YOLOv8 是 Ultralytics 公司推出的最新一代目标检测模型以其出色的精度和速度平衡而闻名。它使用 PyTorch 框架进行训练和推理。然而在 C# 生态中直接加载 PyTorch 模型 (.pt 或 .pth 文件) 是极其复杂且不推荐的。因此我们需要一个中间桥梁——ONNX。ONNX 是一种开放的模型表示格式它允许你在不同框架如 PyTorch, TensorFlow之间转换和运行模型。ONNX Runtime 是一个高性能推理引擎专门用于运行 ONNX 格式的模型并且对 .NET 平台提供了原生支持。整个集成流程可以概括为以下几步模型准备在 Python 环境中使用 Ultralytics 库训练或导出 YOLOv8 模型并将其转换为 ONNX 格式。这一步是前置条件本文会提供关键命令。环境搭建在 C# 开发环境中通常是 Visual Studio通过 NuGet 包管理器安装 ONNX Runtime 的 .NET 库。工程实现在 C# 项目中编写代码其核心任务包括加载 ONNX 模型、对输入图像进行预处理尺寸变换、归一化、张量构建、执行模型推理、对模型输出进行后处理解析边界框、置信度、类别并应用非极大值抑制。验证与优化运行程序验证检测结果是否正确并考虑性能优化如使用 GPU 加速。理解这个链路后我们就知道C# 端的核心工作是处理第 3 步即利用 ONNX Runtime 这个“引擎”来驱动我们已经转换好的“图纸”ONNX 模型。2. 环境准备与项目创建一个稳定的开发环境是成功的第一步。我们将使用 Visual Studio 2022 作为 IDE这是 .NET 开发的主流工具。2.1 开发环境与依赖项首先确保你的系统满足以下基础要求组件要求说明操作系统Windows 10/11 64位ONNX Runtime 对 Windows 支持最完善。开发环境Visual Studio 2022 (社区版即可)确保安装时勾选了“.NET 桌面开发”工作负载。.NET 版本.NET 6.0 或 .NET 8.0 (LTS)建议使用长期支持版本以获得稳定性和性能。ONNX RuntimeNuGet 包Microsoft.ML.OnnxRuntime这是 C# 调用 ONNX 模型的核心库。模型文件预训练或自定义的 YOLOv8.onnx模型需提前通过 Python 环境导出。测试图像任意格式的图片文件 (如 .jpg, .png)用于验证程序功能。注意如果你计划使用 GPU 加速推理还需要确保系统已安装兼容的 NVIDIA GPU 及对应版本的 CUDA 和 cuDNN。ONNX Runtime 提供了支持 GPU 的 NuGet 包 (Microsoft.ML.OnnxRuntime.Gpu)但配置更为复杂。为简化入门本文先以 CPU 推理为例。2.2 创建 C# 控制台应用程序打开 Visual Studio 2022点击“创建新项目”。在模板搜索框中输入“Console”选择“控制台应用”对应 .NET 6.0/8.0点击“下一步”。为项目命名例如Yolov8OnnxRuntimeDemo选择合适的位置然后点击“创建”。项目创建后你将看到一个简单的Program.cs文件。2.3 安装必要的 NuGet 包我们需要通过 NuGet 包管理器安装 ONNX Runtime 库。在解决方案资源管理器中右键单击你的项目选择“管理 NuGet 程序包”。在打开的“NuGet 包管理器”窗口中点击“浏览”选项卡。在搜索框中输入Microsoft.ML.OnnxRuntime。在搜索结果中选择正确的包对于 CPU 推理请选择Microsoft.ML.OnnxRuntime。确保版本是稳定版如 1.16.3。点击“安装”按钮。接受任何许可协议。安装完成后你可以在项目依赖项的“包”下看到Microsoft.ML.OnnxRuntime。为了更方便地处理图像我们还可以安装System.Drawing.Common包它提供了Bitmap等类用于图像加载和操作。同样通过 NuGet 安装此包。现在基础环境就准备好了。3. 获取并理解 YOLOv8 ONNX 模型C# 项目本身不负责训练模型我们需要一个预先转换好的 ONNX 模型文件。这里有两种方式3.1 使用官方预训练模型快速开始如果你只是想快速验证流程可以使用 Ultralytics 官方提供的预训练模型如yolov8n.pt并转换为 ONNX。前提你需要一个安装了 Python 和ultralytics包的环境。在命令行中执行以下命令# 安装 ultralytics pip install ultralytics # 导出 YOLOv8n 模型为 ONNX 格式动态批次大小opset12 yolo export modelyolov8n.pt formatonnx opset12执行成功后你会在当前目录下得到yolov8n.onnx文件。将其复制到你的 C# 项目目录下例如放在项目根目录的Models文件夹中。在 Visual Studio 中右键点击Models文件夹选择“添加” - “现有项”将该.onnx文件添加到项目中。重要在文件属性中将“复制到输出目录”设置为“如果较新则复制”或“始终复制”确保程序运行时能找到模型文件。3.2 使用自定义训练模型工业场景在工业缺陷检测等场景中你需要用自己的数据集训练模型。按照 Ultralytics 官方文档准备数据集YOLO 格式并训练模型得到best.pt。使用类似的导出命令指定你的模型路径yolo export modelpath/to/best.pt formatonnx将导出的best.onnx文件同样放入 C# 项目。3.3 理解模型输入输出在编写 C# 代码前必须知道模型期望什么以及它会返回什么。你可以使用 Netron 工具一个开源模型可视化工具打开.onnx文件查看。对于标准的 YOLOv8 检测模型输入一个名为images的张量形状为[batch_size, 3, height, width]。其中batch_size批次大小推理时通常为 1。3通道数对应 RGB 三通道。height,width模型固定的输入尺寸例如 640x640。图像在输入前必须缩放到此尺寸。像素值需要从[0, 255]的整数归一化到[0, 1]的浮点数。输出一个名为output0的张量形状为[batch_size, 84, 8400]以 640x640 输入为例。这是 YOLOv8 的输出关键需要解释84每个预测框的属性数量。对于 COCO 数据集80类它是4 (box) 1 (confidence) 80 (class probabilities) 85。但 YOLOv8 输出是4 80 84置信度是 80 个类别概率中最大的那个。8400模型在输入图像上生成的锚点数量预测框数量。640x640 输入下通常是(80*80 40*40 20*20) * 3 8400。我们的后处理代码就是要解析这个[1, 84, 8400]的输出筛选出有效的检测框。4. 核心代码实现加载、推理与后处理现在进入 C# 编码的核心部分。我们将创建一个专门的类Yolov8OnnxRuntime来封装所有检测逻辑。4.1 定义模型元数据和结果类首先在项目中创建一个新的类文件例如Yolov8OnnxRuntime.cs。在文件顶部引入必要的命名空间using System.Drawing; using System.Drawing.Imaging; using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors;然后定义一些常量和辅助类来存储模型信息和检测结果public class Yolov8OnnxRuntime { // 模型元数据 public const int ModelInputWidth 640; public const int ModelInputHeight 640; public const int ClassCount 80; // COCO 数据集类别数自定义模型需修改 private readonly string[] _classNames LoadClassNames(); // 类别名称数组 // ONNX Runtime 相关 private readonly InferenceSession _session; // 检测结果类 public class DetectionResult { public Rectangle BoundingBox { get; set; } // 边界框 (x, y, width, height) public string Label { get; set; } // 类别标签 public float Confidence { get; set; } // 置信度 } }LoadClassNames方法可以硬编码 COCO 类别或从文件加载自定义类别。4.2 构造函数与模型加载在Yolov8OnnxRuntime类中创建构造函数用于初始化 ONNX Runtime 推理会话public Yolov8OnnxRuntime(string modelPath) { // 设置会话选项例如线程数 var sessionOptions new SessionOptions(); sessionOptions.ExecutionMode ExecutionMode.ORT_SEQUENTIAL; sessionOptions.GraphOptimizationLevel GraphOptimizationLevel.ORT_ENABLE_ALL; // 如果使用GPU需要安装 Microsoft.ML.OnnxRuntime.Gpu 并设置如下 // sessionOptions.AppendExecutionProvider_CUDA(0); // 使用第0块GPU try { _session new InferenceSession(modelPath, sessionOptions); Console.WriteLine($模型加载成功。输入节点: {_session.InputMetadata.First().Key}); } catch (Exception ex) { Console.WriteLine($模型加载失败: {ex.Message}); throw; } }4.3 图像预处理方法预处理是将任意尺寸的输入图像转换为模型所需的[1, 3, 640, 640]张量的过程。private DenseTensorfloat PreprocessImage(Bitmap image) { // 1. 调整图像大小保持宽高比进行填充 var resized ResizeImageWithPadding(image, ModelInputWidth, ModelInputHeight); // 2. 将 Bitmap 转换为 RGB 字节数组 (Height, Width, Channel) var bitmapData resized.LockBits(new Rectangle(0, 0, resized.Width, resized.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); int bytesPerPixel 3; byte[] pixelData new byte[bitmapData.Stride * resized.Height]; System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, pixelData, 0, pixelData.Length); resized.UnlockBits(bitmapData); // 3. 创建张量并填充数据同时进行归一化 [0, 255] - [0, 1] var inputTensor new DenseTensorfloat(new[] { 1, 3, ModelInputHeight, ModelInputWidth }); for (int y 0; y ModelInputHeight; y) { for (int x 0; x ModelInputWidth; x) { // 计算在字节数组中的索引 int index y * bitmapData.Stride x * bytesPerPixel; // 注意Bitmap 数据顺序可能是 BGR但 YOLOv8 训练通常用 RGB。 // 这里按 BGR 顺序读取并转换为 RGB 顺序放入张量。 inputTensor[0, 2, y, x] pixelData[index] / 255.0f; // R 通道 inputTensor[0, 1, y, x] pixelData[index 1] / 255.0f; // G 通道 inputTensor[0, 0, y, x] pixelData[index 2] / 255.0f; // B 通道 } } return inputTensor; } private Bitmap ResizeImageWithPadding(Bitmap sourceImage, int targetWidth, int targetHeight) { // 计算缩放比例保持宽高比 float scale Math.Min((float)targetWidth / sourceImage.Width, (float)targetHeight / sourceImage.Height); int newWidth (int)(sourceImage.Width * scale); int newHeight (int)(sourceImage.Height * scale); // 创建新位图并绘制 Bitmap result new Bitmap(targetWidth, targetHeight, PixelFormat.Format24bppRgb); using (Graphics g Graphics.FromImage(result)) { g.Clear(Color.Gray); // 用灰色填充边缘 int x (targetWidth - newWidth) / 2; int y (targetHeight - newHeight) / 2; g.DrawImage(sourceImage, x, y, newWidth, newHeight); } return result; }关键点预处理中的颜色通道顺序BGR vs RGB和归一化范围必须与模型训练时完全一致。YOLOv8 官方训练默认使用 RGB 顺序和0-1归一化。上述代码在读取时按 BGR 顺序但在放入张量时调整为了 RGB。4.4 执行推理与后处理这是最复杂的部分涉及解析模型的原始输出。public ListDetectionResult Detect(Bitmap image) { // 1. 预处理 var inputTensor PreprocessImage(image); var inputs new ListNamedOnnxValue { NamedOnnxValue.CreateFromTensor(images, inputTensor) }; // 2. 推理 using IDisposableReadOnlyCollectionDisposableNamedOnnxValue results _session.Run(inputs); var output results.First().AsTensorfloat(); // 3. 后处理解析 output0 张量 [1, 84, 8400] var detections ParseModelOutput(output, image.Width, image.Height); // 4. 应用非极大值抑制 (NMS) 去除重叠框 return ApplyNMS(detections, 0.45f, 0.5f); // 置信度阈值 0.45IoU 阈值 0.5 } private ListDetectionResult ParseModelOutput(Tensorfloat output, int originalWidth, int originalHeight) { var detections new ListDetectionResult(); int dimensions ClassCount 4; // 84 long numPredictions output.Dimensions[2]; // 8400 for (int i 0; i numPredictions; i) { // 获取当前预测框的 84 个值 float[] rowData new float[dimensions]; for (int j 0; j dimensions; j) { rowData[j] output[0, j, i]; } // 提取边界框中心点、宽高 (xywh 格式且是相对于 640x640 的归一化值) float xCenter rowData[0]; float yCenter rowData[1]; float width rowData[2]; float height rowData[3]; // 找到最大类别置信度及其索引 float maxConfidence 0; int classId 0; for (int c 4; c dimensions; c) { if (rowData[c] maxConfidence) { maxConfidence rowData[c]; classId c - 4; } } // 应用置信度阈值过滤 float confidenceThreshold 0.25f; if (maxConfidence confidenceThreshold) continue; // 将归一化的中心点坐标转换为左上角坐标 float xMin (xCenter - width / 2); float yMin (yCenter - height / 2); // **关键步骤将坐标映射回原始图像尺寸** // 由于预处理时进行了等比例缩放和填充需要逆变换。 // 这里简化处理假设预处理是直接拉伸非等比例填充。 // 对于等比例填充需要更复杂的坐标反算记录填充偏移量。 float scaleX (float)originalWidth / ModelInputWidth; float scaleY (float)originalHeight / ModelInputHeight; int x (int)(xMin * ModelInputWidth * scaleX); int y (int)(yMin * ModelInputHeight * scaleY); int w (int)(width * ModelInputWidth * scaleX); int h (int)(height * ModelInputHeight * scaleY); // 确保坐标在图像范围内 x Math.Max(0, Math.Min(x, originalWidth - 1)); y Math.Max(0, Math.Min(y, originalHeight - 1)); w Math.Max(1, Math.Min(w, originalWidth - x)); h Math.Max(1, Math.Min(h, originalHeight - y)); detections.Add(new DetectionResult { BoundingBox new Rectangle(x, y, w, h), Label _classNames[classId], Confidence maxConfidence }); } return detections; } private ListDetectionResult ApplyNMS(ListDetectionResult detections, float confidenceThreshold, float iouThreshold) { // 按置信度降序排序 var sortedDetections detections.OrderByDescending(d d.Confidence).ToList(); var selectedDetections new ListDetectionResult(); while (sortedDetections.Count 0) { // 取出置信度最高的检测框 var current sortedDetections[0]; selectedDetections.Add(current); sortedDetections.RemoveAt(0); // 计算与剩余框的 IoU移除重叠度高的 for (int i sortedDetections.Count - 1; i 0; i--) { if (CalculateIoU(current.BoundingBox, sortedDetections[i].BoundingBox) iouThreshold) { sortedDetections.RemoveAt(i); } } } return selectedDetections; } private float CalculateIoU(Rectangle boxA, Rectangle boxB) { int xA Math.Max(boxA.Left, boxB.Left); int yA Math.Max(boxA.Top, boxB.Top); int xB Math.Min(boxA.Right, boxB.Right); int yB Math.Min(boxA.Bottom, boxB.Bottom); int interArea Math.Max(0, xB - xA) * Math.Max(0, yB - yA); int boxAArea boxA.Width * boxA.Height; int boxBArea boxB.Width * boxB.Height; return (float)interArea / (boxAArea boxBArea - interArea); }后处理是代码的核心难点尤其是坐标映射。上述代码中的ParseModelOutput方法做了简化处理直接拉伸映射。在实际工业应用中如果预处理采用了等比例填充Letterbox则必须记录填充的偏移量在后处理时进行精确的反向计算否则检测框位置会偏移。5. 运行验证与结果可视化现在我们将在Program.cs的主方法中整合所有功能完成一个完整的检测流程。5.1 主程序流程using System.Drawing; using System.Drawing.Imaging; class Program { static void Main(string[] args) { // 1. 路径配置 string modelPath Models\yolov8n.onnx; // 确保模型文件已复制到输出目录 string imagePath test_image.jpg; // 你的测试图片路径 string outputPath detected_image.jpg; // 2. 初始化检测器 var detector new Yolov8OnnxRuntime(modelPath); // 3. 加载图像 Bitmap originalImage; try { originalImage new Bitmap(imagePath); Console.WriteLine($成功加载图像: {imagePath}, 尺寸: {originalImage.Width}x{originalImage.Height}); } catch (Exception ex) { Console.WriteLine($加载图像失败: {ex.Message}); return; } // 4. 执行检测 Console.WriteLine(开始目标检测...); var stopwatch System.Diagnostics.Stopwatch.StartNew(); ListYolov8OnnxRuntime.DetectionResult results; try { results detector.Detect(originalImage); stopwatch.Stop(); Console.WriteLine($检测完成耗时: {stopwatch.ElapsedMilliseconds} ms); Console.WriteLine($检测到 {results.Count} 个目标:); } catch (Exception ex) { Console.WriteLine($检测过程中发生错误: {ex.Message}); return; } // 5. 在图像上绘制结果 using (var graphics Graphics.FromImage(originalImage)) { var font new Font(Arial, 12, FontStyle.Bold); var brush new SolidBrush(Color.Red); var pen new Pen(Color.Red, 2); foreach (var result in results) { // 绘制矩形框 graphics.DrawRectangle(pen, result.BoundingBox); // 绘制标签和置信度 string labelText ${result.Label}: {result.Confidence:F2}; graphics.DrawString(labelText, font, brush, result.BoundingBox.X, result.BoundingBox.Y - 20); Console.WriteLine($ - {labelText}, 位置: [{result.BoundingBox.X}, {result.BoundingBox.Y}, {result.BoundingBox.Width}, {result.BoundingBox.Height}]); } } // 6. 保存结果图像 try { originalImage.Save(outputPath, ImageFormat.Jpeg); Console.WriteLine($结果已保存至: {outputPath}); } catch (Exception ex) { Console.WriteLine($保存结果图像失败: {ex.Message}); } Console.WriteLine(程序执行完毕。); } }5.2 预期输出与验证运行程序如果一切顺利你将在控制台看到类似输出成功加载图像: test_image.jpg, 尺寸: 1920x1080 开始目标检测... 检测完成耗时: 245 ms 检测到 3 个目标: - person: 0.89, 位置: [450, 200, 120, 350] - car: 0.95, 位置: [800, 300, 300, 150] - dog: 0.78, 位置: [100, 400, 80, 120] 结果已保存至: detected_image.jpg同时在项目输出目录下会生成一张detected_image.jpg上面用红框标出了检测到的物体及其类别和置信度。6. 常见问题排查与解决方案在实际集成过程中你可能会遇到以下问题。这里提供排查思路。6.1 模型加载失败问题现象可能原因检查与解决InferenceSession初始化时抛出异常提示“模型路径无效”或“文件格式错误”。1. 模型文件路径不正确。2. 模型文件损坏或不是有效的 ONNX 文件。3. ONNX Runtime 版本与模型 opset 不兼容。1. 使用Path.GetFullPath(modelPath)打印绝对路径确认。2. 确保模型文件是通过yolo export正确导出的。可用 Netron 工具打开验证。3. 导出模型时指定opset12较通用。确保 NuGet 包是最新稳定版。提示System.BadImageFormatException项目目标平台x86/x64与 ONNX Runtime 原生库不匹配。在 Visual Studio 中将项目属性 - 生成 - 目标平台改为x64推荐或x86并与 ONNX Runtime 包版本一致。6.2 推理过程出错问题现象可能原因检查与解决session.Run时提示输入节点名称不匹配。输入张量的名称与模型期望的不一致。使用_session.InputMetadata.First().Key打印正确的输入名称。YOLOv8 通常是images。输出张量形状不符合预期不是[1, 84, 8400]。1. 使用的模型不是 YOLOv8 检测模型可能是分类或分割模型。2. 导出模型时参数有误。1. 确认导出的是detect模型。2. 使用 Netron 查看模型输出节点名称和形状。调整后处理代码。推理速度极慢。1. 在 CPU 上运行大模型。2. 预处理或后处理代码效率低。1. 考虑使用 GPU 版本 (Microsoft.ML.OnnxRuntime.Gpu)并正确配置 CUDA。2. 优化图像处理逻辑如使用并行或查找表。6.3 检测结果异常问题现象可能原因检查与解决检测框位置严重偏移不在物体上。坐标映射错误。预处理Resize方式与后处理坐标还原方式不匹配。确保预处理中的缩放填充逻辑与ParseModelOutput中的反算逻辑完全对应。如果预处理用了 Letterbox后处理必须考虑偏移。置信度普遍很低或检测不到任何物体。1. 预处理归一化或颜色通道顺序错误。2. 自定义模型类别数 (ClassCount) 设置错误。3. 置信度阈值 (confidenceThreshold) 设得过高。1. 确认训练时预处理方式。YOLOv8 默认 RGB/0-1。在PreprocessImage中仔细检查通道顺序和归一化。2. 修改ClassCount和_classNames为你的自定义类别。3. 暂时降低阈值至 0.1 观察。同一个物体被重复检测多次。非极大值抑制 (NMS) 的 IoU 阈值 (iouThreshold) 设置过高或 NMS 逻辑有误。检查ApplyNMS函数中的 IoU 计算是否正确。通常 IoU 阈值设置在 0.45-0.6 之间。6.4 内存与性能问题问题现象可能原因检查与解决内存占用持续增长最终OutOfMemoryException。1.InferenceSession或Bitmap未释放。2. 在循环中频繁创建大张量。1. 确保InferenceSession单例化不要重复创建。使用using语句释放Bitmap。2. 考虑复用输入张量对象。GPU 推理时报错提示找不到 CUDA 或加载失败。1. 未安装对应版本的 CUDA/cuDNN。2. 安装了 GPU 包但未在SessionOptions中启用。1. 安装与Microsoft.ML.OnnxRuntime.Gpu包版本要求匹配的 CUDA 和 cuDNN。2. 在代码中取消注释sessionOptions.AppendExecutionProvider_CUDA(0);。7. 生产环境最佳实践与扩展方向将演示代码用于实际工业项目前需要考虑以下优化点。7.1 代码结构与性能优化模型会话管理InferenceSession的创建开销较大。在生产环境中应将其设计为单例或池化对象在整个应用生命周期内复用。预处理优化Bitmap操作和逐像素循环是性能瓶颈。对于实时视频流考虑使用System.Drawing的并行操作或改用更高效的图像库如 OpenCvSharp进行预处理。异步处理对于需要处理多张图片或视频帧的场景将Detect方法改为异步避免阻塞主线程。配置化将模型路径、置信度阈值、IoU 阈值、类别名称文件等参数提取到配置文件如appsettings.json中。7.2 功能扩展支持自定义类别将类别名称从硬编码改为从文件如coco.names加载提高灵活性。完善 Letterbox 处理实现真正的等比例缩放与填充并在后处理中精确还原坐标这是保证检测精度的关键。批量推理修改预处理和模型输入支持一次处理多张图片batch_size 1提升吞吐量。集成到 GUI 应用将检测逻辑封装成服务或组件集成到 WPF、WinForms 或 Avalonia 等桌面 UI 框架中实现实时摄像头检测和结果展示。添加跟踪功能结合目标跟踪算法如 ByteTrack对视频序列中的物体进行 ID 关联实现计数或轨迹分析。7.3 部署注意事项依赖打包如果使用 GPU 版本目标部署机器必须安装对应版本的 VC Redist、CUDA 和 cuDNN。考虑使用独立部署或容器化。日志与监控添加详细的日志记录如推理耗时、检测数量便于监控系统性能和排查问题。异常处理增强鲁棒性如图片加载失败、模型文件被占用、GPU 内存不足等情况应有降级方案如切换回 CPU或友好提示。版本管理严格管理 ONNX 模型文件、C# 代码和 ONNX Runtime 库的版本对应关系避免因版本升级导致的不兼容。通过以上步骤你不仅能够跑通一个基础的 C# 集成 YOLOv8 的 demo更能理解从模型准备到工程部署的完整链条并具备解决实际开发中常见问题的能力。这为在工业上位机、质检系统等场景中应用深度学习目标检测打下了坚实的基础。
网站建设
高端定制
企业官网