首页 > 编程语言 >【OpenVINO™】YOLOv10在CPU上也能实现50+FPS推理—使用OpenVINO C++部署YOLOv10

【OpenVINO™】YOLOv10在CPU上也能实现50+FPS推理—使用OpenVINO C++部署YOLOv10

时间:2024-07-05 10:09:51浏览次数:19  
标签:std OpenVINO frame 50 640 YOLOv10 推理 cv

​ 英特尔发行版 OpenVINO™ 工具套件基于 oneAPI 而开发,可以加快高性能计算机视觉和深度学习视觉应用开发速度工具套件,适用于从边缘到云的各种英特尔平台上,帮助用户更快地将更准确的真实世界结果部署到生产系统中。YOLOv10是清华大学研究人员近期提出的一种实时目标检测方法,通过消除NMS、优化模型架构和引入创新模块等策略,在保持高精度的同时显著降低了计算开销,为实时目标检测领域带来了新的突破。

  在本文中,我们将演示如何使用Intel OpenVINO™ C++ API 部署YOLOv10目标检测模型,并使用 OpenVINO™ 异步推理接口实现模型推理加速。下面看一下YOLOv10模型在OpenVINO™上的运行效果吧:

【B站】YOLOv10在CPU上也能轻松实现50+FPS推理—使用OpenVINO C++部署YOLOv10实现异步推理

1. 前言

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

03b06c0aec22e58235cd01fa6d9b6db4

  2024年4月25日,英特尔发布了开源 OpenVINO™ 2024.1 工具包,用于在各种硬件上优化和部署人工智能推理。更新了更多的 Gen AI 覆盖范围和框架集成,以最大限度地减少代码更改。同时提供了更广泛的 LLM 模型支持和更多的模型压缩技术。通过压缩嵌入的额外优化减少了 LLM 编译时间,改进了采用英特尔®高级矩阵扩展 (Intel® AMX) 的第 4 代和第 5 代英特尔®至强®处理器上 LLM 的第 1 令牌性能。通过对英特尔®锐炫™ GPU 的 oneDNN、INT4 和 INT8 支持,实现更好的 LLM 压缩和改进的性能。最后实现了更高的可移植性和性能,可在边缘、云端或本地运行 AI。

  YOLOv10是清华大学研究人员近期提出的一种实时目标检测方法,该方法在Ultralytics Python包的基础上进行了多项创新和改进,主要有以下特点

  1. 消除非极大值抑制(NMS):YOLOv10通过引入一致的双重分配策略,在训练时使用一对多的标签分配来提供丰富的监督信号,在推理时使用一对一的匹配,从而消除了对NMS的依赖。这一改进在保持高精度的同时,减少了推理延迟和计算量。
  2. 全面优化的模型架构:YOLOv10从推理效率和准确性的角度出发,全面优化了模型的各个组成部分。这包括采用轻量级分类头、空间通道去耦下采样和等级引导块设计等,以减少计算冗余并提高模型性能。
  3. 引入大核卷积和部分自注意模块:为了提高性能,YOLOv10在不增加大量计算成本的前提下,引入了大核卷积和部分自注意模块。
  4. 多种模型尺寸可选:官方发布了从N到X各种型号的模型,以满足不同应用的需求。这些模型包括超小型版本YOLOv10-N(用于资源极其有限环境)、小型版本YOLOv10-S(兼顾速度和精度)、中型版本YOLOv10-M(通用)、平衡型版本YOLOv10-B(宽度增加,精度更高)、大型版本YOLOv10-L(精度更高,但计算资源增加)以及超大型版本YOLOv10-X(可实现最高的精度和性能)。

  通过广泛的实验验证,YOLOv10在多个模型尺度上实现了卓越的精度-延迟权衡。例如,在COCO数据集上,YOLOv10-S在相似精度下比其他实时目标检测方法更快,同时参数和浮点运算量也大幅减少。综上所述,YOLOv10通过消除NMS、优化模型架构和引入创新模块等策略,在保持高精度的同时显著降低了计算开销,为实时目标检测领域带来了新的突破。

image-20240604135821296

2. 项目开发环境

  下面简单介绍一下项目的开发环境,开发者可以根据自己的设备情况进行配置:

  • 系统平台:Windows 11
  • Intel Core i7-1165G7
  • 开发平台:Visual Studio 2022
  • OpenVINO™:2024.1.0
  • OpenCV:4.8.0

  此处代码开发平台使用的是C++,因此在项目配置时,需要配置第三方依赖库,分别是 OpenVINO™和OpenCV两个个依赖库,其配置方式此处不做详述。

3. 模型获取与INT8量化

  为了提升模型的推理速度,我们此处使用 OpenVINO™ 进行推理加速,并使用OpenVINO™NNCF 工具对模型进行一个INT8量化。量化的详细流程可以参考下面notebooks,该notebooks记录了YOLOv10使用OpenVINO™量化的详细流程,链接如下所示:

openvino_notebooks/notebooks/yolov10-optimization

  模型量化完成后,我们对比了一下量化前后模型变化,如下图所示:

图片1

4. 定义YOLOv10 Process

4.1 数据预处理

  数据预处理此处通过OpenCV实现,将输入的图片数据转为模型需要的数据情况,代码如下所示:

void pre_process(cv::Mat* img, int length, float* factor, std::vector<float>& data) {
    cv::Mat mat;
    int rh = img->rows;
    int rw = img->cols;
    int rc = img->channels();
    cv::cvtColor(*img, mat, cv::COLOR_BGR2RGB);
    int max_image_length = rw > rh ? rw : rh;
    cv::Mat max_image = cv::Mat::zeros(max_image_length, max_image_length, CV_8UC3);
    max_image = max_image * 255;
    cv::Rect roi(0, 0, rw, rh);
    mat.copyTo(cv::Mat(max_image, roi));
    cv::Mat resize_img;
    cv::resize(max_image, resize_img, cv::Size(length, length), 0.0f, 0.0f, cv::INTER_LINEAR);

    *factor = (float)((float)max_image_length / (float)length);
    resize_img.convertTo(resize_img, CV_32FC3, 1 / 255.0);
    rh = resize_img.rows;
    rw = resize_img.cols;
    rc = resize_img.channels();
    for (int i = 0; i < rc; ++i) {
        cv::extractChannel(resize_img, cv::Mat(rh, rw, CV_32FC1, data.data() + i * rh * rw), i);
    }
}

  在调用时也相对简单,将相关变量传入即可,代码如下所示:

Mat frame = new frame();
std::vector<float> input_data(640 * 640 * 3);
float factor = 0;
pre_process(&frame, 640, &factor, input_data);

4.2 结果后处理

  首先此处定义了一个结果类:

struct DetResult {
    cv::Rect bbox;
    float conf;
    int lable;
    DetResult(cv::Rect bbox,float conf,int lable):bbox(bbox),conf(conf),lable(lable){}
};

  然后定义模型的结果处理方式,代码如下所示:

std::vector<DetResult> post_process(float* result, float factor, int outputLength) {
    std::vector<cv::Rect> position_boxes;
    std::vector <int> class_ids;
    std::vector <float> confidences;
    // Preprocessing output results
    for (int i = 0; i < outputLength; i++)
    {
        int s = 6 * i;
        if ((float)result[s + 4] > 0.2)
        {
            float cx = result[s + 0];
            float cy = result[s + 1];
            float dx = result[s + 2];
            float dy = result[s + 3];
            int x = (int)((cx)*factor);
            int y = (int)((cy)*factor);
            int width = (int)((dx - cx) * factor);
            int height = (int)((dy - cy) * factor);
            cv::Rect box(x, y, width, height);

            position_boxes.push_back(box);
            class_ids.push_back((int)result[s + 5]);
            confidences.push_back((float)result[s + 4]);
        }
    }
    std::vector<DetResult> re;
    for (int i = 0; i < position_boxes.size(); i++)
    {
        DetResult det(position_boxes[i], confidences[i], class_ids[i]);
        re.push_back(det);
    }
    return re;

}

  最后为了让结果可视化,定义了结果绘制方法,代码如下所示:

void draw_bbox(cv::Mat& img, std::vector<DetResult>& res) {
    for (size_t j = 0; j < res.size(); j++) {
        cv::rectangle(img, res[j].bbox, cv::Scalar(255, 0, 255), 2);
        cv::putText(img, std::to_string(res[j].lable) + "-" + std::to_string(res[j].conf),
            cv::Point(res[j].bbox.x, res[j].bbox.y - 1), cv::FONT_HERSHEY_PLAIN,
            1.2, cv::Scalar(0, 0, 255), 2);
    }
}

  上述方式调用依旧十分容易,使用代码如下所示:

std::vector<float> output_data(300 * 6);
std::vector<DetResult> result = post_process(output_data.data(), factor, 300);
draw_bbox(frame, result);

5. 模型推理实现

5.1 基本推理实现

  首先实现一下常规的同步推理代码,如下面所示:

void yolov10_infer_without_process() {
    std::string videoPath = "E:\\Text_dataset\\car_test.mov";
    std::string model_path = "E:\\Text_Model\\yolov10s_openvino_model\\yolov10s.xml";
    ov::Core core;
    auto model = core.read_model(model_path);
    auto compiled_model = core.compile_model(model, "CPU");
ov::InferRequest request =compiled_model.create_infer_request();
    cv::VideoCapture capture(videoPath);
    if (!capture.isOpened()) {
        std::cerr << "ERROR: 视频无法打开" << std::endl;
        return;
    }
    float factor = 0;
    request.get_input_tensor().set_shape(std::vector<size_t>{1, 3, 640, 640});
    std::vector<float> inputData(640 * 640 * 3);
    std::chrono::time_point<std::chrono::steady_clock> t_beg;
    std::chrono::time_point<std::chrono::steady_clock> t_end;
    while (true)
    {
        cv::Mat frame;
        if (!capture.read(frame)) {
            break;
        }
        t_beg = std::chrono::high_resolution_clock::now();
        pre_process(&frame, 640, &factor, inputData);
        memcpy(request.get_input_tensor().data<float>(), inputData.data(), 640 * 640 * 3);
        request.infer();
        float* output_data = request.get_output_tensor().data<float>();
        std::vector<DetResult> result = post_process(output_data, factor, 300);
        t_end = std::chrono::high_resolution_clock::now();

        cv::putText(frame, "FPS: " + std::to_string(1000.0 / std::chrono::duration<float, std::milli>(t_end - t_beg).count()) 
            + ", Time: " + std::to_string(std::chrono::duration<float, std::milli>(t_end - t_beg).count()) + "ms",
            cv::Point(20, 40), 1, 2, cv::Scalar(255, 0, 255), 2);
        draw_bbox(frame, result);
        imshow("读取视频", frame);
        cv::waitKey(1);	//延时30
    }
    cv::destroyAllWindows();
    return;
}

5.2 使用异步推理实现

  视频一般1s中有25帧左右,这就意味着我们需要1s推理25张图片才可以实现视频推理。一般情况下,在CPU设备推理视觉模型很难实现实时推理,我们在使用 OpenVINO™推理时,经过一些优化技术后,勉强可以实现25FPS的推理,但是如果需要处理其他业务将会很难实现。因此为了提升推理速度,我们采用异步推理技术,实现代码如下所示:

void yolov10_infer_ansy_without_process() {
    std::string videoPath = "E:\\Text_dataset\\car_test.mov";
    std::string model_path = "E:\\Text_Model\\yolov10s_openvino_model\\yolov10s.xml";
    ov::Core core;
    auto model = core.read_model(model_path);
    auto compiled_model = core.compile_model(model, "CPU");
    std::vector<ov::InferRequest>requests = { compiled_model.create_infer_request(), compiled_model.create_infer_request() };
    cv::VideoCapture capture(videoPath);
    // 检查摄像头是否成功打开
    if (!capture.isOpened()) {
        std::cerr << "ERROR: 视频无法打开" << std::endl;
        return;
    }
    float factor = 0;
    requests[0].get_input_tensor().set_shape(std::vector<size_t>{1, 3, 640, 640});
    requests[1].get_input_tensor().set_shape(std::vector<size_t>{1, 3, 640, 640});
    cv::Mat frame;
    capture.read(frame);
    std::vector<float> inputData(640 * 640 * 3);
    pre_process(&frame, 640, &factor, inputData);
    memcpy(requests[0].get_input_tensor().data<float>(), inputData.data(), 640 * 640 * 3);
    requests[0].start_async();
    std::chrono::time_point<std::chrono::steady_clock> t_beg;
    std::chrono::time_point<std::chrono::steady_clock> t_end;
    while (true)
    {
        cv::Mat next_frame;
        if (!capture.read(next_frame)) {
            break;
        }
        t_beg = std::chrono::high_resolution_clock::now();
        pre_process(&next_frame, 640, &factor, inputData);
        memcpy(requests[1].get_input_tensor().data<float>(), inputData.data(), 640 * 640 * 3);
        requests[1].start_async();
        requests[0].wait();
        float* output_data = requests[0].get_output_tensor().data<float>();
        std::vector<DetResult> result = post_process(output_data, factor, 300);
        t_end = std::chrono::high_resolution_clock::now();
        draw_bbox(frame, result);
        cv::putText(frame, "FPS: " + std::to_string(1000.0 / std::chrono::duration<float, std::milli>(t_end - t_beg).count())
            + ", Time: " + std::to_string(std::chrono::duration<float, std::milli>(t_end - t_beg).count()) + "ms",
            cv::Point(20, 40), 1, 2, cv::Scalar(255, 0, 255), 2);
        imshow("读取视频", frame);
        cv::waitKey(1);	//延时30
        frame = next_frame;
        std::swap(requests[0], requests[1]);
    }
    cv::destroyAllWindows();
    return;
}

  文章中已经提供了全部代码文件以及模型的获取方式,如果您还有任何疑问,可以下载现成的文件资源。为了方便开发者使用,我们将代码、测试视频与模型文件以及项目所需依赖打包,发布到了CSDN上,大家可以根据自己需求进行下载,下载链接:

https://download.csdn.net/download/Grape_yan/89473342

6. 时间测试

最后我们对推理时间进行了测试,分别测试了量化前后模型在同步推理以及异步推理的表现,如下表所示:

Model API PrePocess Inference PostProcess Total FPS
Float 32 Sync 9.72 ms 65.44 ms 0 ms 75.16 ms 13.30
Float 32 Async 15.29 ms 40.52 ms 0 ms 55.81 ms 17.92
INT 8 Sync 15.25 ms 25.64 ms 0 ms 43.89 ms 22.78
INT 8 Async 17.84 ms 1.86 ms 0 ms 19.70 ms 50.76

  通过该表可以看出,量化前后,模型推理速度提升了1.5倍,并且通过使用异步推理接口,经过IN8量化后的模型,在本设备上可以实现50FPS的推理速度,与为量化的模型相比,速度推升了2.8倍。通过异步接口,我们可以在CPU下轻松的实现视频推理。

7. 总结

   在本文中,我们演示了如何使用Intel OpenVINO™ C++ API 部署YOLOv10目标检测模型,并使用 OpenVINO™ 异步推理接口实现模型推理加速。 最后如果各位开发者在使用中有任何问题,欢迎大家与我联系。

个人账号 - 2

标签:std,OpenVINO,frame,50,640,YOLOv10,推理,cv
From: https://www.cnblogs.com/guojin-blogs/p/18284497

相关文章

  • 【YOLOv10改进 - 注意力机制】 MHSA:多头自注意力(Multi-Head Self-Attention)
    YOLOv10目标检测创新改进与实战案例专栏专栏链接:YOLOv10创新改进有效涨点介绍摘要我们介绍了BoTNet,这是一个概念简单但功能强大的骨干架构,将自注意力引入多个计算机视觉任务,包括图像分类、物体检测和实例分割。通过仅在ResNet的最后三个瓶颈块中用全局自注意力替换......
  • 玩玩快速冥(LeetCode50题与70题以及联系斐波那契)
    一.算法快速幂今天刷到两个题,比较有意思,还是记录一下.先来讲讲50题.LeetCode50(Pow(x,n))实现pow(x,n),即计算x的整数n次幂函数(即,xn)。这道题一看很平常啊,不就一直乘嘛,循环走一次就够了.但是很抱歉,单纯的想法终究迎来了超时.而且还是个中等的题目,意识到没......
  • Python基于PyQt5和卷积神经网络分类模型(ResNet50分类算法)实现生活垃圾分类系统GUI界
    说明:这是一个机器学习实战项目(附带数据+代码+文档+视频讲解),如需数据+代码+文档+视频讲解可以直接到文章最后获取。1.项目背景在当今社会,随着人们对环境保护意识的增强以及科技的快速发展,智能化的垃圾分类系统成为了一个热门的研究方向。结合深度学习技术,尤其是先进的图像识......
  • 【适用于各种工业应用】IMLT65R050M2H IMLT65R040M2H IMLT65R015M2H IMLT65R060M2H Co
    摘要CoolSiC™650VG2MOSFET可通过降低能耗来充分利用碳化硅的性能,从而在功率转换过程中实现更高效率。这些CoolSiC650VG2MOSFET适用于各种功率半导体应用,如光伏、能量存储、电动汽车直流充电、电机驱动器和工业电源。配备CoolSiCG2的电动汽车用直流快速充电站与前几代产品......
  • 小白个人向[攻防世界]wtf.sh-150( 需要Shell脚本知识 )
    wtf.sh-150(需要Shell脚本知识)注册---->登录---->是一个论坛----->我们也发文章(如上图)发现http://61.147.171.105:56056/post.wtf?post=HYBRM#,post参数好像可以修改,尝试路径穿越学习链接:https://zhuanlan.zhihu.com/p/593376086拿到网页源码网页搜索flag看到cookie和use......
  • [Leetcode]Top 150
    链表旋转链表给你一个链表的头节点head,旋转链表,将链表每个节点向右移动k个位置。#Definitionforsingly-linkedlist.#classListNode:#def__init__(self,val=0,next=None):#self.val=val#self.next=nextclassSolution:defro......
  • <sa8650>sa8650 qcxserver-之-摄像头传感器VB56G4A驱动开发<1>
    <sa8650>sa8650qcxserver-之-摄像头传感器VB56G4A驱动开发<1>一、前言二、QCX架构三、QCX传感器驱动程序定制开发3.1sensor硬件接口3.2sensor配置文件3.2.1cameraconfig.c3.2.2cameraconfigsa8650_water.c3.2.3新增编译MK3.2.4参数解析3.2.4.1st......
  • SRC实战:分享一个不到500块的高危,史低价(在steam都可以上头条了)。
    最近工作忙得飞起,挖洞的时间变少了,一般有什么活动之类的才会花个半天时间去尝试一下。今天这个漏洞有点意思,不到500块的高危,但是我个人认为是严重低估了的,但没办法,白帽子在审核面前没人权啊。以下主要是过程与思路分析,没截图,因为几百块的高危不配有图。0x00入口这次的......
  • P3350 [ZJOI2016] 旅行者
    咕了2天才写的题解还是比较经典的题目,分治处理网格图最短路离线下来,利用分治的思想,用一条线把网格图平均劈成两半,每次只考虑询问在两块的一对点,所有的线必须经过直线上的一个点,于是我把线上所有点都在规定范围内跑一次dijkstra,最后直接算答案,显然我想让最短路跑的次数最小,每次选......
  • [IOT2050 question] Unable to listen on http://127.0.0.1:1880/ 端口被占用错误
    1.背景第一次连接node-red的时候,一直出现错误Unabletolistenonhttp://127.0.0.1:1880/。如下:2.原因分析估计是早前利用iot2050setup小工具把node-red设置为开机自动启动项了,导致1880端口一直被占用。3.验证首先查看端口是否真的被占用,利用sudonetstat-ltup命......