天天看點

彩色空間轉換實驗: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檔案:

彩色空間轉換實驗:RGB2YUV

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檢視:

彩色空間轉換實驗:RGB2YUV

繼續閱讀