RGB与YUV图像视频格式的相互转换
2007-08-29 21:35:09 来源:WEB开发网核心提示: 在整个视频行业中,定义了很多 YUV 格式,RGB与YUV图像视频格式的相互转换(3),我以UYVY格式标准来说明,4:2:2 格式UYVY每像素占16 位,您可以执行本文附带的程序 (功能菜单->转换并写入YUV两场) 查看转换过程,RGB转UYVY公式如下:公式:(RGB =&g
在整个视频行业中,定义了很多 YUV 格式,我以UYVY格式标准来说明,4:2:2 格式UYVY每像素占16 位,UYVY字节顺序如下图:
(图3 UYVY字节顺序)
其中第一个字节为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
void CRGB2YUVView::RGB2YUV(byte *pRGB, byte *pYUV)
{
byte r,g,b;
r = *pRGB; pRGB++;
g = *pRGB; pRGB++;
b = *pRGB;
*pYUV = static_cast<byte>(0.257*r + 0.504*g + 0.098*b + 16); pYUV++; // y
*pYUV = static_cast<byte>(-0.148*r - 0.291*g + 0.439*b + 128); pYUV++; // u
*pYUV = static_cast<byte>(0.439*r - 0.368*g - 0.071*b + 128); // v
}
像素转换实现:// 转换RGB
void CRGB2YUVView::OnConvertPAL()
{
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;
byte yuv_y0, yuv_u0, yuv_v0, yuv_v1; // {y0, u0, v0, v1};
byte bufRGB[3]; // 临时保存{R,G,B}
byte bufYUV[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 位图操作
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
char strFileName[MAX_PATH]="720bmp.bmp";
CFile* f;
f = new CFile();
f->Open(strFileName, CFile::modeRead);
f->SeekToBegin();
f->Read(&bmfh, sizeof(bmfh));
f->Read(&bmih, sizeof(bmih));
// 分配图片像素内存
RGBTRIPLE *rgb;
rgb = new RGBTRIPLE[bmih.biWidth*bmih.biHeight];
f->SeekToBegin();
f->Seek(54,CFile::begin); // BMP 54个字节之后的是位像素数据
f->Read(rgb, bmih.biWidth * bmih.biHeight * 3); // 这里只读24位RGB(r,g,b)图像
// 上场 (1,3,5,7...行)
for (int i = bmih.biHeight-1; i>=0; i--) {
for (int j = 0; j<bmih.biWidth; 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 (int k=0; k<1000; k++) ; //延时
// 视图中显示
pDC->SetPixel(j, (bmih.biHeight-1)-i, RGB(bufRGB[0], bufRGB[1], bufRGB[2]));
// UYVY标准 [U0 Y0 V0 Y1] [U1 Y2 V1 Y3] [U2 Y4 V2 Y5]
// 每像素点两个字节,[内]为四个字节
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++;
}// end if i%2
}
}
// 下场 (2,4,6,8...行)
for (int i_ = bmih.biHeight-1; i_>=0; i_--) {
for (int j_ = 0; j_<bmih.biWidth; 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 (int k=0; k<1000; k++) ; //延时
// 视图中显示
pDC->SetPixel(j_, (bmih.biHeight-1)-i_, RGB(bufRGB[0], bufRGB[1], bufRGB[2]));
// UYVY标准 [U0 Y0 V0 Y1] [U1 Y2 V1 Y3] [U2 Y4 V2 Y5]
// 每像素点两个字节,[内]为四个字节
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_ );
delete f;
delete rgb;
}
YUV转RGB
更多精彩
赞助商链接