数据压缩 彩色空间转换

彩色空间转换


实验原理

  • YUV到RGB空间的转换公式
    由电视原理知识可知,数字视频系统中对色差信号的压缩公式为:
    Cr=0.713(RY)
    Cb=0.564(BY)
    此时色差信号经过归一化处理,动态范围为-0.5-0.5,让色差零电平对应码电平128(8比特量化时),则有:
    V=0.713(RY)+128
    U=0.564(BY)+128
    易推得YUV到RGB空间的转换公式为:
    R=Y+1.4075(V128)
    G=Y0.3455(U128)0.7169(V128)
    B=Y+1.779(U128)

  • 取样格式
    YUV空间的取样格式为4:2:0,U、V两个色差信号的亮度信号取样频率的四分之一,在水平方向和垂直方向上的取样点数均为Y的一半。而RGB空间的取样格式为4:4:4,则需要对U、V分量做上采样,让水平方向和垂直方向上取样点数与Y一致。

  • 存储方式与占用空间
    YUV格式的视频将整帧图像的Y打包存储,依次再存整帧的U、V,然后再是下一帧的数据;YUV格式按4:2:0取样的视频所占空间大小为Width*Height*Frame*1.5(Width –宽、Height–高、Frame–帧数)。RGB格式的视频按每个像素B、G、R的顺序储存数据;所占空间大小为Width*Height*Frame*3。
  • YUV转换为RGB后颜色溢出的处理
    经过上式计算,原来为整数的Y、U、V转换为浮点数R、G、B,由于RGB文件的颜色是8bit量化,则数值不在0-255范围内的颜色会出现溢出,显示错误。本实验采用了定义float型的中间变量存放计算RGB的直接结果,而后通过简单的判断防止颜色溢出。
  • 带参数的主函数的参数输入
    打开项目属性窗口,通过设置工作目录、命令参数完成主函数的参数输入。
    这里写图片描述

YUV2RGB文件转换流程分析

  1. 程序初始化(打开两个文件、定义变量和缓冲区 等)
  2. 读取YUV文件,抽取YUV数据写入缓冲区
  3. 调用YUV2RGB的函数实现YUV到RGB数据的转换
  4. 写RGB文件
  5. 程序收尾工作(关闭文件,释放缓冲区)

关键代码及其分析

分析部分写在代码注释中

YUVtoRGB.cpp

    #include "stdlib.h"
    #include "YUVtoRGB.h"
    static float YUVRGB14075[256];
    static float YUVRGB03455[256];
    static float YUVRGB07169[256];
    static float YUVRGB1779[256];
    int YUV2RGB (int Width, int Height, void *rgb_out, void *y_in, void *u_in, void *v_in)
    {
    static int init_done = 0;
    long i, j, size;
    unsigned char *r, *g, *b;
    float rf, gf, bf;//中间变量,用来防止色彩溢出
    unsigned char *y, *u, *v;
    unsigned char *y_buffer, *u_buffer, *v_buffer,*rgb_buffer;
    unsigned char *sub_u_buf, *sub_v_buf;

    if (init_done == 0)
    {
        InitLookupTable();
        init_done = 1;
    }

    // check to see if width and height are divisible by 2
    if ((Width % 2) || (Height % 2)) return 1;
    size = Width * Height;
    y_buffer = (unsigned char *)y_in;
    u_buffer = (unsigned char *)u_in;
    v_buffer = (unsigned char *)v_in;
    rgb_buffer = (unsigned char *)rgb_out;
    sub_u_buf = (unsigned char *)malloc(Width*Height);
    sub_v_buf = (unsigned char *)malloc(Width*Height);
    //sub_u_buf、sub_v_buf用于存放宽高都上采样以后的U/V,便于计算RGB

    /************************上采样***********************/
    for (i = 0; i < Height; i++)
    {
        for (j = 0; j < Width; j++)
        {
            *(sub_u_buf + i*Width + j) = *(u_buffer + (i / 2)*Width/2 + j / 2);
            *(sub_v_buf + i*Width + j) = *(v_buffer + (i / 2)*Width /2+ j / 2);
        }
    }
    /****************************************************/

    b = rgb_buffer;//通过r/g/b三个指针改变rgb_buffer的内容值
    y = y_buffer;
    u = sub_u_buf;
    v = sub_v_buf;
    for (i = 0; i < Height; i++)
    {
        for (j = 0; j < Width; j++)
        {
            g = b + 1;//RGB格式文件储存按照BGR的顺序
            r = b + 2;
            rf= (*y + YUVRGB14075[*v]);
            gf = (*y - YUVRGB03455[*u] - YUVRGB07169[*v]);
            bf = (*y + YUVRGB1779[*u]);
            *r =(rf>0?(rf>255? 255:(unsigned char)rf):0);
            *g = (gf>0 ? (gf>255 ? 255 : (unsigned char)gf) : 0);
            *b = (bf>0 ? (bf>255 ? 255 : (unsigned char)bf) : 0);
    /*由YUV到RGB的转换公式可知,经过计算得到的RGB值都不是整数,由于计算
    系数的精度,RGB值有可能出现超出0-255的范围。由于unsigned char型
    数据占1字节,超出0-255的数值会溢出,发生彩色显示错误。例如256会变为
    0。
    可以采用一个float型中间变量(rf/bf/gf)来暂存由彩色转换公式计算得
    来的RGB值,经过判断以后,再给unsigned char型的r/g/b进行赋值,大
    于255则赋值为255,小于0则赋值为0,在0-255范围内的数值则进行强制类
    型转换。*/
            b += 3;
            y++;
            u++;
            v++;
        }
    }

    if (sub_u_buf != NULL)  free(sub_u_buf);
    if (sub_v_buf != NULL)  free(sub_v_buf);
    return 0;
    }

    void InitLookupTable()
    {
    int i;
    for (i = 0; i < 256; i++) YUVRGB14075[i] = (float)1.4075 * (i-128);
    for (i = 0; i < 256; i++) YUVRGB03455[i] = (float)0.3455 * (i-128);
    for (i = 0; i < 256; i++) YUVRGB07169[i] = (float)0.7169 * (i-128);
    for (i = 0; i < 256; i++) YUVRGB1779[i] = (float)1.779 * (i-128);
    }
    /*提前计算彩色转换公式中的分量值。需要特别注意的是UV值需要提前
    减128,也就是说,计算分量值时保证UV的范围在-128~127。*/

main.cpp

    #include<stdio.h>
    #include<stdlib.h>
    #include<malloc.h>
    #include"YUVtoRGB.h"
    #define u_int8_t    unsigned __int8 
    #define u_int       unsigned __int32
    #define u_int32_t   unsigned __int32
//为了防止不同编程软件数据类型int的字节数不一致,利用define进行规定
    int main(int argc,char ** argv)
    {
    u_int frameWidth = 352;     
    u_int frameHeight = 240;
    int i;

    char *rgbFileName= NULL;
    char *yuvFileName= NULL;
    FILE *rgbFile=NULL;
    FILE *yuvFile=NULL;
    u_int8_t* rgbBUFFER = NULL;
    u_int8_t* yBUFFER = NULL;
    u_int8_t* uBUFFER= NULL;
    u_int8_t* vBUFFER = NULL;
    u_int32_t videoFramesWritten = 0;
    yuvFileName = argv[1];
    rgbFileName = argv[2];
    frameWidth = atoi(argv[3]);
    frameHeight = atoi(argv[4]);
    //argv[0]是缺省的,软件自动会装填Project当中的exe文件名
    yuvFile = fopen(yuvFileName, "rb");
    if (yuvFile == NULL)
    {
        printf("cannot find yuv file\n");
        exit(1);
    }
    else
    {
        printf("The output yuv file is %s\n", yuvFileName);
    }

    rgbFile = fopen(rgbFileName, "wb");
    if (rgbFile == NULL)
    {
        printf("cannot find rgb file\n");
        exit(1);
    }
    else
    {
        printf("The input rgb file is %s\n", rgbFileName);
    }

    /* get an input buffer for a frame */
    rgbBUFFER= (u_int8_t *)malloc(frameWidth * frameHeight * 3);
    /* get the output buffers for a frame */
    yBUFFER= (u_int8_t *)malloc(frameWidth * frameHeight);
    uBUFFER= (u_int8_t *)malloc(frameWidth * frameHeight/4);
    vBUFFER= (u_int8_t *)malloc(frameWidth * frameHeight/4);

    if (rgbBUFFER == NULL || yBUFFER == NULL || uBUFFER == NULL || vBUFFER == NULL)
    {
        printf("no enought memory\n");
        exit(1);
    }
    while(fread(yBUFFER,1,frameWidth*frameHeight,yuvFile)
    &&fread(uBUFFER,1,frameWidth*frameHeight/4,yuvFile)
    &&fread(vBUFFER,1,frameWidth*frameHeight/4,yuvFile))
/*当读入成功时fread函数返回实际读取的数据项个数,当整帧的YUV都能读
入时,进入while循环。由于文件指针自动向后移动到下一个未读字节,故该
程序也适用于视频文件,整段视频被读完fread函数返回0,则结束循环。*/
    {
        if(YUV2RGB(frameWidth, frameHeight, rgbBUFFER, yBUFFER, uBUFFER, vBUFFER))
        {
            printf("error");
            return 0;
        }
        fwrite(rgbBUFFER, 1, frameWidth * frameHeight*3, rgbFile);
        printf("\r...%d", ++videoFramesWritten);
    }

    printf("\n%u %ux%u video frames written\n", 
        videoFramesWritten, frameWidth, frameHeight);
    if(rgbFile!=NULL)   fclose(rgbFile);
    if(yuvFile!=NULL)   fclose(yuvFile);
    if(rgbBUFFER!=NULL) free(rgbBUFFER);
    if(yBUFFER!=NULL)   free(yBUFFER);
    if(uBUFFER!=NULL)   free(uBUFFER);
    if(vBUFFER!=NULL)   free(vBUFFER);
    return(0);
    }

实验结果及其分析

将转换得的RGB文件输入已有的RGB2YUV程序,得到新的YUV文件,用YUVplayer打开新的YUV文件查看结果。
down.yuv

这里写图片描述
这里写图片描述

由于计算系数精度、强制类型转换等带来的误差,经过彩色空间转换的YUV值会存在些许差异,但从画面、数据上看,这个差异都不算太明显。

没有采用中间变量,彩色溢出的情况
这里写图片描述

下面给出另外三个YUV视频文件进行彩色空间转换的结果,佐证该程序是正确的。
akiyo.yuv
这里写图片描述
这里写图片描述

src01.yuv
这里写图片描述
这里写图片描述

src04.yuv
这里写图片描述
这里写图片描述


发布了7 篇原创文章 · 获赞 8 · 访问量 6890
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览