欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

YUV420图像的剪裁与填充操作

最编程 2024-08-15 16:27:06
...

前言

图像数据处理由于处理繁琐,数据量大,为了不占用cpu资源,一般由硬件模块处理,或者由GPU做处理。但是,对于一些简单的处理操作,我们还是有必要学习一下,因为有些简单场景没有必要调用硬件资源。以下操作虽然简单,但是我也调试了千万遍,才知道“纸上得来终觉浅,绝知此事要躬行”。

一、RGB的flip操作

staic void _rgb_flip(uint16 *addr,int width,int height)
{   
    uint8 i = 0, j = 0;
    uint16 tmp_data = 0;
    for(i = 0;i < height;i++)
    {
        for(j = 0;j < width / 2;j++)
        {
            tmp_data = *(addr + j);
            *(addr + j) = *(addr + width - j - 1);
            *(addr + width - j - 1) = tmp_data;
        }
        addr += width;
    }   
}

rgb的存储格式是一个像素点对应一个数据存储,例如,RGB565的存储格式如下,所以一个像素点占俩字节,该算法也只适用565格式,不适用888格式


rgb565存储格式

二、YUV的填充操作

void _ISP_YUV420pFillBlack(ISP_ADDRESS_T *src, ISP_ADDRESS_T *dst,
                                       uint32 src_w, uint32 src_h,     uint32 dst_w, uint32 dst_h)
{
       uint32 s_y = src->yaddr, s_u = src->uaddr;
       uint32 d_y = dst->yaddr, d_u = dst->uaddr;
       uint8 y_value = 0, u_value = 128; //black
       if (dst_w > src_w && dst_h == src_h && (dst_w - src_w) % 4 == 0) {
               ISPSRV_LOGI("Fill in width");
               uint32 offset = (dst_w - src_w) >> 1;
               uint32 offset_ = dst_w - src_w - offset;
               for (int i = 0; i < dst_h; i++){
                       SCI_MEMSET((void *)d_y, y_value, offset);
                       d_y += offset;
                       SCI_MEMCPY((void *)d_y, s_y, src_w);
                       d_y += src_w, s_y += src_w;
                       SCI_MEMSET((void *)d_y, y_value, offset_);
                       d_y += offset_;

                       SCI_MEMSET((void *)d_u, u_value, offset >> 1);
                       d_u +=  offset >> 1;
                       SCI_MEMCPY((void *)d_u, s_u, src_w>>1);
                       d_u += src_w>>1, s_u += src_w>>1;
                       SCI_MEMSET((void *)d_u, u_value, offset_ >> 1);
                       d_u += offset_ >> 1;
               }
       } else if (dst_w == src_w && dst_h > src_h && (dst_h - src_h) % 4 == 0) {
               ISPSRV_LOGI("Fill in height");
               uint32 offset = (dst_h - src_h)>>1;
               uint32 offset_ = dst_h - src_h - offset;
               SCI_MEMSET((void *)d_y, y_value, offset * dst_w);
               d_y += offset * dst_w;
               SCI_MEMCPY((void *)d_y, s_y, src_w * src_h);
               d_y += dst_w * src_h;
               SCI_MEMSET((void *)d_y, y_value, offset_ * dst_w);
               d_y += offset_ * dst_w;

               SCI_MEMSET((void *)d_u, u_value, (offset * src_w) >>1);
               d_u += (offset * src_w) >>1;
               SCI_MEMCPY((void *)d_u, s_u, (src_w * src_h) >> 1);
               d_u += (src_w * src_h) >> 1;
               SCI_MEMSET((void *)d_u, u_value, (offset_ * src_w)>>1);
               d_u += (offset_ * src_w)>>1;
       } else {
               ISPSRV_LOGE("Size error !!!");
       }
}

该填充操作上半部分是未经过验证的,应该还存在问题,贴上去只为了凑点字数,看Fill in height即可
三、YUV的裁剪操作

void _ISP_YUV420pCutBlack(ISP_ADDRESS_T *src, ISP_ADDRESS_T *dst,
                                       uint32 src_w, uint32 src_h,     uint32 dst_w, uint32 dst_h)
{
       uint32 s_y = src->yaddr, s_u = src->uaddr;
       uint32 d_y = dst->yaddr, d_u = dst->uaddr;
       uint8 y_value = 0,u_value = 128;
       uint32 width = 0,height = 0;

       ISPSRV_LOGI("cut in width");
       uint32 offset = (src_w - dst_w)>>1;
       uint32 offset_ = src_w - dst_w - offset;
       s_y += offset;
       for(height = 0;height < dst_h;height++)
       {
            SCI_MEMCPY(d_y,s_y,dst_w);
            d_y += dst_w;
            s_y += src_w;
       }

       s_u += offset;
       for(height = 0;height < (dst_h >> 1);height++)
       {
            SCI_MEMCPY(d_u,s_u,dst_w);
            d_u += dst_w;
            s_u += src_w;
       }
}

针对YUV的填充和裁剪,最重要的就是要搞清楚YUV的数据存储格式,以上算法也是针对YUV420格式的


yuv420存储格式

之前参考别人代码,纳闷为什么只对y和u地址操作,而不做v地址操作,仔细一看会发现做u地址处理时已经把v数据也处理了。

推荐阅读