sws_scale转yuv到rgb

在linux上开发ffmpeg需要安装的dev库
sudo apt-get install libavcodec-dev
sudo apt-get install libavformat-dev
sudo apt-get install libavfilter-dev
sudo apt-get install libjpeg-dev
sudo apt-get install libswscale-dev 

sws_scale将yuv转换为rgb

输入m_pFrame存储的是yuv格式,输出到videoFrame是rgb格式,转换代码如下,先创建格式为AV_PIX_FMT_RGB24的swsContext,然后分配内存,再调用sws_scale完成转换。

    videoFrame.width = m_pFrame->width;
    videoFrame.height = m_pFrame->height;

    int linesize[4];
    uint8_t* data[4];
    struct SwsContext *swsContext = NULL;
    swsContext = sws_getContext(m_pFrame->width, m_pFrame->height, AVPixelFormat(m_pFrame->format),
                             videoFrame.width, videoFrame.height, AV_PIX_FMT_RGB24,
                             SWS_BILINEAR, NULL, NULL, NULL);
    av_image_alloc(data, linesize, videoFrame.width, videoFrame.height, AV_PIX_FMT_RGB24, 1);
    sws_scale(swsContext, m_pFrame->data, m_pFrame->linesize, 0, m_pFrame->height, data, linesize);

    videoFrame.lineSize = linesize[0];
    videoFrame.frameData.resize(videoFrame.lineSize * videoFrame.height);
    memcpy(videoFrame.frameData.data(), data[0], videoFrame.frameData.size());

    sws_freeContext(swsContext);
    avpicture_free((AVPicture *)data);
代码分析
  1. 首先获取SwsContext
  2. av_image_alloc根据width, height, pix_fmt初始化linesize[4], data[4]
  3. sws_scale色彩转换,完成yuv到rgb的转换
  4. 将data[0]的数据全部copy到framedata.data()

说明:data[0]中放的是rgb数据,非平面数据,所以data[1],data[2],data[3]都是没有数据的。
linesize[0]的值就是rgb数据的总size。

  • 对于packed格式的数据(例如RGB24),会存到data[0]里面。
  • 对于planar格式的数据(例如YUV420P),则会分开成data[0],data[1],data[2]…(YUV420P中data[0]存Y,data[1]存U,data[2]存V)

YUV背景知识

YUV Formats分成两个格式
  • 紧缩格式(packed formats):将Y、U、V值存储成Macro Pixels数组,和RGB的存放方式类似。
  • 平面格式(planar formats):将Y、U、V的三个分量分别存放在不同的矩阵中。
常用的YUV格式
  • YUV 4:4:4采样,每一个Y对应一组UV分量,一个YUV占8+8+8 = 24bits 3个字节。
  • YUV4:2:2采样,每两个Y共用一组UV分量,一个YUV占8+4+4 = 16bits 2个字节。
  • YUV4:2:0采样,每四个Y共用一组UV分量,一个YUV占8+2+2 = 12bits 1.5个字节。
4:4:4表示每个像素采样YUV三个分量
  • 4:2:2表示2:1的水平取样,垂直完全采样。
  • 4:2:0表示2:1的水平取样,垂直2:1采样。
  • 4:1:1表示4:1的水平取样,垂直完全采样。

YUV4:2:0并不是说只有U(即Cb),V(即Cr)一定为0,而是指U:V互相援引,时见时隐,也就是说对于每一个行,只有一个U或者V分量,如果一行是4:2:0的话,下一行就是4:0:2,再下一行是4:2:0…以此类推。

我们最常见的YUV420P和YUV420SP都是基于4:2:0采样的,所以如果图片的宽为width,高为heigth,在内存中占的空间为width*height * 3 / 2,其中前width * height的空间存放Y分量,接着width * height / 4存放U分量,最后width * height / 4存放V分量。

YUV420P又叫plane平面模式,Y , U , V分别在不同平面,也就是有三个平面,它是YUV标准格式4:2:0,主要分为:YU12和YV12。

YV12排列举例
  • 2*2图像, 4个Y: YYYYVU;
  • 4*4图像, 16个Y: YYYYYYYYYYYYYYYYVVVVUUUU
YU12格式(I420)
  • YU12:亮度(行×列) + U(行×列/4) + V(行×列/4)
  • YU12: YYYYYYYY UUVV => YUV420P
  • YV12: YYYYYYYY VVUU => YUV420P

YUV420SP格式的图像阵列,首先是所有Y值,然后是UV或者VU交替存储,NV12和NV21属于YUV420SP格式,是一种two-plane模式,即Y和UV分为两个plane,但是UV(CbCr)为交错存储,而不是分为三个平面。

android手机从摄像头采集的预览数据一般都是NV21,存储顺序是先存Y,再VU交替存储,NV21存储顺序是先存Y值,再VU交替存储:YYYYVUVUVU,以
4 X 4 图片为例子,占用内存为 4 X 4 X 3 / 2 = 24 个字节

yuv viewer工具
  • https://github.com/liuyang1/yuv-viewer.git

  • 需要注意的是Cb,Cr在计算过程中是会出现负数的,但是从-128到127这些数值都用一个字节表示,读取的时候就映射0到255这个区间,成为了无符号的值,所以要减去128,才能参与公式计算。

  • 由于 U(Cb)、V(Cr) 取值范围是 [﹣128, 127],对应的浮点数表示为 [﹣0.5, 0.5];而在存储时,为了方便存储,跟Y数据一样,统一用一个(无符号)字节表示, 即取值范围是 [0, 255],对应的浮点数表示为:[0, 1],所以在读取是,需要 将 U(Cb)、V(Cr)的浮点值 [0, 1] 减去 128(0.5) 使其变为 :[﹣0.5, 0.5]。

常见错误
  • 没有对计算得到的RGB数值进行判断,导致强制类型转换成unsigned char之后数据溢出了,从而导致图像上某些点变成红色和蓝色。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值