24、OpenVX 数据处理与跨框架互操作性解析

OpenVX 数据处理与跨框架互操作性解析

1. OpenVX 数据输入输出

在处理 OpenVX 数据时,高效的输入输出操作至关重要。以下是一些关键的操作和函数:
- 映射张量
- 对于 vx_tensor 对象,有映射和取消映射函数。这些函数与图像和重映射对象的对应函数类似,获取的数据是对象数据的一部分,即“补丁”。不同的是,图像和重映射补丁是二维的,而张量补丁是 n 维的,维度数最高可达 VX_CONTEXT_MAX_TENSOR_DIMS (至少为 4)。
- 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);
- 参数说明:
    - `tensor`:张量本身。
    - `number_of_dims`:张量的维度数。
    - `view_start` 和 `view_end`:定义要检索的 n 维长方体的角点,每个是包含 `number_of_dims` 个元素的索引数组。若传入 `NULL`,`view_start` 默认全为 0 的数组,`view_end` 默认张量的全维度。
    - `map_id`、`stride` 和 `ptr`:函数的输出值,描述映射到用户空间的补丁。`map_id` 用于取消映射,`ptr` 指向补丁的左上角(前端等),`stride` 是另一个 n 维数组,表示在相应维度上从一个元素到下一个元素的字节距离。
- 使用完补丁后,使用 `vxUnmapTensorPatch(tensor, map_id)` 取消映射。
  • 复制张量补丁
    • vxCopyTensorPatch 函数用于复制张量补丁,参数如下:
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` 个元素的数组,描述应用程序空间中要复制的数据区域。
2. 数据访问的最佳实践

在实际应用中,为了提高性能和降低功耗,需要注意以下几点:
- 减少数据访问量 :只访问所需的数据部分,避免访问整个图像、数组或张量。
- 选择高效的访问模式 :根据具体需求选择合适的访问模式,尽量减少数据复制。
- 合理使用映射和取消映射函数 :避免过度使用这些函数,以免导致额外的数据复制和性能下降。

3. OpenVX 与 OpenCV 的互操作性

OpenCV 是广泛使用的计算机视觉库,OpenVX 设计为可与 OpenCV 一起使用。两者的可能用例分为两类:
- OpenCV 在 OpenVX 图外使用 :用于创建输入数据或处理输出数据,例如在桌面平台上处理网络摄像头视频流的应用程序中,OpenCV 可用于获取视频流并将其提供给 OpenVX 图。
- OpenCV 在 OpenVX 图内使用 :用于实现用户节点,对于中等复杂度的管道,开发者可能需要使用用户节点。

4. 将 OpenCV 图像转换为 OpenVX 图像

可以使用 VXA 库中的 vxa_cv2vx 函数将 OpenCV 图像转换为 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 函数,以写入模式映射图像。
4. 检查图像布局,确保 OpenVX 图像使用密集像素打包。
5. 逐行复制数据。
6. 调用 vxUnmapImagePatch 释放主机内存。

5. 将 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 图像类型,仅支持 8 位灰度和 RGB 彩色图像。
3. 准备数据并调用 vxMapImagePatch 函数,以只读模式映射图像。
4. 检查图像布局,确保 OpenVX 图像使用密集像素打包。
5. 创建具有相同维度和类型的 OpenCV 图像,并逐行复制数据。
6. 调用 vxUnmapImagePatch 释放主机内存。

6. 转换其他数据类型

除了图像,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。

7. 总结

通过上述操作和函数,可以实现 OpenVX 与 OpenCV 之间的数据高效转换和互操作。在实际应用中,需要根据具体需求选择合适的方法和函数,以提高性能和降低功耗。同时,要注意避免不必要的数据复制,合理使用映射和取消映射函数。

以下是将 OpenCV 图像转换为 OpenVX 图像的流程图:

graph TD;
    A[开始] --> B[检查 OpenCV 图像类型];
    B -- 8 位灰度或 RGB --> C[创建 OpenVX 图像];
    B -- 其他类型 --> D[返回错误];
    C --> E[准备数据并映射图像];
    E -- 映射成功 --> F[检查图像布局];
    F -- 布局支持 --> G[逐行复制数据];
    F -- 布局不支持 --> D;
    G --> H[释放主机内存];
    H --> I[结束];
    E -- 映射失败 --> D;

通过这些操作和方法,可以更好地利用 OpenVX 和 OpenCV 的优势,实现高效的计算机视觉应用。

OpenVX 数据处理与跨框架互操作性解析

8. OpenVX 与 OpenCL 的互操作性

在实际开发中,软件开发者常倾向于使用标准编程 SDK 来加速应用。当遇到超出标准 OpenVX 函数的应用场景,如特定的预处理、后处理以及难以映射到标准 OpenVX 函数的新算法时,可利用 OpenVX 用户内核实现这些算法并插入到 OpenVX 图中,以享受图优化带来的好处。然而,目前 OpenVX 标准仅支持可在主机上调度的用户内核,使用 vxCopyXxx vxMapXxx/vxUnmapXxx API 在 OpenVX 与其他编程 SDK 之间交换数据缓冲区时,会导致目标设备与主机之间的数据来回复制,影响性能。

为解决这些效率问题,OpenVX 支持通过 vxCopyXxx vxMapXxx/vxUnMapXxx vxCreateXxxFromHandle vxSwapXxxHandle API 交换内存句柄。下面介绍 OpenCL 的基础知识以及 OpenVX 与 OpenCL 应用之间的互操作机制。

9. OpenCL 基础

OpenCL 是用于异构计算的开放标准,以下是其主要组成部分:
- OpenCL C 程序 :基于 C 语言扩展的编程语言,用于编写在 OpenCL 设备上执行的内核代码。
- OpenCL 上下文 :管理 OpenCL 设备、内存对象和命令队列的环境。
- OpenCL 即时编译器 :在运行时将 OpenCL C 代码编译为设备可执行的代码。
- OpenCL 缓冲区 :用于在主机和设备之间传输数据的内存对象。
- OpenCL 内核执行 :通过命令队列将内核代码发送到设备上执行。

10. OpenVX 与 OpenCL 的互操作

OpenVX 与 OpenCL 的互操作主要涉及以下方面:
- 具有 OpenCL 互操作性的 OpenVX 上下文 :创建支持 OpenCL 互操作的 OpenVX 上下文,以便在 OpenVX 中使用 OpenCL 资源。
- OpenCL 缓冲区访问 :通过 OpenVX API 访问 OpenCL 缓冲区,实现数据的共享和传输。
- 使用 OpenCL 加速的用户内核 :在 OpenVX 用户内核中使用 OpenCL 进行加速,提高计算性能。

11. 互操作示例

以下是一个简单的示例,展示如何在 OpenVX 中使用 OpenCL 缓冲区:

// 创建 OpenCL 上下文和缓冲区
cl_context cl_context = create_cl_context();
cl_mem cl_buffer = create_cl_buffer(cl_context);

// 创建具有 OpenCL 互操作性的 OpenVX 上下文
vx_context vx_context = vxCreateContextWithCL(cl_context);

// 创建 OpenVX 图像对象
vx_image vx_image = vxCreateImageFromHandle(vx_context, VX_DF_IMAGE_U8, width, height, cl_buffer);

// 执行 OpenVX 图
vx_graph vx_graph = vxCreateGraph(vx_context);
// 添加节点和连接
// ...
vxScheduleGraph(vx_graph);
vxWaitGraph(vx_graph);

// 释放资源
vxReleaseImage(&vx_image);
vxReleaseGraph(&vx_graph);
vxReleaseContext(&vx_context);
clReleaseMemObject(cl_buffer);
clReleaseContext(cl_context);

操作步骤如下:
1. 创建 OpenCL 上下文和缓冲区。
2. 创建具有 OpenCL 互操作性的 OpenVX 上下文。
3. 使用 vxCreateImageFromHandle 函数从 OpenCL 缓冲区创建 OpenVX 图像对象。
4. 创建并执行 OpenVX 图。
5. 释放所有资源。

12. 总结

OpenVX 与 OpenCL 的互操作性为开发者提供了更多的选择和灵活性。通过合理利用 OpenCL 的并行计算能力,可以显著提高 OpenVX 应用的性能。在实际应用中,需要根据具体需求选择合适的互操作方式,避免不必要的数据复制和性能损失。

以下是 OpenVX 与 OpenCL 互操作的流程图:

graph TD;
    A[创建 OpenCL 上下文和缓冲区] --> B[创建具有 OpenCL 互操作性的 OpenVX 上下文];
    B --> C[从 OpenCL 缓冲区创建 OpenVX 图像对象];
    C --> D[创建并执行 OpenVX 图];
    D --> E[释放所有资源];

通过以上对 OpenVX 与 OpenCV、OpenCL 的互操作性的介绍,我们可以看到在不同的计算机视觉和计算场景中,可以根据具体需求选择合适的框架和方法,实现高效的数据处理和计算。在实际开发中,要充分考虑性能、资源利用和代码复杂度等因素,以达到最佳的开发效果。

下面是一个对比 OpenVX 与 OpenCV、OpenCL 互操作性特点的表格:
| 互操作性类型 | 优点 | 缺点 | 适用场景 |
| ---- | ---- | ---- | ---- |
| OpenVX 与 OpenCV | 易于实现,利用 OpenCV 丰富的功能 | 可能存在数据复制开销 | 处理常见的计算机视觉任务,结合 OpenCV 现有算法 |
| OpenVX 与 OpenCL | 利用 OpenCL 的并行计算能力,提高性能 | 实现复杂度较高 | 需要大规模并行计算的场景,如深度学习推理 |

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值