OpenVX 的 立即模式(immediate mode)和图模式(graph mode)和示例讲解

2023-10-26

OpenVX 的 立即模式(immediate mode)和图模式(graph mode)

openvx的执行模型一般分为 立即模式 和 图模式。
直接模式非常简单,以vxu…开头的函数类似于opencv,习惯于传统的opencv api的,能够快速体验到openvx的功能,调用一个函数便可得到一个结果。
OpenCVX的真正的强大之处在于graph 模式,他将每一个函数功能抽象成一个node,函数原型被称为kernel, 一个graph 内可以有多个node, 了解过神经网络的朋友便清楚,这样的设计结构非常的契合神经网络的结构,每一个layer都可以被看作是一个node,虽然训练一个神经网络不是openvx的功能,但是在openvx上做推理是它的强大功能之一。关于openvx神经网络的说明,可参考我之前的一篇文章。✈

立即模式(immediate mode)

示例讲解1

接下来会以代码示例的方式讲解立即模式和图模式的应用
程序的功能如下:

编写openvx 程序想到第一个事情就是创建一个context。同时要想到release.
vx_context context = vxCreateContext();//创建
vxReleaseContext(&context);释放

第二件事就是想到如何对创建的上下文进行校验,以验证是否创建成功。一般来说,我们可以调用vxGetStatus()函数来检验vx_reference任何类型的返回值。

void errorCheck(vx_context *context_p, vx_status status, const char *message)
{
    if (status)
    {
      puts("ERROR! ");
      puts(message);
      vxReleaseContext(context_p);
      exit(1);
    }
}
errorCheck(&context, vxGetStatus((vx_reference)context), "Could notcreate a vx_context\n");

然后我们看一下接下来的函数,用于在黑色的背景上创建一个矩形框的图形,用于做Fast角点计算。

vx_image makeInputImage(vx_context context)
{
  //创建一个100*100 vx_image对象
  vx_image image = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8);
  vx_rectangle_t rect = {
    .start_x = 20, .start_y = 40, .end_x=80, .end_y = 60
  };

  if (VX_SUCCESS == vxGetStatus((vx_reference)image))
  {
  	//获取image的ROI
    vx_image roi = vxCreateImageFromROI(image, &rect);
    vx_pixel_value_t pixel_white, pixel_black;
    pixel_white.U8 = 255;
    pixel_black.U8 = 0;
    //设置ROI的像素值
    if (VX_SUCCESS == vxGetStatus((vx_reference)roi) &&
        VX_SUCCESS == vxSetImagePixelValues(image, &pixel_black) &&
        VX_SUCCESS == vxSetImagePixelValues(roi, &pixel_white))
      vxReleaseImage(&roi);
    else
      vxReleaseImage(&image);
  }

  return image;
}

我们通过填充所有的图像来制作黑色的背景,然后通过在这个背景中填充一个ROI(感兴趣的区域)来制作一个白色的矩形。当我们完成ROI后,我们释放它,因为我们不再需要它。注意,如果我们在创建图像时遇到错误,我们将没有进一步触摸图像或ROI,如果我们没有创建ROI或复制数据,那么我们将释放图像并返回一个空指针。
在调用Fast角点检测函数之前,我们还需要其他参数

//用于角点检测的阈值(当中心点的像素值大于绝大多数周围16个点像素值,那么就认为该像素是角点)
vx_float32 strength_thresh_value = 128.0; 
vx_scalar strength_thresh = vxCreateScalar(context, VX_TYPE_FLOAT32,&strength_thresh_value);
//保存坐标点的数组
vx_array corners = vxCreateArray(context, VX_TYPE_KEYPOINT, 100);
vx_array corners1 = vxCreateArray(context, VX_TYPE_KEYPOINT, 100);
vx_size num_corners_value = 0;
vx_scalar num_corners = vxCreateScalar(context, VX_TYPE_SIZE,
&num_corners_value);
vx_scalar num_corners1 = vxCreateScalar(context, VX_TYPE_SIZE,
&num_corners_value);
vx_keypoint_t *kp = calloc( 100, sizeof(vx_keypoint_t));

最后的部分就是执行采用非极大值抑制和不采用非极大值抑制的Fast焦点检测,然后将坐标显示出来。
所有代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <VX/vx.h>
#include <VX/vxu.h>
//错误检测函数
void errorCheck(vx_context *context_p, vx_status status, const char *message)
{
    if (status)
    {
      puts("ERROR! ");
      puts(message);
      vxReleaseContext(context_p);
      exit(1);
    }
}
//图像创建函数
vx_image makeInputImage(vx_context context)
{
  //创建一个100*100的image对象
  vx_image image = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8);
  //坐标
  vx_rectangle_t rect = {
    .start_x = 20, .start_y = 40, .end_x=80, .end_y = 60
  };
  if (VX_SUCCESS == vxGetStatus((vx_reference)image))
  {
  	//创建一个ROI区域
    vx_image roi = vxCreateImageFromROI(image, &rect);
    vx_pixel_value_t pixel_white, pixel_black;
    pixel_white.U8 = 255;
    pixel_black.U8 = 0;
    //像素填充
    if (VX_SUCCESS == vxGetStatus((vx_reference)roi) &&
        VX_SUCCESS == vxSetImagePixelValues(image, &pixel_black) &&
        VX_SUCCESS == vxSetImagePixelValues(roi, &pixel_white))
      vxReleaseImage(&roi);
    else
      vxReleaseImage(&image);
  }
  return image;
}

int main(void)
{
  //创建上下文
  vx_context context = vxCreateContext();
  errorCheck(&context, vxGetStatus((vx_reference)context), "Could not create a vx_context\n");
 //创建测试图像
  vx_image image1 = makeInputImage(context);
  errorCheck(&context, vxGetStatus((vx_reference)image1), "Could not create image");
 //快速角点检测阈值
  vx_float32 strength_thresh_value = 128.0;
  vx_scalar strength_thresh = vxCreateScalar(context, VX_TYPE_FLOAT32, &strength_thresh_value);
  vx_array corners = vxCreateArray(context, VX_TYPE_KEYPOINT, 100);
  vx_array corners1 = vxCreateArray(context, VX_TYPE_KEYPOINT, 100);
  vx_size num_corners_value = 0;
  vx_scalar num_corners = vxCreateScalar(context, VX_TYPE_SIZE, &num_corners_value);
  vx_scalar num_corners1 = vxCreateScalar(context, VX_TYPE_SIZE, &num_corners_value);
  vx_keypoint_t *kp = calloc( 100, sizeof(vx_keypoint_t));

  errorCheck(&context,
             kp == NULL ||
             vxGetStatus((vx_reference)strength_thresh) ||
             vxGetStatus((vx_reference)corners) ||
             vxGetStatus((vx_reference)num_corners) ||
             vxGetStatus((vx_reference)corners1) ||
             vxGetStatus((vx_reference)num_corners1),
             "Could not create parameters for FastCorners");
//采用非极大值移抑制的Fast角点检测算子
  errorCheck(&context, vxuFastCorners(context, image1, strength_thresh, vx_true_e, corners, num_corners), "Fast Corners function failed");
//不采用非极大值移抑制的Fast角点检测算子
  errorCheck(&context, vxuFastCorners(context, image1, strength_thresh, vx_false_e, corners1, num_corners1), "Fast Corners function failed");
//角点个数
  errorCheck(&context, vxCopyScalar(num_corners, &num_corners_value, VX_READ_ONLY, VX_MEMORY_TYPE_HOST), "vxCopyScalar failed");
  printf("Found %zu corners with non-max suppression\n", num_corners_value);
//坐标保存数组
  errorCheck(&context, vxCopyArrayRange( corners, 0, num_corners_value, sizeof(vx_keypoint_t), kp,VX_READ_ONLY, VX_MEMORY_TYPE_HOST), "vxCopyArrayRange failed");
//显示坐标                                      
  for (int i=0; i<num_corners_value; ++i)
  {
    printf("Entry %3d: x = %d, y = %d\n", i, kp[i].x, kp[i].y);
  }
//角点个数
  errorCheck(&context, vxCopyScalar(num_corners1, &num_corners_value, VX_READ_ONLY, VX_MEMORY_TYPE_HOST), "vxCopyScalar failed");
  printf("Found %zu corners without non-max suppression\n", num_corners_value);
//坐标保存数组
  errorCheck(&context, vxCopyArrayRange( corners1, 0, num_corners_value, sizeof(vx_keypoint_t), kp,VX_READ_ONLY, VX_MEMORY_TYPE_HOST), "vxCopyArrayRange failed");
  //显示坐标
  for (int i=0; i<num_corners_value; ++i)
  {
    printf("Entry %3d: x = %d, y = %d\n", i, kp[i].x, kp[i].y);
  }

  free(kp);
  vxReleaseContext(&context);
  return 0;
}

运算结果:采用非极大值抑制,检测到4个角点,不采用检测到24个角点。
在这里插入图片描述

示例讲解2

接下来我们丰富一些示例1 的内容, 我们可以采用vxuWarpAffine仿射变换将输入图像旋转90度,然后vxuOr()逻辑或处理两个图像,在黑色背景上创建一个白色的十字,这样我们看到12个角了。
关于什么是仿射变换,有兴趣的朋友可以参考一些连接 :仿射变换✈
在这里插入图片描述
仿射变换是一种旋转,其中每个输出x和y像素从输入地址的线性组合给出的地址处的像素中获取;如果输入地址不是积分,则按照指定执行插值。在代码中给出的系数作为matrix_values只是简单地交换x和y,没有偏移量;

//仿射变换所需的Mtrix
vx_matrix warp_matrix = vxCreateMatrix(context, VX_TYPE_FLOAT32, 2U,3U);
vx_float32 matrix_values[3][2] = { /* Rotate through 90 degrees */
	{0.0, 1.0}, /* x coefficients */
	{1.0, 0.0}, /* y coefficients */
	{0.0, 0.0} /* offsets */
};
errorCheck(&context, vxCopyMatrix(warp_matrix, matrix_values,VX_WRITE_ONLY,VX_MEMORY_TYPE_HOST), "Could not initialize thematrix");
/* Now image2 set to image 1 rotated */
//仿射变换
errorCheck(&context, vxuWarpAffine(context, image1, warp_matrix,VX_INTERPOLATION_NEAREST_NEIGHBOR, image2) ||
/* image3 set to logical OR of images 1 and 2 */
//逻辑或
vxuOr(context, image1, image2, image3) ||
/*And now count the corners */
//Fast角点检测
vxuFastCorners(context, image3, strength_thresh, vx_true_e,corners, num_corners),"Image functions failed");
实例讲解3

更进一步的优化,我们增加对图像的处理,使用Sobel、Magnitude和 Dilate filters(膨胀过滤器)用白色十字的轮廓替换白色十字,计算和内外角点的计数。
Sobel滤波器是一个边缘检测滤波器,将其应用于交叉得到两个输出图像,x方向的梯度和y方向的梯度。
取x和y图像的Magnitude,我们得到了一条勾画交叉的单像素实线。注意,vxuMagnitude函数接受两个作为vxuSobel输出的16位有符号图像的输出,并生成一个16位有符号图像作为输出。然而,大多数其他的OpenVX函数只操作8位无符号图像,所以我们使用vxu转换深度函数来生成这样的图像,VX_CONVERT_POLICY_SATURATE指定低于零的值应该设置为零,高于255的值应该设置为255。
最后,vxuDilate3x3功能应用膨胀过滤器,使薄轮廓更实质性。

/* Now image2 set to image 1 rotated */
 vx_status status = vxuWarpAffine(context, image1, warp_matrix, VX_INTERPOLATION_NEAREST_NEIGHBOR, image2);
/* image3 set to logical OR of images 1 and 2 and then processed as described above */
errorCheck(&context, vxuOr(context, image1, image2, image3) ||
	vxuSobel3x3(context, image3, grad_x, grad_y) ||
	vxuMagnitude(context, grad_x, grad_y, magnitude) ||
	vxuConvertDepth(context, magnitude, converted, VX_CONVERT_POLICY_SATURATE, 1) ||
	vxuDilate3x3(context, converted, dilated) ||
	/* And now count the corners */
	vxuFastCorners(context, dilated, strength_thresh, vx_true_e, corners, num_corners),"Image functions failed");

在这里插入图片描述
以下代码是经过多次优化后的整体代码

#include <stdio.h>
#include <stdlib.h>
#include "VX/vx.h"
#include "VX/vxu.h"

void errorCheck(vx_context *context_p, vx_status status, const char *message)
{
    if (status)
    {
      puts("ERROR! ");
      puts(message);
      vxReleaseContext(context_p);
      exit(1);
    }
}

vx_image makeInputImage(vx_context context)
{
  vx_image image = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8);
  vx_rectangle_t rect = {
    .start_x = 20, .start_y = 40, .end_x=80, .end_y = 60
  };

  if (VX_SUCCESS == vxGetStatus((vx_reference)image))
  {
    vx_image roi = vxCreateImageFromROI(image, &rect);
    vx_pixel_value_t pixel_white, pixel_black;
    pixel_white.U8 = 255;
    pixel_black.U8 = 0;
    if (VX_SUCCESS == vxGetStatus((vx_reference)roi) &&
        VX_SUCCESS == vxSetImagePixelValues(image, &pixel_black) &&
        VX_SUCCESS == vxSetImagePixelValues(roi, &pixel_white))
      vxReleaseImage(&roi);
    else
      vxReleaseImage(&image);
  }

  return image;
}

int main(void)
{
  vx_context context = vxCreateContext();

  errorCheck(&context, vxGetStatus((vx_reference)context), "Could not create a vx_context\n");

  vx_image image1 = makeInputImage(context);

  errorCheck(&context, vxGetStatus((vx_reference)image1), "Could not create first image");

  vx_image image2 = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8);
  vx_image image3 = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8);
  vx_image grad_x = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_S16);
  vx_image grad_y = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_S16);
  vx_image magnitude = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_S16);
  vx_image converted = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8);
  vx_image dilated = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8);
  vx_image image4 = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8);
  vx_matrix warp_matrix = vxCreateMatrix(context, VX_TYPE_FLOAT32, 2U, 3U);
  vx_float32 matrix_values[3][2] = {       /* Rotate through 90 degrees */
      {0.0, 1.0},         /* x coefficients */
      {1.0, 0.0},         /* y coefficients */
      {0.0, 0.0}          /* offsets */
  };
  vx_float32 strength_thresh_value = 128.0;
  vx_scalar strength_thresh = vxCreateScalar(context, VX_TYPE_FLOAT32, &strength_thresh_value);
  vx_array corners = vxCreateArray(context, VX_TYPE_KEYPOINT, 100);
  vx_size num_corners_value = 0;
  vx_scalar num_corners = vxCreateScalar(context, VX_TYPE_SIZE, &num_corners_value);
  vx_keypoint_t *kp = calloc( 100, sizeof(vx_keypoint_t));

  errorCheck(&context,
             kp == NULL ||
             vxGetStatus((vx_reference)strength_thresh) ||
             vxGetStatus((vx_reference)corners) ||
             vxGetStatus((vx_reference)num_corners) ||
             vxGetStatus((vx_reference)image2) ||
             vxGetStatus((vx_reference)image3) ||
             vxGetStatus((vx_reference)grad_x) ||
             vxGetStatus((vx_reference)grad_y) ||
             vxGetStatus((vx_reference)magnitude) ||
             vxGetStatus((vx_reference)converted) ||
             vxGetStatus((vx_reference)dilated) ||
             vxGetStatus((vx_reference)image4) ||
             vxGetStatus((vx_reference)warp_matrix),
             "Could not create objects");

  errorCheck(&context, vxCopyMatrix(warp_matrix, matrix_values, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST), "Could not initialise the matrix");

  /* Now image2 set to image 1 rotated */
  vx_status status = vxuWarpAffine(context, image1, warp_matrix, VX_INTERPOLATION_NEAREST_NEIGHBOR, image2);
  /* image3 set to logical OR of images 1 and 2 and then processed as described above */
  errorCheck(&context, vxuOr(context, image1, image2, image3) ||
                                   vxuSobel3x3(context, image3, grad_x, grad_y) ||
                                   vxuMagnitude(context, grad_x, grad_y, magnitude) ||
                                   vxuConvertDepth(context, magnitude, converted, VX_CONVERT_POLICY_SATURATE, 1) ||
                                   vxuDilate3x3(context, converted, dilated) ||
                                   /* And now count the corners */
                                   vxuFastCorners(context, dilated, strength_thresh, vx_true_e, corners, num_corners),
                                   "Image functions failed");

  errorCheck(&context, vxCopyScalar(num_corners, &num_corners_value, VX_READ_ONLY, VX_MEMORY_TYPE_HOST), "vxCopyScalar failed");
  printf("Found %zu corners with non-max suppression\n", num_corners_value);

  errorCheck(&context, vxCopyArrayRange( corners, 0, num_corners_value, sizeof(vx_keypoint_t), kp,
                                        VX_READ_ONLY, VX_MEMORY_TYPE_HOST), "vxCopyArrayRange failed");
  for (int i=0; i<num_corners_value; ++i)
  {
    printf("Entry %3d: x = %d, y = %d\n", i, kp[i].x, kp[i].y);
  }

  free(kp);
  vxReleaseContext(&context);
  return 0;
}

我们使用了很多image 对象,事实上,大多数这些图像确实不需要保留,因为我们对中间数据不感兴趣。唯一感兴趣的图像实际上是我们创建的输入图像。在此之后,我们只使用来自最终转角检测器的输出。

图模式(Graph mode)

关于Graph mode的使用,我们可以对比上面的模式,graph模式是如何实现和执行的。
先直接上代码,大家可以看一下实现共一个功能的程序在两种模式下有什么区别,然后我会一一讲解其中主要函数的意义。

#include <stdio.h>
#include <stdlib.h>
#include "VX/vx.h"

void errorCheck(vx_context *context_p, vx_status status, const char *message)
{
    if (status)
    {
      puts("ERROR! ");
      puts(message);
      vxReleaseContext(context_p);
      exit(1);
    }
}

vx_image makeInputImage(vx_context context, vx_uint32 width, vx_uint32 height)
{
  vx_image image = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8);
  if (width > 48)
      width = 48;
  if (height > 48)
      height = 48;
  vx_rectangle_t rect = {
    .start_x = 50 - width, .start_y = 50 - height, .end_x = 50 + width, .end_y = 50 + height
  };

  if (VX_SUCCESS == vxGetStatus((vx_reference)image))
  {
    vx_image roi = vxCreateImageFromROI(image, &rect);
    vx_pixel_value_t pixel_white, pixel_black;
    pixel_white.U8 = 255;
    pixel_black.U8 = 0;
    if (VX_SUCCESS == vxGetStatus((vx_reference)roi) &&
        VX_SUCCESS == vxSetImagePixelValues(image, &pixel_black) &&
        VX_SUCCESS == vxSetImagePixelValues(roi, &pixel_white))
      vxReleaseImage(&roi);
    else
      vxReleaseImage(&image);
  }
  return image;
}

vx_graph makeTestGraph(vx_context context)
{
    vx_graph graph = vxCreateGraph(context);
    int i;
    vx_image imagesU8[5], imagesS16[3];
    vx_image input = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8);

	//节点所需的虚拟图像对象
    for (i = 0; i < 5; ++i)
      imagesU8[i] = vxCreateVirtualImage(graph, 100, 100, VX_DF_IMAGE_U8);
    for (i = 0; i < 3; ++i)
        imagesS16[i] = vxCreateVirtualImage(graph, 0, 0, VX_DF_IMAGE_VIRT);
	//所需参数创建
    vx_matrix warp_matrix = vxCreateMatrix(context, VX_TYPE_FLOAT32, 2U, 3U);
    vx_float32 matrix_values[6] = {0.0, 1.0, 1.0, 0.0, 0.0, 0.0 };       /* Rotate through 90 degrees */
    vx_float32 strength_thresh_value = 128.0;
    vx_scalar strength_thresh = vxCreateScalar(context, VX_TYPE_FLOAT32, &strength_thresh_value);
    vx_array corners = vxCreateArray(context, VX_TYPE_KEYPOINT, 100);
    vx_size num_corners_value = 0;
    vx_int32 shift_value = 1;
    vx_scalar num_corners = vxCreateScalar(context, VX_TYPE_SIZE, &num_corners_value);
    vx_scalar shift = vxCreateScalar(context, VX_TYPE_INT32, &shift_value);

    vxCopyMatrix(warp_matrix, matrix_values, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST);
	//节点连接:节点的连接方式是以数据为支撑
    /* Create the nodes to do the processing, order of creation is not important */
    vx_node last_node = vxFastCornersNode(graph, imagesU8[4], strength_thresh, vx_true_e, corners, num_corners);
    vxDilate3x3Node(graph, imagesU8[3], imagesU8[4]);
    vxConvertDepthNode(graph, imagesS16[2], imagesU8[3], VX_CONVERT_POLICY_SATURATE, shift);
    vxMagnitudeNode(graph, imagesS16[0], imagesS16[1], imagesS16[2]);
    vxSobel3x3Node(graph, imagesU8[2], imagesS16[0], imagesS16[1]);
    vxOrNode(graph, imagesU8[0], imagesU8[1], imagesU8[2]);
    vxWarpAffineNode(graph, imagesU8[0], warp_matrix, VX_INTERPOLATION_NEAREST_NEIGHBOR, imagesU8[1]);

	//设置grpah 输入,Index:节点的第几个参数
    /* Setup input parameter using a Copy node */
    vxAddParameterToGraph(graph, vxGetParameterByIndex(vxCopyNode(graph, (vx_reference)input, (vx_reference)imagesU8[0]), 0));
	//设置grpah 输出,Index:节点的第几个参数
    /* Setup the output parameters from the last node */
    vxAddParameterToGraph(graph, vxGetParameterByIndex(last_node, 3));    /* array of corners */
    vxAddParameterToGraph(graph, vxGetParameterByIndex(last_node, 4));    /* number of corners */

    /* Release resources */
    vxReleaseImage(&input);
    for (i = 0; i < 5; ++i)
        vxReleaseImage(&imagesU8[i]);
    for (i = 0; i < 3; ++i)
        vxReleaseImage(&imagesS16[i]);
    vxReleaseMatrix(&warp_matrix);
    vxReleaseScalar(&strength_thresh);
    vxReleaseScalar(&num_corners);
    vxReleaseScalar(&shift);
    vxReleaseArray(&corners);

    return graph;
}
//获取Graph的参数
vx_reference getGraphParameter(vx_graph graph, vx_uint32 index)
{
    vx_parameter p = vxGetGraphParameterByIndex(graph, index);
    vx_reference ref = NULL;
    vxQueryParameter(p, VX_PARAMETER_REF, &ref, sizeof(ref));
    vxReleaseParameter(&p);
    return ref;
}

void showResults(vx_graph graph, vx_image image, const char * message)
{
	vx_status status;
	//创建上下文
    vx_context context = vxGetContext((vx_reference)graph);
    puts(message);
	//设置Graph的输出参数,Index:0 第0个参数
    vxSetGraphParameterByIndex(graph, 0, (vx_reference)image);
	//graph 认证
	errorCheck(&context,vxVerifyGraph(graph),"Graph verify fail!");
	//graph 执行
	status = vxProcessGraph(graph);	
    if (VX_SUCCESS == status)
    {
        vx_size num_corners_value = 0;
        vx_keypoint_t *kp = calloc( 100, sizeof(vx_keypoint_t));
		//执行成功后,将scaler参数拷贝值num_corners_value
        errorCheck(&context, vxCopyScalar((vx_scalar)getGraphParameter(graph, 2), &num_corners_value,
                                          VX_READ_ONLY, VX_MEMORY_TYPE_HOST), "vxCopyScalar failed");
        printf("Found %zu corners with non-max suppression\n", num_corners_value);

        /* Array can only hold 100 values */
        if (num_corners_value > 100)
            num_corners_value = 100;
		//执行成功后,将scaler参数拷贝值vx_keypoint_t 数组
        errorCheck(&context, vxCopyArrayRange((vx_array)getGraphParameter(graph, 1), 0,
                                              num_corners_value, sizeof(vx_keypoint_t), kp,
                                              VX_READ_ONLY, VX_MEMORY_TYPE_HOST), "vxCopyArrayRange failed");
        for (int i=0; i<num_corners_value; ++i)
        {
            printf("Entry %3d: x = %d, y = %d\n", i, kp[i].x, kp[i].y);
        }

        free(kp);
    }
    else
    {
        printf("Graph processing failed[%d]!\n",status);
    }
}

int main(void)
{
    vx_context context = vxCreateContext();
    errorCheck(&context, vxGetStatus((vx_reference)context), "Could not create a vx_context\n");

    vx_graph graph = makeTestGraph(context);

    vx_image image1 = makeInputImage(context, 30, 10);
    vx_image image2 = makeInputImage(context, 25, 25);

    showResults(graph, image1, "Results for Image 1");
    showResults(graph, image2, "Results for Image 2");

    vxReleaseContext(&context);
    return 0;
}


各函数的含义及讲解

  • vx_graph graph = vxCreateGraph(context);
    基于上下文创建一个graph
  • vxCreateVirtualImage( )
    该函数用于创建一个虚拟image,我们往往不关心中间的数据,但是节点的连接还是要以数据对象作为支撑,所以,虚拟对象技术就应用而生。
  • vxCreateMatrix( )
    创建一个矩阵对象
  • vxCreateScalar( )
    创建一个标量对象
  • vxCreateArray( )
    创建一个数组对象
  • vxCopyNode( )
    复制一个节点
  • vxGetParameterByIndex(vx_node node, vx_uint32 index)
    获取节点的参数,其中的index,便是节点的参数的索引,是几便是除去graph参数之外的第几个参数。
  • vxAddParameterToGraph(vx_graph graph, vx_parameter param)
    将参数添加进graph
  • vxVerifyGraph(graph)
    节点认证,主要验证graph结构是否合法
    -vxProcessGraph(graph)
    graph 执行,此函数便会执行graph中所有node函数功能。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenVX 的 立即模式(immediate mode)和图模式(graph mode)和示例讲解 的相关文章

  • 【Qt5】频繁调用update()若干时间后无法自动重绘界面的问题

    最近在项目中遇到了这样的问题 程序的结构 主线程用于界面控制 线程1用于生成显示内容 线程2用于精确控制在正确的时刻控制界面显示生成的内容 实现方式 在线程2中直接调用显示控件的update 方法 让主线程重绘 运行效果 运行开始后的一段时
  • git创建本地仓库的三种方法

    首先新建文件夹E git repository repo1 然后打开该目录 接下来 有三种方式创建本地仓库 第一种方法 右键 单击git GUI here 点击创建仓库 然后关闭即可 此时可以看到新生成的文件夹 git 注意 git是隐藏文
  • Hive 分组取Top N

    成绩表S 字段sid cid score 求每门课程的前2名与后2名sid with a as select sid rank over distribute by cid sort by score rn rank over distri
  • 入门电机系列之3舵机

    入门电机系列 基于STM32硬件 本文章学习借鉴于野火团队资料 以表感谢 官网http products embedfire com 舵机的原理与应用 文章目录 入门电机系列 基于STM32硬件 前言 一 舵机的分类 舵机结构 舵机工作原理
  • 安装Java后端钉钉新的jar包指令

    install install file Dfile lib dingtalk sdk java taobao sdk java auto 1479188381469 20200403 jar DgroupId com taobao Dar
  • linux命令高亮显示,linux命令行高亮显示

    Loading gt 我们常用命令的朋友知道 纯黑白的看命令 时间长了很是不舒服 那么接下来就进行下命令行高亮的个性化的配置吧 想永久性的设置命令行高亮的效果 就需要修改 bashrc配置文件 一 进入 bashrc文件目录 bashrc这
  • 了解基础的docker容器化

    一 什么是docker容器 docker容器简单说来就是码头工具 docker是一个开源的的应用容器引擎 docker的图标是一个鲸鱼上有很多集装箱 docker就是鲸鱼 他上面的集装箱就代表各个容器 docker容器在目前的环境中大部分公
  • Freemarker常用语法简例(二)

    为使用Freemarker模板时的关键性语法编写代码示例或说明 主要用于回忆性检索或速查 阅读需要一定代码基础或对Freemarker有了解 常用Freemarker语法简例 常用Freemarker语法简例 二 bool值 lt 输出 t
  • 大数据——Java多线程

    多线程 认识线程 进程 线程 多线程的优势 编写线程类 使用Thread类创建线程 使用Runnable接口创建线程 使用Callable接口创建线程 线程的状态 新生状态 New Thread 可运行状态 Runnable 阻塞状态 Bl
  • 【网络】【应用层】常见的应用层协议

    文章目录 1 HTTP HTTPS 概述 HTTP工作过程 HTTP特点 发展过程 HTTP0 9 HTTP1 0 HTTP1 1 当前普遍使用的版本 SPDY 了解 HTTP2 0 二进制分帧层 头部压缩 服务端推送 流优先级 HTTP2
  • springCloud中feign的使用

    springCloud中feign的使用 定义一个feign的接口 定义一个feign的接口 定义fegin接口 使用 FeginClient注解指向对应的服务 FeignClient value iyb test url ms iyb t
  • 【Elasticsearch】ES内存满问题排查思路

    1 概述 转载 https bbs huaweicloud com forum thread 69710 1 1 html 2 问题排查思路 场景1 内存参数配置不合理 场景2 查询返回的size过大 场景3 深度翻页查询 场景4 聚合的数
  • 【iOS】属性关键字

    文章目录 前言 一 深拷贝与浅拷贝 1 OC的拷贝方式有哪些 2 OC对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝 3 自定义对象实现的copy和mutableCopy分别为浅拷贝还是深拷贝 4 判断当前的深拷贝的类型
  • 论文阅读技巧之三遍法

    本文介绍了三遍法及其在文献调查中的应用 关键的思想是 你应该以三遍的时间来阅读论文 而不是从一开始就一直读到最后 每个pass都实现了特定的目标 并建立在前一个遍的基础上 第一个pass让您对本文有一个大致的了解 第二步让你掌握论文的内容
  • es查询对应索引下的数据结果

    执行语句 GET test index mapping pretty test index 索引名称 mapping 查询索引的结构 pretty 是参数 意思是格式化数据
  • spring手动开启事务,手动提交事务,手动回滚事务

    1 未加事务注解 或者事务配置 所以需要手动开启事务和手动提交事务和手动回滚事务 Autowired private PlatformTransactionManager txManager Autowired private ShopGr
  • Vue中表单手机号验证与手机号归属地查询

    下面是一篇关于Vue中如何进行表单手机号验证与手机号归属地查询的Markdown格式的文章 包含代码示例 Vue中表单手机号验证与手机号归属地查询 手机号验证和归属地查询是许多Web应用程序中常见的功能之一 在Vue js中 我们可以轻松地

随机推荐

  • 安装npm和cnpm

    一 简介 npm是nodejs的包管理工具 用于node插件管理 cnpm是淘宝在中国做的nodejs镜像 避免访问国外的nodejs网站出现异常 二 安装nodejs 1 安装 有两种选择一种是安装文件安装 一种是免安装的zip包 这里我
  • 使用Rust写操作系统(1)-安装rust开发环境

    安装cargo及rust编译环境 sudo curl https sh rustup rs sSf sh 如图 选择自定义安装 在版本选择的时候 一定要选择nightly 因为开发操作系统要使用到一些非稳定版本的功能 选择完成后 继续安装即
  • Java序列化详解

    序列化是一种将对象转换成字节流的过程 以便在网络上传输或将其保存到磁盘上 Java提供了一种称为Java序列化的机制 它可以将Java对象转换成字节流 并在需要时将其还原回对象 在本文中 我们将介绍Java序列化的使用方法 并提供一些示例代
  • 都2021了作为一名Android开发者,还不学音视频开发?我劝你早点认清现实!

    缘起 最近经常遇到一些同学问我如何学习音视频 怎样才能快速上手 还有一些对音视频不了解的同学问我该不该学习音视频 作为一名音视频行业的10年Android老兵 我有一些思考分享给大家 希望能对你有所帮助 大趋势 从未来的大趋势来看 随着5G
  • 【C语言位运算符及原码输出】

    C语言位运算符及原码输出 原码 补码 反码基础概念 按位与 按位或 按位异或 按位与 lt lt 按位左移 gt gt 按位右移 位运算符注意事项 两个操作数均以补码参与计算 得到的结果为补码 需将结果转为原码才是最终答案 原码 补码 反码
  • element-plus 提示找不到名称“ElMessage”。ts(2304)

    文章目录 1 安装element plus 2 main ts 引入ElMessage 3 vite config ts 中配置 4 在vscode中使用会报错 找不到名称 ElMessage ts 2304 1 安装element plu
  • umi使用mock

    引入 Mock import Request Response from umijs deps compiled express import Mock from mockjs 定义数据类型 export default GET api t
  • 微信小程序vant组件库安装

    vant组件库安装步骤 1 通过npm安装 在微信开发者工具目录空白处右击 在外部终端窗口中打开或直接在文件路径中输入cmd回车 2 安装之前初始化npm包 再安装 npm init y 通过 npm 安装 npm i vant weapp
  • Ubuntu下如何将普通用户提升到root权限

    1 打开超级终端 输入指令sudo gedit etc passwd 2 则找到crystal x 1000 1000 crystal home linuxidc bin bash 将两个1000改成0即可 3 重新登陆之后打开超级终端发现
  • BLEU 评价指标总结

    Bleu 评测 一 Bleu通常用来度量一组机器产生的翻译句子集合 candidates 与一组人工翻译句子集合 references 的相似程度 Bleu的具体计算过程看下图 在这里解释一下 式中的n 为当前匹配n gram的长度 这里的
  • Win10 + vs2017 编译并配置tesseract4.1.0

    tesseract 是一个开源的OCR Optical Character Recognition 光学字符识别 引擎 本文就介绍一下自己在编译 tesseract4 1 0时遇到的一些坑 希望能给大家带来一些帮助 一 下载 tessera
  • mybatis mysql autoreconnect=true_Mysql8.0主从搭建,shardingsphere+springboot+mybatis读写分离...

    cd usr local mysql mkdir mysql files chown mysql mysql mysql files chmod 750 mysql files bin mysqld initialize user mysq
  • StringUtils中 isNotEmpty 和isNotBlank的区别 以及StringUtil类的方法

    StringUtils方法的操作对象是java lang String类型的对象 是JDK提供的String类型操作方法的补充 并且是null安全的 即如果输入参数String为null则不会抛出NullPointerException 而
  • Apollo:实时通信架构CyberRT入门

    发现一开始就深入源码 很容易陷进去 特别是模块非常多的情况 需要看很多遍才能理解清楚 要写出更容易理解的文档 需要的不是事无巨细的分析代码 更主要的是能够把复杂的东西抽象出来 变为简单的东西 一个很简答的例子是画函数调用流程图很简单 但是要
  • C++指针定义和使用

    目录 1 指针简介 2 指针的声明和使用 1 指针简介 学习指针前需要先分清几个概念 1 1内存单元的地址和内存单元的内容 在程序中定义一个变量 当程序进行编译时就会给定义的变量分配内存单元 这个内存单元的大小由变量的数据类型决定 例如对有
  • 算法

    算法的效率 算法的效率主要由以下两个复杂度来评估 时间复杂度 评估执行程序所需要的时间 可以估算出程序对处理器的使用程度 空间复杂度 评估执行程序所需要的的存储空间 可以估算出程序对计算机内存的使用程度 设计算法时 一般要先考虑系统环境 然
  • torch.exp()的使用举例

    参考链接 torch exp input out None 参考链接 exp 代码实验展示 Microsoft Windows 版本 10 0 18363 1256 c 2019 Microsoft Corporation 保留所有权利 C
  • Highcharts的3D饼图上下颠倒及解决办法

    我需要的样式效果 那发现常规的echarts不满足了 就改成了highcharts进行3d图表开发 but在开发的过程中发现了这个现象 来吧 看看问题 第一次进入页面 饼图是正常的 戳这个进去瞅瞅 喵 那你进去就看到这个样子的饼图呢 但是由
  • Spring5框架一:IOC实现的两种方式和底层原理

    1 概念 先了解 原理后面再写 1 spring是一个轻量级的开源的JavaEE框架 引入jar包的数量以及体积都比较小 2 spring框架可以解决企业应用开发的复杂性 3 spring里面有很多组成部分 IOC和AOP两个核心部分 IO
  • OpenVX 的 立即模式(immediate mode)和图模式(graph mode)和示例讲解

    OpenVX 的 立即模式 immediate mode 和图模式 graph mode openvx的执行模型一般分为 立即模式 和 图模式 直接模式非常简单 以vxu 开头的函数类似于opencv 习惯于传统的opencv api的 能