写点什么

征程 6 VP 简介与单算子实操

  • 2025-03-18
    广东
  • 本文字数:3999 字

    阅读完需:约 13 分钟

征程 6 VP简介与单算子实操

1.如何理解 VP

  • VP,全称 Vision Process,指 UCP 中的视觉处理功能模块。

  • Backends,指 UCP 框架中的可分配处理单元。


VP 模块主要用于模型的前后处理环节,在地平线统一架构中,多种硬件均已搭载了图像处理的算子,而 VP 模块将图像处理相关的硬件调用进行了封装, 通过设置 backend 来选择不同的硬件方案(若不指定 backend,UCP 会自动适配负载更低的处理单元),从而平衡开发板负载。


VP 模块规避了不同硬件调用区别带来的不便,用户可更多地关注软件功能。


VP 模块功能架构图如下:



通过 VP 模块提供的算子的任务构造函数,如 hbVPResize、hbVPRotate 等,生成对应算子的任务句柄。


创建好任务句柄后,可以通过设置调度参数指定后端、任务优先级,设备 ID 和自定义 ID,从而将任务提交到对应的处理单元。 提交任务后,需要调用 API 等待任务完成。任务完成后,使用 API 释放任务句柄和相关资源,以确保系统资源得到有效管理和释放。 在从任务创建到任务提交、释放的过程中,UCP Service 层对各个环节均提供接口及功能支持。

2.VP 算子支持情况

性能数据:提供的 VP 算子,相比于 OpenCV3.4.5 在 A78 上的速度 会快一些。


支持情况:


3.VP 算子执行流程

以 Rotate 算子异步执行为例,展示算子实际的调用流程,其他算子的使用方法基本与其流程一致。



1.准备输入输出数据:即申请图片的内存空间并构建相关的描述信息。


2.创建算子任务:此步骤为直接调用算子任务接口,同时传入算子执行所需的参数,执行完成后输出 UCP 任务句柄。


3.提交任务:通过传入调度参数将算子任务提交到不同处理核心,任务提交支持指定 backend,如不指定则系统会自动适配 backend。


4.指定接口等待任务结束:任务结束时,系统会根据不同的执行状态返回不同的返回值,此时,您可根据返回值来查看任务执行结果。


5.销毁任务:任务成功执行后需要销毁任务,并释放申请的内存。


x86 和板端的一致性:x86 和板端的精度是能对齐的,且算子约束条件相同:比如说板端运行时,设置了超限的参数会导致运行结果异常,那么在 x86 设置同样的参数也会有同样的异常

4.VP 算子调用实操

4.1 配置 DSP 环境

使用 VP 算子仅在板端推理是不需要专门申请 DSP license 的,需要 X86 仿真,则需要找地平线技术支持申请 DSP license,关于环境配置不在这儿介绍了。

4.2 单算子示例代码详解

本节通过一个简单的 rotate 算子调用展示如何使用 VP 封装的算子实现图片处理的功能。


主要步骤包含:图片载入、任务创建、任务提交、任务完成、销毁任务、保存输出等,可以阅读相应源码和注释进行学习。


示例功能:使用 hbVPRotate 算子将图片顺时针旋转 90 度,主要代码解读如下:


1.读取图片


std::string src_img = "../../data/images/input.jpg";
cv::Mat src_mat = cv::imread(src_img.c_str(), cv::IMREAD_GRAYSCALE);
LOGE_AND_RETURN_IF(src_mat.empty(), HB_UCP_INVALID_ARGUMENT, "Read image {} failed", src_img.c_str());
复制代码


图片路径:src_img 是源图片的路径。


读取图片:通过 OpenCV 的 cv::imread 函数以灰度模式 (IMREAD_GRAYSCALE) 读取图片。


错误处理:如果图片读取失败,输出错误日志并返回错误状态码。


2.内存初始化和数据填充


hbUCPSysMem src_mem, dst_mem;hbUCPMallocCached(&src_mem, src_width * src_height, 0);hbUCPMallocCached(&dst_mem, dst_width * dst_height, 0);memcpy(src_mem.virAddr, src_mat.data, src_width * src_height);hbUCPMemFlush(&src_mem, HB_SYS_MEM_CACHE_CLEAN);
复制代码


内存分配:


  • 使用 hbUCPMallocCached 分配源图片和目标图片的缓存内存。

  • src_mem:存储源图片的数据。

  • dst_mem:存储旋转后图片的数据。


数据填充:


  • 通过 memcpy 将源图片数据复制到 src_mem 的虚拟地址空间。

  • 使用 hbUCPMemFlush 清空缓存,将数据刷新到物理内存。


3.输入和输出图片信息的封装


hbVPImage src{HB_VP_IMAGE_FORMAT_Y, HB_VP_IMAGE_TYPE_U8C1, src_width, src_height, src_stride, src_mem.virAddr, src_mem.phyAddr, 0, 0, 0};hbVPImage dst{HB_VP_IMAGE_FORMAT_Y, HB_VP_IMAGE_TYPE_U8C1, dst_width, dst_height, dst_stride, dst_mem.virAddr, dst_mem.phyAddr, 0, 0, 0};
复制代码


hbVPImage 用于描述图片的基本信息。


格式为 HB_VP_IMAGE_FORMAT_Y,表示灰度图片。


类型为 HB_VP_IMAGE_TYPE_U8C1,表示单通道 8 位无符号整型数据。


其他字段包括图片的宽、高、步幅(stride),以及内存的虚拟地址和物理地址。


4.提交任务并等待执行


hbUCPSchedParam sched_param;HB_UCP_INITIALIZE_SCHED_PARAM(&sched_param);sched_param.backend = HB_UCP_DSP_CORE_0;
hbUCPSubmitTask(rotate_task, &sched_param);hbUCPWaitTaskDone(rotate_task, 0);
复制代码


调度参数:


  • 使用 hbUCPSchedParam 配置任务的调度参数,例如执行设备 (DSP_CORE_0)。


提交任务:调用 hbUCPSubmitTask 提交旋转任务到指定设备。


等待完成:hbUCPWaitTaskDone 阻塞等待任务完成,超时时间为 0 表示无限等待。


5.结果保存


hbUCPMemFlush(&dst_mem, HB_SYS_MEM_CACHE_INVALIDATE);cv::Mat dst_mat(dst_height, dst_width, CV_8U, dst.dataVirAddr);cv::imwrite("./rotate.jpg", dst_mat);
复制代码


刷新内存:将目标图片数据从物理内存同步到缓存,确保数据可用。


结果保存:


  • 将目标图片数据封装为 OpenCV 的 cv::Mat 对象。

  • 使用 cv::imwrite 将旋转后的图片保存为文件 rotate.jpg。


6.资源释放


hbUCPReleaseTask(rotate_task);hbUCPFree(&src_mem);hbUCPFree(&dst_mem);
复制代码


任务释放:释放任务句柄,避免资源泄漏。


内存释放:释放分配的源图片和目标图片内存。


全部代码如下:


#include <cstring>
#include "opencv2/opencv.hpp"#include "hobot/vp/hb_vp_rotate.h"#include "rotate.h"#include "hobot/hb_ucp_sys.h"#include "hobot/hb_ucp.h"
int32_t single_rotate() { // Fill the operator parameter hbVPRotateDegree rotate_code = HB_VP_ROTATE_90_CLOCKWISE;
// Read the input image std::string src_img = "../../data/images/input.jpg"; cv::Mat src_mat = cv::imread(src_img.c_str(), cv::IMREAD_GRAYSCALE); LOGE_AND_RETURN_IF(src_mat.empty(), HB_UCP_INVALID_ARGUMENT, "Read image {} failed", src_img.c_str());
const int32_t src_width = src_mat.cols; const int32_t src_height = src_mat.rows; const int32_t src_stride = src_width; const int32_t dst_width = src_height; const int32_t dst_height = src_width; const int32_t dst_stride = dst_width;
// Initialize the input and output memory and fill the input memory with images hbUCPSysMem src_mem, dst_mem; hbUCPMallocCached(&src_mem, src_width * src_height, 0); hbUCPMallocCached(&dst_mem, dst_width * dst_height, 0); memcpy(src_mem.virAddr, src_mat.data, src_width * src_height); hbUCPMemFlush(&src_mem, HB_SYS_MEM_CACHE_CLEAN);
// Fill the input image information hbVPImage src{HB_VP_IMAGE_FORMAT_Y, HB_VP_IMAGE_TYPE_U8C1, src_width, src_height, src_stride, src_mem.virAddr, src_mem.phyAddr, 0, 0, 0};
// Fill the output image information hbVPImage dst{HB_VP_IMAGE_FORMAT_Y, HB_VP_IMAGE_TYPE_U8C1, dst_width, dst_height, dst_stride, dst_mem.virAddr, dst_mem.phyAddr, 0, 0, 0};
// Create a task through the operator interface provided by VP, the task handle can be set to nullptr, // and this task will be executed in synchronized mode hbUCPTaskHandle_t rotate_task{nullptr}; // UCP task handle hbVPRotate(&rotate_task/*task handle*/, &dst/*output image*/, &src/*input image*/, rotate_code/*operator parameter*/);
// Set scheduling parameters to adjust task priority, select execution terminals etc hbUCPSchedParam sched_param; HB_UCP_INITIALIZE_SCHED_PARAM(&sched_param); sched_param.backend = HB_UCP_DSP_CORE_0; /*Specify the execution device ID*/
// Submit the task hbUCPSubmitTask(rotate_task, &sched_param);
// Wait for the task to complete, set the timeout parameter, a value of 0 means to wait all the time hbUCPWaitTaskDone(rotate_task, 0);
// Release the task handle hbUCPReleaseTask(rotate_task);
/**=============================================================== * Dump output image * ===============================================================*/ hbUCPMemFlush(&dst_mem, HB_SYS_MEM_CACHE_INVALIDATE); cv::Mat dst_mat(dst_height, dst_width, CV_8U, dst.dataVirAddr); cv::imwrite("./rotate.jpg", dst_mat);
// Release the memory resource hbUCPFree(&src_mem); hbUCPFree(&dst_mem); std::cout << "======Rotate finish======" << std::endl; return 0;}
复制代码


旋转前后图片如下:


dst.dataVirAddr);cv::imwrite("./rotate.jpg", dst_mat);


// Release the memory resourcehbUCPFree(&src_mem);hbUCPFree(&dst_mem);std::cout << "======Rotate finish======" << std::endl;
return 0;
复制代码


}



用户头像

还未添加个人签名 2021-03-11 加入

还未添加个人简介

评论

发布
暂无评论
征程 6 VP简介与单算子实操_自动驾驶_地平线开发者_InfoQ写作社区