为什么需要OpenVX:可以使软件开发人员能够高效地使用计算机视觉硬件加速功能,而不需要考虑硬件加速的具体实现细节。OpenVX具有良好的可移植性,支持各种现代硬件系统,如常见的移动嵌入式SOC系统以及桌面系统。这些硬件系统通常是异构的、并行的,它们可能包含多核CPU、DSP子系统、图形处理器以及各种各样的用于计算机视觉计算的硬件,它们的存储器层次结构可能是复杂的、分布式的、不完全一致的。OpenVX提出的目的就是为了在这些不同的硬件平台上最大限度地提供可移植性,通过它提供的抽象层,使当前和未来的硬件系统对计算机视觉度用程序源代码的影响微乎其微。
OpenVX主要包含:一个预先定义的计算机视觉核函数(Kernel Function)库,基于图的执行模型及一组物理内存的抽象内存对象。在OpenVX中,计算机视觉算法或应用程序都被分解并抽象为一个有向无环图(DAG)而构成送个有向无环图的结点就是OpenVX中预定义的计算机视觉核函数,通过这些核函数的组合形成更加复杂的计算机视觉应用。
OpenVX如何实现加速的:这些核函数在计算机视觉应用中是很常用的而且适合于硬件加速,OpenVX通过对有向无环图中的结点函数进行加速,从而实现对整个算法或程序的加速。
image.png
OpenVX还定义了C的应用程序接口API,用于构建,验证,协调图的执行,以及及用于内存对象的访问。
简单OpenVX demo示例:

  1. // init
  2. vx_context context = vxCreateContext();//创建openvx上下文
  3. vx_graph graph = vxCreateGraph( context );//基于上下文创建图
  4. vx_image input = vxCreateImage( context, 640, 480, VX_DF_IMAGE_U8 );//基于上下文创建image变量
  5. vx_image output = vxCreateImage( context, 640, 480, VX_DF_IMAGE_U8 );
  6. vx_image intermediate = vxCreateVirtualImage( graph, 640, 480, VX_DF_IMAGE_U8 );//在图中创建虚拟image变量
  7. vx_node F1 = vxF1Node( graph, input, intermediate );
  8. vx_node F2 = vxF2Node( graph, intermediate, output );
  9. vxVerifyGraph( graph );//校验graph中的node是否正常
  10. vxProcessGraph( graph );//运行graph,这里会将刚才链接进来的所有Node的func执行一次

image.png

一、OpenVX中的对象

OpenVX中的对象(Object)是对OpenVX所涉及的一些概念和内容的抽象,主要分为两大类:OpenVX框架对象(Framework Objects)和OpenVX数据对象(Data Objects)。

1.1 框架对象(Framework Objects)

OpenVX框架对象主要包括:Context ,Kernel,Parameter ,Node ,Graph 。

1.1.1 Context

Context是上下文环境,负责管理OpenVX中的其他对象和系统资源(程序运行状态与相关资源的合集)。Context对象是所有OpenVX对象的作用域,所有数据对象以及及其他的框架对象都存在于上下文Context中。Context对象在其他所有对象上都保持引用计数,当要释放对象的引用时,必须对其引用计数进行判断并进行垃圾回收。

1.1.2 Kernel

在OpenVX中,核函数Kernel是对一个计算机视觉功能的抽象表示,而不是指一个操作系统内核,一个核函数能够完成一个独立的函数功能,比如Kernel:Gaussian Filter可以完成对图像高斯滤波处理。OpenVX中预定义的核函数有40个,这40个核函数在计算机视觉应用中经常会被用到,但仅仅用送40个预定义的核函数还不能完全表示所有的计算机视觉应用。为此,OpenVX中提供了一个扩展,即用户可以自定义核函数。用户自定义的核函数的使用与OpenVX中预定义的核函数一样,用户自定义的核函数也可以实例化为一个结点,通过设置数据依赖关系与其他结点连接形成一个有向无环图。

1.1.3 Parameter

Parameter是对核函数之间互相传递的数据的抽象,Parameter具有一定的方向性,可以是输入,输出或双向的,Parameter会被传递给核函数进行处理,并在核函数之间进行传递。

1.1.4 Node

在OpenVX中,一个结点Node是一个核函数的实例化,结点中包含了核函数的信息、核函数执行时的参数以及其他状态信息,Node之间通过数据依赖关系可连接形成Graph。

1.1.5 Graph

计算机视觉应用往往可以分解成若干个小一点的函数模块,这些函数模块之间存在着数据依赖关系,按照模块之间的数据依赖关系(Node)就可把这些模块连接形成一个有向无环图,Graph就是对这个有向无环图的抽象,用Graph能够清楚地表示计算机视觉的处理流程。
image.png

1.1.6 node与kernel的关系

在 OpenVX 中,把参数定义初始化好后的 kernel 叫做 node,因此,node 就是 kernel 的一个实例化——-即拥有指定参数的 kernel。

1.2 数据对象(Data Objects)

数据对象描述了OpenVX中的一些数据类型,如Array 表示数组,其数组元素的类型可以是基本类型也可是结构类型。Array,ImageTensor 等。
image.png
vx_user_data_object()用户数据对象。用户数据对象是其他数据结构的强类型容器。vxCreateUserDataObject()定义为:

  1. VX_API_ENTRY vx_user_data_object VX_API_CALL vxCreateUserDataObject ( vx_context context,
  2. const vx_char * type_name,
  3. vx_size size, //The number of bytes required to store this instance of the user data object.
  4. const void * ptr //The pointer to the initial value of the user data object.
  5. )

使用示例:

  1. captureObj->config = vxCreateUserDataObject(context, "tivx_capture_params_t", sizeof(tivx_capture_params_t), &captureObj->params);
  2. //创建对用户数据对象的引用。用户数据对象可用于将用户内核定义的数据结构或内存块作为参数传递给用户内核。

1.2.1 不透明(opaque)对象

用户无法直接访问数据,只能通过已定义的 API 操作它们,比如Image对象需要通过vxMapImagePatch()vxUnmapImagePatch()函数来访问数据。以上数据都是不透明对象。
Image对象的数据访问示例代码:

  1. vx_status status = VX_SUCCESS;
  2. void *base_ptr = NULL;
  3. vx_uint32 width = 640, height = 480, plane = 0;
  4. vx_image image = vxCreateImage(context, width, height, VX_DF_IMAGE_U8);
  5. vx_rectangle_t rect;
  6. vx_imagepatch_addressing_t addr;
  7. vx_map_id map_id;
  8. rect.start_x = rect.start_y = 0;
  9. rect.end_x = rect.end_y = PATCH_DIM;
  10. status = vxMapImagePatch(image, &rect, plane, &map_id,
  11. &addr, &base_ptr,
  12. VX_READ_AND_WRITE, VX_MEMORY_TYPE_HOST, 0);
  13. if (status == VX_SUCCESS)
  14. {
  15. vx_uint32 x,y,i,j;
  16. vx_uint8 pixel = 0;
  17. /* a couple addressing options */
  18. /* use linear addressing function/macro */
  19. for (i = 0; i < addr.dim_x*addr.dim_y; i++) {
  20. vx_uint8 *ptr2 = vxFormatImagePatchAddress1d(base_ptr,
  21. i, &addr);
  22. *ptr2 = pixel;
  23. }
  24. /* 2d addressing option */
  25. for (y = 0; y < addr.dim_y; y+=addr.step_y) {
  26. for (x = 0; x < addr.dim_x; x+=addr.step_x) {
  27. vx_uint8 *ptr2 = vxFormatImagePatchAddress2d(base_ptr,
  28. x, y, &addr);
  29. *ptr2 = pixel;
  30. }
  31. }
  32. /* direct addressing by client
  33. * for subsampled planes, scale will change
  34. */
  35. for (y = 0; y < addr.dim_y; y+=addr.step_y) {
  36. for (x = 0; x < addr.dim_x; x+=addr.step_x) {
  37. vx_uint8 *tmp = (vx_uint8 *)base_ptr;
  38. i = ((addr.stride_y*y*addr.scale_y) /
  39. VX_SCALE_UNITY) +
  40. ((addr.stride_x*x*addr.scale_x) /
  41. VX_SCALE_UNITY);
  42. tmp[i] = pixel;
  43. }
  44. }
  45. /* more efficient direct addressing by client.
  46. * for subsampled planes, scale will change.
  47. */
  48. for (y = 0; y < addr.dim_y; y+=addr.step_y) {
  49. j = (addr.stride_y*y*addr.scale_y)/VX_SCALE_UNITY;
  50. for (x = 0; x < addr.dim_x; x+=addr.step_x) {
  51. vx_uint8 *tmp = (vx_uint8 *)base_ptr;
  52. i = j + (addr.stride_x*x*addr.scale_x) /
  53. VX_SCALE_UNITY;
  54. tmp[i] = pixel;
  55. }
  56. }
  57. /* this commits the data back to the image.程序访问内存位置失效
  58. */
  59. status = vxUnmapImagePatch(image, map_id);
  60. }
  61. vxReleaseImage(&image);

1.2.2 虚拟数据对象(Virtual Data Objects)

如果不需要对图执行过程中所产生的临时数据对象进行访问,那么可以将临时数据对象创建为虚拟数据对象(Virtual Data Objects)。虚拟数据对象可以像非虚拟数据对象那样连接图中的结点,但虚拟数据对象还是有着不同之处:

  • 虚拟数据对象是无法被访问的,OpenVX中不提供用于虚拟数据对象访问的接口
  • 虚拟数据对象的作用域仅限于它存在的那个图中,不能在以外的范围被使用。

image.png

1.2.3 Reference

Reference是OpenVX中最基本的类型,可以完成对其他数据类型的引用,其他所有类型都能安全地转换成该类型,也就是说其他任何类型都继承于Reference(用面向对象的观点),在实现过程中,其他的数据对象都要包含一个Reference对象。为了查询对象的属性或将对象作为参数传递给采用 \ref vx_reference 类型的函数。
对node状态查询:

  1. vx_status app_create_graph_scaler(vx_context context, vx_graph graph, ScalerObj *scalerObj)
  2. {
  3. vx_status status = VX_SUCCESS;
  4. vx_image input = (vx_image)vxGetObjectArrayItem((vx_object_array)scalerObj->input.arr[0], 0);
  5. vx_image output1 = (vx_image)vxGetObjectArrayItem((vx_object_array)scalerObj->output1.arr[0], 0);
  6. vx_image output2 = (vx_image)vxGetObjectArrayItem((vx_object_array)scalerObj->output2.arr[0], 0);
  7. scalerObj->node = tivxVpacMscScaleNode(graph, input, output1, output2, NULL, NULL, NULL);
  8. status = vxGetStatus((vx_reference)scalerObj->node);//
  9. if(status == VX_SUCCESS)
  10. {
  11. status = vxSetNodeTarget(scalerObj->node, VX_TARGET_STRING, TIVX_TARGET_VPAC_MSC1);
  12. }
  13. if(status == VX_SUCCESS)
  14. {
  15. status = vxSetReferenceName((vx_reference)scalerObj->node, "ScalerNode");
  16. }
  17. vx_bool replicate[] = { vx_true_e, vx_true_e, vx_true_e, vx_false_e, vx_false_e, vx_false_e};
  18. if(status == VX_SUCCESS)
  19. {
  20. status = vxReplicateNode(graph, scalerObj->node, replicate, 6);
  21. }
  22. if(status == VX_SUCCESS)
  23. {
  24. status = vxReleaseImage(&input);
  25. }
  26. if(status == VX_SUCCESS)
  27. {
  28. status = vxReleaseImage(&output1);
  29. }
  30. if(status == VX_SUCCESS)
  31. {
  32. status = vxReleaseImage(&output2);
  33. }
  34. return status;
  35. }

对vx_image状态查询:

  1. status = vxGetStatus((vx_reference)in_img);

作为函数参数的使用:

  1. status = vxGraphParameterEnqueueReadyRef(obj->graph, imgMosaicObj->graph_parameter_index, (vx_reference*)&imgMosaicObj->output_image[obj->enqueueCnt], 1);

1.2.4 数据对象的存储

在OpenVX中数据对象被存储在host memory或accelerator memory。同样,data movement存在于应用中,需要实际经验来权衡。

二、基于图的执行模型

在OpenVX中,计算机视觉的处理过程往往被分解为一系列的核函数,这些核函数之间存在着一定的数据传递关系,即一个核函数的输出可能会传递给另一个核函数作为它的输入进行处理,也就是数据依赖关系。可把核函数当作结点,而根据核函数之间的数据依赖关系将这些结点连接了起来,这样就形成了一个有向无环图,这个有向无环图就表示了升算机视觉的处理过程,能够清楚地反应整个处理过程中的各个模块和它们之间的数据依赖关系。

2.1 图的构建

用于表示执行过程的有向无环图由一组结点N1-Nn,和一组数据对象D1-Dn组成。
image.png
image.png

  1. vx_context context = vxCreateContext();
  2. vx_image images[] = {
  3. vxCreateImage(context, 640, 480, VX_DF_IMAGE_UYVY),
  4. vxCreateImage(context, 640, 480, VX_DF_IMAGE_S16),
  5. vxCreateImage(context, 640, 480, VX_DF_IMAGE_U8),
  6. };
  7. vx_graph graph = vxCreateGraph(context);
  8. vx_image virts[] = {
  9. vxCreateVirtualImage(graph, 0, 0, VX_DF_IMAGE_VIRT),
  10. vxCreateVirtualImage(graph, 0, 0, VX_DF_IMAGE_VIRT),
  11. vxCreateVirtualImage(graph, 0, 0, VX_DF_IMAGE_VIRT),
  12. vxCreateVirtualImage(graph, 0, 0, VX_DF_IMAGE_VIRT),
  13. };
  14. vxChannelExtractNode(graph, images[0], VX_CHANNEL_Y, virts[0]),
  15. vxGaussian3x3Node(graph, virts[0], virts[1]),
  16. vxSobel3x3Node(graph, virts[1], virts[2], virts[3]),
  17. vxMagnitudeNode(graph, virts[2], virts[3], images[1]),
  18. vxPhaseNode(graph, virts[2], virts[3], images[2]),
  19. status = vxVerifyGraph(graph);
  20. if (status == VX_SUCCESS)
  21. {
  22. status = vxProcessGraph(graph);
  23. }
  24. vxReleaseContext(&context); /* this will release everything */

每一个结点都代表一个OpenVX中的核函数,数据对象用来表示核函数之间的数据依赖关系。基于图的执行模型有很高的确定性,当计算机视觉应用的执行图创建完成以后,整个处理过程、核函数执行的先后次序、数据流就已经确定了。

2.2 图的验证

图模型在被执行之前,必须要经过严格的验证,以满足图执行的条件,从而保证图能够安全的执行,并且可消除一些运行时的开销(例如:参数的检查)。首先要对结点的参数进行验证,验证图中每一个结点的每个参数的方向(输入、输出或双向)和类型是否与结点所代表的核函数所需的一致,如果参数是一个标量且核函数对参数的范围有明确的要求,则还要验证参数是否处于范围么内。其次是对数据对象的验证,主要是验证数据对象是否单写入,如果被多个结点同时写入,则会发生数据冲突。最后还要对图连接进行验证,以确保图是有向无环图,图中若出现环路,则导致图执行时不能正确结束,而陷入死循环中。被验证过且无错误的图可以被多次执行,如果图的连接或结点的参数发生变化,就要对图重新进行验证,验证正确后才能执行。

2.3 图的执行

基于图的执行模型中,对图的执行是按照结点之间的数据依赖关系依次执行每个结点。结点的执行要遵循"点火原则(firing rule)",即图中一个结点的所有输入数据都准备就绪后,该结点才可以被执行,结点被执行后,消耗了每个输入数据对象的数据,并产生了每个输出数据对象的数据。图的执行有两种:

  • 同步阻塞(Synchronous blocking。由vxProcessGraph完成,此过程中vxProcessGraph将阻塞,无法运行或者处理它的任务,直到图完成
  • 异步(Asynchronous)。由vxScheduleGraphvxWaitGraph完成,不会等等这个任务的结束才开始下一个任务,开启过后立即往后执行下一个任务

2.4 生命周期

2.4.1 Context生命周期

image.png

2.4.2 Graph生命周期

image.png
Deconstruction - Graphs are released via . All Nodes in the Graph are released.vxReleaseGraph

2.4.4 数据生命周期

OpenVX中,所有的数据都有一样的生命周期。以Image为例:
image.png

三、TI OpenVX (TIOVX)

该软件包包含 TI 的 OpenVX 实现。 它包括用于 VPAC, DMPAC, CSI2RX, Display, TIDL, Video decode的 OpenVX 节点。TIOVX 完全符合 OpenVX v1.1 规范。TIOVX 还为 C66x DSP 提供优化的 OpenVX 内核。TIOVX 的顶层框图如下所示:
image.png
该图的组成部分描述如下:

TIVOX模块 描述
Khronos Conformance Test 来自 Khronos 的 OpenVX 一致性测试,以确保实现根据规范实现 OpenVX
TI Extension Conformance Test 来自 TI 的其他测试用例,用于测试 TI 扩展接口
OpenVX API Khronos 定义的 OpenVX API
TIOVX API TI 扩展和附加 API,以便在 TI 平台上高效使用 OpenVX
TIOVX Framework TI 对 OpenVX 规范的实施。该层与底层 SoC、OS 平台无关
TIOVX Platform 该层将 TIOVX 框架绑定到特定平台。例如,用于 TDA4x SOC 的处理器 SDK 平台。该层还将 TIOVX 框架绑定到特定的操作系统,如 Linux 或 TI-RTOS。
TIOVX Kernel Wrapper 内核包装器允许 TI 和客户将本机实现的核函数集成到 TIOVX 框架中。
Examples 这些示例展示了 TIOVX 与其他系统级组件的使用情况。这些不包含在 TIOVX 包中。用户应参考 SDK 了解这些示例。
User Kernels / Target Kernels 用户核函数是使用标准 Khronos OpenVX API 在主机 CPU 上集成用户核函数的接口。目标核函数是 TI 特定的接口,用于在目标 CPU 上集成用户核函数,例如 DSP

image.png

3.1 tiovx目录结构

  1. lyw@lyw:~/PSDK0010/ti-processor-sdk-rtos-j721e-evm-07_01_00_11/tiovx$ tree -L 1
  2. .
  3. ├── build_flags.mak
  4. ├── concerto //Makefile 构建基础设施
  5. ├── conformance_tests //添加的内核仅用于测试目的(用于测试目标内核 API)
  6. ├── docs //用户文档
  7. ├── include //Khronos OpenVX 接口,TI OpenVX 扩展接口
  8. ├── kernels //OpenVX 定义的内核
  9. ├── kernels_j7 //TI 供应商特定的内核,包括内核的单元测试
  10. ├── lib
  11. ├── Makefile
  12. ├── out //构建生成的文件和可执行文件
  13. ├── prebuilt_libs
  14. ├── psdkra_tools_path.mak
  15. ├── source //TI OpenVX implementation
  16. ├── target.mak
  17. ├── tiovx_release_notes.html
  18. ├── tools //OpenVX 用例和内核包装器代码生成工具
  19. ├── tutorial //TI OpenVX 教程
  20. └── utils //Helpful utilities like image readers

3.2 TIOVX支持的核函数

TIOVX支持的核函数包括OpenVX标准核函数与TI拓展的核函数。如果需要不同的目标,可以使用vxSetNodeTarget() API从下面指示的可用目标中进行选择。

  1. vx_status VX_API_CALL vxSetNodeTarget ( vx_node node,
  2. vx_enum target_enum,
  3. const char * target_string
  4. )
  1. postProcObj->node = tivxPixelVizNode(graph,
  2. postProcObj->viz_kernel,
  3. postProcObj->viz_config,
  4. out_args,
  5. input_image,
  6. postProcObj->num_output_tensors,
  7. output_tensors,
  8. output_images);
  9. APP_ASSERT_VALID_REF(postProcObj->node);
  10. vxSetNodeTarget(postProcObj->node, VX_TARGET_STRING, TIVX_TARGET_DSP2);
  11. vxSetReferenceName((vx_reference)postProcObj->node, "PostProcNode");
  1. /*! \brief Name for DSP target class, instance 1
  2. *
  3. * Assigned to C66_0 core
  4. *
  5. * \ingroup group_tivx_ext_targets
  6. */
  7. #define TIVX_TARGET_DSP1 "DSP-1"
  8. /*! \brief Name for DSP target class, instance 2
  9. *
  10. * Assigned to C66_1 core
  11. *
  12. * \ingroup group_tivx_ext_targets
  13. */
  14. #define TIVX_TARGET_DSP2 "DSP-2"
  15. /*! \brief Name for EVE target class, instance 1
  16. * \ingroup group_tivx_ext_targets
  17. */
  18. #define TIVX_TARGET_EVE1 "EVE-1"
  19. /*! \brief Name for EVE target class, instance 1
  20. * \ingroup group_tivx_ext_targets
  21. */
  22. #define TIVX_TARGET_EVE2 "EVE-2"
  23. /*! \brief Name for EVE target class, instance 1
  24. * \ingroup group_tivx_ext_targets
  25. */
  26. #define TIVX_TARGET_EVE3 "EVE-3"
  27. /*! \brief Name for EVE target class, instance 1
  28. * \ingroup group_tivx_ext_targets
  29. */
  30. #define TIVX_TARGET_EVE4 "EVE-4"
  31. /*! \brief Name for A15 target class, core 0
  32. * \ingroup group_tivx_ext_targets
  33. */
  34. #define TIVX_TARGET_A15_0 "A15-0"
  35. /*! \brief Name for IPU1 target class, core 0
  36. *
  37. * \if DOCS_J7
  38. * Assigned to MCU2_0 core
  39. * \else
  40. * Assigned to M4_0 core
  41. * \endif
  42. *
  43. * \ingroup group_tivx_ext_targets
  44. */
  45. #define TIVX_TARGET_IPU1_0 "IPU1-0"
  46. /*! \brief Name for IPU1 target class, core 1
  47. *
  48. * \if DOCS_J7
  49. * Assigned to MCU2_1 core
  50. * \else
  51. * Assigned to M4_1 core
  52. * \endif
  53. *
  54. * \ingroup group_tivx_ext_targets
  55. */
  56. #define TIVX_TARGET_IPU1_1 "IPU1-1"
  57. /*! \brief Name for IPU2 target class
  58. * \ingroup group_tivx_ext_targets
  59. */
  60. #define TIVX_TARGET_IPU2 "IPU2"

ti扩展(实现)核函数包括以下:
image.png

3.2.1 tivxCaptureNode()

用于创建相机捕获节点,接口定义为:

  1. VX_API_ENTRY vx_node VX_API_CALL tivxCaptureNode ( vx_graph graph,
  2. vx_user_data_object input,
  3. vx_object_array output
  4. )

接收用户数据对象类型tivx_capture_params_t以配置一个或多个捕获通道。输出是vx_object_array类型,它包含vx_image或tivx_raw_image数据类型的数组。vx_image用于如果所述传感器输出处理的图像格式,而tivx_raw_image用于原始传感器输出。tivx_capture_params_t定义:

  1. typedef struct
  2. {
  3. uint32_t enableCsiv2p0Support;
  4. uint32_t numDataLanes;
  5. uint32_t dataLanesMap[4];
  6. uint32_t laneBandSpeed;
  7. } tivx_capture_inst_params_t;
  8. typedef struct
  9. {
  10. tivx_capture_inst_params_t instCfg[TIVX_CAPTURE_MAX_INST]; //CSI2Rx Instance configuration
  11. uint32_t instId[TIVX_CAPTURE_MAX_INST]; //CSI2Rx 实例 ID,0:CSIRx0 1:CSIRx0
  12. uint32_t numInst; //当前捕获节点中的实例数
  13. uint32_t numCh; //要在当前 Node 实例上处理的通道数
  14. uint32_t chVcNum[TIVX_CAPTURE_MAX_CH]; //每个通道的虚拟频道编号
  15. uint32_t chInstMap[TIVX_CAPTURE_MAX_CH]; //每个通道的实例 ID
  16. uint32_t timeout; //所摄像机的总超时(以毫秒为单位)。如果超过此超时时间,则未接收到的摄像机已死。
  17. uint32_t timeoutInitial; //捕获节点第一次失败的初始失败超时(以毫秒为单位)。一旦超过此超时,将使用“超时”参数。
  18. } tivx_capture_params_t;

3.2.2 tivxDisplayNode()

用于创建DSS显示节点,接口:

  1. VX_API_ENTRY vx_node VX_API_CALL tivxDisplayNode ( vx_graph graph,
  2. vx_user_data_object configuration,
  3. vx_image image
  4. )

其中,configuration类型为单个显示参数结构的输入用户数据对象tivx_display_params_t,定义为:

  1. typedef struct {
  2. uint32_t opMode; //操作模式 ,两种模式下运行:它可以用于仅重复显示一个缓冲区或应用程序每次 VSYNC 提供一个新缓冲区
  3. uint32_t pipeId; // 管道标识,0:VID1、1:VIDL1、2:VID2 和 3:VIDL2
  4. uint32_t outWidth; //显示输出时图片的水平尺寸
  5. uint32_t outHeight; //显示输出时图片的垂直尺寸
  6. uint32_t posX; //视频缓冲区的 X 位置
  7. uint32_t posY; //视频缓冲区的 Y 位置
  8. } tivx_display_params_t;

实际使用例:
configuration初始化

  1. vx_status app_init_display(vx_context context, DisplayObj *displayObj, char *objName)
  2. {
  3. vx_status status = VX_SUCCESS;
  4. if ((vx_true_e == tivxIsTargetEnabled(TIVX_TARGET_DISPLAY1)) && (displayObj->display_option == 1))
  5. {
  6. status = VX_SUCCESS;
  7. }
  8. else
  9. {
  10. status = VX_FAILURE;
  11. }
  12. if(VX_SUCCESS == status)
  13. {
  14. memset(&displayObj->disp_params, 0, sizeof(tivx_display_params_t));
  15. displayObj->disp_params.opMode = TIVX_KERNEL_DISPLAY_ZERO_BUFFER_COPY_MODE;//TIVX_KERNEL_DISPLAY_BUFFER_COPY_MODE;
  16. displayObj->disp_params.pipeId = 0;
  17. displayObj->disp_params.outWidth = DISPLAY_WIDTH;
  18. displayObj->disp_params.outHeight = DISPLAY_HEIGHT;
  19. displayObj->disp_params.posX = (1920-DISPLAY_WIDTH)/2;
  20. displayObj->disp_params.posY = (1080-DISPLAY_HEIGHT)/2;
  21. displayObj->disp_params_obj = vxCreateUserDataObject(context, "tivx_display_params_t", sizeof(tivx_display_params_t), &displayObj->disp_params);
  22. status = vxGetStatus((vx_reference)displayObj->disp_params_obj);
  23. }
  24. return status;
  25. }

创建graph

  1. vx_status app_create_graph_display(vx_graph graph, DisplayObj *displayObj, vx_image disp_image)
  2. {
  3. vx_status status = VX_SUCCESS;
  4. if ((vx_true_e == tivxIsTargetEnabled(TIVX_TARGET_DISPLAY1)) && (displayObj->display_option == 1))
  5. {
  6. displayObj->disp_node = tivxDisplayNode(graph, displayObj->disp_params_obj, disp_image);//
  7. vxSetNodeTarget(displayObj->disp_node, VX_TARGET_STRING, TIVX_TARGET_DISPLAY1);
  8. vxSetReferenceName((vx_reference)displayObj->disp_node, "display_node");
  9. status = vxGetStatus((vx_reference)displayObj->disp_node);
  10. }
  11. return status;
  12. }

3.2.3 tivxTIDLNode()

用于创建TIDL节点,接口定义为:

  1. VX_API_ENTRY vx_node VX_API_CALL tivxTIDLNode ( vx_graph graph,
  2. vx_kernel kernel,
  3. vx_reference appParams[],
  4. vx_tensor input_tensors[],
  5. vx_tensor output_tensors[]
  6. )

其中appParams是一个包含 5 个参数的数组:

  • config vx_user_data_object 对应配置的类型(命名字符串:sTIDL_IOBufDesc_t)
  • 网络对应的网络 vx_user_data_object 类型(命名字符串:TIDL_network)
  • createParams:vx_user_data_object 类型对应的结构体 TIDL_CreateParams。此结构包含创建时参数,仅用于初始化节点一次。目前成员 quantHistoryParam1、quantHistoryParam2、quantMargin 需要由应用程序初始化。
  • inArgs:对应结构体TIDL_InArgs的vx_user_data_object类型。用于传递在执行图时节点的进程调用期间生效的输入参数。这些参数可以在图的每一帧执行之前更新。当前参数列表为空。所以 inArgs 只是未来扩展的占位符。
  • outArgs:与结构体TIDL_outArgs对应的vx_user_data_object类型。用于收集图执行时节点进程调用返回的输出参数。这些参数在图形的每一帧执行后刷新。当前参数列表为空。所以 outArgs 只是未来扩展的占位符。

其中kernel为:

  1. tidlObj->kernel = tivxAddKernelTIDL(context, tidlObj->num_input_tensors, tidlObj->num_output_tensors);

TIDL params structure:

  1. typedef struct{
  2. /** Checksum placeholder for TIDL config params */
  3. vx_uint8 config_checksum[TIVX_TIDL_J7_CHECKSUM_SIZE];
  4. /** Checksum placeholder for TIDL network params */
  5. vx_uint8 network_checksum[TIVX_TIDL_J7_CHECKSUM_SIZE];
  6. /** Flag to indicate if config params checksum is to be computed or not */
  7. vx_uint32 compute_config_checksum;
  8. /** Flag to indicate if network params checksum is to be computed or not */
  9. vx_uint32 compute_network_checksum;
  10. /**TIDL input/output buffer descriptor*/
  11. sTIDL_IOBufDesc_t ioBufDesc;
  12. }tivxTIDLJ7Params;

sTIDL_IOBufDesc_t:定义给模型所需的输入和输出缓冲区描述符。

  1. typedef struct
  2. {
  3. /** Number of Input buffer required by the Layer group */
  4. int32_t numInputBuf;
  5. /** Number of Output buffer required by the Layer group */
  6. int32_t numOutputBuf;
  7. /** Input Tensor format @ref eTIDL_inDataFormat */
  8. int32_t inDataFormat[TIDL_MAX_ALG_IN_BUFS];
  9. /** Input Tensor resize Type @ref eTIDL_inResizeType */
  10. int32_t inResizeType[TIDL_MAX_ALG_IN_BUFS];
  11. /** Resize width for compressed image before crop */
  12. int32_t resizeWidth[TIDL_MAX_ALG_IN_BUFS];
  13. /** Resize Height for compressed image before crop */
  14. int32_t resizeHeight[TIDL_MAX_ALG_IN_BUFS];
  15. /** Feature width of each input buffer */
  16. int32_t inWidth[TIDL_MAX_ALG_IN_BUFS];
  17. /** Feature Height of each input buffer */
  18. int32_t inHeight[TIDL_MAX_ALG_IN_BUFS];
  19. /** Number of channels in each input buffer */
  20. int32_t inNumChannels[TIDL_MAX_ALG_IN_BUFS];
  21. /** Minimum Channel pitch for the input tensor */
  22. int32_t inChannelPitch[TIDL_MAX_ALG_IN_BUFS];
  23. /** Left zero padding required for each input buffer */
  24. int32_t inPadL[TIDL_MAX_ALG_IN_BUFS];
  25. /** Top zero padding required for each input buffer */
  26. int32_t inPadT[TIDL_MAX_ALG_IN_BUFS];
  27. /** Right zero padding required for each input buffer */
  28. int32_t inPadR[TIDL_MAX_ALG_IN_BUFS];
  29. /** Bottom zero padding required for each input buffer */
  30. int32_t inPadB[TIDL_MAX_ALG_IN_BUFS];
  31. /** Number of extra channels required in each input buffer*/
  32. int32_t inPadCh[TIDL_MAX_ALG_IN_BUFS];
  33. /** Element type of each input buffer \ref eTIDL_ElementType */
  34. int32_t inElementType[TIDL_MAX_ALG_IN_BUFS];
  35. /** Element type of each input data buffer (applicable only for inFileFormat 1 or 3) \ref eTIDL_ElementType */
  36. int32_t rawDataInElementType[TIDL_MAX_ALG_IN_BUFS];
  37. /** Data ID as per Net structure for each input buffer */
  38. int32_t inDataId[TIDL_MAX_ALG_IN_BUFS];
  39. /** Tensor scale for input data */
  40. float32_tidl inTensorScale[TIDL_MAX_ALG_IN_BUFS];
  41. /** In Tensor name in the original input networks */
  42. int8_t inDataName[TIDL_MAX_ALG_IN_BUFS][TIDL_STRING_SIZE];
  43. /** Feature width of each output buffer */
  44. int32_t outWidth[TIDL_MAX_ALG_OUT_BUFS];
  45. /** Feature Height of each output buffer */
  46. int32_t outHeight[TIDL_MAX_ALG_OUT_BUFS];
  47. /** Number of channels in each output buffer */
  48. int32_t outNumChannels[TIDL_MAX_ALG_OUT_BUFS];
  49. /** Channel pitch for the output tensor */
  50. int32_t outChannelPitch[TIDL_MAX_ALG_OUT_BUFS];
  51. /** Left zero padding required for each output buffer */
  52. int32_t outPadL[TIDL_MAX_ALG_OUT_BUFS];
  53. /** top zero padding required for each output buffer */
  54. int32_t outPadT[TIDL_MAX_ALG_OUT_BUFS];
  55. /** Right zero padding required for each output buffer */
  56. int32_t outPadR[TIDL_MAX_ALG_OUT_BUFS];
  57. /** Bottom zero padding required for each output buffer */
  58. int32_t outPadB[TIDL_MAX_ALG_OUT_BUFS];
  59. /** Number of extra channels required in each output buffer*/
  60. int32_t outPadCh[TIDL_MAX_ALG_OUT_BUFS];
  61. /** Element type of each output buffer \ref eTIDL_ElementType */
  62. int32_t outElementType[TIDL_MAX_ALG_OUT_BUFS];
  63. /** Data ID as per Net structure for each output buffer */
  64. int32_t outDataId[TIDL_MAX_ALG_OUT_BUFS];
  65. /** Out Tensor name in the original input networks */
  66. int8_t outDataName[TIDL_MAX_ALG_OUT_BUFS][TIDL_STRING_SIZE];
  67. } sTIDL_IOBufDesc_t;
  1. tidlObj->config = readConfig(context, &tidlObj->config_file_path[0], &num_input_tensors, &num_output_tensors, &tidlObj->config_checksum[0]);

sTIDL_Network_t:这个结构定义了TIDL中CNN/Deep learning net的参数

  1. typedef struct {
  2. /** Version Number for TIDl net format */
  3. int32_t netVersion;
  4. /** Device for which the network has been compiled*/
  5. int32_t deviceName;
  6. /** Number of layers in the network including the input and output data Layers */
  7. int32_t numLayers;
  8. /** Size of compute layer weight parameters in bytes */
  9. int32_t weightsElementSize;
  10. /** Size of PRelU layer weight/slope parameters in bytes */
  11. int32_t slopeElementSize;
  12. /** Size of compute layer Bias parameters in bytes */
  13. int32_t biasElementSize;
  14. /** Size of compute layer input and data buffers in bytes */
  15. int32_t dataElementSize;
  16. /** Size of compute layer intermediate data in bytes */
  17. int32_t interElementSize;
  18. /** Variable to indicate different types of quantization Styles */
  19. int32_t quantizationStyle;
  20. /** Variable to indicate different types of calibration options. Please
  21. refer eTIDL_CalibOption for various options available. Even though
  22. user can try multiple options following options are recommended :
  23. 0 : Simple calibration ( Default)
  24. 1 : Histogram based activation range collection
  25. 7 : Advanced bias calibration*/
  26. int32_t calibrationOption;
  27. /** Calibration specific parameters, parameters are specific to
  28. calibration method and are only applicable if the corresponding
  29. option is set by user as part of eTIDL_CalibOption*/
  30. sTIDL_CalibParams_t calibrationParams;
  31. /** dataFlow Info from offline Analysis */
  32. int32_t dataFlowInfo;
  33. /** Quantization statistics availability - Updated with tidl_quant_stats_tool */
  34. int32_t isQuantStatsAvailable;
  35. /** Reserved Parameters */
  36. int32_t reserved;
  37. /** Layer Parameter for each layer */
  38. sTIDL_Layer_t TIDLLayers[TIDL_NUM_MAX_LAYERS];
  39. }sTIDL_Network_t;
  1. tidlObj->network = readNetwork(context, &tidlObj->network_file_path[0], &tidlObj->network_checksum[0]);

TIDL_CreateParams:这个结构体包含了 TIDL 库在创建时的所有参数

  1. typedef struct
  2. {
  3. /** Common parameters for all ivision based modules */
  4. IVISION_Params visionParams;
  5. /** Group ID of the each layer */
  6. int32_t currLayersGroupId;
  7. /** Indicates Whether the input buffers are padded or Not. */
  8. int32_t isInbufsPaded;
  9. /** Optimization level for External memory used bu TIDL */
  10. int32_t optimiseExtMem;
  11. /** Expansion factor used for range of activation while quantization */
  12. float32_tidl quantRangeExpansionFactor;
  13. /** Update factor used for range of activation while quantization */
  14. float32_tidl quantRangeUpdateFactor;
  15. /** Level for debug messages */
  16. int32_t traceLogLevel;
  17. /** Level for debug trace dumps of tensors and other data buffers */
  18. int32_t traceWriteLevel;
  19. /** Reserved control for debug */
  20. int32_t reservedCtrl;
  21. /** control for selecting different flows. Applicable only for advanced user */
  22. int32_t flowCtrl;
  23. /** trace files base Name */
  24. void * traceBaseName;
  25. /** UDMA driver object pointer */
  26. /* Note : All the addresses given to TIDL are assumed to be virtual addresses.
  27. If udma driver is initialized with virtual to physical function pointer initPrms.virtToPhyFxn
  28. then the same function will be used for this conversion in TIDL whereever it is needed.*/
  29. void * udmaDrvObj;
  30. /** Pointer to TILD Model/Network */
  31. sTIDL_Network_t * net;
  32. /** Call back Function pointer to Write Log*/
  33. int32_t(*TIDLVprintf)(const char * format, va_list arg);
  34. /** Call back Function pointer to Write Binary data to a file*/
  35. int32_t(*TIDLWriteBinToFile)(const char * fileName, void * addr, int32_t size);
  36. /** Call back Function pointer to read data from a binary file */
  37. int32_t(*TIDLReadBinFromFile)(const char * fileName, void * addr, int32_t size);
  38. /**
  39. * \brief Custom Layer Process API.
  40. *
  41. * \param tidlHandle Pointer to the tidlHandle.
  42. * \param tidlLayer TIDL layer parameters. Refer #sTIDL_Layer_t for details.
  43. * \param inPtrs Input buffers to custom layer.
  44. * \param outPtrs Output buffers from custom layer.
  45. * \param params Custom layer parameters.
  46. * \param dmaUtilsContext Pointer to DMA Utils Handle. Current TIDL initializes 12
  47. * DMA channels and same can be used by custom layer
  48. * \param sysMems System Memory Handle.
  49. * \param execMode Execution mode of custom layer process API. Refer \ref
  50. * eTIDL_CustomLayerExecMode for details.
  51. * \param maxTensorScale Maximum value of tensor scale allowed.
  52. *
  53. * \return None
  54. */
  55. int32_t (*TIDL_CustomLayerProcess)(void * tidlHandle,
  56. sTIDL_Layer_t *tidlLayer,
  57. void *inPtrs[],
  58. void *outPtrs[],
  59. void *params,
  60. void *dmaUtilsContext,
  61. const sTIDL_sysMemHandle_t sysMems[TIDL_SYSMEM_MAX],
  62. int32_t execMode,
  63. float maxTensorScale);
  64. }
  65. TIDL_CreateParams;
  1. capacity = sizeof(TIDL_CreateParams);
  2. tidlObj->createParams = vxCreateUserDataObject(context, "TIDL_CreateParams", capacity, NULL );
  3. status = setCreateParams(context, tidlObj->createParams);

TIDL_InArgs:此结构包含在创建时控制小程序的所有参数

  1. typedef struct
  2. {
  3. /** Common inArgs for all ivision based modules */
  4. IVISION_InArgs iVisionInArgs;
  5. /** Number of input buffers */
  6. int32_t numInBufs;
  7. /** Scale factor for each input tensor */
  8. float32_tidl scale[TIDL_NUM_IN_BUFS];
  9. /** if enable layer level traces are generated in that particular process call */
  10. int32_t enableLayerPerfTraces;
  11. } TIDL_InArgs;
  1. vx_user_data_object inArgs;
  2. capacity = sizeof(TIDL_InArgs);
  3. inArgs = vxCreateUserDataObject(context, "TIDL_InArgs", capacity, NULL );
  4. tidlObj->in_args_arr = vxCreateObjectArray(context, (vx_reference)inArgs, NUM_CH);

TIDL_outArgs:此结构包含在创建时控制小程序的所有参数

  1. typedef struct
  2. {
  3. /** Common outArgs for all ivision based modules */
  4. IVISION_OutArgs iVisionOutArgs;
  5. /** Number of output buffers */
  6. int32_t numOutBufs;
  7. /** Scale factor for each output tensor */
  8. float32_tidl scale[TIDL_NUM_OUT_BUFS];
  9. } TIDL_outArgs;
  1. vx_user_data_object outArgs;
  2. capacity = sizeof(TIDL_outArgs);
  3. outArgs = vxCreateUserDataObject(context, "TIDL_outArgs", capacity, NULL );
  4. tidlObj->out_args_arr = vxCreateObjectArray(context, (vx_reference)outArgs, NUM_CH);

创建图:

  1. vx_status app_create_graph_tidl(vx_context context, vx_graph graph, TIDLObj *tidlObj, vx_object_array input_tensor_arr[])
  2. {
  3. vx_status status = VX_SUCCESS;
  4. vx_reference params[APP_TIDL_MAX_PARAMS];
  5. vx_tensor input_tensor[APP_MAX_TENSORS];
  6. vx_tensor output_tensor[APP_MAX_TENSORS];
  7. vx_int32 i;
  8. tidlObj->node = NULL;
  9. /* Initialize param array */
  10. initParam(params, APP_TIDL_MAX_PARAMS);
  11. /* The 1st param MUST be config array */
  12. addParam(params, (vx_reference)tidlObj->config);
  13. /* The 2nd param MUST be network tensor */
  14. addParam(params, (vx_reference)tidlObj->network);
  15. /* The 3rd param MUST be create params */
  16. addParam(params, (vx_reference)tidlObj->createParams);
  17. /* The 4th param MUST be inArgs */
  18. vx_user_data_object inArgs = (vx_user_data_object)vxGetObjectArrayItem((vx_object_array)tidlObj->in_args_arr, 0);
  19. addParam(params, (vx_reference)inArgs);
  20. vxReleaseUserDataObject(&inArgs);
  21. /* The 5th param MUST be outArgs */
  22. vx_user_data_object outArgs = (vx_user_data_object)vxGetObjectArrayItem((vx_object_array)tidlObj->out_args_arr, 0);
  23. addParam(params, (vx_reference)outArgs);
  24. vxReleaseUserDataObject(&outArgs);
  25. #ifdef APP_TIDL_TRACE_DUMP
  26. vx_user_data_object traceData = (vx_user_data_object)vxGetObjectArrayItem((vx_object_array)tidlObj->trace_data_arr, 0);
  27. addParam(params, (vx_reference)traceData);
  28. vxReleaseUserDataObject(&traceData);
  29. #else
  30. /* The 6th param MUST be NULL if trace data dump is not enabled */
  31. addParam(params, NULL);
  32. #endif
  33. /* Create TIDL Node */
  34. for(i = 0; i < tidlObj->num_input_tensors; i++)
  35. {
  36. input_tensor[i] = (vx_tensor)vxGetObjectArrayItem((vx_object_array)input_tensor_arr[i], 0);
  37. }
  38. for(i = 0; i < tidlObj->num_output_tensors; i++)
  39. {
  40. output_tensor[i] = (vx_tensor)vxGetObjectArrayItem((vx_object_array)tidlObj->output_tensor_arr[i], 0);
  41. }
  42. tidlObj->node = tivxTIDLNode(graph, tidlObj->kernel, params, input_tensor, output_tensor);
  43. status = vxGetStatus((vx_reference)tidlObj->node);
  44. vxSetReferenceName((vx_reference)tidlObj->node, "TIDLNode");
  45. for(i = 0; i < tidlObj->num_input_tensors; i++)
  46. {
  47. vxReleaseTensor(&input_tensor[i]);
  48. }
  49. for(i = 0; i < tidlObj->num_output_tensors; i++)
  50. {
  51. vxReleaseTensor(&output_tensor[i]);
  52. }
  53. return status;
  54. }

3.2.4 tivxVpacVissNode()

用于创建VPAC_VISS节点,将 RAW 图像传感器数据转换为经过处理的 YUV 或 RGB 图像。VISS 节点支持 5 个可选输出(输出 0 到输出 4),并且必须至少启用其中一个输出。接口为:

  1. VX_API_ENTRY vx_node VX_API_CALL tivxVpacVissNode ( vx_graph graph,
  2. vx_user_data_object configuration,//类型为单个 params 结构的输入对象tivx_vpac_viss_params_t。这些参数本质上定义了 VISS 内部的路径,用于选择输出格式。
  3. vx_user_data_object ae_awb_result,//(可选)类型为单个 params 结构的输入对象tivx_ae_awb_params_t。通常,此输入参数将来自 2A 算法节点。
  4. vx_user_data_object dcc_buf,//(可选)给定传感器的 DCC 调谐数据库 vx_user_data_object
  5. tivx_raw_image raw,//P12 或 U16 或 U8 格式的 RAW 输入图像(最多可包含 3 次曝光和元数据)
  6. vx_image output0,
  7. vx_image output1,
  8. vx_image output2,
  9. vx_image output3,
  10. vx_image output4,
  11. vx_user_data_object h3a_output,//(可选)AEWB/AF 输出。此输出用于获得 AEWB/AF 输出。tivx_h3a_data_t类型的用户数据对象用于 AEWB/AF 输出。一次只能启用和输出 AEWB 和 AF 之一。
  12. vx_distribution histogram //(可选)输出直方图。此直方图的 bin 数量固定为 256 位。为这个直方图分配的内存大小为 256 x sizeof(uint32_t),足以存储 256x20bit 直方图。
  13. )

tivx_vpac_viss_params_t :

  1. typedef struct {
  2. uint32_t sensor_dcc_id;//DCC 配置文件的标识符,直接传递给 DCC 解析器以获取 VISS 配置
  3. uint32_t use_case;//DCC文件中sensor配置中子设置对应的标识符,直接传递给DCC解析器获取VISS配置
  4. uint32_t mux_output0;//MUX以选择输出格式OUTPUT0的参数tivxVpacVissNode
  5. uint32_t mux_output1;//Mux 为tivxVpacVissNode的output1参数选择输出格式
  6. uint32_t mux_output2;
  7. uint32_t mux_output3;
  8. uint32_t mux_output4;
  9. uint32_t bypass_glbce;//启用/绕过 GLBCE 处理的标志:1:绕过 GLBCE,0:启用 GLBCE
  10. uint32_t bypass_nsf4;//标志来启用/旁路NSF4处理:1:绕过NSF4,0:允许NSF4如果h3a_aew_af的输出端口tivxVpacVissNode不是NULL,这提供H3A的输入源
  11. uint32_t h3a_in;//
  12. uint32_t h3a_aewb_af_mode;
  13. uint32_t ee_mode;//启用/禁用边缘增强器 (EE) 并选择要启用 EE 的亮度通道
  14. uint32_t chroma_mode;//当在mux_output1或mux_output3 中选择仅色度输出时,选择色度输出格式。
  15. uint32_t enable_ctx;
  16. uint32_t channel_id;
  17. } tivx_vpac_viss_params_t;

下表提供了此结构中相应 mux 值的不同输出支持的输出格式。请注意,仅当相应的输出图像设置为非空时才使用 mux 值,否则 mux 值将被忽略。
image.png
tivx_ae_awb_params_t:

  1. typedef struct {
  2. uint32_t h3a_source_data;//表示该数据对应的源数据:
  3. uint32_t exposure_time;//曝光时间
  4. uint32_t analog_gain;//模拟增益
  5. uint32_t ae_valid;//AE 输出是否有效
  6. uint32_t ae_converged;//AE是否收敛
  7. uint32_t digital_gain;//数字增益
  8. uint32_t wb_gains[4];//白平衡增益
  9. int32_t wb_offsets[4];//白平衡偏移
  10. uint32_t color_temperature;//色温 (K)
  11. uint32_t awb_valid;//AWB 输出是否有效
  12. uint32_t awb_converged;//AWB 是否收敛
  13. } tivx_ae_awb_params_t;

初始化:

  1. tivx_vpac_viss_params_init(&vissObj->params);
  2. vissObj->params.sensor_dcc_id = sensorObj->sensorParams.dccId;
  3. vissObj->params.ee_mode = 0;
  4. vissObj->params.mux_output0 = 0;
  5. vissObj->params.mux_output1 = 0;
  6. vissObj->params.mux_output2 = 4;
  7. vissObj->params.mux_output3 = 0;
  8. vissObj->params.mux_output4 = 3;
  9. vissObj->params.h3a_in = 3;
  10. vissObj->params.h3a_aewb_af_mode = 0;
  11. vissObj->params.chroma_mode = 0;
  12. vissObj->params.bypass_nsf4 = 0;
  13. vissObj->params.enable_ctx = 1;
  14. if(sensorObj->sensor_wdr_enabled == 1)
  15. {
  16. vissObj->params.bypass_glbce = 0;
  17. }
  18. else
  19. {
  20. vissObj->params.bypass_glbce = 1;
  21. }
  22. vissObj->config = vxCreateUserDataObject(context, "tivx_vpac_viss_params_t", sizeof(tivx_vpac_viss_params_t), &vissObj->params);
  23. if(sensorObj->sensor_dcc_enabled)
  24. {
  25. int32_t dcc_buff_size;
  26. uint8_t * dcc_buf;
  27. vx_map_id dcc_buf_map_id;
  28. dcc_buff_size = appIssGetDCCSizeVISS(sensorObj->sensor_name, sensorObj->sensor_wdr_enabled);
  29. if(dcc_buff_size < 0)
  30. {
  31. printf("Invalid DCC size for VISS \n");
  32. return VX_FAILURE;
  33. }
  34. vissObj->dcc_config = vxCreateUserDataObject(context, "dcc_viss", dcc_buff_size, NULL);
  35. vxMapUserDataObject(
  36. vissObj->dcc_config,
  37. 0,
  38. dcc_buff_size,
  39. &dcc_buf_map_id,
  40. (void **)&dcc_buf,
  41. VX_WRITE_ONLY,
  42. VX_MEMORY_TYPE_HOST,
  43. 0
  44. );
  45. status = appIssGetDCCBuffVISS(sensorObj->sensor_name, sensorObj->sensor_wdr_enabled, dcc_buf, dcc_buff_size);
  46. if(status != VX_SUCCESS)
  47. {
  48. printf("Error getting VISS DCC buffer \n");
  49. return VX_FAILURE;
  50. }
  51. vxUnmapUserDataObject(vissObj->dcc_config, dcc_buf_map_id);
  52. }
  53. else
  54. {
  55. vissObj->dcc_config = NULL;
  56. }
  57. /* Create h3a_aew_af output buffer (uninitialized) */
  58. vx_user_data_object h3a_stats = vxCreateUserDataObject(context, "tivx_h3a_data_t", sizeof(tivx_h3a_data_t), NULL);
  59. vissObj->h3a_stats_arr = vxCreateObjectArray(context, (vx_reference)h3a_stats, sensorObj->num_cameras_enabled);
  60. vxReleaseUserDataObject(&h3a_stats);
  61. img_width = sensorObj->sensorParams.sensorInfo.raw_params.width;
  62. img_height = sensorObj->sensorParams.sensorInfo.raw_params.height;
  63. vx_image output_img = vxCreateImage(context, img_width, img_height, VX_DF_IMAGE_NV12);
  64. vissObj->output_arr = vxCreateObjectArray(context, (vx_reference)output_img, sensorObj->num_cameras_enabled);
  65. vxReleaseImage(&output_img);

创建图:

  1. tivx_raw_image raw_image = (tivx_raw_image)vxGetObjectArrayItem(raw_image_arr, 0);
  2. vx_user_data_object h3a_stats = (vx_user_data_object)vxGetObjectArrayItem(vissObj->h3a_stats_arr, 0);
  3. vx_image output_img = (vx_image)vxGetObjectArrayItem(vissObj->output_arr, 0);
  4. vissObj->node = tivxVpacVissNode(
  5. graph,
  6. vissObj->config,
  7. NULL,
  8. vissObj->dcc_config,
  9. raw_image, NULL, NULL,
  10. output_img, NULL, NULL,
  11. h3a_stats, NULL);
  12. status = vxGetStatus((vx_reference)vissObj->node);
  13. vxSetNodeTarget(vissObj->node, VX_TARGET_STRING, TIVX_TARGET_VPAC_VISS1);
  14. vxSetReferenceName((vx_reference)vissObj->node, "viss_node");

3.3 图流水线(Graph Pipelining)

扩展为应用程序提供了一种机制来执行图形,这样应用程序就不需要为每组输入/输出数据参与数据重新配置和图形的启动处理。
image.png
以上示例中,存在三个计算单元:图像信号处理器 (ISP) HWA、数字信号处理器 (DSP) 和 CPU。同样,示例图具有三个节点:一般标记为节点 0、节点 1 和节点 2。节点可能比计算单元多或少,但在这里,节点数恰好等于计算单元数。在该图中,节点 0 在 ISP 上执行,节点 1 在 DSP 上执行,节点 2 在 CPU 上执行。在未启用流水线的情况下,该图的执行时间线如下所示:
image.png
假设每个节点的执行时间为 33 毫秒,那么整个图的执行时间为 99 毫秒。如果没有这个扩展,OpenVX 要求第二帧不能在同一图上开始图执行,直到第一个图执行完成。这意味着此示例的最大吞吐量将是每 99 毫秒完成一帧。但是,在此示例中,您可以看到每个计算单元的使用时间不超过三分之一。此外,如果相机输入每 33 毫秒生成一帧,那么系统需要“丢弃”三帧中的每两帧,因为此 OpenVX 图实现无法跟上相机的输入帧速率。
流水线图形执行将增加硬件利用率,并增加 OpenVX 实现的吞吐量。这些效果可以在下图流水线执行的时间线中看到:
image.png

3.3.1 Graph参数

定义了一种公开图形外部端口的方式。通过创建一个图参数并显式地从 graph 中进入和离开该参数,应用程序就能够访问对象数据。否则,当使用流水线时,非 graph 参数在 graph 执行期间是不可访问的。

  1. /*
  2. * Utility API used to add a graph parameter from a node, node parameter index
  3. */
  4. void add_graph_parameter_by_node_index(vx_graph graph, vx_node node, vx_uint32 node_parameter_index)
  5. {
  6. vx_parameter parameter = vxGetParameterByIndex(node, node_parameter_index);
  7. vxAddParameterToGraph(graph, parameter);
  8. vxReleaseParameter(&parameter);
  9. }

3.3.2 Node参数

在 TIOVX 实现中,应用使用 node 参数来标识在流水线的情况下需要在何处创建多个缓冲区。

  1. * \param [in] node Node reference
  2. * \param [in] index Node parameter index
  3. * \param [in] num_buf Number of buffers to allocate
  4. *
  5. * \ingroup group_tivx_ext_host
  6. */
  7. vx_status VX_API_CALL tivxSetNodeParameterNumBufByIndex(vx_node node, vx_uint32 index, vx_uint32 num_buf);

3.3.3 缓冲区(buffers)

为了使产生缓冲区的节点与消耗该缓冲区的节点同时运行,必须在缓冲区参数处使用多个缓冲区以避免流水线停顿。高速设备与低速设备的不匹配,势必会让高速设备花时间等待低速设备,我们可以在这两者之间设立一个缓冲区。缓冲区就是一块内存区,它用在输入输出设备和 CPU 之间,用来存储数据。它使得低速的输入输出设备和高速的 CPU 能够协调工作,避免低速的输入输出设备占用 CPU,解放出 CPU,使其能够高效率工作。提高效率的原因:

  • 解除不同设备制约关系。数据可以直接送往缓冲区,高速设备不用再等待低速设备,提高了计算机的效率
  • 减少数据的读写次数。如果每次数据只传输一点数据,就需要传送很多次,这样会浪费很多时间,因为开始读写与终止读写需要花上较多时间,如果将数据送往缓冲区,待缓冲区满后再进行传送会大大减少读写次数,这样就可以节省很多时间

image.png
要创建如下图所示的场景,使用 tivxSetNodeParameterNumBufByIndex 将 Node1 输出的缓冲区数设置为N。
实际用例:

  1. tivxSetNodeParameterNumBufByIndex(obj->scalerObj.node, 2, 6);

3.3.4 流水线深度

为了让 OpenVX 图充分利用 TI 的异构 SoC,必须对 OpenVX 图进行流水线处理。在 TIOVX 框架中,描述异构内核利用率的特征是流水线深度。考虑下图中看到的一个简单的 3 节点图。该图使用 SoC 上的 3 个内核:ISP、DSP 和 CPU。
image.png
如果没有流水线,在初始图执行完成之前,新的图执行将无法开始。但是,由于这些内核中的每一个都可以同时运行,因此这种图形执行无法实现最佳硬件利用率。TIOVX 流水线实现允许通过基于流水线深度值在内部创建图形的多个实例来充分利用硬件。因此,这些图中的每一个都将同时执行,从而每个核心都在积极处理。在这种情况下,最佳管道深度为 3。管道深度为 3 时,框架将图视为同时有 3 个相同图处理的实例,如下所示。下图显示了时间 T=2 时的图形处理,这样每个处理单元都处于活动状态。
image.png

  1. /*! \brief Indicates to the implementation the depth of the graph pipeline
  2. *
  3. * \param [in] graph Graph reference
  4. * \param [in] pipeline_depth Pipeline depth; Max value is (TIVX_GRAPH_MAX_PIPELINE_DEPTH-1)
  5. * else it will return an error
  6. *
  7. * \ingroup group_tivx_ext_host
  8. */
  9. vx_status VX_API_CALL tivxSetGraphPipelineDepth(vx_graph graph, vx_uint32 pipeline_depth);

3.3.5 流水线图的定义

图的定义:

  1. static vx_status app_create_graph(AppObj *obj)
  2. {
  3. vx_status status = VX_SUCCESS;
  4. //vx_graph_parameter_queue_params_t graph_parameters_queue_params_list[2];
  5. //vx_int32 graph_parameter_index;
  6. obj->graph = vxCreateGraph(obj->context);
  7. status = vxGetStatus((vx_reference)obj->graph);
  8. if(status == VX_SUCCESS)
  9. {
  10. status = vxSetReferenceName((vx_reference)obj->graph, "OpenVxGraph");
  11. }
  12. if(status == VX_SUCCESS)
  13. {
  14. status = app_create_graph_scaler(obj->context, obj->graph, &obj->scalerObj);
  15. }
  16. if(status == VX_SUCCESS)
  17. {
  18. status = app_create_graph_pre_proc(obj->graph, &obj->preProcObj, obj->scalerObj.output1.arr[0]);
  19. }
  20. if(status == VX_SUCCESS)
  21. {
  22. status = app_create_graph_tidl(obj->context, obj->graph, &obj->tidlObj, obj->preProcObj.output_tensor_arr);
  23. }
  24. if(status == VX_SUCCESS)
  25. {
  26. status = app_create_graph_draw_detections(obj->graph, &obj->drawDetectionsObj, obj->tidlObj.output_tensor_arr[0], obj->scalerObj.output2.arr[0]);
  27. }
  28. vx_int32 idx = 0;
  29. obj->imgMosaicObj.input_arr[idx++] = obj->drawDetectionsObj.output_image_arr;
  30. obj->imgMosaicObj.num_inputs = idx;
  31. app_create_graph_img_mosaic(obj->graph, &obj->imgMosaicObj);
  32. if(status == VX_SUCCESS)
  33. {
  34. status = app_create_graph_display(obj->graph, &obj->displayObj, obj->imgMosaicObj.output_image[0]);
  35. }
  36. #ifdef APP_ENABLE_PIPELINE_FLOW
  37. /* Scalar Node - input is in Index 0 */
  38. if(status == VX_SUCCESS)
  39. {
  40. graph_parameter_index = 0;
  41. status = add_graph_parameter_by_node_index(obj->graph, obj->scalerObj.node, 0);
  42. obj->scalerObj.graph_parameter_index = graph_parameter_index;
  43. graph_parameters_queue_params_list[graph_parameter_index].graph_parameter_index = graph_parameter_index;
  44. graph_parameters_queue_params_list[graph_parameter_index].refs_list_size = APP_BUFFER_Q_DEPTH;
  45. graph_parameters_queue_params_list[graph_parameter_index].refs_list = (vx_reference*)&obj->scalerObj.input_images[0];
  46. graph_parameter_index++;
  47. }
  48. if(((obj->en_out_img_write == 1) || (obj->test_mode == 1)) && (status == VX_SUCCESS))
  49. {
  50. status = add_graph_parameter_by_node_index(obj->graph, obj->imgMosaicObj.node, 1);
  51. obj->imgMosaicObj.graph_parameter_index = graph_parameter_index;
  52. graph_parameters_queue_params_list[graph_parameter_index].graph_parameter_index = graph_parameter_index;
  53. graph_parameters_queue_params_list[graph_parameter_index].refs_list_size = APP_BUFFER_Q_DEPTH;
  54. graph_parameters_queue_params_list[graph_parameter_index].refs_list = (vx_reference*)&obj->imgMosaicObj.output_image[0];
  55. graph_parameter_index++;
  56. }
  57. if(status == VX_SUCCESS)
  58. {
  59. status = vxSetGraphScheduleConfig(obj->graph,
  60. VX_GRAPH_SCHEDULE_MODE_QUEUE_AUTO,
  61. graph_parameter_index,
  62. graph_parameters_queue_params_list);
  63. }
  64. if(status == VX_SUCCESS)
  65. {
  66. status = tivxSetGraphPipelineDepth(obj->graph, APP_PIPELINE_DEPTH);
  67. }
  68. if(status == VX_SUCCESS)
  69. {
  70. status = tivxSetNodeParameterNumBufByIndex(obj->scalerObj.node, 1, 2);
  71. }
  72. if(status == VX_SUCCESS)
  73. {
  74. status = tivxSetNodeParameterNumBufByIndex(obj->scalerObj.node, 2, 6);
  75. }
  76. if(status == VX_SUCCESS)
  77. {
  78. status = tivxSetNodeParameterNumBufByIndex(obj->preProcObj.node, 2, 2);
  79. }
  80. if(status == VX_SUCCESS)
  81. {
  82. status = tivxSetNodeParameterNumBufByIndex(obj->tidlObj.node, 4, 2);
  83. }
  84. if(status == VX_SUCCESS)
  85. {
  86. status = tivxSetNodeParameterNumBufByIndex(obj->tidlObj.node, 7, 2);
  87. }
  88. if(status == VX_SUCCESS)
  89. {
  90. status = tivxSetNodeParameterNumBufByIndex(obj->drawDetectionsObj.node, 3, 2);
  91. }
  92. if(status == VX_SUCCESS)
  93. {
  94. status = tivxSetNodeParameterNumBufByIndex(obj->imgMosaicObj.node, 1, 2);
  95. }
  96. #endif
  97. return status;
  98. }

3.4 PyTIOVX

用于自动生成 OpenVX 应用程序和kernel函数代码的 TI Python API。
image.png

  1. from tiovx import *
  2. #code = KernelExportCode(Module.IMAGING, "c66", "VISION_APPS_PATH")
  3. code = KernelExportCode("lyw_kernel", "c66", "VISION_APPS_PATH")
  4. code.setCoreDirectory("c66")
  5. kernel = Kernel("lanePostProc")
  6. kernel.setParameter(Type.OBJECT_ARRAY, Direction.INPUT, ParamState.REQUIRED, "IN_tenssor_arr")
  7. kernel.setParameter(Type.USER_DATA_OBJECT, Direction.INPUT, ParamState.REQUIRED, "IN_param")
  8. kernel.setParameter(Type.IMAGE, Direction.INPUT, ParamState.REQUIRED, "IN_image")
  9. kernel.setParameter(Type.IMAGE, Direction.OUTPUT, ParamState.REQUIRED, "OUTPUT")
  10. kernel.setTarget(Target.DSP1)
  11. kernel.setTarget(Target.DSP2)
  12. code.export(kernel)
  13. code.exportDiagram(kernel)

KernelExportCode API 的构造函数包含用作生成代码的目录路径的参数。

  • 第一个参数 Module.IMAGING 是核函数的预期模块。完整的模块列表包含在PyTIOVX的module.py文件中。如果未列出所需模块的名称,则可以将该模块添加到module.py文件中,或者可以将模块名称作为字符串传递给构造函数。
  • 第二个参数 Core.C66 是该内核将在其上运行的特定核函数。与模块名称类似,core.py中列出了核心值列表。如果未列出所需核函数的名称,则可以将核心添加到core.py文件中,或者可以将核心名称作为字符串传递给构造函数。
  • 第三个参数“CUSTOM_APPLICATION_PATH”是输出文件将导出到的路径。
    • 注意:此路径必须设置为环境变量,否则将引发错误。
  • 构造函数有两个可选参数,include_subpath 和 include_filename。
    • include_subpath 应设置为开发者公司名称的字符串值,默认为“TI”。
    • includefilename 包含包含文件的路径名。默认情况下,此值设置为空字符串,导致文件路径设置为“vx”。但是,可以通过填写此可选参数来覆盖文件路径。 ```python lyw@lyw:~/PSDK0010/ti-processor-sdk-rtos-j721e-evm-07_01_00_11/lyw_kernel$ tree -L 2 . ├── c66 │ ├── bam │ ├── concerto.mak │ ├── vx_kernels_lyw_kernel_target.c │ └── vx_lanepostproc_target.c ├── DEVELOPER_TODO.txt ├── host │ ├── concerto.mak │ ├── tivx_kernel_lanepostproc.h │ ├── tivx_lyw_kernel_kernels_priv.h │ ├── tivx_lyw_kernel_node_api.c │ ├── vx_kernels_lyw_kernel_host.c │ └── vx_lanepostproc_host.c ├── include │ └── TI ├── kernel_lanePostPrec.py ├── kernel_odPostPrec.py ├── lanepostproc_img.txt └── test ├── concerto.mak └── test_main.h

```

四、参考:

计算机体系结构——流水线技术(Pipelining)
The OpenVX™ Graph Pipelining, Streaming, and Batch Processing Extension to OpenVX 1.1 and 1.2
TIOVX 用户指南
The OpenVX™ Specification