yuv数据格式介绍与rgb的转换,图像文件的封装

通常我们用RGB表示一种彩色。计算机系统里的LCD显示的数据就是RGB来表示每个像素的颜色。
而在我们生活里,有黑白电视机与彩色电视机两种,拍摄节目源时不可以用两种不同的摄像机来存放两种图像数据。
所以为了兼容两种电视机,专家就引入YUV格式代替RGB,其中Y表示亮度, U和V表示色差。 黑白电视机只用Y信号, 而彩色电视机可由YUV转换成RGB再显示颜色。

通常我们所用的YUV格式是 ITU-R 的标准 , 也叫YCbCr.

YUV是由RGB格式的数据转换得来。

    Y    Y = 0.299 x R + 0.587 x G + 0.114 x B + 0 
    U    Cb = -0.169 x R - 0.331 x G + 0.499 x B + 128 
    V    Cr = 0.499 x R - 0.418 x G - 0.0813 x B + 128 

RGB也可由YUV数据转换:

        R = clamp(Y + 1.402 x (Cr - 128)) 
        G = clamp(Y - 0.344 x (Cb - 128) - 0.714 x (Cr - 128)) 
        B = clamp(Y + 1.772 x (Cb - 128)) 

    //其中clamp函数是限定里面的值是在0-255之间 

注意: RGB格式的数据转换成YUV格式不会压缩数据,数据大小是不变的,即原来RGB888, 转换成YUV也是各占8位

常用的YUV又分成: YUV4:4:4 YUV4:2:2 YUV4:2:0

///////
YUV4:4:4 
    其实就是YUV的数据各占用8位, 每个像素都由YUV组成

    同一行的相邻4个像素数据:   Y0U0V0    Y1U1V1   Y2U2V2  Y3U3V3
                  存储时:    Y0 U0 V0  Y1 U1 V1 Y2 U2 V2 Y3 U3 V3  //即每个像素YUV的数据都会存放起来
    为什么叫4:4:4 , 意思就是4个像素里的数据有4个Y, 4个U, 4个V

//////
YUV4:2:2
    其实绝大部分相邻的两个像素,数据差异应不大。所以为了节点空间便于存储,丢失每个像素的部分数据。
    专家研究表明我们人对亮度比较敏感,而对色彩不怎么敏感。所以每个像素的亮度Y数据是绝对不动的,而色差数据可以进行丢弃。      

    同一行的相邻4个像素数据:   Y0U0V0    Y1U1V1   Y2U2V2  Y3U3V3
              存储时:    Y0 U0  Y1 V1 Y2 U2 Y3 V3  // 每两个相邻的像素, 一个丢弃V数据,一个丢弃U数据
    为什么叫4:2:2,  意思就是相邻的4个像素里有4个Y, 2个U, 2个V。 按上面存储的顺序也叫YUYV.

    但还原成RGB数据必须需要YUV, 像第一个像素只有Y0U0是没法还原的,这时只能用下一像素的V1数据。
          还原时的YUV:  [Y0U0V1] [Y1U0V1] [Y2U2V3] [Y3U2V3]  //这样还原理论上会对图像的质量有影响的,但我们看不出来的.

/////
YUV4:2:0
    专家们进一步研究表示,每一行的相邻两个像素与下一行同位置的两个像素数据差异不大,可以进一步的丢数据。

    如两行的像素数据:
               Y00U00V00   Y01U01V01   Y02U02V02   Y03U03V03  ....  
               Y88U88V88   Y89U89V89   Y90U90V90   Y91U91V91  ....

    存储时:        Y00U00 Y01 Y02U02  Y03    //每个像素的Y数据保留, 两个像素数据只保留一个U数据。这一行不保留V数据(YUV:  420)
               Y88V88 Y89 Y90V90  Y91    // ....  两个像素数据只保留一个V数据, 这行不保留U数据(YUV:  402)

    还原时只能相同位置的上下两行4个像素结合还原:
            Y00U00V88  Y01U00V88  Y02U02V90  Y03U02V90
            Y88U00V88  Y89U00V88  Y90U02V90  Y91U02V90


yuv数据还分成打包的,平面的。
    打包的意思是: yuv数据是顺序存放Y,接着U,再接着V数据存放。
    平面的意思是: yuv数据是分成三个地方存放, 一个地方只存Y数据, 一个只存U数据, 一个只存V数据

///
图像文件格式:

pnm文件是由: "P6\n宽度 高度\n255\n"文件头再加上RGB888数据组成.
bmp文件通常是由: 54字节的文件头再加RGB888数据组成.
建议使用pnm文件格式, 因文件头简单,且像素数据不用按行翻转。

yuv422转成rgb888, 封装成pnm文件的代码:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>

int clamp(int val);
int main(int argc, char *argv[])
{
    int w, h, x, y;
    unsigned char *yuyv, *tmp;
    int yuyv_fd, pnm_fd;
    int y0, u0, y1, v1;
    unsigned char rgb[3];

    if (argc < 5)
    {
        printf("usage: ./a.out yuv_file  pnm_file  w  h \n");
        return 1;
    }   

    w = atoi(argv[3]);
    h = atoi(argv[4]);


    yuyv_fd = open(argv[1], O_RDONLY);
    if (yuyv_fd < 0)
    {
        perror("open yuyv");
        return 1;
    }

    struct stat buf;
    fstat(yuyv_fd, &buf);
    yuyv = mmap(NULL,   buf.st_size, PROT_READ, MAP_SHARED, yuyv_fd, 0);
    tmp = yuyv;

    pnm_fd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0644);
    char pnm_head[100];
    sprintf(pnm_head, "P6\n%d %d\n255\n", w, h);
    write(pnm_fd, pnm_head, strlen(pnm_head)); 

    for ( y = 0; y < h; y++)
    {
        for (x = 0; x < w; x+=2) //每次两个像素数据进行转换
        {
            y0 = tmp[0];    
            u0 = tmp[1];
            y1 = tmp[2];
            v1 = tmp[3];
            tmp += 4; 
            // y0u0v1,  y1u0v1

            rgb[0] = clamp(y0+1.402*(v1-128));
            rgb[1] = clamp(y0-0.344*(u0-128)-0.714*(v1-128));
            rgb[2] = clamp(y0+1.772*(u0-128)); 
            write(pnm_fd, rgb, 3);      

            rgb[0] = clamp(y1+1.402*(v1-128));
            rgb[1] = clamp(y1-0.344*(u0-128)-0.714*(v1-128));
            rgb[2] = clamp(y1+1.772*(u0-128)); 
            write(pnm_fd, rgb, 3);      
        }
    }

    close(pnm_fd);
    munmap(yuyv, buf.st_size);
    close(yuyv_fd);


    return 0;
}

int clamp(int val)
{
    if (val < 0)
        return 0;

    if (val > 255)
        return 255;

    return val;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值