RGB与YUV图像视频格式的相互转换.docx
- 文档编号:29396182
- 上传时间:2023-07-23
- 格式:DOCX
- 页数:13
- 大小:27.26KB
RGB与YUV图像视频格式的相互转换.docx
《RGB与YUV图像视频格式的相互转换.docx》由会员分享,可在线阅读,更多相关《RGB与YUV图像视频格式的相互转换.docx(13页珍藏版)》请在冰豆网上搜索。
RGB与YUV图像视频格式的相互转换
RGB与YUV图像视频格式的相互转换
我们知道普通彩色CRT显示器内部有三支电子枪,电子枪去激活显示器屏幕的荧光粉,三种荧光粉发射出的光生成一个像素位置的颜色点,这就是我们人眼能看到的一个像素。
每个像素对应红、绿、蓝(R、G、B)三个强度等级,每个像素占用24位,可以显示近1700万种颜色,这就是我们所说的真彩色。
普通彩色CRT显示器是基于电视技术的光栅扫描,电子束一次扫描一行,从顶到底依次扫描,整个屏幕扫描一次(我们称它为1帧),电子束扫描完一帧后回到最初位置进行下一次扫描。
电视图像显示概述:
电视显示原理与CRT相似,不过采用的是隔行扫描,我国的广播电视采用的是625行隔行扫描方式。
隔行扫描是将一帧图像分两次(场)扫描。
第一场先扫出1、3、5、7…等奇数行光栅,第二场扫出2、4、6、8…等偶数行光栅。
通常将扫奇数行的场叫奇数场(也称上场),扫偶数行的场叫偶数场(也称下场)。
为什么电视会选择隔行扫描,这是因为会使显示运动图像更平滑。
下面两图为一帧图像的上场和下场的扫描过程。
(图1上场扫描)
(图2下场扫描)
常见的电视的制式有三种:
NTSC、PAL、SECAM,我国的广播电视采用PAL制式,我国电视制式的帧频只有50HZ和我们日常使用的电流频率一样,PAL帧频为25fps,在文章后面我会以一张720x576的图像转换为720x576PAL隔行扫描的电视场视频格式作详细描述。
RGB介绍:
在记录计算机图像时,最常见的是采用RGB(红、绿,蓝)颜色分量来保存颜色信息,例如非压缩的24位的BMP图像就采用RGB空间来保存图像。
一个像素24位,每8位保存一种颜色强度(0-255),例如红色保存为0xFF0000。
YUV介绍:
YUV是被欧洲电视系统所采用的一种颜色编码方法,我国广播电视也普遍采用这类方法。
其中“Y”表示明亮度(Luminance或Luma),也就是灰阶值;而“U”和“V”表示的则是色度(Chrominance或Chroma)。
彩色电视采用YUV空间正是为了用亮度信号Y解决彩色电视机与黑白电视机的兼容问题,使黑白电视机也能接收彩色电视信号。
隔行读取BMP:
下面我说明如何隔行读取BMP图像,为什么我以BMP图像来作演示,因为BMP可以说是最简单的一种图像格式,最容易用它说明原理,那公为什么要用BMP来演示隔行读取呢,因为要实现RGB转电视场制图像,首先就要知识如何隔行读取。
BMP图像颜色信息的保存顺序是由左到右,由下往上,您可以执行一下附带程序的(功能菜单->读取RGB)看到图像的读取和显示过程。
代码首先依次显示奇数行像素,如(1,3,5,7,9….行),完成后再依次显示偶数行像素,代码实现如下:
//隔行显示BMP
voidCRGB2YUVView:
:
OnReadBmp()
{
//TODO:
Addyourcommandhandlercodehere
CDC*pDC=GetDC();
CRectrect;
CBrushbrush(RGB(128,128,128));
GetClientRect(&rect);
pDC->FillRect(&rect,&brush);
BITMAPFILEHEADERbmfh;
BITMAPINFOHEADERbmih;
charstrFileName[MAX_PATH]="720bmp.bmp";
CFile*f;
f=newCFile();
f->Open(strFileName,CFile:
:
modeRead);
f->SeekToBegin();
f->Read(&bmfh,sizeof(bmfh));
f->Read(&bmih,sizeof(bmih));
//分配图片像素内存
RGBTRIPLE*rgb;
rgb=newRGBTRIPLE[bmih.biWidth*bmih.biHeight];
f->SeekToBegin();
f->Seek(54,CFile:
:
begin);//BMP54个字节之后的是像素数据
f->Read(rgb,bmih.biWidth*bmih.biHeight*3); //这里只读24位RGB(r,g,b)图像
//显示上场(奇数行组成的奇数场)
for(inti=0;i for(intj=0;j if(! (i%2)) pDC->SetPixel(j,bmih.biHeight-i, RGB(rgb[i*bmih.biWidth+j].rgbtRed, rgb[i*bmih.biWidth+j].rgbtGreen,rgb[i*bmih.biWidth+j].rgbtBlue)); for(intk=0;k<1000;k++); //延时 } } Sleep(500); //显示下场(偶数行组成的偶数场) for(inti_=0;i_ for(intj_=0;j_ if(i_%2) pDC->SetPixel(j_,bmih.biHeight-i_, RGB(rgb[i_*bmih.biWidth+j_].rgbtRed, rgb[i_*bmih.biWidth+j_].rgbtGreen, rgb[i_*bmih.biWidth+j_].rgbtBlue)); for(intk=0;k<1000;k++); //延时 } } //显示24位BMP信息 LONGdwWidth=bmih.biWidth; LONGdwHeight=bmih.biHeight; WORDwBitCount=bmih.biBitCount; charbuffer[80]; sprintf(buffer,"图像宽为: %ld高为: %ld像数位数: %d",dwWidth,dwHeight,wBitCount); MessageBox(buffer,"每个像素的位数",MB_OK|MB_ICONINFORMATION); f->Close(); deletef; deletergb; }RGB转YUV 在整个视频行业中,定义了很多YUV格式,我以UYVY格式标准来说明,4: 2: 2格式UYVY每像素占16位,UYVY字节顺序如下图: (图3UYVY字节顺序) 其中第一个字节为U0,每二个字节为Y0,依次排列如下: [U0,Y0,U1,Y1][U1,Y2,V1,Y3][U2,Y4,V2,Y5]…… 经过仔细分析,我们要实现RGB转YUV格式的话,一个像素的RGB占用三个节,而UYVY每像素占用两个字节,我演示直接把UYVY字节信息保存到*.pal格式中(这是我自己写来测试用的^_^),*.pal格式中,先保存上场像素,接着保存下场像素,如果是720x576的一张图像转换为YUV格式并保存的话,文件大小应该是829,440字节(720*576*2)。 您可以执行本文附带的程序(功能菜单->转换并写入YUV两场)查看转换过程。 RGB转UYVY公式如下: 公式: (RGB=>YCbCr) Y=0.257R′+0.504G′+0.098B′+16 Cb=-0.148R′-0.291G′+0.439B′+128 Cr=0.439R′-0.368G′-0.071B′+128 代码实现: //RGB转换为YUV voidCRGB2YUVView: : RGB2YUV(byte*pRGB,byte*pYUV) { byter,g,b; r=*pRGB;pRGB++; g=*pRGB;pRGB++; b=*pRGB; *pYUV=static_cast *pYUV=static_cast *pYUV=static_cast }像素转换实现: //转换RGB voidCRGB2YUVView: : OnConvertPAL() { CDC*pDC=GetDC(); CRectrect; CBrushbrush(RGB(128,128,128)); GetClientRect(&rect); pDC->FillRect(&rect,&brush); //PAL720x576: 中国的电视标准为PAL制 intCurrentXRes=720; intCurrentYRes=576; intsize =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; byteyuv_y0,yuv_u0,yuv_v0,yuv_v1; //{y0,u0,v0,v1}; bytebufRGB[3]; //临时保存{R,G,B} bytebufYUV[3]; //临时保存{Y,U,V} //初始化数组空间 ZeroMemory(bufRGB,sizeof(byte)*3); ZeroMemory(bufYUV,sizeof(byte)*3); //初始化内存 ZeroMemory(Video_Field0,CurrentXRes*CurrentYRes); ZeroMemory(Video_Field1,CurrentXRes*CurrentYRes); //BMP位图操作 BITMAPFILEHEADERbmfh; BITMAPINFOHEADERbmih; charstrFileName[MAX_PATH]="720bmp.bmp"; CFile*f; f=newCFile(); f->Open(strFileName,CFile: : modeRead); f->SeekToBegin(); f->Read(&bmfh,sizeof(bmfh)); f->Read(&bmih,sizeof(bmih)); //分配图片像素内存 RGBTRIPLE*rgb; rgb=newRGBTRIPLE[bmih.biWidth*bmih.biHeight]; f->SeekToBegin(); f->Seek(54,CFile: : begin); //BMP54个字节之后的是位像素数据 f->Read(rgb,bmih.biWidth*bmih.biHeight*3); //这里只读24位RGB(r,g,b)图像 //上场 (1,3,5,7...行) for(inti=bmih.biHeight-1;i>=0;i--){ for(intj=0;j if(! (i%2)==0) { bufRGB[0]=rgb[i*bmih.biWidth+j].rgbtRed; // R bufRGB[1]=rgb[i*bmih.biWidth+j].rgbtGreen;//G bufRGB[2]=rgb[i*bmih.biWidth+j].rgbtBlue; //B //RGB转换为YUV RGB2YUV(bufRGB,bufYUV); yuv_y0=bufYUV[0]; //y yuv_u0=bufYUV[1]; //u yuv_v0=bufYUV[2]; //v for(intk=0;k<1000;k++); //延时 //视图中显示 pDC->SetPixel(j,(bmih.biHeight-1)-i,RGB(bufRGB[0],bufRGB[1],bufRGB[2])); //UYVY标准 [U0Y0V0Y1][U1Y2V1Y3][U2Y4V2Y5] //每像素点两个字节,[内]为四个字节 if((j%2)==0) { *Video_Field0=yuv_u0; Video_Field0++; yuv_v1=yuv_v0; //v保存起来供下一字节使用 } else { *Video_Field0=yuv_v1; Video_Field0++; } *Video_Field0=yuv_y0; Video_Field0++; }//endifi%2 } } //下场(2,4,6,8...行) for(inti_=bmih.biHeight-1;i_>=0;i_--){ for(intj_=0;j_ if((i_%2)==0) { bufRGB[0]=rgb[i_*bmih.biWidth+j_].rgbtRed; // R bufRGB[1]=rgb[i_*bmih.biWidth+j_].rgbtGreen;//G bufRGB[2]=rgb[i_*bmih.biWidth+j_].rgbtBlue; //B //RGB转换为YUV RGB2YUV(bufRGB,bufYUV); yuv_y0=bufYUV[0]; //y yuv_u0=bufYUV[1]; //u yuv_v0=bufYUV[2]; //v for(intk=0;k<1000;k++); //延时 //视图中显示 pDC->SetPixel(j_,(bmih.biHeight-1)-i_,RGB(bufRGB[0],bufRGB[1],bufRGB[2])); //UYVY标准 [U0Y0V0Y1][U1Y2V1Y3][U2Y4V2Y5] //每像素点两个字节,[内]为四个字节 if((j_%2)==0) { *Video_Field1=yuv_u0; Video_Field1++; yuv_v1=yuv_v0; //v保存起来供下一字节使用 } else { *Video_Field1=yuv_v1; Video_Field1++; } *Video_Field1=yuv_y0; Video_Field1++; } } } //关闭BMP位图文件 f->Close(); WriteYUV(Video_Field0_,Video_Field1_,size); //释放内存 free(Video_Field0_); free(Video_Field1_); deletef; deletergb; } YUV转RGB 关于YUV转换为RGB公式,我直接使用一篇文章提供的公式,经过思考,我发觉要想实现准确无误的把YUV转换为原有的RGB图像很难实现,因为我从UYVY的字节顺序来分析没有找到反变换的方法(您找到了记得告诉我哟: liyingjiang@),例如我做了一个简单的测试: 假设有六个像素的UYVY格式,要把这12个字节的UYVY要转换回18个字节的RGB,分析如下: 12个字节的UYVY排列方式: [U0Y0V0Y1][U1Y2V1Y3][U2Y4V2Y5] 完全转换为18个字节的RGB所需的UYVY字节排列如下: [Y0U0V0][Y1U1V1][Y2U2V2][Y3U3V3][Y4U4V4][Y5U5V5] 我们可以看到,12个字节的UYVY无法实现,缺少U3V3U4V4。 于是我抛开准确无误地把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代码实现: voidCRGB2YUVView: : YUV2RGB(byte*pRGB,byte*pYUV) { bytey,u,v; y=*pYUV;pYUV++; u=*pYUV;pYUV++; v=*pYUV; *pRGB=static_cast *pRGB=static_cast *pRGB=static_cast } //读取PAL文件转换为RGB并显示 voidCRGB2YUVView: : OnReadPAL() { //TODO: Addyourcommandhandlercodehere CDC*pDC=GetDC(); CRectrect; CBrushbrush(RGB(128,128,128)); GetClientRect(&rect); pDC->FillRect(&rect,&brush); //PAL720x576: 中国的电视标准为PAL制 intCurrentXRes=720; intCurrentYRes=576; intsize =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); byteyuv_y0,yuv_u0,yuv_v0;//yuv_v1; //{y0,u0,v0,v1}; byter,g,b; bytebufRGB[3]; //临时保存{R,G,B} bytebufYUV[3]; //临时保存{Y,U,V} //初始化数组空间 memset(bufRGB,0,sizeof(byte)*3); memset(bufYUV,0,sizeof(byte)*3); charstrFileName[MAX_PATH]="720bmp.pal"; //分配图片像素内存 RGBTRIPLE*rgb; rgb=newRGBTRIPLE[CurrentXRes*CurrentYRes]; memset(rgb,0,sizeof(RGBTRIPLE)*CurrentXRes*CurrentYRes);//初始化内存空间 CFile*f; f=newCFile(); f->Open(strFileName,CFile: : modeRead); f->SeekToBegin(); f->Read(Video_Field0,Current
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- RGB YUV 图像 视频格式 相互 转换
![提示](https://static.bdocx.com/images/bang_tan.gif)