实战精选|使用 OpenVINO™ C# 异步推理接口部署 YOLOv8 ——在哪吒开发板上也可以实现视频推理

2024/11/13 12:35
阅读数 80

点击蓝字

关注我们,让开发变得更有趣

作者 | 颜国进 英特尔边缘计算创新大使 


OpenVINO™


2024英特尔® “走近开发者”互动活动-哪吒开发套件免费试 用 AI 创新计划:哪吒开发板是专为支持入门级边缘 AI 应用程序和设备而设计,能够满足人工智能学习、开发、实训等应用场景。为了测试该开发板的推理性能,同时测试所推出的 OpenVINO™ C# API项目能否应用到该开发板上,我们使用该开发板,结合 OpenVINO™ C# API 的异步推理功能,加速深度学习推理速度。

(https://github.com/guojin-yan/OpenVINO-CSharp-API)


OpenVINO™


哪吒开发板


1.1 产品简介


哪吒(Nezha)开发套件以信用卡大小(85 x 56mm)的开发板-哪吒(Nezha)为核心,哪吒采用Intel® N97处理器(Alder Lake-N),最大睿频 3.6GHz,Intel® UHD Graphics 内核 GPU,可实现高分辨率显示;板载 LPDDR5 内存、eMMC 存储及 TPM 2.0,配备 GPIO 接口,支持 Windows 和 Linux 操作系统,这些功能和无风扇散热方式相结合,为各种应用程序构建高效的解决方案,适用于如自动化、物联网网关、数字标牌和机器人等应用。


该开发板是类树莓派的 x86 主机,可支持 Linux Ubuntu 及完整版 Windows 操作系统。板载英特尔 N97 处理器,最高运行频率可达 3.6 GHz,且内置显卡(iGPU),板载 64GB eMMC 存储及 LPDDR5 4800MHz(4GB/8GB),支持 USB 3.0、HDMI 视频输出、3.5mm 音频接口、1000Mbps 以太网口。完全可把它作为一台 mini 小电脑来看待,且其可外接 Arduino,STM32 等单片机,扩展更多应用及各种传感器模块。


此外, 其主要接口与 Jetson Nano 载板兼容,GPIO 与树莓派兼容,能够最大限度地复用树莓派、Jetson Nano 等生态资源,无论是自动化、物联网网关、数字标牌或是摄像头物体识别、3D 打印,还是 CNC 实时插补控制都能稳定运行。可作为边缘计算引擎用于人工智能产品验证、开发;也可以作为域控核心用于机器人产品开发。


1.2 功能特点


  • Intel® Processor N97

  • 板载 LPDDR5 内存, 8GB

  • 板载 eMMC 存储, 64GB

  • 1Gigabit LAN x 1

  • HDMI 1.4b x 1

  • USB 3.2 Gen 2 (Type-A) x 3, 10针 USB 2.0 x 2/UART x 1

  • 40针 GPIO x 1

  • 12V 直流输入, 5A

  • TPM 2.0


下图为哪吒开发板产品介绍图片:


OpenVINO™


环境配置


2.1NET 8.0 安装


进入微软官网下载地址:

https://dotnet.microsoft.com/zh-cn/download


显示如下页面直接下载即可:



下载完成过后,右击以管理员方式运行即可,进入一下页面后,直接点击安装,按照默认方式安装即可:



安装完成后,在 CMD 中输入 dotnet --info 指令,查看是否安装成功:



2.2 VS Code 配置 C# 环境


VS Code 安装较为简单,大家直接网上下载安装包安装即可,此处不做太多的赘述。进入 VS Code,在扩展中,依次安装一下插件: 



安装完成后,便可以进行 C# 项目编辑。


OpenVINO™


创建并配置 YOLOv8 推理项目


3.1 创建 YOLOv8 推理项目


此处我们使用 CMD 创建项目,首先输入以下指令:

dotnet new console -o yolov8_async_csharp -f net8.0


输入指令后,结果如下图所示:



3.2 配置 YOLOv8 推理项目


该项目中需要配置 OpenCV 和 OpenVINO™ 依赖,其中 OpenCV 我们在 C# 使用的是 OpenCvSharp4,而  OpenVINO™ 就是使用的我们开发的[OpenVINO™ C# API] (https://github.com/guojin-yan/OpenVINO-CSharp-API) 项目,该项目均可以通过 NUGET 安装,在该项目中,输入以下指令,进行以来安装:


OpenCvSharp4 安装指令:

dotnet add package OpenCvSharp4 --version 4.9.0.20240103dotnet add package OpenCvSharp4.runtime.win --version 4.9.0.20240103


OpenVINO™ C# API 安装指令:

dotnet add package OpenVINO.CSharp.API --version 2024.3.0.2dotnet add package OpenVINO.runtime.win --version 2024.3.0.1dotnet add package OpenVINO.CSharp.API.Extensions.OpenCvSharp --version 1.0.6.1


OpenVINO™


编写推理代码


下面为该项目所使用的所有代码,为了提高推理速度,此处使用的为异步推理过程。

using OpenCvSharp.Dnn;using OpenCvSharp;using OpenVinoSharp;using OpenVinoSharp.Extensions.result;using OpenVinoSharp.Extensions.process;using System.Diagnostics;using OpenVinoSharp.preprocess;namespace openvino_async_csharp{   internal class Program  {       static void Main(string[] args){           Console.WriteLine("Hello, World!");           yolov8_async_det();      }       static void yolov8_async_det(){           string video_path = "video.mp4";           string model_path = "yolov8s.onnx";                      Core core = new Core();                      Model model = core.read_model(model_path);                      CompiledModel compiled_model = core.compile_model(model, "GPU.0");                      VideoCapture capture = new VideoCapture(video_path);                      if (!capture.IsOpened())                     {                             Console.WriteLine("ERROR: 视频无法打开");                             return;                     }                      List<InferRequest> requests = new List<InferRequest> { compiled_model.create_infer_request(), compiled_model.create_infer_request() };                      Mat frame = new Mat();                      capture.Read(frame);                      float factor = 0f;                      float[] input_data = preprocess(frame, out factor);                      requests[0].get_input_tensor().set_data(input_data);                      requests[0].start_async();                      Stopwatch sw = new Stopwatch();                      float[] total_infs = new float[3];                      while (true)          {               Mat next_frame = new Mat();               if (!capture.Read(next_frame))              {                                     break;              }               sw.Restart();                              input_data = preprocess(frame, out factor);                              requests[1].get_input_tensor().set_data(input_data);                              sw.Stop();                              total_infs[0] = sw.ElapsedMilliseconds;                              sw.Restart();                              requests[1].start_async();                              requests[0].wait();                              sw.Stop();                              total_infs[1] = sw.ElapsedMilliseconds;               sw.Restart();                              float[] output_data = requests[0].get_output_tensor().get_data<float>(8400 * 84);                              DetResult result = postprocess(output_data, factor);                              sw.Stop();                              total_infs[2] = sw.ElapsedMilliseconds;                              Cv2.PutText(frame, "PreProcess: " + (1000.0 / total_infs[0]).ToString("0.00") + "FPS " + (total_infs[0]).ToString("0.00") + "ms",                   new Point(20, 40), HersheyFonts.HersheyPlain, 2, new Scalar(255, 0, 255), 2);               Cv2.PutText(frame, "Inference: " + (1000.0 / total_infs[1]).ToString("0.00") + "FPS " + (total_infs[1]).ToString("0.00") + "ms",                   new Point(20, 70), HersheyFonts.HersheyPlain, 2, new Scalar(255, 0, 255), 2);               Cv2.PutText(frame, "PostProcess: " + (1000.0 / total_infs[2]).ToString("0.00") + "FPS " + (total_infs[2]).ToString("0.00") + "ms",                   new Point(20, 100), HersheyFonts.HersheyPlain, 2, new Scalar(255, 0, 255), 2);               Cv2.PutText(frame, "Total: " + (1000.0 / (total_infs[0] + total_infs[1] + total_infs[2])).ToString("0.00")                   + "FPS   " + ((total_infs[0] + total_infs[1] + total_infs[2])).ToString("0.00") + "ms",                   new Point(20130), HersheyFonts.HersheyPlain, 2new Scalar(2550255), 2);               Mat res_mat = Visualize.draw_det_result(result, frame);               Cv2.ImShow("Result", res_mat);               Cv2.WaitKey(10);               swap(requests);               frame = next_frame;          }      }       public static float[] preprocess(Mat img, out float factor)      {           Mat mat = new Mat();           Cv2.CvtColor(img, mat, ColorConversionCodes.BGR2RGB);           mat = Resize.letterbox_img(mat, 640, out factor);           mat = Normalize.run(mat, true);           return Permute.run(mat);      }       public static DetResult postprocess(float[] result, float factor){           // Storage results list           List<Rect> positionBoxes = new List<Rect>();           List<int> classIds = new List<int>();           List<float> confidences = new List<float>();           // Preprocessing output results           for (int i = 0; i < 8400; i++)          {               for (int j = 4; j < 84; j++)              {                   float source = result[8400 * j + i];                   int label = j - 4;                   if (source > 0.2)                  {                       float maxSource = source;                                              float cx = result[8400 * 0 + i];                                              float cy = result[8400 * 1 + i];                                              float ow = result[8400 * 2 + i];                                              float oh = result[8400 * 3 + i];                                              int x = (int)((cx - 0.5 * ow) * factor);                                              int y = (int)((cy - 0.5 * oh) * factor);                                              int width = (int)(ow * factor);                                              int height = (int)(oh * factor);                                              Rect box = new Rect(x, y, width, height);                                              positionBoxes.Add(box);                                              classIds.Add(label);                                              confidences.Add(maxSource);                  }              }          }           DetResult re = new DetResult();           int[] indexes = new int[positionBoxes.Count];           CvDnn.NMSBoxes(positionBoxes, confidences, 0.2f, 0.5f, out indexes);           for (int i = 0; i < indexes.Length; i++)          {               int index = indexes[i];               re.add(classIds[index], confidences[index], positionBoxes[index]);          }           return re;      }
      public static void swap(List<InferRequest> requests) {           //(requests[0], requests[1]) = (requests[1], requests[0]);           var tmp = requests[0];           requests[0] = requests[1];           requests[1] = tmp;      }  }}                                                                            


打开创建的项目,编辑 Program.cs 文件,将上述代码替换该文件中的代码即可。


OpenVINO™


项目编译与运行


输入以下指令后进行项目编译:

dotnet build --configuration Release



通过上图可以看出,该项目编译成功,未出现任何问题,接下来输入以下指令进行项目运行:

dotnet run --configuration Release




上图为运行后的输出和推理效果,可以看出使用异步推理后,可以实现20帧以上的推理速度。


为了对比推理效果,此处还开展了同步推理实现,异步推理速度可以实现到 25FPS 以上,而同步推理只能达到 12FPS 左右,其推理速度提升了一倍,且满足视频的实时推理要求。


OpenVINO™


总结


在该项目中,我们使用哪吒开发板套件,通过前期推出的 OpenVINO™ C# API 项目,实现了在开发板环境下部署 YOLOv8 模型,并进行了视频推理测试。在使用哪吒开发板套件的 IGPU 设备进行推理时,其推理速度平均可以达到 25FPS 以上,可以实现视频实时推理。


OpenVINO™


本文分享自微信公众号 - OpenVINO 中文社区(openvinodev)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部