彩色空间转换实验:RGB2YUV

实验原理

一、YUV与RGB空间的转换
在电视原理中我们学到过YUV与RGB的转换公式如下:

Y=0.2990R+0.5870G+0.1140B
V=0.7010R-0.5870G-0.1140B
U=-0.2990R-0.5870G+0.8860B

除此之外,还需要对两个色差信号进行归一化,使得压缩后的色差信号动态范围控制在0.5以内,所以最终的转换公式为:

Y=0.2990R+0.5870G+0.1140B
U=-0.1684R-0.3316G+0.5B
V=0.5R-0.4187G-0.0813B

二、量化后的码电平分配
对YUV信号进行8bit量化,共256个量化等级。Y为亮度信号,其上端留下20级防止信号造成过载,下端留下16级作为动态保护带;UV信号则是上端留下15级,下端留下16级,除此之外,UV信号还需进行零电平调整,使其零电平对应码电平+128

三、4:2:0色度亚采样
4:2:0格式下,色差信号的取样频率为亮度信号的四分之一。

编程思路

1、根据RGB文件的格式,将其数据存入开辟的缓存空间中,再分离R、G、B三个分量,在文件中数据顺序为BGRBGRBGR…。

2、开辟YUV的缓存空间,根据公式和读取的RGB分量计算YUV分量,注意量化码电平分配和UV信号加128。

3、开辟新的UV空间,进行4:2:0格式采样,计算新的UV时取宽高的4个数据为一组求平均值。

3、将YUV分量写入新建的yuv文件,在YUV文件中数据顺序为:所有Y所有U所有V。

C++具体代码:

RGB2YUV:


#include<stdio.h>

#include<stdlib.h>

 

#pragma warning(disable:4996);

 

int main(int argc, char *argv[])

{

       FILE*yuvfile = NULL;

       FILE*rgbfile = NULL;

       unsigned char *r, *g, *b; int i, j;

       const int width = 256; const int height = 256;

       if(!(rgbfile = fopen(argv[1], "rb")))

       {

              printf("Fileopen error!");

       }

       else

       {

              printf("Fileopen success!");

       }

       if(!(yuvfile = fopen(argv[2], "wb")))

       {

              printf("Fileopen error!");

       }

       else

       {

              printf("Fileopen success!");

       }//打开文件

       unsigned char *rgbBUF = (unsigned char*)malloc(sizeof(unsigned char) * 3 * width *height);

       unsigned char *yBUF = (unsigned char*)malloc(sizeof(unsigned char) * width * height);

       unsigned char *uBUF = (unsigned char*)malloc(sizeof(unsigned char) * width * height);

       unsigned char *vBUF = (unsigned char*)malloc(sizeof(unsigned char) * width * height);//缓冲区

       fread(rgbBUF,sizeof(unsigned char),  width* height*3,rgbfile);

       for(int i = 0; i < width*height; i++)

       {

 

                     yBUF[i]= 0.299 * rgbBUF[3 * i + 2] + 0.587 * rgbBUF[3 * i + 1] + 0.114 * rgbBUF[3 *i];

                     uBUF[i]= -0.1684* rgbBUF[3 * i + 2] - 0.3316 * rgbBUF[3 * i + 1]+ 0.5 * rgbBUF [3 * i]+ 128;

                     vBUF[i]= 0.5 * rgbBUF[3 * i + 2] - 0.4187 * rgbBUF[3 * i + 1] - 0.0183 * rgbBUF[3 * i]+ 128;

 

 

                     if(yBUF[i] > 235)

                            yBUF[i]= 235;

                     if(yBUF[i] < 16)

                            yBUF[i]= 16;

                     if(uBUF[i] > 240)

                            uBUF[i]= 240;

                     if(uBUF[i] < 16)

                            uBUF[i]= 16;

                     if(vBUF[i] > 240)

                            vBUF[i]= 240;

                     if(vBUF[i] < 16)

                            vBUF[i]= 16;

                     

              

       }//计算RGB转YUV

       unsigned char *u = (unsigned char*)malloc(sizeof(unsigned char) * width * height*0.25);

       unsigned char *v = (unsigned char*)malloc(sizeof(unsigned char) * width *height*0.25);//用于4:2:0采样写入的UV分量

       for(i = 0; i <height; i += 2)

       {

              for(j = 0; j < width; j += 2)

              {

                     u[i/ 2*width/2+j/2] = (uBUF[i*width+j] + uBUF[i*width+ 1+j] + uBUF[(i+1)*width +j] + uBUF[(i+1)*width + j+1]) / 4;

                     v[i/ 2 * width / 2 + j / 2] = (vBUF[i*width + j] + vBUF[i*width + 1 + j] + vBUF[(i+ 1)*width + j] + vBUF[(i + 1)*width + j + 1]) / 4;

                     

              }

              

       }//uv分量下采样*/

       fwrite(yBUF,sizeof(unsigned char), width*height, yuvfile);

       fwrite(u,sizeof(unsigned char), width*height / 4, yuvfile);

       fwrite(v,sizeof(unsigned char), width*height / 4, yuvfile);//转换后数据写入yuv文件

       free(rgbBUF);

       free(yBUF);

       free(uBUF);

       free(vBUF);

       free(u);

       free(v);

 

       fclose(rgbfile);

       fclose(yuvfile);

       return 0;

}


运行程序后用YUVviewer查看得到的yuv文件:
在这里插入图片描述

YUV2RGB

YUV2RGB的代码可以在RGB2YUV的代码上修改,其思路基本相同,即读取YUV文件,开辟缓存分配数据,计算转换,再写入RGB文件中,其中要注意的有:

1、根据计算系数逆矩阵可以得到YUV2RGB的转换公式:

R=1.000Y+1.4020(V−128)
G=1.000Y−0.3441(U−128)−0.7139(V−128)
B=1.000Y+1.7718(U−128)−0.0013(V−128)​

2、UV分量减去128。

3、开辟新的UV空间,大小为原来的4倍,将读取的UV数据写入。

具体代码:



#include<stdio.h>

#include<stdlib.h>

 

#pragma warning(disable:4996);

 

int main(int argc, char *argv[])

{

       FILE *yuvfile = NULL;

       FILE *rgbfile = NULL;

       unsigned char *r, *g, *b; int i, j;

       const int width = 256; const int height = 256;

       if(!(yuvfile = fopen(argv[1], "rb")))

       {

              printf("Fileopen error!");

       }

       else

       {

              printf("Fileopen success!");

       }

       if(!(rgbfile = fopen(argv[2], "wb")))

       {

              printf("Fileopen error!");

       }

       else

       {

              printf("Fileopen success!");

       }//打开文件

       unsigned char *rgbBUF = (unsigned char*)malloc(sizeof(unsigned char) * 3 * width *height);

   
       unsigned char *yuvBUF = (unsigned char*)malloc(sizeof(unsigned char) *width * height*1.5);

       unsigned char *yBUF = (unsigned char*)malloc(sizeof(unsigned char) * width*height);

       unsigned char *uBUF = (unsigned char*)malloc(sizeof(unsigned char) * width * height/4);

       unsigned char *vBUF = (unsigned char*)malloc(sizeof(unsigned char) * width * height/4);

       fread(yuvBUF,sizeof(unsigned char), width * height*1.5 , yuvfile);

       for(i = 0; i < width*height; i++)

       {

              yBUF[i]= yuvBUF[i];

       }

       for(i = width*height; i < width*height*1.25; i++)

       {

              uBUF[i-width*height]= yuvBUF[i];

              

       }

       for(i = width * height*1.25; i < width*height*1.5; i++)

       {

              vBUF[i- width * height - width * height / 4] = yuvBUF[i];

       }

 

       unsigned char *u = (unsigned char*)malloc(sizeof(unsigned char) * width * height);

       unsigned char *v = (unsigned char*)malloc(sizeof(unsigned char) * width * height);

       for(i = 0; i < height; i += 2)

       {

              for(j = 0; j < width; j += 2)

              {

                     u[i*width+ j] = uBUF[i / 2*width / 2 + j / 2];

                     u[i*width+ j+1] = uBUF[i / 2* width / 2 + j / 2];

                     u[(i+1)*width+ j] = uBUF[i / 2 * width / 2 + j / 2]; 

                     u[(i+1)*width+ j+1] = uBUF[i / 2 * width / 2 + j / 2]; 

                     v[i*width+ j] = vBUF[i / 2 * width / 2 + j / 2];

                     v[i*width+ j + 1] = vBUF[i / 2 * width / 2 + j / 2];

                     v[(i+ 1)*width + j] = vBUF[i / 2 * width / 2 + j / 2];

                     v[(i+ 1)*width + j + 1] = vBUF[i / 2 * width / 2 + j / 2];

                     

 

              }

 

       }

       for(int i = 0; i < height; i++)

       {

              for(int j = 0; j < width; j++)

              {

                     rgbBUF[3* (i*width + j)] = yBUF[i*width + j] + 1.7718 * (u[i*width + j] - 128) - 0.0013* (v[i*width + j] - 128);

                     rgbBUF[3* (i*width + j) + 1] = yBUF[i*width + j] - 0.3441 * (u[i*width + j] - 128) -0.7139 * (v[i*width + j] - 128);

                     rgbBUF[3* (i*width + j) + 2] = yBUF[i*width + j] + 1.4020 *(v[i*width + j] - 128);

 

              }

       }

       

       

       fwrite(rgbBUF,sizeof(unsigned char), width*height*3, rgbfile);

       

       free(rgbBUF);

       free(yBUF);

       free(uBUF);

       free(vBUF);

       free(u);

       free(v);

 

       fclose(rgbfile);

       fclose(yuvfile);

       return 0;

}

运行程序得到RGB文件,用YUVviewer查看:
在这里插入图片描述

发布了5 篇原创文章 · 获赞 0 · 访问量 128

猜你喜欢

转载自blog.csdn.net/weixin_44949552/article/details/105187526