分类: LINUX
2010-02-11 15:55:38
(参考上的《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′ +
Cb = -0.148R′ -
Cr = 0.439R′ -
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
*pYUV = static_cast
*pYUV = static_cast
}
// 转换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
*pRGB = static_cast
*pRGB = static_cast
}
// 读取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位)转换会造成数据丢失,对于大的图片感觉不明显,但对于小图片,感觉比较明显。网上查,好象有些算法可以改进一些。