YUV转RGB

在整个视频行业中,定义了很多 YUV 格式,我以UYVY格式标准来说明,4:2:2 格式UYVY每像素占16 位,UYVY字节顺序如下图:

YUV转RGB - 天之骄子 - 天之骄子

(图3 UYVY字节顺序)

 

其中第一个字节为U0,每二个字节为Y0,依次排列如下:

[U0,Y0,V0,Y1] [U1,Y2,V1,Y3] [U2,Y4,V2,Y5] ……

经过仔细分析,我们要实现RGB转YUV格式的话,一个像素的RGB占用三个节,而UYVY每像素占用两个字节,在演示中直接把UYVY字节信息保存到*.pal格式中(这是我自己写来测试用的^_^),*.pal格式字节顺序是先保存上场像素,接着保存下场像素,如果是720x576的一张图像转换为YUV格式并保存的话,文件大小应该是829,440字节(720*576*2)。您可以执行本文附带的程序 (功能菜单->转换并写入YUV两场) 查看转换过程。

关于YUV转换为RGB公式,我直接使用一篇文章提供的公式,经过思考,我发觉要想实现准确无误的把YUV还原为原有的RGB图像很难实现,因为我从UYVY的字节顺序来分析没有找到反变换的方法(您找到了记得告诉我哟: [email protected] ),例如我做了一个简单的分析,假设有六个像素的UYVY格式,要把这12个字节的UYVY要转换回18个字节的RGB,分析如下:

 

12个字节的UYVY排列方式:

[U0 Y0 V0 Y1] [U1 Y2 V1 Y3] [U2 Y4 V2 Y5]

完全转换为18个字节的RGB所需的UYVY字节排列如下:

[Y0 U0 V0] [Y1 U1 V1] [Y2 U2 V2] [Y3 U3 V3] [Y4 U4 V4] [Y5 U5 V5]

我们可以看到,12个字节的UYVY无法实现,缺少U3 V3 U4 V4。于是我抛开准确无误地把UYVY转换回RGB的想法,直接使用最近的UV来执行转换,结果发觉转换回来的RGB图像用肉眼根本分辩不出原有RGB图像与反变换回来的RGB图像差别,您可以执行本文附带的程序 (功能菜单->读取YUV并显示) 查看效果,下面是反变换公式和代码的实现:

 

// 反变换公式

 R= 1.0Y + 0 +1.402(V-128) 

 G= 1.0Y - 0.34413 (U-128)-0.71414(V-128) 

 B= 1.0Y + 1.772 (U-128)+0

 

代码实现:

void CRGB2YUVView::YUV2RGB(byte *pRGB, byte *pYUV)

{

    byte y, u, v;

    y = *pYUV; pYUV++;

    u = *pYUV; pYUV++;

    v = *pYUV;

 

    *pRGB = static_cast<byte>(1.0*y + 8 + 1.402*(v-128));    pRGB++;                 // r

    *pRGB = static_cast<byte>(1.0*y - 0.34413*(u-128) - 0.71414*(v-128)); pRGB++;   // g

    *pRGB = static_cast<byte>(1.0*y + 1.772*(u-128) + 0);                            // b

}

 

// 读取PAL文件转换为RGB并显示

void CRGB2YUVView::OnReadPAL()

{

    // TODO: Add your command handler code here

    CDC *pDC = GetDC();

    CRect rect;

    CBrush brush(RGB(128,128,128));

    GetClientRect(&rect);

    pDC->FillRect(&rect, &brush);

 

    // PAL 720x576 : 中国的电视标准为PAL制

    int CurrentXRes = 720;

    int CurrentYRes = 576;

    int size        = CurrentXRes * CurrentYRes;

   

    // 分配内存

    byte *Video_Field0 = (byte*)malloc(CurrentXRes*CurrentYRes); 

    byte *Video_Field1 = (byte*)malloc(CurrentXRes*CurrentYRes);

 

    // 保存内存指针

    byte *Video_Field0_ = Video_Field0;

    byte *Video_Field1_ = Video_Field1;

 

    // 初始化内存

    ZeroMemory(Video_Field0, CurrentXRes*CurrentYRes);

    ZeroMemory(Video_Field1, CurrentXRes*CurrentYRes);

 

    byte yuv_y0, yuv_u0, yuv_v0; // yuv_v1; // {y0, u0, v0, v1};

    byte r, g, b;

    byte bufRGB[3]; // 临时保存{R,G,B}

    byte bufYUV[3]; // 临时保存{Y,U,V}

   

    // 初始化数组空间

    memset(bufRGB,0, sizeof(byte)*3);

    memset(bufYUV,0, sizeof(byte)*3);

   

    char strFileName[MAX_PATH]="720bmp.pal";

 

    // 分配图片像素内存

    RGBTRIPLE *rgb;

    rgb = new RGBTRIPLE[CurrentXRes*CurrentYRes];

 

    memset(rgb,0, sizeof(RGBTRIPLE)*CurrentXRes*CurrentYRes); // 初始化内存空间

 

    CFile* f;

    f = new CFile();

    f->Open(strFileName, CFile::modeRead);

    f->SeekToBegin();

    f->Read(Video_Field0, CurrentXRes*CurrentYRes);

    f->Read(Video_Field1, CurrentXRes*CurrentYRes);

 

    // 上场 (1,3,5,7...行)

    for ( int i = CurrentYRes-1; i>=0; i--) {

        for ( int j = 0; j<CurrentXRes; j++) {

            if(!(i%2)==0)

            {

                // UYVY标准 [U0 Y0 V0 Y1] [U1 Y2 V1 Y3] [U2 Y4 V2 Y5] 每像素点两个字节,[内]为四个字节

                if ((j%2)==0)

                {

                    yuv_u0 = *Video_Field0; 

                    Video_Field0++;

                }

                else

                {

                    yuv_v0 = *Video_Field0; 

                    Video_Field0++;

                }

                yuv_y0 = *Video_Field0;     

                Video_Field0++;

 

                bufYUV[0] = yuv_y0; // Y

                bufYUV[1] = yuv_u0; // U

                bufYUV[2] = yuv_v0; // V

 

                // RGB转换为YUV

                YUV2RGB(bufRGB,bufYUV);

                r = bufRGB[0];   // y

                g = bufRGB[1];   // u

                b = bufRGB[2];   // v

                if (r>255) r=255; if (r<0) r=0;

                if (g>255) g=255; if (g<0) g=0;

                if (b>255) b=255; if (b<0) b=0;

 

                for (int k=0; k<1000; k++) ; //延时

                // 视图中显示

                pDC->SetPixel(j, CurrentYRes-1-i, RGB(r, g, b));

 

            }// end if i%2

        }

    }

 

    // 下场 (2,4,6,8...行)

    for ( int i_ = CurrentYRes-1; i_>=0; i_--) {

        for ( int j_ = 0; j_<CurrentXRes; j_++) {

            if((i_%2)==0)

            {

                // UYVY标准 [U0 Y0 V0 Y1] [U1 Y2 V1 Y3] [U2 Y4 V2 Y5] 每像素点两个字节,[内]为四个字节

                if ((j_%2)==0)

                {

                    yuv_u0 = *Video_Field1; 

                    Video_Field1++;

                }

                else

                {

                    yuv_v0 = *Video_Field1; 

                    Video_Field1++;

                }

                yuv_y0 = *Video_Field1;     

                Video_Field1++;

 

                bufYUV[0] = yuv_y0; // Y

                bufYUV[1] = yuv_u0; // U

                bufYUV[2] = yuv_v0; // V

 

                // RGB转换为YUV

                YUV2RGB(bufRGB,bufYUV);

                r = bufRGB[0];   // y

                g = bufRGB[1];   // u

                b = bufRGB[2];   // v

                if (r>255) r=255; if (r<0) r=0;

                if (g>255) g=255; if (g<0) g=0;

                if (b>255) b=255; if (b<0) b=0;

 

                for (int k=0; k<1000; k++) ; //延时

                // 视图中显示

                pDC->SetPixel(j_, CurrentYRes-1-i_, RGB(r, g, b));

            }

        }

    }

   

    // 提示完成

    char buffer[80];

    sprintf(buffer,"完成读取PAL文件:%s ", strFileName);

    MessageBox(buffer, "提示信息", MB_OK | MB_ICONINFORMATION);

 

    // 关闭PAL电视场文件

    f->Close();

   

    // 释放内存

    free( Video_Field0_ );

    free( Video_Field1_ );

    delete f;

    delete rgb;

}


你可能感兴趣的:(command,video,delete,buffer,Path,byte)