首页 > 其他分享 >openVX加速-新增自定义节点和示例代码

openVX加速-新增自定义节点和示例代码

时间:2024-09-09 15:25:16浏览次数:11  
标签:kernel OpenVX 自定义 示例 filter vx openVX 节点

在 OpenVX 中添加自定义节点大概通过以下步骤实现:

  1. 定义自定义节点的计算逻辑:你需要编写一个 C 函数来实现自定义的图像处理操作。

  2. 创建自定义节点:通过定义一个自定义节点核(kernel),并将其注册到 OpenVX 上下文中。

  3. 在图中使用自定义节点:使用你定义的节点与 OpenVX 提供的内置节点一样,在图中添加该节点并设置参数。

假如在 Sobel 边缘检测后进行巴特沃斯滤波,详细说明如何实现。

实际上这个滤波器使用openvx实现不一定合理,因为openvx主要是为了用在图像处理的算法优化,而信号的维度就没有图像那么大,数据量也不会有那么多, 不一定合适,这里只是举一个例子, 大家可以随便拿自己算法封装进去,来实际测试加速效果。

1. 定义巴特沃斯滤波器的计算逻辑

首先,我们编写一个简单的巴特沃斯滤波器函数。假设我们已经将图像数据转化为一维信号,以下是一个简化的巴特沃斯滤波器示例:

#include <math.h>

void butterworth_filter(float* signal, int length, float cutoff_frequency, int order) {
    for (int i = 0; i < length; ++i) {
        float freq = (float)i / length;
        float filter_value = 1.0 / (1.0 + pow(freq / cutoff_frequency, 2 * order));
        signal[i] *= filter_value;
    }
}

2. 创建和注册自定义节点核

接下来,我们需要定义和注册这个自定义节点。一个自定义节点的实现包括:

  • Kernel Function:执行核心计算的函数。
  • Parameter Setup:定义节点输入和输出参数。
  • Validation Function:用于验证节点参数是否合法。
  • Execution Function:实际执行节点操作的函数。
Kernel Function

假设 butterworth_filter 是核心计算逻辑函数,我们需要将其封装到 OpenVX 的 kernel 中。

vx_status VX_CALLBACK butterworth_filter_node(vx_node node, const vx_reference *parameters, vx_uint32 num) {
    vx_array input_signal = (vx_array)parameters[0];
    vx_array output_signal = (vx_array)parameters[1];
    vx_scalar cutoff_frequency = (vx_scalar)parameters[2];
    vx_scalar order = (vx_scalar)parameters[3];

    // 获取参数数据
    float* input_data;
    vx_size length;
    vxAccessArrayRange(input_signal, 0, length, &stride, (void**)&input_data, VX_READ_ONLY);

    float cutoff;
    vxCopyScalar(cutoff_frequency, &cutoff, VX_READ_ONLY, VX_MEMORY_TYPE_HOST);

    int filter_order;
    vxCopyScalar(order, &filter_order, VX_READ_ONLY, VX_MEMORY_TYPE_HOST);

    // 执行滤波
    butterworth_filter(input_data, length, cutoff, filter_order);

    // 写回结果
    vxCommitArrayRange(output_signal, 0, length, input_data);

    return VX_SUCCESS;
}
Parameter Setup

然后需要定义和描述这个节点的输入输出参数:

vx_kernel custom_kernel = vxAddUserKernel(
    context,                    // 上下文
    "user.butterworth_filter",  // 核的名称
    USER_KERNEL_ID,             // 核的ID
    butterworth_filter_node,    // 核函数
    4,                          // 参数数量
    butterworth_filter_validator, // 验证函数
    NULL, NULL);                // 初始化和清理函数

// 设置参数
vxAddParameterToKernel(custom_kernel, 0, VX_INPUT, VX_TYPE_ARRAY, VX_PARAMETER_STATE_REQUIRED);
vxAddParameterToKernel(custom_kernel, 1, VX_OUTPUT, VX_TYPE_ARRAY, VX_PARAMETER_STATE_REQUIRED);
vxAddParameterToKernel(custom_kernel, 2, VX_INPUT, VX_TYPE_SCALAR, VX_PARAMETER_STATE_REQUIRED);
vxAddParameterToKernel(custom_kernel, 3, VX_INPUT, VX_TYPE_SCALAR, VX_PARAMETER_STATE_REQUIRED);

// 注册核
vxFinalizeKernel(custom_kernel);

3. 在图中使用自定义节点

创建图时可以使用自定义节点:

vx_array input_signal = vxCreateArray(context, VX_TYPE_FLOAT32, length);
vx_array output_signal = vxCreateArray(context, VX_TYPE_FLOAT32, length);
vx_scalar cutoff_frequency = vxCreateScalar(context, VX_TYPE_FLOAT32, 0.3f);
vx_scalar order = vxCreateScalar(context, VX_TYPE_INT32, 2);

vx_node filter_node = vxCreateGenericNode(graph, custom_kernel);
vxSetParameterByIndex(filter_node, 0, (vx_reference)input_signal);
vxSetParameterByIndex(filter_node, 1, (vx_reference)output_signal);
vxSetParameterByIndex(filter_node, 2, (vx_reference)cutoff_frequency);
vxSetParameterByIndex(filter_node, 3, (vx_reference)order);

// 验证和执行图
vxVerifyGraph(graph);
vxProcessGraph(graph);

4. 总结关键步骤

  • 定义巴特沃斯滤波器的核心计算逻辑。
  • 在 OpenVX 中创建和注册一个自定义节点核,将巴特沃斯滤波器封装为 OpenVX 节点。
  • 在图中使用自定义节点,并设置输入输出参数。

虽然 OpenVX 的接口看起来复杂,但这些设计是为了确保代码的可移植性、可扩展性和高效执行。如果你只在单一平台上运行代码,并且不需要这些扩展性和硬件优化,代码的复杂性可以大大简化。但如果你需要支持多种硬件和复杂的计算任务,这种复杂性是不可避免的。

理论上就可以将自定义的图像处理操作集成到 OpenVX 的处理流水线中,从而利用异构硬件加速。实际上你肯定还会遇到各种问题, 比如硬件的适配,算子的适配,vx的版本等等。

 
 

下面再补充说明一下,方便大家更深入的理解:

5. 补充说明: Kernel(核)的概念

OpenVX 的设计目标是实现跨平台的高性能图像处理,允许用户将自定义的图像处理算法集成到现有的加速框架中。为了确保这种集成的灵活性和效率,OpenVX 引入了“kernel(核)”的概念,并提供了相应的接口用于注册和使用自定义的计算节点。这些概念和接口的复杂性源于以下几个原因:

Kernel 是 OpenVX 中的一个基本概念,表示执行特定计算任务的函数。每个 kernel 是一个独立的计算单元,可以在图(graph)中作为节点(node)使用。OpenVX 的核心优势在于它支持在不同的硬件平台上高效地执行这些计算单元,如 CPU、GPU、DSP 等。

为何使用 kernel

  • 可扩展性:允许用户扩展 OpenVX 的功能,添加自定义的计算节点(即自定义 kernel),用于执行特定的计算任务。
  • 硬件优化:每个 kernel 可以针对特定的硬件平台进行优化,确保计算效率。
  • 模块化:kernel 是独立的计算单元,可以在多个图中复用,提高代码的可维护性。

6. 补充说明:vxAddUserKernelbutterworth_filter_node 的关系

  • vxAddUserKernel:用于在 OpenVX 中注册一个自定义的 kernel。这个函数告诉 OpenVX 框架:“这里有一个新定义的 kernel,它执行特定的任务,并且可以在图中作为节点使用。”
  • butterworth_filter_node:这个函数是我们实际执行巴特沃斯滤波操作的核心计算逻辑。它定义了节点在运行时具体的计算行为。

vxAddUserKernel 的主要作用是将 butterworth_filter_node 注册为 OpenVX 的一个可用 kernel,从而可以在图中使用这个自定义节点。

7. 补充说明:为什么这么复杂的封装实现

OpenVX 要支持多种硬件平台和多种计算任务,封装复杂的接口是为了:

  • 参数验证:确保传递给 kernel 的输入输出参数类型正确、范围合法。通过参数验证(validator),防止在运行时发生错误。
  • 执行管理:OpenVX 框架通过封装,能够控制 kernel 的执行顺序、并行度、内存管理等,保证在不同硬件上都能高效执行。
  • 扩展性和灵活性:封装使得框架可以动态地加载和使用不同的 kernel,而无需修改底层代码。

8. 补充说明:验证和执行图的代码解释

在 OpenVX 中,图(graph)表示计算任务的整体流程。图由多个节点(node)组成,每个节点代表一个 kernel 操作。验证和执行图的代码是 OpenVX 图的核心操作流程:

  • vxVerifyGraph(graph):验证图的结构和参数。它会检查整个图中的节点、数据依赖关系、输入输出参数是否正确。如果有任何问题,这一步会返回错误。验证成功后,表明图可以被执行。

  • vxProcessGraph(graph):执行图。OpenVX 会根据图中的节点和它们的依赖关系,自动决定执行顺序。它会将图的计算任务分发到合适的硬件(如 CPU、GPU)上执行。整个图会按照定义的流程运行,直到所有节点的计算都完成。

9. 补充说明:为什么 butterworth_filter 要被 butterworth_filter_node 包一层?

butterworth_filter 是一个实现具体功能的普通 C++ 函数,而 butterworth_filter_node 是 OpenVX 框架中的一个回调函数。这个回调函数被 OpenVX 调用,用于执行自定义节点的计算逻辑。

包一层的原因

  • 接口统一:OpenVX 需要所有的 kernel(核)函数都符合特定的函数签名(即参数类型和返回值类型)。butterworth_filter_node 是符合 OpenVX 规范的一个函数,而 butterworth_filter 只是一个普通的计算函数,因此需要包装成符合 OpenVX 规范的形式。
  • 参数处理butterworth_filter_node 负责从 OpenVX 的数据结构中提取参数,并将这些参数传递给 butterworth_filter 函数。在 butterworth_filter_node 中,你可以访问 OpenVX 传递的输入输出数据,并调用 butterworth_filter 进行实际的计算。

10. 补充说明:vxCreateGenericNode 是干啥的?

vxCreateGenericNode 用于在 OpenVX 的图中创建一个通用节点。这个函数接收一个已注册的 kernel 作为参数,并返回一个节点对象。这个节点对象可以被添加到图中,参与整个计算流程。

vx_node node = vxCreateGenericNode(graph, custom_kernel);

在上面例子中,custom_kernel 是之前通过 vxAddUserKernel 注册的 kernel。当你调用 vxCreateGenericNode 时,实际上是告诉 OpenVX 框架:“我要在这个图中使用这个 kernel 进行计算”。

11. 补充说明:vxProcessGraph(graph) 是系统自己决定跑在什么硬件上吗?如何查看跑在了什么平台上?

vxProcessGraph(graph) 是 OpenVX 框架自动调度的执行步骤。OpenVX 框架根据每个节点的属性和平台的硬件配置,自动决定在哪个硬件单元(如 CPU、GPU、DSP 等)上执行每个节点。

查看运行平台

  • OpenVX 标准中并没有明确要求提供一个 API 来查看某个节点在什么硬件上运行,这取决于具体的 OpenVX 实现。如果你使用的是某些特定厂商的 OpenVX 实现(如 Khronos 参考实现、NVIDIA 的实现),可能会有相应的工具或日志来查看运行平台。
  • 有时候,可以通过开启调试模式或使用特定的日志功能来查看节点的执行硬件。

标签:kernel,OpenVX,自定义,示例,filter,vx,openVX,节点
From: https://blog.csdn.net/tyfwin/article/details/142059239

相关文章

  • 发票真伪识别接口费用-发票真伪查验接口-发票验真示例
    发票信息核验是一个重要的财务和会计过程,涉及到对发票上的信息进行验证,以确保其真实性和准确性。在数字化时代,这一过程企业通常想通过调用发票查验接口的方式实现自动化管理模式。发票查验接口费用不同的服务提供商会有不同的收费标准,例如翔云平台提供的发票验真接口可以免费测试......
  • 身份证实名认证接口费用-身份证信息核验-实名认证示例
    身份证信息核验是实名认证中一种常见的认证方式,它通过比对用户提交的身份证信息与权威数据库中的信息,来确认用户身份是否真实有效,一般情况下,线上平台实现实名认证功能主要依赖于调用第三方接口​平台来实现。实名认证接口费用通常由提供该服务的第三方平台来设定,以翔云实名认......
  • echarts的tooltip自定义
    tooltip:{      trigger:'axis',      formatter:function(params){       varhtml=params[0].name+'<br>';       //params[i].marker:对应数据的圆点样式       for(vari=0;i<params......
  • 自定义界面扫码,满足应用个性化定制需求
    二维码识别技术已经成为我们日常生活中不可或缺的一部分,广泛应用于支付、交通、餐饮、生活服务以及智能家居等领域。它不仅是移动应用的重要流量入口,更是连接线上线下世界的桥梁。不同的App在扫码界面的设计上各展其特色,从页面元素到交互方式,都体现了开发者对用户体验的重视。然......
  • java自定义校验注解
    一个简单的自定义规则校验注释,校验图片名是不是.jpg或者.png校验规则的类packagecom.wzw.pdfconverword.validator;importcom.wzw.pdfconverword.annotation.Img;importjakarta.validation.ConstraintValidator;importjakarta.validation.ConstraintValidatorContext;//Im......
  • 17 Python异常处理(捕获异常、抛出异常、自定义异常)
    本篇是Python系列教程第17篇,更多内容敬请访问我的Python合集当我们编写代码时,可能会遇到各种各样的错误情况,比如除数为零、找不到文件、网络问题等等。为了优雅地处理这些问题,Python提供了异常处理机制。1异常处理的基本结构Python中的异常处理主要依赖于try和ex......
  • 鹏哥C语言自定义笔记重点(44-)
    44.不能给地址复制,strcpy拷贝过去连带着\045.46. 47. 48.strstr   //查找子串的一个函数49.strtok  //切割字符串 50.strerror   //返回错误码,所对应的错误信息 检查的是文件51.如果不正确则返回0  52.memcpy负责拷贝两块独立空间......
  • 8章8节:绘制自定义的高质量动态图和交互式动态图
    在数据科学和数据可视化的领域,动态图和交互式图形越来越受到重视,因为它们可以帮助用户更好地理解数据并发现潜在的模式。R语言作为数据分析和可视化的强大工具,提供了丰富的功能来创建这些图形。一、认识动态图动态图,顾名思义,是一种可以随时间或某些变量的变化而动态呈现的......
  • c# Csv文件读写示例,如果文件存在追加写入
    功能    1.写入    2.读取导出文件效果调用示例注意示例中的ToDataTable()方法是自己的封装的扩展方法,源码在集合扩展方法-CSDN博客privateList<MarkDataModel>createMarkDataList(intcount){varmarkDataModels=newList<M......
  • Dotnetty学习笔记——自定义初始化处理器
    常常我们需要开一个服务单,对接不同的客户端,编码器、解码器等都不同,需要针对不同IP添加不同的处理器。publicclassCustomInitializer:Channellnitializer<lSocketChannel>{Action<string,string>_dealMsgAction;lServer_server;publicCustomInitializer(Action<st......