[C#]C#中使用OpenVINO部署YOLO-World模型实现实时开放词汇对象检测的onnx模型

【测试环境】

vs2019,net framework4.7.2,opencvsharp4.9.0,全部依赖库截图

【部署流程】

YOLO-World是一个融合了实时目标检测与增强现实(AR)技术的创新平台,旨在将现实世界与数字世界无缝对接。该平台以YOLO(You Only Look Once)算法为核心,实现了对视频中物体的快速准确识别,并通过AR技术将虚拟元素与真实场景相结合,为用户带来沉浸式的交互体验。在本文中,我们将结合OpenVINO™ C# API 使用最新发布的OpenVINO™ 2024.0部署 YOLO-World实现实时开放词汇对象检测:

OpenVINO™ C# API项目链接:

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

特尔发行版 OpenVINO™ 工具套件基于 oneAPI 而开发,可以加快高性能计算机视觉和深度学习视觉应用开发速度工具套件,适用于从边缘到云的各种英特尔平台上,帮助用户更快地将更准确的真实世界结果部署到生产系统中。通过简化的开发工作流程,OpenVINO™ 可赋能开发者在现实世界中部署高性能应用程序和算法。

2024年3月7日,英特尔发布了开源 OpenVINO™ 2024.0 工具包,用于在各种硬件上优化和部署人工智能推理。OpenVINO™ 是英特尔出色的开源 AI 工具包,不仅可以在 x86_64 CPU 上加速 AI 推断,还可以在 ARM CPU 和其他架构、英特尔集成显卡和独立显卡等硬件上加速 AI 推断,包括最近推出的 NPU 插件,用于利用新酷睿超 “Meteor Lake “系统芯片中的英特尔神经处理单元。 OpenVINO™ 2024.0 更注重生成式人工智能(GenAI),为 TensorFlow 句子编码模型提供了更好的开箱即用体验,支持专家混合(MoE)。同时还提高了 LLM 的 INT4 权重压缩质量,增强了 LLM 在英特尔 CPU 上的性能,简化了 Hugging Face 模型的优化和转换,并改进了其他 Hugging Face 集成。

OpenVINO™ C# API 是一个 OpenVINO™ 的 .Net wrapper,应用最新的 OpenVINO™ 库开发,通过 OpenVINO™ C API 实现 .Net 对 OpenVINO™ Runtime 调用,使用习惯与 OpenVINO™ C++ API 一致。OpenVINO™ C# API 由于是基于 OpenVINO™ 开发,所支持的平台与 OpenVINO™ 完全一致,具体信息可以参考 OpenVINO™。通过使用 OpenVINO™ C# API,可以在 .NET、.NET Framework等框架下使用 C# 语言实现深度学习模型在指定平台推理加速。

YOLO-World

YOLO-World是一种创新的实时开放词汇对象检测技术,由腾讯AI实验室开发。它旨在解决传统目标检测方法在开放场景中受预定义类别限制的问题,通过视觉语言建模和大规模数据集预训练,增强了YOLO系列检测器对开放词汇的检测能力。

该技术的核心思想在于,利用一个可重参数化的视觉语言路径聚合网络(RepVL-PAN)来连接文本和图像特征,并引入基于区域的文本对比损失进行预训练。这种设计使得YOLO-World能够在没有见过具体样本的情况下,检测出广泛的物体类别。同时,它还能在保持高性能的同时,降低计算要求,从而适用于实时应用。

通过这种方式,YOLO-World能够增强对开放词汇的检测能力,使其能够在没有预先定义类别的情况下识别出广泛的物体。这种能力使得YOLO-World在实时应用中,如自动驾驶、视频监控、工业质检等领域具有广泛的应用前景。同时,YOLO-World还通过优化模型架构和训练策略,实现了高性能和实时性的平衡。它能够在保持高准确率的同时,降低计算要求,从而满足实际应用中对于实时性的需求。

总的来说,YOLO-World是一种高效、实时且灵活的开放词汇目标检测器,具有广泛的应用前景和巨大的潜力。它不仅能够解决传统目标检测方法在开放场景中的局限性,还能够为各行业提供实时、准确的物体检测解决方案。

2. 模型获取

YOLO-World模型可以通过YOLO-World GitHub获取,小编尝试了一下,步骤比较复杂,且配置起来比较麻烦,因此如果是初学者,不建议使用,下面介绍一个比较简单的导出方式:通过Ultralytics 导出。

Ultralytics 提供了一系列用于计算机视觉任务的工具,包括目标检测、图像分类、语义分割和人脸识别等。这些工具基于流行的深度学习框架如PyTorch,并通过简化复杂任务的实现过程,使用户能够更轻松地进行模型训练和性能评估。

  • 首先安装Ultralytics 环境:

Ultralytics 可以通过pip安装,在环境中输入以下指令即可:

pip install ultralytics 
  • 然后通过Python导出模型:

模型导出代码如下所示:

from ultralytics import YOLO
# Initialize a YOLO-World model
model = YOLO('yolov8s-worldv2.pt')
# Define custom classes
model.set_classes(["person", "bus"])
# Export the model
model.export(format='onnx')

模型导出后结构如下图所示:

 

与其他模型不同的时,YOLO-World模型在推理时需要指定目标对象名称,因此其输入包括一个目标对象名称的节点,但是目前ONNX模型不支持字符输入。因此在模型导出时,根据自己的模型需求,对需要进行识别的对象名称,进行定义;接着在导出模型时,会将定义的类别字符转换为权重,直接加载到模型中。

这样在模型推理时,就无需再进行文本权重转换,提升模型推理的速度;但这样也会导致导出的模型无法再修改类别,如果需要更改类别,就需要重新导出模型。

项目配置

源码下载与项目配置

代码下载完成后,使VS2019打开解决方案FIRC.sln文件,如下图所示:

接下来安装依赖项。首先是安装OpenVINO™ C# API项目依赖,通过NuGet安装一下包即可:

OpenVINO.CSharp.API
OpenVINO.runtime.win
OpenVINO.CSharp.API.Extensions
OpenVINO.CSharp.API.Extensions.OpenCvSharp

关于在不同平台上搭建 OpenVINO™ C# API 开发环境请参考以下文章: 《在Windows上搭建OpenVINO™C#开发环境》 、《在Linux上搭建OpenVINO™C#开发环境》《在MacOS上搭建OpenVINO™C#开发环境》

接下来安装使用到的图像处理库 OpenCvSharp,通过NuGet安装一下包即可:

OpenCvSharp4
OpenCvSharp4.Extensions
OpenCvSharp4.runtime.win

关于在其他平台上搭建 OpenCvSharp 开发环境请参考以下文章:《【OpenCV】在Linux上使用OpenCvSharp》 、《【OpenCV】在MacOS上使用OpenCvSharp》

添加完成项目依赖后,项目的配置文件如下所示:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net472" />
  <package id="OpenCvSharp4" version="4.9.0.20240103" targetFramework="net472" />
  <package id="OpenCvSharp4.Extensions" version="4.9.0.20240103" targetFramework="net472" />
  <package id="OpenCvSharp4.runtime.win" version="4.9.0.20240103" targetFramework="net472" />
  <package id="OpenVINO.CSharp.API" version="2024.1.0.1" targetFramework="net472" />
  <package id="OpenVINO.CSharp.API.Extensions" version="1.0.2" targetFramework="net472" />
  <package id="OpenVINO.CSharp.API.Extensions.OpenCvSharp" version="1.0.6.1" targetFramework="net472" />
  <package id="OpenVINO.runtime.win" version="2024.1.0.1" targetFramework="net472" />
  <package id="SharpCompress" version="0.37.2" targetFramework="net472" />
  <package id="System.Buffers" version="4.5.1" targetFramework="net472" />
  <package id="System.Drawing.Common" version="7.0.0" targetFramework="net472" />
  <package id="System.Memory" version="4.5.5" targetFramework="net472" />
  <package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
  <package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net472" />
  <package id="System.Text.Encoding.CodePages" version="8.0.0" targetFramework="net472" />
  <package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net472" />
  <package id="ZstdSharp.Port" version="0.8.0" targetFramework="net472" />
</packages>
定义模型预测方法

使用 OpenVINO™ C# API 部署模型主要包括以下几个步骤:

  • 初始化 OpenVINO Runtime Core
  • 读取本地模型(将图片数据预处理方式编译到模型)
  • 将模型编译到指定设备
  • 创建推理通道
  • 处理图像输入数据
  • 设置推理输入数据
  • 模型推理
  • 获取推理结果
  • 处理结果数据

下面根据模型部署流程,详细介绍一下该模型的部署代码:

该项目主要使用到OpenCvSharpOpenVINO™ C# API这两个工具包,因此需要添加以下命名空间:

using OpenCvSharp;
using OpenVinoSharp;
using OpenVinoSharp.Extensions.process;
  • 初始化 OpenVINO Runtime Core

    Core core = new Core();
    
  • 读取本地模型(将图片数据预处理方式编译到模型)

    Model model = core.read_model(tb_model_path.Text);
    
  • 将模型编译到指定设备

    CompiledModel compiled_model = core.compile_model(model, cb_device.SelectedItem.ToString());
    
  • 创建推理通道

    InferRequest request = compiled_model.create_infer_request();
    
  • 处理图像输入数据

    Mat image = Cv2.ImRead(tb_input_path.Text);
    Mat mat = new Mat();
    Cv2.CvtColor(image, mat, ColorConversionCodes.BGR2RGB);
    mat = OpenVinoSharp.Extensions.process.Resize.letterbox_img(mat, (int)input_shape[2], out factor);
    mat = Normalize.run(mat, true);
    float[] input_data = Permute.run(mat);
    
  • 设置推理输入数据

    Tensor input_tensor = request.get_input_tensor();
    input_tensor.set_data(input_data);
    
  • 模型推理

     request.infer();
    
  • 获取推理结果

    Tensor output_boxes = request.get_tensor("boxes");
    float[] boxes = output_boxes.get_data<float>((int)output_boxes.get_size());
    Tensor output_scores = request.get_tensor("scores");
    float[] scores = output_scores.get_data<float>((int)output_scores.get_size());
    Tensor output_labels = request.get_tensor("labels");
    int[] labels = output_labels.get_data<int>((int)output_labels.get_size());
    
  • 处理结果数据

    DetResult postprocess(float[] result, int categ_nums, float factor) 
    {
        Mat result_data = new Mat(4 + categ_nums, 8400, MatType.CV_32F,result);
        result_data = result_data.T();
    
        // Storage results list
        List<Rect> position_boxes = new List<Rect>();
        List<int> classIds = new List<int>();
        List<float> confidences = new List<float>();
        // Preprocessing output results
        for (int i = 0; i < result_data.Rows; i++)
        {
            Mat classesScores = new Mat(result_data, new Rect(4, i, categ_nums, 1));
            Point maxClassIdPoint, minClassIdPoint;
            double maxScore, minScore;
            // Obtain the maximum value and its position in a set of data
            Cv2.MinMaxLoc(classesScores, out minScore, out maxScore,
                out minClassIdPoint, out maxClassIdPoint);
            // Confidence level between 0 ~ 1
            // Obtain identification box information
            if (maxScore > 0.25)
            {
                float cx = result_data.At<float>(i, 0);
                float cy = result_data.At<float>(i, 1);
                float ow = result_data.At<float>(i, 2);
                float oh = result_data.At<float>(i, 3);
                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();
                box.X = x;
                box.Y = y;
                box.Width = width;
                box.Height = height;
    
                position_boxes.Add(box);
                classIds.Add(maxClassIdPoint.X);
                confidences.Add((float)maxScore);
            }
        }
        // NMS non maximum suppression
        int[] indexes = new int[position_boxes.Count];
        float score = float.Parse(tb_score.Text);
        float nms = float.Parse(tb_nms.Text);
        CvDnn.NMSBoxes(position_boxes, confidences, score, nms, out indexes);
        DetResult re = new DetResult();
        // 
        for (int i = 0; i < indexes.Length; i++)
        {
            int index = indexes[i];
            re.add(classIds[index], confidences[index], position_boxes[index]);
        }
        return re;
    }
    

以上就是使用 OpenVINO™ C# API 部署YOLO-World模型的关键代码,具体代码可以下载项目源码进行查看。

【效果展示】

配置好项目后,点击运行,本次我们提供给大家的是使用.NET Framework4.7.2开发的窗体应用程序,如下图所示,主要还包含五个部分,分别为推理设置区域、控制按钮、推理信息输出区域、原图展示区域、推理结果展示区域。在使用时,用户可以根据自己需求,选择导出的模型以及待推理数据,支持图片数据以及视频数据,接着输入自己导出的lables名称,同时还可以修改推理设备、数据处理所需的参数等,接着就可以依次点击加载模型、模型推理按钮,进行模型推理。最后模型推理结果如图所示。

该模型在导出时,默认导出80类别,因此在模型推理后,可以识别出常见80类别。

【部分代码展示】

using OpenCvSharp;
using OpenCvSharp.Dnn;
using OpenVinoSharp;
using OpenVinoSharp.Extensions.model;
using OpenVinoSharp.Extensions.process;
using OpenVinoSharp.Extensions.result;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace FIRC
{
    public partial class Form1 : Form
    {
        public Core core = null;
        public Model model = null;
        public CompiledModel compiled_model = null;
        public InferRequest request = null;
        DateTime start = DateTime.Now;
        DateTime end = DateTime.Now;
        public List<string> classes = null;
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            start = DateTime.Now;
            core = new Core();
            end = DateTime.Now;
            tb_msg.AppendText("Initialize OpenVINO Runtime Core: " + (end - start).TotalMilliseconds + "ms.\r\n");
            List<string> devices = core.get_available_devices();
            foreach (var item in devices)
            {
                cb_device.Items.Add(item);
            }
            cb_device.SelectedIndex = 0;
            tb_model_path.Text = Application.StartupPath + "\\weights\\yolov8s-worldv2.onnx";
             tb_input_path.Text= Application.StartupPath + "\\images\\bus.jpg";
        }

        private void btn_select_model_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            //若要改变对话框标题
            dlg.Title = "选择推理模型文件";
            //设置文件过滤效果
            dlg.Filter = "模型文件(*.pdmodel,*.onnx,*.xml)|*.pdmodel;*.onnx;*.xml";
            //判断文件对话框是否打开
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                tb_model_path.Text = dlg.FileName;
            }
        }

        private void btn_select_input_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            //若要改变对话框标题
            dlg.Title = "选择测试输入文件";
            //设置文件过滤效果
            dlg.Filter = "图片文件(*.png,*.jpg,*.jepg,*.mp4)|*.png;*.jpg;*.jepg;*.mp4";
            //判断文件对话框是否打开
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                tb_input_path.Text = dlg.FileName;
            }
        }


        private void btn_load_model_Click(object sender, EventArgs e)
        {
            string[] lines = tb_msg.Text.Split(new string[] { "\r\n" }, StringSplitOptions.None);
            tb_msg.Clear();
            tb_msg.Text = lines[0] + "\r\n";
            start = DateTime.Now;
            model = core.read_model(tb_model_path.Text);
            end = DateTime.Now;
            tb_msg.AppendText("Read inference model: " + (end - start).TotalMilliseconds + "ms.\r\n");
            start = DateTime.Now;
            compiled_model = core.compile_model(model, cb_device.SelectedItem.ToString());
            end = DateTime.Now;
            tb_msg.AppendText("Loading a model to the device: " + (end - start).TotalMilliseconds + "ms.\r\n");
            start = DateTime.Now;
            request = compiled_model.create_infer_request();
            end = DateTime.Now;
            tb_msg.AppendText("Create an infer request: " + (end - start).TotalMilliseconds + "ms.\r\n");
        }
        private void btn_infer_Click(object sender, EventArgs e)
        {
            string[] words = tb_classes.Text.Split(',');
            classes = new List<string>(words);
            if (Path.GetExtension(tb_input_path.Text) == ".mp4")
            {
                VideoCapture video = new VideoCapture(tb_input_path.Text);
                if (video.IsOpened()) 
                {
                    Mat frame = new Mat();
                    video.Read(frame);
                    while (!frame.Empty())
                    {
                        image_predict(frame);
                        video.Read(frame);
                        Thread.Sleep(10);
                    }
                }
            }
            else 
            { 
                Mat image = Cv2.ImRead(tb_input_path.Text); 
                image_predict(image); 
            }
           
     
           
            
        }
        void image_predict(Mat image) 
        {
            Tensor input_tensor = request.get_input_tensor();
            Shape input_shape = input_tensor.get_shape();
            float factor = 0f;
            pictureBox1.BackgroundImage = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(image);

            start = DateTime.Now;
            Mat mat = new Mat();
            Cv2.CvtColor(image, mat, ColorConversionCodes.BGR2RGB);
            mat = OpenVinoSharp.Extensions.process.Resize.letterbox_img(mat, (int)input_shape[2], out factor);
            mat = Normalize.run(mat, true);
            float[] input_data = Permute.run(mat);
            input_tensor.set_data(input_data);
            end = DateTime.Now;
            tb_msg.AppendText("Process input images: " + (end - start).TotalMilliseconds + "ms.\r\n");
            start = DateTime.Now;
            request.infer();
            end = DateTime.Now;
            tb_msg.AppendText("Do inference synchronously: " + (end - start).TotalMilliseconds + "ms.\r\n");
            float fps = (float)(1000.0f / ((end - start).TotalMilliseconds));
            start = DateTime.Now;

            Tensor output_tensor = request.get_output_tensor();

            Shape output_shape = output_tensor.get_shape();

            int categ_nums = (int)output_shape[1] - 4;
            DetResult result = postprocess(output_tensor.get_data<float>((int)output_tensor.get_size()), categ_nums, factor);

            Mat result_mat = image.Clone();
            for (int i = 0; i < result.count; i++)
            {
                Cv2.Rectangle(result_mat, result.datas[i].box, new Scalar(0.0, 0.0, 255.0), 2);
                Cv2.Rectangle(result_mat, new Point(result.datas[i].box.TopLeft.X, result.datas[i].box.TopLeft.Y + 30), new Point(result.datas[i].box.BottomRight.X, result.datas[i].box.TopLeft.Y), new Scalar(0.0, 255.0, 255.0), -1);
                Cv2.PutText(result_mat, classes[result.datas[i].index] + "-" + result.datas[i].score.ToString("0.00"), new Point(result.datas[i].box.X, result.datas[i].box.Y + 25), HersheyFonts.HersheySimplex, 0.8, new Scalar(0.0, 0.0, 0.0), 2);
            }
            end = DateTime.Now;
            tb_msg.AppendText("Process result data: " + (end - start).TotalMilliseconds + "ms.\r\n");
            start = DateTime.Now;
            Cv2.Rectangle(result_mat, new Point(30,20), new Point(250,60), new Scalar(0.0, 255.0, 255.0), -1);
            Cv2.PutText(result_mat, "FPS: " + fps.ToString("0.00"), new Point(50, 50), HersheyFonts.HersheySimplex, 0.8, new Scalar(0, 0, 0), 2);
            Cv2.WaitKey(1);
            pictureBox2.BackgroundImage = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(result_mat);
        }


        DetResult postprocess(float[] result, int categ_nums, float factor) 
        {
            Mat result_data = new Mat(4 + categ_nums, 8400, MatType.CV_32F,result);
            result_data = result_data.T();

            // Storage results list
            List<Rect> position_boxes = new List<Rect>();
            List<int> classIds = new List<int>();
            List<float> confidences = new List<float>();
            // Preprocessing output results
            for (int i = 0; i < result_data.Rows; i++)
            {
                Mat classesScores = new Mat(result_data, new Rect(4, i, categ_nums, 1));
                Point maxClassIdPoint, minClassIdPoint;
                double maxScore, minScore;
                // Obtain the maximum value and its position in a set of data
                Cv2.MinMaxLoc(classesScores, out minScore, out maxScore,
                    out minClassIdPoint, out maxClassIdPoint);
                // Confidence level between 0 ~ 1
                // Obtain identification box information
                if (maxScore > 0.25)
                {
                    float cx = result_data.At<float>(i, 0);
                    float cy = result_data.At<float>(i, 1);
                    float ow = result_data.At<float>(i, 2);
                    float oh = result_data.At<float>(i, 3);
                    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();
                    box.X = x;
                    box.Y = y;
                    box.Width = width;
                    box.Height = height;

                    position_boxes.Add(box);
                    classIds.Add(maxClassIdPoint.X);
                    confidences.Add((float)maxScore);
                }
            }
            // NMS non maximum suppression
            int[] indexes = new int[position_boxes.Count];
            float score = float.Parse(tb_score.Text);
            float nms = float.Parse(tb_nms.Text);
            CvDnn.NMSBoxes(position_boxes, confidences, score, nms, out indexes);
            DetResult re = new DetResult();
            // 
            for (int i = 0; i < indexes.Length; i++)
            {
                int index = indexes[i];
                re.add(classIds[index], confidences[index], position_boxes[index]);
            }
            return re;
        }

    }
}

【视频演示】

C#中使用OpenVINO部署YOLO-World模型实现实时开放词汇对象检测的onnx模型_哔哩哔哩_bilibili【测试环境】vs2019,net framework4.7.2,opencvsharp4.9.0,全部依赖库截图更详细部署过程参考博文:blog.csdn.net/FL1623863129/article/details/139309836, 视频播放量 1、弹幕量 0、点赞数 0、投硬币枚数 0、收藏人数 0、转发人数 0, 视频作者 未来自主研究中心, 作者简介 未来自主研究中心,相关视频:C++使用纯opencv部署yolov9的onnx模型,使用C#部署yolov8-cls的图像分类的tensorrt模型,使用C#部署yolov8的目标检测tensorrt模型,基于onnx模型加密与解密深度学习模型保护方法介绍,YOLOv10杀疯了!Github上刚刚发布!超热乎的实时端到端目标检测来了!CV方向的初学者请速速看过来!,用C#部署yolov8的tensorrt模型进行目标检测winform最快检测速度,YOLOv8检测界面-PyQt5实现,使用纯opencv部署yolov8目标检测模型onnx,使用C#部署yolov8-seg的实例分割的tensorrt模型,基于yolov5的单目测距视频演示icon-default.png?t=N7T8https://www.bilibili.com/video/BV1F1421q7cw/?vd_source=989ae2b903ea1b5acebbe2c4c4a635ee

【代码下载】https://download.csdn.net/download/FL1623863129/89375811

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FL1623863129

你的打赏是我写文章最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值