【OpenVINO™】使用 OpenVINO™ C++ 异步推理接口部署YOLOv8 ——在Intel IGPU 上实现80+FPS视频推理

本文主要是介绍【OpenVINO™】使用 OpenVINO™ C++ 异步推理接口部署YOLOv8 ——在Intel IGPU 上实现80+FPS视频推理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

​ OpenVINO Runtime支持同步或异步模式下的推理。Async API的主要优点是,当设备忙于推理时,应用程序可以并行执行其他任务(例如,填充输入或调度其他请求),而不是等待当前推理首先完成。 当我们使用异步API时,第二个请求的传输与第一个推理的执行重叠,这防止了任何硬件空闲时间。

  本文章中,我们以YOLOv8模型为例,对比了OpenVINO分别使用同步推理接口以及异步推理接口的推理速度情况。其中同步推理一帧平均推理时间为43.02毫秒,而异步接口一帧平均推理时间仅为11.37毫秒,异步接口一秒钟平均可以实现87.98FPS的推理,是同步推理的3.78倍,速度快到飞起!!

  下面是异步推理与同步推理对比效果演示视频:

YOLOv8在集成显卡可实现80+FPS推理——使用 OpenVINO™ C++ 异步推理接口部署YOLOv8 实现视频推理

1. OpenVINO™ 工具套件

  英特尔发行版 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。

2. OpenVINO™ 异步接口

  OpenVINO Runtime支持同步或异步模式下的推理。Async API的主要优点是,当设备忙于推理时,应用程序可以并行执行其他任务(例如,填充输入或调度其他请求),而不是等待当前推理首先完成。 在同步模式下,我们等待第一次推理的结果,然后再发送下一个请求。在发送请求时,硬件处于空闲状态。当我们使用异步API时,第二个请求的传输与第一个推理的执行重叠,这防止了任何硬件空闲时间。如下图所示,该图展示了同步推理与异步推理流程情况以及时间安排:

async分析

  可以看出,通过异步接口,我们可以实现模型推理和数据前处理以及后处理并行执行,这将会大大压缩模型推理全流程的时间。下面简单介绍一下在C++中,同步推理以及异步推理接口:

2.1 创建推断请求

  可以从以下位置创建:ov::InferRequestov::CompiledModel

auto infer_request = compiled_model.create_infer_request();

ov::InferRequest可以运行推理,支持同步和异步模式进行推理。

2.2 同步模式

ov::InferRequest::infer可以使用来阻止应用程序执行,以同步模式下进行模型推理。

infer_request.infer();

2.3 异步模式

  异步模式可以提高应用程序的整体帧速率,方法是使其在加速器繁忙时在主机上工作,而不是等待推理完成。要在异步模式下推断模型,需要使用ov::InferRequest::start_async接口。

infer_request.start_async();

  异步模式支持应用程序等待推理结果的两种方式:

ov::InferRequest::wait_for:指定阻止方法的最大持续时间(以毫秒为单位)。该方法将被阻止,直到指定的时间过去,或者结果变得可用,以先到者为准。

infer_request.wait_for(std::chrono::milliseconds(10));

  ov::InferRequest::wait:等到推理结果可用

infer_request.wait();

  其中这两种等待推理结果的方法都是线程安全的。

3. 项目环境

  • 推理设备:OpenVINO IGPU
  • CPU: Intel Core i7-1165G7
  • IGPU: Intel Iris Xe Graphics
  • 推理模型: YOLOv8s
  • 视频分辨率:1920×1080

4. 代码实现

4.1 定义YOLOv8数据处理方法

  首先定义了一下YOLOv8模型前后处理的方法,包括输入数据处理接口pre_process(cv::Mat* img, int length, float* factor, std::vector<float>& data)以及预测结果处理接口std::vector<DetResult> post_process(float* result, float factor, int outputLength) ,具体不做过多讲解,代码如下所示:

struct DetResult {cv::Rect bbox;float conf;int lable;DetResult() {}DetResult(cv::Rect bbox, float conf, int lable) :bbox(bbox), conf(conf), lable(lable) {}
};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);}
}
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);}
}
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 resultsfor (int i = 0; i < outputLength; i++){for (int j = 4; j < 4 + 80; j++){float source = result[outputLength * j + i];int label = j - 4;if (source > 0.2){float maxSource = source;float cx = result[outputLength * 0 + i];float cy = result[outputLength * 1 + i];float ow = result[outputLength * 2 + i];float oh = result[outputLength * 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);cv::Rect box(x, y, width, height);position_boxes.push_back(box);class_ids.push_back(label);confidences.push_back(maxSource);}}}std::vector<int> indices;cv::dnn::NMSBoxes(position_boxes, confidences, 0.2f, 0.5f, indices);std::vector<DetResult> re;for (int i = 0; i < indices.size(); i++){int index = indices[i];DetResult det(position_boxes[index], confidences[index], class_ids[index]);re.push_back(det);}return re;
}

4.2 异步推理实现

  异步推理在实现时,需要创建两个以上的推理通道ov::InferRequest,首先读取第一帧数据并将其添加在第一个推理通道上,并开启异步推理;然后读取下一帧视频数据,并加载到另一个推理通道上;接着等待上一帧数据推理是否结束,如果结束,便会读取推理结果,进行结果处理;接着读取下一帧数据,并将推理结果加载到图里玩的通道上,依次往复,便可以实现视频数据的异步推理。

void yolov8_async_infer() {std::string video_path = "E:\\ModelData\\NY.mp4";std::string model_path = "E:\\Model\\yolo\\yolov8s.onnx";ov::Core core;auto model = core.read_model(model_path);auto compiled_model = core.compile_model(model, "GPU");std::vector<ov::InferRequest>requests = { compiled_model.create_infer_request(), compiled_model.create_infer_request() };cv::VideoCapture capture(video_path);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();while (true){cv::Mat next_frame;if (!capture.read(next_frame)) {break;}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, 8400);draw_bbox(frame, result);imshow("读取视频", frame);cv::waitKey(1);	//延时30frame = next_frame;std::swap(requests[0], requests[1]);}cv::destroyAllWindows();return;
}

  上面已经展示了该项目实现的全部代码,如果想获取项目文件,通过下面链接进行下载:OpenVINOC++异步推理接口部署YOLOv8代码资源

5. 时间测试

写完代码后,对同步接口以及异步推理接口进行了时间测试,如下表所示:

APIPrePocessInferencePostProcessTotalFPS
Sync9.83 ms33.18 ms0.1 ms43.02 ms23.25
Async11.27 ms0.02 ms0.08 ms11.37 ms87.98

  其中同步推理一帧平均推理时间为43.02毫秒,而异步接口一帧平均推理时间仅为11.37毫秒,异步接口一秒钟平均可以实现87.98FPS的推理,是同步推理的3.78倍,速度快到飞起!!

6. 总结

  在该项目中,我们实现了OpenVINO异步模式下的推理,并和同步推理进行了对比,异步推理速度提升了3.78倍,并且在没有进行任何优化掉前提下,使用集成显卡中便实现了视频的快速推理。

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

个人账号 - 2

这篇关于【OpenVINO™】使用 OpenVINO™ C++ 异步推理接口部署YOLOv8 ——在Intel IGPU 上实现80+FPS视频推理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1059602

相关文章

如何使用celery进行异步处理和定时任务(django)

《如何使用celery进行异步处理和定时任务(django)》文章介绍了Celery的基本概念、安装方法、如何使用Celery进行异步任务处理以及如何设置定时任务,通过Celery,可以在Web应用中... 目录一、celery的作用二、安装celery三、使用celery 异步执行任务四、使用celery

使用Python绘制蛇年春节祝福艺术图

《使用Python绘制蛇年春节祝福艺术图》:本文主要介绍如何使用Python的Matplotlib库绘制一幅富有创意的“蛇年有福”艺术图,这幅图结合了数字,蛇形,花朵等装饰,需要的可以参考下... 目录1. 绘图的基本概念2. 准备工作3. 实现代码解析3.1 设置绘图画布3.2 绘制数字“2025”3.3

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Jsoncpp的安装与使用方式

《Jsoncpp的安装与使用方式》JsonCpp是一个用于解析和生成JSON数据的C++库,它支持解析JSON文件或字符串到C++对象,以及将C++对象序列化回JSON格式,安装JsonCpp可以通过... 目录安装jsoncppJsoncpp的使用Value类构造函数检测保存的数据类型提取数据对json数

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

springboot整合 xxl-job及使用步骤

《springboot整合xxl-job及使用步骤》XXL-JOB是一个分布式任务调度平台,用于解决分布式系统中的任务调度和管理问题,文章详细介绍了XXL-JOB的架构,包括调度中心、执行器和Web... 目录一、xxl-job是什么二、使用步骤1. 下载并运行管理端代码2. 访问管理页面,确认是否启动成功

使用Nginx来共享文件的详细教程

《使用Nginx来共享文件的详细教程》有时我们想共享电脑上的某些文件,一个比较方便的做法是,开一个HTTP服务,指向文件所在的目录,这次我们用nginx来实现这个需求,本文将通过代码示例一步步教你使用... 在本教程中,我们将向您展示如何使用开源 Web 服务器 Nginx 设置文件共享服务器步骤 0 —