ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-12-27 16:00:51  阅读:396  来源: 互联网

标签:img Image TDA4 TIOVX vx 图像 VX image IMAGE


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


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


一、创建图像

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

函数名说明

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;即释放已有的图像。


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

水平有限,欢迎指正!


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

标签:img,Image,TDA4,TIOVX,vx,图像,VX,image,IMAGE
来源: https://blog.csdn.net/AIRKernel/article/details/122170313

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有