[TI TDA4 J721E]TIOVX Image图像相关操作(修正版)

大家好,首先感谢阅读,如果您也对TDA4相关的开发感兴趣,可以私信联系博主,我们这边有个学习交流群,可以入群和大家一起交流学习。
        也可以加博主WX :AIR_12 我会拉你入群。保持开源精神,共同分享、进步!


        很久之前写了一篇关于图像相关操作的博客,经过一段时间的研究,有了比较深入的了解,现在将比较系统的总结一下,并修正之前版本的一些错误。


一、创建图像

目前在官方给出的版本内,有以下几种方法可以实现创建图像的操作。

[TI TDA4 J721E]TIOVX Image图像相关操作(修正版)

函数名 说明

vxCreateImage

直接创建一个图像 根据图像格式定义

vxCreateImageFromHandle

从一个句柄中创建一个图像 可以是一个文件的句柄/或者另一个图像在内存内的指针索引

vxCreateImageFromChannel

从另一个图像的单个平面通道创建子图像。 子图像是指原始图像中的数据。 对此图像的更新会更新父图像,反之亦然。 该功能仅支持占据多平面图像整个平面的通道,如下所列。 不支持其他情况。 VX_CHANNEL_Y 来自 YUV4、IYUV、NV12、NV21 VX_CHANNEL_U 来自 YUV4、IYUV VX_CHANNEL_V 来自 YUV4、IYUV 需要满足一定的条件

vxCreateImageFromROI

给定一个矩形,从另一个图像创建一个图像。 第二个参考是指原始图像中的数据。 对此图像的更新会更新父图像。 矩形必须在父图像的像素空间内定义。 从另一个图像内部截取或者复制整个图像

vxCreateVirtualImage

创建对图像缓冲区的不透明引用,用户无法直接访问。 此功能允许设置图像宽度、高度或格式。 暂时没研究

vxCreateUniformImage

创建对在所有像素中具有单一、统一值的图像对象的引用。 创建的统一图像是只读的。 暂时没研究

1、直接创建图像:

vx_image image = vxCreateImage(context, width, height, <FORMAT>);

vxCreateImage 用于创建图像,输入参数
context:上下文
widht:所需创建图像的宽度
height:所需创建图像的高度
<FORMAT>:所需创建图像的格式。(TDA4 所有支持的格式如下所示)
目前我们比较常用的是NV12格式的图片。

enum vx_df_image_e {
    /*! \brief A virtual image of no defined type. */
    VX_DF_IMAGE_VIRT = VX_DF_IMAGE('V','I','R','T'),
    /*! \brief A single plane of 24-bit pixel as 3 interleaved 8-bit units of
     * R then G then B data. This uses the BT709 full range by default.
     */
    VX_DF_IMAGE_RGB  = VX_DF_IMAGE('R','G','B','2'),
    /*! \brief A single plane of 32-bit pixel as 4 interleaved 8-bit units of
     * R then G then B data, then a <i>don't care</i> byte.
     * This uses the BT709 full range by default.
     */
    VX_DF_IMAGE_RGBX = VX_DF_IMAGE('R','G','B','A'),
    /*! \brief A 2-plane YUV format of Luma (Y) and interleaved UV data at
     * 4:2:0 sampling. This uses the BT709 full range by default.
     */
    VX_DF_IMAGE_NV12 = VX_DF_IMAGE('N','V','1','2'),
    /*! \brief A 2-plane YUV format of Luma (Y) and interleaved VU data at
     * 4:2:0 sampling. This uses the BT709 full range by default.
     */
    VX_DF_IMAGE_NV21 = VX_DF_IMAGE('N','V','2','1'),
    /*! \brief A single plane of 32-bit macro pixel of U0, Y0, V0, Y1 bytes.
     * This uses the BT709 full range by default.
     */
    VX_DF_IMAGE_UYVY = VX_DF_IMAGE('U','Y','V','Y'),
    /*! \brief A single plane of 32-bit macro pixel of Y0, U0, Y1, V0 bytes.
     * This uses the BT709 full range by default.
     */
    VX_DF_IMAGE_YUYV = VX_DF_IMAGE('Y','U','Y','V'),
    /*! \brief A 3 plane of 8-bit 4:2:0 sampled Y, U, V planes.
     * This uses the BT709 full range by default.
     */
    VX_DF_IMAGE_IYUV = VX_DF_IMAGE('I','Y','U','V'),
    /*! \brief A 3 plane of 8 bit 4:4:4 sampled Y, U, V planes.
     * This uses the BT709 full range by default.
     */
    VX_DF_IMAGE_YUV4 = VX_DF_IMAGE('Y','U','V','4'),
    /*! \brief A single plane of unsigned 8-bit data.
     * The range of data is not specified, as it may be extracted from a YUV or
     * generated.
     */
    VX_DF_IMAGE_U8 = VX_DF_IMAGE('U','0','0','8'),
    /*! \brief A single plane of unsigned 16-bit data.
     * The range of data is not specified, as it may be extracted from a YUV or
     * generated.
     */
    VX_DF_IMAGE_U16  = VX_DF_IMAGE('U','0','1','6'),
    /*! \brief A single plane of signed 16-bit data.
     * The range of data is not specified, as it may be extracted from a YUV or
     * generated.
     */
    VX_DF_IMAGE_S16  = VX_DF_IMAGE('S','0','1','6'),
    /*! \brief A single plane of unsigned 32-bit data.
     * The range of data is not specified, as it may be extracted from a YUV or
     * generated.
     */
    VX_DF_IMAGE_U32  = VX_DF_IMAGE('U','0','3','2'),
    /*! \brief A single plane of unsigned 32-bit data.
     * The range of data is not specified, as it may be extracted from a YUV or
     * generated.
     */
    VX_DF_IMAGE_S32  = VX_DF_IMAGE('S','0','3','2'),
};

2、从句柄内创建图像

        可以从已经打开的图像文件内创建图像,或者复制其他图像。

        vx_imagepatch_addressing_t image_addr;

        image_addr.dim_x = width;
        image_addr.dim_y = height;
        image_addr.stride_x = bpp;        //每个像素所占字节数,根据不同格式的图像有所不同
        image_addr.stride_y = bpp*width;    //这个值需要设置为 bpp*width 
        image_addr.scale_x = VX_SCALE_UNITY;
        image_addr.scale_y = VX_SCALE_UNITY;
        image_addr.step_x = 1;
        image_addr.step_y = 1;

        ptrs[0] = data_ptr;

        image = vxCreateImageFromHandle(context, df, &image_addr, ptrs, (vx_enum)VX_MEMORY_TYPE_HOST);

stride_x:代表在X 轴(宽度)上每一步的大小,比如RGB 图像每个像素为3个字节,则这里需要设置为3;

stride_y:代表在Y 轴(高度)上每一步的大小,即切换到下一行,所需要的字节个数。等于 stride_x * width。(这样解释应该比较清晰)。

ptrs:即读入图像文件的句柄,或者是已知图像的内存某个地址。

3、从图像通道内创建图像

        vxCreateImageFromChanne (暂未研究,等待补充!)

4、从图像的矩形内创建图像

typedef struct _vx_rectangle_t {
    volatile vx_uint32 start_x;          /*!< \brief The Start X coordinate. */
    volatile vx_uint32 start_y;          /*!< \brief The Start Y coordinate. */
    volatile vx_uint32 end_x;            /*!< \brief The End X coordinate. */
    volatile vx_uint32 end_y;            /*!< \brief The End Y coordinate. */
} vx_rectangle_t;

vx_image vxCreateImageFromROI(vx_image img, const vx_rectangle_t *rect);

        入参比较清晰,主要功能是实现从一个已知图像内截取一部分创建一个新的图像。举行代表了起始点和结束点的坐标。

5、创建虚图像

vxCreateVirtualImage(暂未研究,等待补充!)

6、 创建一个纯色的图(统一的值)

        图像的入参主要需要设置一个图像格式和像素值;创建函数会根据这两个值,去填充整个图像。
创建对应格式的图像,需要设置对应的像素内的填充值!

typedef union _vx_pixel_value_t {
    vx_uint8 RGB[3]; /*!< \brief <tt>\ref VX_DF_IMAGE_RGB</tt> format in the R,G,B order */
    vx_uint8 RGBX[4]; /*!< \brief <tt>\ref VX_DF_IMAGE_RGBX</tt> format in the R,G,B,X order */
    vx_uint8 YUV[3]; /*!< \brief All YUV formats in the Y,U,V order */
    vx_uint8 U8; /*!< \brief <tt>\ref VX_DF_IMAGE_U8</tt> */
    vx_uint16 U16; /*!< \brief <tt>\ref VX_DF_IMAGE_U16</tt> */
    vx_int16 S16; /*!< \brief <tt>\ref VX_DF_IMAGE_S16</tt> */
    vx_uint32 U32; /*!< \brief <tt>\ref VX_DF_IMAGE_U32</tt> */
    vx_int32 S32; /*!< \brief <tt>\ref VX_DF_IMAGE_S32</tt> */
    vx_uint16 P12; /*!< \brief <tt>\ref TIVX_DF_IMAGE_P12</tt> */
    vx_uint16 YUV_12[3]; /*!< \brief <tt>\ref TIVX_DF_IMAGE_NV12_P12</tt> */
    vx_uint8 reserved[16];
} vx_pixel_value_t;

vx_pixel_value_t value;

value.YUV_12[0] = 127; 
value.YUV_12[1] = 127; 
value.YUV_12[2] = 127; 

vx_image vxCreateUniformImage(vx_context context, vx_uint32 width, vx_uint32 height, vx_df_image format, const vx_pixel_value_t *value)

二、图像的基本操作

        首先要明确一点,创建了图像以后,是属于tiovx的内核空间的,在tiovx内部进行操作,这个是不需要将图像进行映射;如果需要对图像进行修改或者将图像读取出来,需要对图像进行映射,获取到对应的指针,才可以对图像进行操作。

        重点哈:对于映射出来的指针地址,可以进行读取,也可以进行修改(覆盖原有的图像,更新图像的内容。)

1、vxMapImagePatch :图像映射获取图像tiovx内核空间的地址

        status = vxMapImagePatch(image,
            &rect,
            0,
            &map_id,
            &image_addr,
            &data_ptr,
            (vx_enum)VX_READ_ONLY,
            (vx_enum)VX_MEMORY_TYPE_HOST,
            (vx_enum)VX_NOGAP_X
            );

这里重点讲一下:
image:需要映射的图像
rect:需要映射的图像宽/高
plane_index:映射图像的通道索引(一个图像可能会映射多次,这里的索引需要递增,后面给的实际用例里面可以看到区别。)
map_id:图像映射的唯一标识符,这里是输出值。由调用的函数返回。
data_ptr:图像映射的指针地址,即起始点。
usage:内存空间的属性,只读/只写/读写等三种模式
后面两个参数,目前没有作研究,保持这个参数就可以了,待后面研究后,补充。(暂缺!)

示例:将图像数据读取出来,并写入一个文件内。(分了两个步骤进行映射,这里需要注意的是第三个参数,映射的索引。)


注意:需要及时释放这个图像的映射,否则会造成内存泄漏。
vxUnmapImagePatch(out_img, map_id);

vx_status writeMosaicOutput(char* file_name, vx_image out_img)
{
  vx_status status;

  status = vxGetStatus((vx_reference)out_img);

  if(status == VX_SUCCESS)
  {
    FILE * fp = fopen(file_name,"wb");

    if(fp == NULL)
    {
      printf("File could not be opened \n");
      return (VX_FAILURE);
    }

    {
      vx_rectangle_t rect;
      vx_imagepatch_addressing_t image_addr;
      vx_map_id map_id;
      void * data_ptr;
      vx_uint32  img_width;
      vx_uint32  img_height;
      vx_uint32  num_bytes;

      vxQueryImage(out_img, VX_IMAGE_WIDTH, &img_width, sizeof(vx_uint32));
      vxQueryImage(out_img, VX_IMAGE_HEIGHT, &img_height, sizeof(vx_uint32));

      rect.start_x = 0;
      rect.start_y = 0;
      rect.end_x = img_width;
      rect.end_y = img_height;
      status = vxMapImagePatch(out_img,
                               &rect,
                               0,
                               &map_id,
                               &image_addr,
                               &data_ptr,
                               VX_READ_ONLY,
                               VX_MEMORY_TYPE_HOST,
                               VX_NOGAP_X);

      //Copy Luma
      num_bytes = fwrite(data_ptr,1,img_width*img_height, fp);

      if(num_bytes != (img_width*img_height))
        printf("Luma bytes written = %d, expected = %d", num_bytes, img_width*img_height);

      vxUnmapImagePatch(out_img, map_id);

      status = vxMapImagePatch(out_img,
                               &rect,
                               1,
                               &map_id,
                               &image_addr,
                               &data_ptr,
                               VX_READ_ONLY,
                               VX_MEMORY_TYPE_HOST,
                               VX_NOGAP_X);


      //Copy CbCr
      num_bytes = fwrite(data_ptr,1,img_width*img_height/2, fp);

      if(num_bytes != (img_width*img_height/2))
        printf("CbCr bytes written = %d, expected = %d", num_bytes, img_width*img_height/2);

      vxUnmapImagePatch(out_img, map_id);

    }

    fclose(fp);
  }

  return(status);
}

2、vxCopyImagePatch :从 或者 向 图像对象平面复制矩形块

示例1:给出了一个将一个图像拷贝到另一个图像的示例:首先使用映射,获取到源图像的tiovx内核空间地址;再通过vxCopyImagePatch,拷贝到目标图像内。(目标图像不需要映射)

其中值得注意的地方:image_addr_1 这个参数,这个如果是从图像内拷贝图像,则这个参数是传出参数,由vxMapImagePatch传出,直接给vxCopyImageatch使用;
如果是直接使用vxCopyImagePatch 拷贝内存到图像内,则需要自行设置这个参数。(这里和1.2章节这里比较相似,详细见接下来的示例2)。

        vx_imagepatch_addressing_t image_addr;

        image_addr.dim_x = width;
        image_addr.dim_y = height;
        image_addr.stride_x = bpp;        //每个像素所占字节数,根据不同格式的图像有所不同
        image_addr.stride_y = bpp*width;    //这个值需要设置为 bpp*width 
        image_addr.scale_x = VX_SCALE_UNITY;
        image_addr.scale_y = VX_SCALE_UNITY;
        image_addr.step_x = 1;
        image_addr.step_y = 1;
示例 1:
vx_status ptkdemo_copy_image_to_image(vx_image srcImage, vx_image dstImage)
{
    vx_map_id                  map_id;
    vx_rectangle_t             rect;
    vx_imagepatch_addressing_t  image_addr_1;
    vx_imagepatch_addressing_t  image_addr_2;
    vx_df_image                img_format;
    vx_uint32                  img_width;
    vx_uint32                  img_height;
    vx_status                  vxStatus;

    uint8_t                    *data_ptr_src_1;
    uint8_t                    *data_ptr_src_2;

    vxQueryImage(srcImage, VX_IMAGE_FORMAT, &img_format, sizeof(vx_df_image));
    vxQueryImage(srcImage, VX_IMAGE_WIDTH, &img_width, sizeof(vx_uint32));
    vxQueryImage(srcImage, VX_IMAGE_HEIGHT, &img_height, sizeof(vx_uint32));

    rect.start_x = 0;
    rect.start_y = 0;
    rect.end_x   = img_width;
    rect.end_y   = img_height;

    // get source pointer
    vxStatus = vxMapImagePatch(srcImage,
                               &rect,
                               0,
                               &map_id,
                               &image_addr_1,
                               (void **)&data_ptr_src_1,
                               VX_READ_ONLY,
                               VX_MEMORY_TYPE_HOST,
                               VX_NOGAP_X);
    PTK_assert(VX_SUCCESS == vxStatus);
    vxUnmapImagePatch(srcImage, map_id);

    vxCopyImagePatch(dstImage, &rect, 0, &image_addr_1, data_ptr_src_1, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST);

    // chroma
    if (img_format == VX_DF_IMAGE_NV12)
    {
        // get source pointer
        vxStatus = vxMapImagePatch(srcImage,
                                   &rect,
                                   1,
                                   &map_id,
                                   &image_addr_2,
                                   (void **)&data_ptr_src_2,
                                   VX_READ_ONLY,
                                   VX_MEMORY_TYPE_HOST,
                                   VX_NOGAP_X);
        PTK_assert(VX_SUCCESS == vxStatus);
        vxUnmapImagePatch(srcImage, map_id);

        vxCopyImagePatch(dstImage, &rect, 1, &image_addr_2, data_ptr_src_2, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST);
    }

    return vxStatus;

}

示例2:image_addr.stride 这个参数是根据不同图像的类型而变动的。


比如YUYV 是422图像,大小是W*H*2;这里设置为2;
如果是RGB ,则大小为:W*H*3,所以设置为3;
特殊情况:如果是YUV420,则需要进行两次映射了;这里需要借助vxMapImagePatch;先获取两次映射的地址,然后再拷贝数据到这个内存里面。(注意那个映射的索引变化,在示例1内)

vx_status app_running_usbCamera(USBCameraObj * usbCameraObj)
{
    vx_status status = VX_SUCCESS;
    memset(&usbCameraObj->buf, 0, sizeof(usbCameraObj->buf));
    usbCameraObj->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    usbCameraObj->buf.memory = V4L2_MEMORY_MMAP;
    ioctl(usbCameraObj->fd, VIDIOC_DQBUF, &usbCameraObj->buf); /* 将已经捕获好视频的内存拉出已捕获视频的队列 */
    ioctl(usbCameraObj->fd, VIDIOC_QBUF, &usbCameraObj->buf);  /* 将空闲的内存加入可捕获视频的队列 */

    vx_rectangle_t rect;
    vx_imagepatch_addressing_t image_addr;

    vx_uint32 img_width;
    vx_uint32 img_height;

    vxQueryImage(usbCameraObj->imageInputYUYV, VX_IMAGE_WIDTH, &img_width, sizeof(vx_uint32));
    vxQueryImage(usbCameraObj->imageInputYUYV, VX_IMAGE_HEIGHT, &img_height, sizeof(vx_uint32));

    //拷贝图像到内核空间
    rect.start_x = 0;
    rect.start_y = 0;
    rect.end_x = img_width;
    rect.end_y = img_height;

    image_addr.dim_x = img_width;
    image_addr.dim_y = img_height;
    image_addr.stride_x = 2; /* YUYV */
    image_addr.stride_y = img_width * 2;
    image_addr.scale_x = VX_SCALE_UNITY;
    image_addr.scale_y = VX_SCALE_UNITY;
    image_addr.step_x = 1;
    image_addr.step_y = 1;
    //执行拷贝操作,将USB 摄像头的YUYV图像拷贝到目标图像内
    status = vxCopyImagePatch(usbCameraObj->imageInputYUYV,                                     //目标图像
                              &rect,
                              0,
                              &image_addr,
                              (void *)usbCameraObj->buffers[usbCameraObj->buf.index].start,     // 传入需要拷贝的图像
                              VX_WRITE_ONLY,
                              VX_MEMORY_TYPE_HOST);
    return status;
}

3、图像查询函数

vxQueryImage;函数可以通过设置不同的入参,查询不同的图像属性:
比较简单,不做赘述。(图像格式/宽度/高度等等)

vxQueryImage(dstImage, VX_IMAGE_FORMAT, &img_format, sizeof(vx_df_image));
vxQueryImage(dstImage, VX_IMAGE_WIDTH,  &img_width, sizeof(vx_uint32));
vxQueryImage(dstImage, VX_IMAGE_HEIGHT, &img_height, sizeof(vx_uint32));

可供查询的属性,见下列代码:

enum vx_image_attribute_e {
    /*! \brief Queries an image for its width. Read-only. Use a <tt>\ref vx_uint32</tt> parameter. */
    VX_IMAGE_WIDTH = VX_ATTRIBUTE_BASE(VX_ID_KHRONOS, VX_TYPE_IMAGE) + 0x0,
    /*! \brief Queries an image for its height. Read-only. Use a <tt>\ref vx_uint32</tt> parameter. */
    VX_IMAGE_HEIGHT = VX_ATTRIBUTE_BASE(VX_ID_KHRONOS, VX_TYPE_IMAGE) + 0x1,
    /*! \brief Queries an image for its format. Read-only. Use a <tt>\ref vx_df_image</tt> parameter. */
    VX_IMAGE_FORMAT = VX_ATTRIBUTE_BASE(VX_ID_KHRONOS, VX_TYPE_IMAGE) + 0x2,
    /*! \brief Queries an image for its number of planes. Read-only. Use a <tt>\ref vx_size</tt> parameter. */
    VX_IMAGE_PLANES = VX_ATTRIBUTE_BASE(VX_ID_KHRONOS, VX_TYPE_IMAGE) + 0x3,
    /*! \brief Queries an image for its color space (see <tt>\ref vx_color_space_e</tt>). Read-write. Use a <tt>\ref vx_enum</tt> parameter. */
    VX_IMAGE_SPACE = VX_ATTRIBUTE_BASE(VX_ID_KHRONOS, VX_TYPE_IMAGE) + 0x4,
    /*! \brief Queries an image for its channel range (see <tt>\ref vx_channel_range_e</tt>). Read-only. Use a <tt>\ref vx_enum</tt> parameter. */
    VX_IMAGE_RANGE = VX_ATTRIBUTE_BASE(VX_ID_KHRONOS, VX_TYPE_IMAGE) + 0x5,
    /*! \brief Queries an image for its total number of bytes. Read-only. Use a <tt>\ref vx_size</tt> parameter. */
    VX_IMAGE_SIZE = VX_ATTRIBUTE_BASE(VX_ID_KHRONOS, VX_TYPE_IMAGE) + 0x6,
    /*! \brief Queries memory type if created using vxCreateImageFromHandle. If vx_image was not created using
        vxCreateImageFromHandle, VX_MEMORY_TYPE_NONE is returned. Use a <tt>\ref vx_memory_type_e</tt> parameter. */
    VX_IMAGE_MEMORY_TYPE = VX_ATTRIBUTE_BASE(VX_ID_KHRONOS, VX_TYPE_IMAGE) + 0x7,
};

4、释放/销毁图像

vxReleaseImage;即释放已有的图像。


其他操作,后续会不停的更新。

水平有限,欢迎指正!


【声明】
【欢迎转载转发,请注明出处。原创比较辛苦,请尊重原创,祝大家学习愉快!】
【博主专注嵌入式开发,具有多年嵌入式软、硬件开发经验,欢迎大家学习交流!】
【如有嵌入式相关项目需求,欢迎私信】

上一篇:HRBU_20211112训练


下一篇:GeoGebra笔记5 踪迹动画(简单的旋转体的生成过程)