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

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实时插补控制都能稳定运行。可作为边缘计算引擎用于人工智能产品验证、开发;也可以作为域控核心用于机器人产品开发。

功能特点

  • 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

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

 

 

 

 

 

2. 环境配置

2.1 .NET 8.0安装

  进入微软官网下载地址https://dotnet.microsoft.com/zh-cn/download,显示如下页面直接下载即可

 

 

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

 

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

 

 

2.2 VS Code配置C#环境

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

 

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

3. 创建并配置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# APII项目,该项目均可以通过NUGET安装,在该项目中,输入以下指令,进行以来安装:

  • OpenCvSharp4安装指令:

dotnet add package OpenCvSharp4 --version 4.9.0.20240103
dotnet add package OpenCvSharp4.runtime.win --version 4.9.0.20240103
  • OpenVINO™ C# API 安装指令:

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

4. 编写推理代码

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

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(20, 130), HersheyFonts.HersheyPlain, 2, new Scalar(255, 0, 255), 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文件,将上述代码替换该文件中的代码即可。

5. 项目编译与运行

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

dotnet build --configuration Release

 

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

dotnet run --configuration Release

 

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

6. 效果对比

  为了对比推理效果,此处还开展了同步推理实现,对比视频如下所示:

  • 异步推理效果视频:

【哪吒开发板试用】在哪吒开发板上也可以实现视频推理-异步推

  • 同步推理效果视频:

【哪吒开发板试用】在哪吒开发板上也可以实现视频推理-同

  可以看出,异步推理速度可以实现到25FPS以上,而同步推理只能达到12FPS左右,其推理速度提升了一倍,且满足视频的实时推理要求。

7. 总结

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

  最后如果各位开发者在使用中有任何问题,欢迎大家与我联系。

 

 

 

相关文件下载
yolov8_async_csharp.rar
40.92 MB
下载
Logo

为开发者提供丰富的英特尔开发套件资源、创新技术、解决方案与行业活动。欢迎关注!

更多推荐