RGB与YUV图像格式的相互转换
(参考上的《RGB与YUV图像视频格式的相互转换》文章,做了些修改)
RGB介绍:在记录计算机图像时,最常见的是采用RGB(红、绿,蓝)颜色分量来保存颜色信息。
例如:非压缩的24位的BMP图像就采用RGB空间来保存图像。一个像素24位,每8位保存一种颜色强度(0-255),例如红色保存为0xFF0000。还有16位的RGB格式,如RGB565。
YUV介绍:YUV是被欧洲电视系统所采用的一种颜色编码方法,我国广播电视也普遍采用这类方法。其中“Y”表示明亮度(Luminance或Luma),也就是灰阶值;而“U”和“V”表示的则是色度(Chrominance或Chroma)。彩色电视采用YUV空间正是为了用亮度信号Y解决彩色电视机与黑白电视机的兼容问题,使黑白电视机也能接收彩色电视信号。
RGB转YUV:
YUV以UYVY格式标准来说明,4:2:2格式UYVY每像素占16位,UYVY字节顺序如下图:
(UYVY字节顺序)
其中第一个字节为U0,每二个字节为Y0,依次排列如下:[U0,Y0,V0,Y1] [U1,Y2,V1,Y3] [U2,Y4,V2,Y5] ……经过仔细分析,我们要实现RGB转YUV格式的话,一个像素的RGB占用三个节,而UYVY平均每像素占用两个字节
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
YUV转RGB:
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
实现代码
输入文件:test.bmp(RGB24格式)
输出文件:test.yuv(YCbCr 4:2:2格式)
/
// CRGB2YUVView message handlers
/*根据BMP文件更改,否则不能正确转换*/
#define BMP_WITH 640
#define BMP_HEIGHT 480
void CRGB2YUVView::OnReadBmp()
{
CDC *pDC = GetDC();
CRect rect;
CBrush brush(RGB(128,128,128));
GetClientRect(&rect);
pDC->FillRect(&rect, &brush);
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
char strFileName[MAX_PATH]="test.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)图像
//显示
for (int i = 0; i
for (int j = 0; j
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 (int k=0; k<1000; k++) ;//延时
}
}
Sleep(500);
//显示24位BMP信息
LONG dwWidth = bmih.biWidth;
LONG dwHeight = bmih.biHeight;
WORD wBitCount = bmih.biBitCount;
char buffer[80];
sprintf(buffer,"图像宽为:%ld高为:%ld像数位数:%d", dwWidth, dwHeight, wBitCount);
MessageBox(buffer, "每个像素的位数", MB_OK | MB_ICONINFORMATION);
f->Close();
delete f;
delete rgb;
}
// RGB转换为YUV
void CRGB2YUVView::RGB2YUV(byte *pRGB, byte *pYUV)
{
byte r,g,b;
r = *pRGB; pRGB++;
g = *pRGB; pRGB++;
b = *pRGB;
*pYUV = static_cast(0.257*r + 0.504*g + 0.098*b + 16);pYUV++;// y
*pYUV = static_cast(-0.148*r - 0.291*g + 0.439*b + 128);pYUV++;// u
*pYUV = static_cast(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);
int CurrentXRes = BMP_WITH;
int CurrentYRes = BMP_HEIGHT;
int size= CurrentXRes * CurrentYRes;
byte yuv_y0, yuv_u0, yuv_v0;// {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);
// BMP位图操作
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
char strFileName[MAX_PATH]="test.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)图像
//分配内存
byte *buffer_y = (byte*)malloc(CurrentXRes*CurrentYRes);
byte *buffer_cb = (byte*)malloc(CurrentXRes*CurrentYRes/2);
byte *buffer_cr = (byte*)malloc(CurrentXRes*CurrentYRes/2);
//保存内存指针
byte *buffer_y_ = buffer_y;
byte *buffer_cb_ = buffer_cb;
byte *buffer_cr_ = buffer_cr;
//初始化内存
ZeroMemory(buffer_y, CurrentXRes*CurrentYRes);
ZeroMemory(buffer_cb, CurrentXRes*CurrentYRes/2);
ZeroMemory(buffer_cr, CurrentXRes*CurrentYRes/2);
for (int i = bmih.biHeight-1; i>=0; i--) {
for (int j = 0; j
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<10000; k++) ;//延时
//视图中显示
pDC->SetPixel(j, (bmih.biHeight-1)-i, RGB(bufRGB[0], bufRGB[1], bufRGB[2]));
if ((j%2)==0)
{
*buffer_cb = yuv_u0;
*buffer_cr = yuv_v0;
buffer_cb++;
buffer_cr++;
}
*buffer_y = yuv_y0;
buffer_y++;
}
}
//关闭BMP位图文件
f->Close();
WriteYUV(buffer_y_, buffer_cb_, buffer_cr_,size);
//释放内存
free( buffer_y_ );
free( buffer_cb_ );
free( buffer_cr_ );
delete f;
delete rgb;
}
//写入到*.yuv文件
BOOL CRGB2YUVView::WriteYUV(byte *Video_Field0, byte *Video_Field1,byte *Video_Field2, int size)
{
char strFileName[MAX_PATH]="test.yuv";
CFile* f;
f = new CFile();
f->Open(strFileName, CFile::modeCreate |CFile::modeWrite);
f->SeekToBegin();
f->Write(Video_Field0, size);
f->Write(Video_Field1, size/2);
f->Write(Video_Field2, size/2);
f->Close();
char buffer[80];
sprintf(buffer,"YUV图像保存为文件:%s ", strFileName);
MessageBox(buffer, "提示信息", MB_OK | MB_ICONINFORMATION);
return TRUE;
}
// YUV转换为RGB
void CRGB2YUVView::YUV2RGB(byte *pRGB, byte *pYUV)
{
byte y, u, v;
y = *pYUV; pYUV++;
u = *pYUV; pYUV++;
v = *pYUV;
*pRGB = static_cast(1.0*y + 0 + 1.402*(v-128));pRGB++;// r
*pRGB = static_cast(1.0*y - 0.34413*(u-128) - 0.71414*(v-128));pRGB++;// g
*pRGB = static_cast(1.0*y + 1.772*(u-128) + 0);// b
}
//读取YUV文件转换为RGB24并显示
void CRGB2YUVView::OnReadPAL()
{
CDC *pDC = GetDC();
CRect rect;
CBrush brush(RGB(128,128,128));
GetClientRect(&rect);
pDC->FillRect(&rect, &brush);
int CurrentXRes = BMP_WITH;
int CurrentYRes = BMP_HEIGHT;
int size= CurrentXRes * CurrentYRes;
//分配内存
byte *buffer_y = (byte*)malloc(CurrentXRes*CurrentYRes);
byte *buffer_cb = (byte*)malloc(CurrentXRes*CurrentYRes/2);
byte *buffer_cr = (byte*)malloc(CurrentXRes*CurrentYRes/2);
//保存内存指针
byte *buffer_y_ = buffer_y;
byte *buffer_cb_ = buffer_cb;
byte *buffer_cr_ = buffer_cr;
//初始化内存
ZeroMemory(buffer_y, CurrentXRes*CurrentYRes);
ZeroMemory(buffer_cb, CurrentXRes*CurrentYRes/2);
ZeroMemory(buffer_cr, CurrentXRes*CurrentYRes/2);
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]="test.yuv";
//分配图片像素内存
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(buffer_y, CurrentXRes*CurrentYRes);
f->Read(buffer_cb, CurrentXRes*CurrentYRes/2);
f->Read(buffer_cr, CurrentXRes*CurrentYRes/2);
for ( int i = CurrentYRes-1; i>=0; i--) {
for ( int j = 0; j
{
if ((j%2)==0)
{
yuv_u0 = *buffer_cb;
yuv_v0 = *buffer_cr;
}
else
{
yuv_u0 = *buffer_cb;
yuv_v0 = *buffer_cr;
buffer_cb++;
buffer_cr++;
}
yuv_y0 = *buffer_y;
buffer_y++;
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<10000; k++) ;//延时
//视图中显示
pDC->SetPixel(j, CurrentYRes-1-i, RGB(r, g, b));
}
}
}
//提示完成
char buffer[80];
sprintf(buffer,"完成读取YUV文件:%s ", strFileName);
MessageBox(buffer, "提示信息", MB_OK | MB_ICONINFORMATION);
f->Close();
//释放内存
free( buffer_y_ );
free( buffer_cb_ );
free( buffer_cr_ );
delete f;
delete rgb;
}
总结:RGB24(24位)到YCbCr(16位)转换会造成数据丢失,对于大的图片感觉不明显,但对于小图片,感觉比较明显。网上查,好象有些算法可以改进一些。