当前位置: 首页 > news >正文

openVX加速-结合AI推理引擎代码示例

假设我们使用 OpenVX 进行图像的 Resize 和饱和度矫正,然后将处理后的图像直接传递给 GPU 上的 AI 推理引擎。

以下是两段 C++ 示例代码,展示如何在 GPU 上实现这些操作,我给了前后两个代码示例,分别是在CPU和GPU进行IO的和全部都在GPU操作的。注意代码是伪代码,大家最好不要直接照搬照抄,目的是为了理解内在逻辑。

这个代码假设已经有一个 OpenVX 环境和一个 AI 推理引擎(如 OpenCV DNN 模块,或其他 GPU 加速的推理引擎):

1. 安装依赖

确保系统安装了 OpenVX 库和 OpenCV 库。

2. C++ 示例代码1-存在数据拷贝

#include <VX/vx.h>
#include <VX/vxu.h>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>// 自定义饱和度校正核
vx_node vxColorSaturationNode(vx_graph graph, vx_image src, vx_image dst, float saturationScale) {vx_context context = vxGetContext((vx_reference)graph);vx_float32 scale = saturationScale;vx_scalar s_scale = vxCreateScalar(context, VX_TYPE_FLOAT32, &scale);vx_node node = vxCreateGenericNode(graph, vxGetKernelByEnum(context, VX_KERNEL_COLOR_CONVERT));vxSetParameterByIndex(node, 0, (vx_reference)src);vxSetParameterByIndex(node, 1, (vx_reference)dst);vxSetParameterByIndex(node, 2, (vx_reference)s_scale);return node;
}int main() {// 初始化 OpenVX contextvx_context context = vxCreateContext();// 创建 OpenCV Mat 并加载图像cv::Mat inputImage = cv::imread("input.jpg");// 定义图像尺寸和参数vx_uint32 width = inputImage.cols;vx_uint32 height = inputImage.rows;vx_uint32 newWidth = 224;  // 目标尺寸vx_uint32 newHeight = 224;// 创建 OpenVX 图像vx_image vxInputImage = vxCreateImage(context, width, height, VX_DF_IMAGE_RGB);vx_image vxResizedImage = vxCreateImage(context, newWidth, newHeight, VX_DF_IMAGE_RGB);vx_image vxOutputImage = vxCreateImage(context, newWidth, newHeight, VX_DF_IMAGE_RGB);// 将 OpenCV Mat 数据复制到 OpenVX 图像vx_rectangle_t rect = {0, 0, width, height};vx_imagepatch_addressing_t addr = {width * 3, 3, width, height};vx_uint8 *base = inputImage.data;vxCopyImagePatch(vxInputImage, &rect, 0, &addr, base, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST);// 创建 OpenVX 图形vx_graph graph = vxCreateGraph(context);// 添加图像 resize 节点vx_node resizeNode = vxResizeImageNode(graph, vxInputImage, vxResizedImage, VX_INTERPOLATION_BILINEAR);// 添加饱和度矫正节点vx_node saturationNode = vxColorSaturationNode(graph, vxResizedImage, vxOutputImage, 1.5f);// 验证图形vx_status status = vxVerifyGraph(graph);if (status == VX_SUCCESS) {// 处理图形vxProcessGraph(graph);// 从 OpenVX 图像复制到 OpenCV Matvx_rectangle_t rectOutput = {0, 0, newWidth, newHeight};vx_imagepatch_addressing_t addrOutput = {newWidth * 3, 3, newWidth, newHeight};vx_uint8 *outputBase = new vx_uint8[newWidth * newHeight * 3];vxCopyImagePatch(vxOutputImage, &rectOutput, 0, &addrOutput, outputBase, VX_READ_ONLY, VX_MEMORY_TYPE_HOST);cv::Mat outputImage(newHeight, newWidth, CV_8UC3, outputBase);// 使用 OpenCV DNN 或其他推理引擎进行 AI 推理cv::dnn::Net net = cv::dnn::readNet("model.onnx");cv::Mat blob = cv::dnn::blobFromImage(outputImage, 1.0, cv::Size(224, 224), cv::Scalar(104, 117, 123));net.setInput(blob);cv::Mat output = net.forward();// 处理推理结果// ... (处理 AI 推理结果的代码)delete[] outputBase;} else {std::cerr << "Graph verification failed!" << std::endl;}// 释放资源vxReleaseImage(&vxInputImage);vxReleaseImage(&vxResizedImage);vxReleaseImage(&vxOutputImage);vxReleaseGraph(&graph);vxReleaseContext(&context);return 0;
}

3. 上面2的代码说明

  1. 初始化 OpenVX:首先创建一个 OpenVX context,然后加载输入图像并创建对应的 OpenVX 图像对象。

  2. 图像预处理

    • Resize:使用 vxResizeImageNode 节点将图像缩放到指定尺寸。
    • 饱和度矫正:自定义一个节点 vxColorSaturationNode,调整图像的饱和度。
  3. AI 推理

    • 使用 OpenCV 的 DNN 模块读取预训练模型并进行推理。
  4. 数据处理关键分析

这段代码片段涉及到从 OpenVX 图像 (vx_image) 转换为 OpenCV 的 cv::Mat,这个过程是将图像数据从 OpenVX 的 GPU 内存空间复制到 CPU 的内存空间。

vx_uint8 *outputBase = new vx_uint8[newWidth * newHeight * 3];
vxCopyImagePatch(vxOutputImage, &rectOutput, 0, &addrOutput, outputBase, VX_READ_ONLY, VX_MEMORY_TYPE_HOST);
cv::Mat outputImage(newHeight, newWidth, CV_8UC3, outputBase);

vx_uint8:

  • vx_uint8 *outputBase = new vx_uint8[newWidth * newHeight * 3]; 这句话创建的 outputBase 指针指向的是在 CPU 内存中分配的空间。

  • new 关键字

    • 在 C++ 中,new 关键字用于动态分配内存,默认情况下,分配的内存是在 CPU 的主内存中,也就是主机内存(Host Memory)。
  • outputBase 指向的内存

    • outputBase 指针指向的是通过 new 在 CPU 内存中分配的字节数组。这个数组的大小是 newWidth * newHeight * 3 字节,表示分配了一个足够存储图像数据的空间。

vxCopyImagePatch:

  • 这个函数的作用是将 OpenVX 图像(可能在 GPU 内存中)的数据复制到 outputBase 指向的 CPU 内存中。VX_MEMORY_TYPE_HOST 指定了内存类型为主机(即 CPU)内存。
  • 因此,这个操作实际上将图像数据从 GPU 内存(如果 vxOutputImage 存储在 GPU 上)复制到 CPU 内存。这是一个 I/O 操作,涉及 CPU 和 GPU 之间的数据传输。

cv::Mat:

  • outputBase 是一个指向 CPU 内存的指针,cv::Mat 的构造函数使用这个指针创建一个 OpenCV 的矩阵(图像)。outputImage 是一个指向 CPU 内存中图像数据的 OpenCV 对象。

在这个过程中,图像数据从 GPU 内存迁移到 CPU 内存。这是因为 OpenVX 的 vxCopyImagePatch 函数在复制时明确指定了目标内存类型为 VX_MEMORY_TYPE_HOST,这意味着要将数据从 GPU 拷贝到 CPU。所以,这个过程不完全在 GPU 内部,而是涉及到数据从 GPU 到 CPU 的迁移。

4. 如何避免数据迁移:

如果想避免这种数据迁移,可以尝试在 GPU 内部完成所有处理。具体做法包括:

  • 直接在 GPU 上完成所有前处理和后处理。
  • 在推理引擎也支持 GPU 的情况下,确保推理引擎能够直接使用 GPU 内存中的数据,而不需要将数据复制到 CPU 内存。

优化方案

如果推理引擎也在 GPU 上运行,并且可以接受 OpenVX 输出的 GPU 内存中的数据,可以避免调用 vxCopyImagePatch,直接传递 vxOutputImage 到推理引擎,这样数据就不会从 GPU 迁移到 CPU,避免了 I/O 开销。

这能极大提升处理效率,尤其在实时视频处理场景中。

GPU 内存与 CPU 内存

如果我们希望在 GPU 上分配内存,通常需要使用特定的 API 来分配 GPU 内存,比如 CUDA 中的 cudaMalloc,或 OpenCL 中的 clCreateBuffer。而 new 分配的内存默认是在 CPU 上。

5. C++ 示例代码-避免数据迁移

#include <VX/vx.h>
#include <VX/vxu.h>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>// 自定义饱和度校正核(在 GPU 上操作)
vx_node vxColorSaturationNode(vx_graph graph, vx_image src, vx_image dst, float saturationScale) {vx_context context = vxGetContext((vx_reference)graph);vx_float32 scale = saturationScale;vx_scalar s_scale = vxCreateScalar(context, VX_TYPE_FLOAT32, &scale);vx_node node = vxCreateGenericNode(graph, vxGetKernelByEnum(context, VX_KERNEL_COLOR_CONVERT));vxSetParameterByIndex(node, 0, (vx_reference)src);vxSetParameterByIndex(node, 1, (vx_reference)dst);vxSetParameterByIndex(node, 2, (vx_reference)s_scale);return node;
}int main() {// 初始化 OpenVX contextvx_context context = vxCreateContext();// 加载图像数据到 OpenCV Matcv::Mat inputImage = cv::imread("input.jpg");vx_uint32 width = inputImage.cols;vx_uint32 height = inputImage.rows;vx_uint32 newWidth = 224;  // 目标尺寸vx_uint32 newHeight = 224;// 创建 OpenVX 图像,使用 GPU 内存(VX_MEMORY_TYPE_OPENCL 可选,根据实现库)vx_image vxInputImage = vxCreateImage(context, width, height, VX_DF_IMAGE_RGB);vx_image vxResizedImage = vxCreateImage(context, newWidth, newHeight, VX_DF_IMAGE_RGB);vx_image vxOutputImage = vxCreateImage(context, newWidth, newHeight, VX_DF_IMAGE_RGB);// 将 OpenCV Mat 数据复制到 OpenVX 图像(假设是 CPU -> GPU 拷贝)vx_rectangle_t rect = {0, 0, width, height};vx_imagepatch_addressing_t addr = {width * 3, 3, width, height};vx_uint8 *base = inputImage.data;vxCopyImagePatch(vxInputImage, &rect, 0, &addr, base, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST);// 创建 OpenVX 图形vx_graph graph = vxCreateGraph(context);// 添加图像 resize 节点vx_node resizeNode = vxResizeImageNode(graph, vxInputImage, vxResizedImage, VX_INTERPOLATION_BILINEAR);// 添加饱和度矫正节点vx_node saturationNode = vxColorSaturationNode(graph, vxResizedImage, vxOutputImage, 1.5f);// 验证图形vx_status status = vxVerifyGraph(graph);if (status == VX_SUCCESS) {// 处理图形(在 GPU 上处理)vxProcessGraph(graph);// 创建 OpenCV DNN 模型并设置为 GPU 模式cv::dnn::Net net = cv::dnn::readNet("model.onnx");net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);  // 选择 CUDA 作为后端net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);    // 在 GPU 上运行// 将 OpenVX 结果直接转换为 OpenCV GPU Mat(不回到 CPU)cv::cuda::GpuMat gpuOutputImage(newHeight, newWidth, CV_8UC3, vxAccessImagePatch(vxOutputImage, &rect, 0, &addr, (void**)&base, VX_READ_ONLY));// 进行 AI 推理cv::Mat blob = cv::dnn::blobFromImage(gpuOutputImage, 1.0, cv::Size(224, 224), cv::Scalar(104, 117, 123), false, false, CV_32F);net.setInput(blob);cv::Mat output = net.forward();// 处理推理结果// ... (处理 AI 推理结果的代码)// 释放 OpenVX 结果图像vxCommitImagePatch(vxOutputImage, &rect, 0, &addr, base);} else {std::cerr << "Graph verification failed!" << std::endl;}// 释放资源vxReleaseImage(&vxInputImage);vxReleaseImage(&vxResizedImage);vxReleaseImage(&vxOutputImage);vxReleaseGraph(&graph);vxReleaseContext(&context);return 0;
}

6. 上面5的代码说明

  1. 图像处理:

    • 使用 OpenVX 在 GPU 上进行图像处理,包括 Resize 和饱和度矫正。
    • 数据在 GPU 上被处理,并且不会传回 CPU。
  2. 数据传递:

    • 使用 cv::cuda::GpuMat 直接从 OpenVX 结果图像读取数据(在 GPU 上),并将其传递给 OpenCV 的 DNN 模块进行推理。
  3. 推理:

    • 在 GPU 上进行推理(使用 CUDA),所有操作在 GPU 内部完成,避免了不必要的 CPU 和 GPU 之间的数据传输。
  4. 优化:

    • 整个数据流从图像加载、预处理、推理到最终结果,完全在 GPU 内部完成,最大限度地减少了 I/O 开销,提高了处理效率。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 集群聊天服务器项目【C++】(三)muduo库的简单介绍
  • 网页模板该怎么选
  • MVC 控制器
  • Java | Leetcode Java题解之第401题二进制手表
  • yolov8 rect batch_shapes 672 图像大小变化
  • PHP智驭未来悦享生活智慧小区物业管理小程序系统源码
  • Java的发展史与前景
  • SQL Server详细使用教程(包含启动SQL server服务、建立数据库、建表的详细操作) 非常适合初学者
  • 4G模块、WIFI模块、NBIOT模块通过AT指令连接华为云物联网服务器(MQTT协议)
  • 高效数据移动指南 | 如何快速实现数据库 MySQL 到 MongoDB 的数据同步?
  • Python selenium 破解腾讯滑块行为验证码
  • 【Hadoop|MapReduce篇】Hadoop序列化概述
  • 【运维监控】influxdb 2.0+grafana 监控java 虚拟机以及方法耗时情况(完整版)
  • RTC、ADC
  • 深入理解Python函数参数传递:可变与不可变对象的实战解析20240914
  • Apache Spark Streaming 使用实例
  • ES6核心特性
  • Eureka 2.0 开源流产,真的对你影响很大吗?
  • js继承的实现方法
  • Laravel Mix运行时关于es2015报错解决方案
  • Python3爬取英雄联盟英雄皮肤大图
  • session共享问题解决方案
  • Yii源码解读-服务定位器(Service Locator)
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 给Prometheus造假数据的方法
  • 简单数学运算程序(不定期更新)
  • 三栏布局总结
  • 译米田引理
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • ​Benvista PhotoZoom Pro 9.0.4新功能介绍
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​补​充​经​纬​恒​润​一​面​
  • #{}和${}的区别是什么 -- java面试
  • #pragam once 和 #ifndef 预编译头
  • #QT(串口助手-界面)
  • (四)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • (转)iOS字体
  • (转)大道至简,职场上做人做事做管理
  • (转)大型网站架构演变和知识体系
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .net core控制台应用程序初识
  • .net/c# memcached 获取所有缓存键(keys)
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • @Async注解的坑,小心
  • @PreAuthorize与@Secured注解的区别是什么?
  • @selector(..)警告提示
  • @Transactional注解下,循环取序列的值,但得到的值都相同的问题
  • [ CTF ] WriteUp-2022年春秋杯网络安全联赛-冬季赛
  • [ 云计算 | Azure 实践 ] 在 Azure 门户中创建 VM 虚拟机并进行验证
  • [100天算法】-不同路径 III(day 73)
  • [ANT] 项目中应用ANT