OpenVX编程:高效数据处理与跨框架互操作性
1. 高效数据输入/输出
在处理数据时,有一些参数用于描述应用程序空间中正在进行复制操作的用户内存区域,如
user_ptr
、
user_stride
和
user_mem_type
。下面重点介绍张量映射相关内容。
1.1 张量映射
对于
vx_tensor
对象,有映射和取消映射的函数。这些函数与图像和重映射对象的对应函数有相似之处,即检索的数据是对象数据的一部分,也就是“补丁”。不过,图像和重映射的补丁是二维的,而张量补丁是n维的,维度数最高可达
VX_CONTEXT_MAX_TENSOR_DIMS
(该值取决于OpenVX实现,但至少为4)。所以,张量的“补丁”是一个n维的长方体,由起点和终点数组定义,数组大小由
number_of_dims
参数指定,该参数必须等于张量本身的维度数。
以下是
vxMapTensorPatch
函数的定义:
vx_status vxMapTensorPatch(
vx_tensor tensor,
vx_size number_of_dims,
const vx_size* view_start,
const vx_size* view_end,
vx_map_id* map_id,
vx_size* stride,
void** ptr,
vx_enum usage,
vx_enum mem_type);
参数说明:
- 前两个参数分别是张量本身和它的维度数。
-
view_start
和
view_end
定义了要检索的长方体的角点,它们是包含
number_of_dims
个元素的索引数组。如果要检索整个张量,可以将这两个数组设为
NULL
,此时
view_start
默认是全零数组,
view_end
默认是张量的完整维度。
-
map_id
、
stride
和
ptr
是该函数的输出值,用于描述映射到用户空间的补丁。
map_id
用于取消映射,
ptr
指向补丁的左上角(前端),
stride
是一个n维数组,表示在相应维度上从一个元素到下一个元素的字节距离。
使用完补丁后,使用
vxUnmapTensorPatch(tensor, map_id)
取消映射。
还有一个复制补丁的函数:
vx_status vxCopyTensorPatch(
vx_tensor tensor,
vx_size number_of_dims,
const vx_size* view_start,
const vx_size* view_end,
const vx_size* user_stride,
void* user_ptr,
vx_enum usage,
vx_enum user_memory_type);
这里的
view_start
、
view_end
和
user_stride
输入参数都是数组,每个数组有
number_of_dims
个元素,它们描述了应用程序空间中要复制的数据源或目标区域,具体取决于
usage
参数。
整个张量映射和复制的流程可以用以下mermaid流程图表示:
graph LR
A[开始] --> B[调用vxMapTensorPatch]
B --> C{映射是否成功}
C -- 是 --> D[处理映射数据]
D --> E[调用vxUnmapTensorPatch取消映射]
C -- 否 --> F[处理错误]
E --> G[是否需要复制数据]
G -- 是 --> H[调用vxCopyTensorPatch]
G -- 否 --> I[结束]
H --> I[结束]
F --> I
2. 跨框架互操作性
在实际开发中,软件开发者常使用标准编程SDK来加速应用程序。除了使用标准OpenVX函数和图来加速视觉和AI工作负载外,还可能需要对超出标准OpenVX函数范围的部分进行加速,例如特定的预处理和后处理,以及一些无法很好映射到标准OpenVX函数的新算法。
2.1 OpenCV与OpenVX的互操作性
OpenCV是广泛采用的计算机视觉库,OpenVX设计为可与OpenCV一起使用。由于OpenCV不一定在支持OpenVX的平台上可用,且OpenVX的大多数数据结构是不透明的,所以两者的连接仅通过保证存储在主机内存中的OpenVX外部数据结构实现。OpenVX与OpenCV的使用场景主要分为两类:
-
OpenCV在OpenVX图外部使用
:用于创建输入数据或处理输出数据。例如,在桌面平台上处理网络摄像头视频流的应用程序中,OpenCV可用于从摄像头获取视频流并将其提供给OpenVX图,也可用于实现OpenVX图外的部分视觉管道。
-
OpenCV在OpenVX图内部使用
:用于实现用户节点。由于OpenVX功能有限,对于中等复杂度的管道,开发者通常需要使用用户节点。将所有功能放在一个图中,而不是在多个图之间混合使用OpenCV代码,可以让OpenVX实现更好地规划数据管理,在某些平台上可显著提高性能。
为了实现两者之间的高效数据传输,OpenVX的映射/取消映射函数支持与OpenCV兼容的数据格式。下面介绍具体的数据转换方法。
2.2 将OpenCV图像转换为OpenVX图像
VXA库中的
vxa_cv2vx
函数实现了将OpenCV的
cv::Mat
对象转换为OpenVX图像的功能。具体步骤如下:
vx_image vxa_cv2vx(const cv::Mat& cv_img, vx_context context)
{
vx_df_image img_type;
switch(cv_img.type())
{
case CV_8UC1:
img_type = VX_DF_IMAGE_U8;
break;
case CV_8UC3:
img_type = VX_DF_IMAGE_RGB;
break;
default:
return(NULL);
}
int width = cv_img.cols, height = cv_img.rows;
vx_image image = vxCreateImage(context, width, height, img_type);
vx_rectangle_t roi;
roi.start_x = 0;
roi.start_y = 0;
roi.end_x = width;
roi.end_y = height;
vx_map_id map_id;
vx_imagepatch_addressing_t addr;
unsigned char* ptr;
vx_status status = vxMapImagePatch(image, &roi, 0, &map_id, &addr,
(void**)&ptr, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST, VX_NOGAP_X);
if(status != VX_SUCCESS)
{
VXA_PRINT("vxMapImagePatch returned error with code %d\n", status);
return(NULL);
}
if(addr.stride_x != 1 && addr.stride_x != 3)
{
VXA_PRINT("addressing structure not supported, stride_x = %d\n",
addr.stride_x);
return(0);
}
for(int y = 0; y < height; y++)
{
unsigned char* ptr_y = ptr + y*addr.stride_y;
memcpy(ptr_y, cv_img.ptr(y), addr.stride_y);
}
vxUnmapImagePatch(image, map_id);
return image;
}
步骤说明:
1. 检查OpenCV图像类型,只支持8位灰度和RGB彩色图像。
2. 创建与输入图像尺寸相同的OpenVX图像。
3. 准备
vxMapImagePatch
函数的参数并调用该函数,将OpenVX图像映射到主机内存。
4. 检查OpenVX图像的像素布局是否为密集排列,如果不是则返回错误。
5. 逐行复制数据。
6. 调用
vxUnmapImagePatch
释放主机内存。
需要注意的是,该函数会将OpenCV图像的所有数据复制到OpenVX图像中。可以使用
vxCreateImageFromHandle
函数创建外部分配的OpenVX图像来避免数据复制,但要确保在将OpenCV图像的内存交给OpenVX后不再使用该图像。
2.3 将OpenVX图像转换为OpenCV图像
VXA库中的
vxa_vx2cv
函数实现了将OpenVX图像转换为OpenCV图像的功能。具体步骤如下:
int vxa_vx2cv(vx_image image, cv::Mat& cv_img)
{
int width, height;
vxQueryImage(image, VX_IMAGE_WIDTH, &width, 4);
vxQueryImage(image, VX_IMAGE_HEIGHT, &height, 4);
vx_df_image img_type;
vxQueryImage(image, VX_IMAGE_FORMAT, &img_type, 4);
int cv_type;
switch(img_type)
{
case VX_DF_IMAGE_U8:
cv_type = CV_8UC1;
break;
case VX_DF_IMAGE_RGB:
cv_type = CV_8UC3;
break;
default:
VXA_PRINT("Format %d not supported\n", img_type);
return(-1);
}
vx_rectangle_t roi;
roi.start_x = 0;
roi.start_y = 0;
roi.end_x = width;
roi.end_y = height;
vx_map_id map_id;
vx_imagepatch_addressing_t addr;
unsigned char* ptr;
vx_status status = vxMapImagePatch(image, &roi, 0, &map_id, &addr,
(void**)&ptr, VX_READ_ONLY, VX_MEMORY_TYPE_HOST, VX_NOGAP_X);
if(status != VX_SUCCESS)
{
VXA_PRINT("vxMapImagePatch returned error with code %d\n", status);
return(-1);
}
if(addr.stride_x != 1 && addr.stride_x != 3)
{
VXA_PRINT("addressing structure not supported, stride_x = %d\n",
addr.stride_x);
return(0);
}
cv_img = cv::Mat(height, width, cv_type);
for(int y = 0; y < height; y++)
{
unsigned char* ptr_y = ptr + y*addr.stride_y;
memcpy(cv_img.ptr(y), ptr_y, addr.stride_y);
}
status = vxUnmapImagePatch(image, map_id);
if(status != VX_SUCCESS)
{
VXA_PRINT("vxUnmapImagePatch failed...\n");
return(0);
}
return 1;
}
步骤说明:
1. 获取OpenVX图像的尺寸和类型。
2. 根据图像类型确定对应的OpenCV类型。
3. 准备
vxMapImagePatch
函数的参数并调用该函数,将OpenVX图像映射到主机内存。
4. 检查OpenVX图像的像素布局是否为密集排列,如果不是则返回错误。
5. 创建相同尺寸和类型的OpenCV图像,并逐行复制数据。
6. 调用
vxUnmapImagePatch
释放主机内存。
2.4 转换其他数据类型
除了图像,VXA库还可以处理将OpenCV准备的重映射数据结构导入到OpenVX中。
vxa_import_opencv_remap
函数实现了该功能,具体代码如下:
int vxa_import_opencv_remap(const char* filename, const char* nodename,
vx_context context, vx_remap* remap, int* _dst_width, int* _dst_height)
{
FileStorage fs(filename, FileStorage::READ);
Mat cv_remap;
fs[nodename] >> cv_remap;
int src_width, src_height, dst_width, dst_height;
fs[(std::string(nodename) + std::string("_src_width")).c_str()] >> src_width;
fs[(std::string(nodename) + std::string("_src_height")).c_str()] >> src_height;
fs[(std::string(nodename) + std::string("_dst_width")).c_str()] >> dst_width;
fs[(std::string(nodename) + std::string("_dst_height")).c_str()] >> dst_height;
if(cv_remap.type() != CV_32FC2)
{
return(0);
}
vx_rectangle_t roi;
roi.start_x = 0;
roi.start_y = 0;
roi.end_x = cv_remap.cols;
roi.end_y = cv_remap.rows;
*remap = vxCreateRemap(context, src_width, src_height,
dst_width, dst_height);
if(vxCopyRemapPatch(*remap, &roi, cv_remap.step, cv_remap.ptr(),
VX_TYPE_COORDINATES2DF, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST) != VX_SUCCESS)
{
return(0);
}
return(1);
}
步骤说明:
1. 从文件中读取OpenCV重映射矩阵。
2. 检查矩阵类型是否为
CV_32FC2
,如果不是则返回错误。
3. 创建OpenVX重映射对象。
4. 使用
vxCopyRemapPatch
函数将数据从OpenCV复制到OpenVX。
通过以上方法,可以实现OpenVX与OpenCV之间的高效数据转换和互操作,为开发者在不同场景下的应用开发提供了便利。
OpenVX编程:高效数据处理与跨框架互操作性
2.3 OpenCL与OpenVX的互操作性
OpenCL是一种用于并行计算的开放标准,在很多场景下可以加速计算任务。OpenVX支持与OpenCL应用程序进行互操作,下面将介绍OpenCL的基础知识以及OpenVX与OpenCL的互操作机制。
2.3.1 OpenCL基础
OpenCL的主要组成部分如下:
-
OpenCL C程序
:用于编写并行计算的内核代码。
-
OpenCL上下文
:管理计算设备、内存对象和命令队列等资源。
-
OpenCL即时编译器
:在运行时将OpenCL C程序编译为设备可执行代码。
-
OpenCL缓冲区
:用于在主机和设备之间传输数据。
-
OpenCL内核执行
:将内核代码在计算设备上执行。
这些组成部分的关系可以用以下表格表示:
| 组成部分 | 描述 |
| — | — |
| OpenCL C程序 | 并行计算的内核代码 |
| OpenCL上下文 | 管理计算资源 |
| OpenCL即时编译器 | 运行时编译内核代码 |
| OpenCL缓冲区 | 数据传输通道 |
| OpenCL内核执行 | 执行内核代码 |
2.3.2 OpenVX与OpenCL的互操作机制
OpenVX与OpenCL的互操作主要通过以下几个方面实现:
-
OpenVX上下文与OpenCL互操作性
:创建支持OpenCL互操作的OpenVX上下文,使得OpenVX可以利用OpenCL的计算资源。
-
OpenCL缓冲区访问
:通过OpenVX的API访问OpenCL缓冲区,实现数据在两者之间的传输。
-
用户内核的OpenCL加速
:使用OpenCL加速OpenVX的用户内核,提高计算性能。
整个互操作的流程可以用以下mermaid流程图表示:
graph LR
A[创建支持OpenCL互操作的OpenVX上下文] --> B[创建OpenCL缓冲区]
B --> C[将数据从OpenVX传输到OpenCL缓冲区]
C --> D[在OpenCL设备上执行内核计算]
D --> E[将计算结果从OpenCL缓冲区传输回OpenVX]
E --> F[在OpenVX中继续处理数据]
3. 总结与建议
在使用OpenVX进行开发时,为了实现高效的数据处理和跨框架互操作性,我们可以遵循以下建议:
-
高效数据访问
:合理使用映射和取消映射函数,减少数据复制,提高性能。例如,在只需要访问部分数据时,避免访问整个图像、数组或张量。
-
跨框架互操作
:
- 与OpenCV互操作时,根据具体场景选择合适的转换方法,如使用
vxCreateImageFromHandle
避免数据复制。
- 与OpenCL互操作时,充分利用OpenCL的并行计算能力,加速用户内核的执行。
-
性能优化
:
- 避免不必要的数据复制,如在数据交换时,尽量使用内存句柄进行数据传输。
- 合理规划数据管理,将所有功能放在一个图中,让OpenVX实现更好地优化数据处理流程。
通过以上方法和建议,开发者可以在实际项目中更好地利用OpenVX的功能,结合其他框架的优势,实现高效、高性能的应用开发。
总之,OpenVX在数据处理和跨框架互操作性方面提供了丰富的功能和机制。开发者可以根据具体需求,灵活运用这些功能,提高开发效率和应用性能。无论是处理视觉和AI工作负载,还是与其他计算机视觉库和并行计算框架进行集成,OpenVX都能发挥重要作用。希望本文介绍的内容能对开发者在OpenVX编程中有所帮助。
OpenVX高效数据处理与跨框架互操作
超级会员免费看
21

被折叠的 条评论
为什么被折叠?



