实验原理
·BMP文件格式
位图文件头BITMAPFILEHEADER |
位图信息头BITMAPINFOHEADER |
调色板Palette |
实际的位图数据ImageData |
·位图文件头BITMAPFILEHEADER结构体
typedef struct tagBITMAPFILEHEADER {
WORD bfType; /* 说明文件的类型 */
DWORD bfSize; /* 说明文件的大小,用字节为单位 */
/*注意此处的字节序问题*/
WORD bfReserved1; /* 保留,设置为0 */
WORD bfReserved2; /* 保留,设置为0 */
DWORD bfOffBits; /* 说明从BITMAPFILEHEADER结构开始到实际的图像数据之间的字节偏移量 */
} BITMAPFILEHEADER;
·位图信息头BITMAPINFOHEADER结构体
typedef struct tagBITMAPINFOHEADER {
DWORD biSize; /* 说明结构体所需字节数 */
LONG biWidth; /* 以像素为单位说明图像的宽度 */
LONG biHeight; /* 以像素为单位说明图像的高速 */
WORD biPlanes; /* 说明位面数,必须为1 */
WORD biBitCount; /* 说明位数/像素,1、2、4、8、24 */
DWORD biCompression; /* 说明图像是否压缩及压缩类型 BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS */
DWORD biSizeImage; /* 以字节为单位说明图像大小,必须是4 的整数倍*/
LONG biXPelsPerMeter; /* 目标设备的水平分辨率,像素/米 */
LONG biYPelsPerMeter; /*目标设备的垂直分辨率,像素/米 */
DWORD biClrUsed; /* 说明图像实际用到的颜色数,如果为0则颜色数为2的biBitCount次方 */
DWORD biClrImportant; /*说明对图像显示有重要影响的颜色 索引的数目,如果是0,表示都重要。*/
} BITMAPINFOHEADER;
·调色板实际上是一个数组,它所包含的元素与位图所具有的颜色数相同,决定于biClrUsed和biBitCount字段。数组中每个元素的类型是一个RGBQUAD结构。真彩色无调色板部分。
typedef struct tagRGBQUAD {
BYTE rgbBlue; /*指定蓝色分量*/
BYTE rgbGreen; /*指定绿色分量*/
BYTE rgbRed; /*指定红色分量*/
BYTE rgbReserved; /*保留,指定为0*/
} RGBQUAD;
·位图数据
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节,按顺序分别为B,G,R;
Windows规定一个扫描行所占的字节数必须是
4的倍数(即以long为单位),不足的以0填充,
biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;
下图为24位bmp图像的二进制数据(截取)
实验流程
1.程序初始化(打开两个文件、定义变量和缓冲区等)
2.读取BMP文件,抽取或生成RGB数据写入缓冲区
3.调用RGB2YUV的函数实现RGB到YUV数据的转换
4.写YUV文件
5.程序收尾工作(关闭文件,释放缓冲区)
主要代码
yuvFile = fopen(yuvFileName, "ab");
读入bmp图像的数据
if(fread(&File_header,sizeof(File_header),1,bmpFile) != 1)
{
printf("read file header error!");
exit(0);
}
if (File_header.bfType != 0x4D42)
{
printf("Not bmp file!");
exit(0);
}
else
{ printf("this is a %d\n",File_header.bfType);}
if(fread(&Info_header,sizeof(BITMAPINFOHEADER),1,bmpFile) != 1)
{ printf("read info header error!");
exit(0);}
获取生成yuv的长与宽
frameWidth=Info_header.biWidth;
frameHeight=Info_header.biHeight;
开空间
rgbBuf = (unsigned char*)malloc(frameWidth * frameHeight * 3*sizeof(unsigned char));
yBuf = (unsigned char*)malloc(frameWidth * frameHeight*sizeof(unsigned char));
uBuf = (unsigned char*)malloc((frameWidth * frameHeight*sizeof(unsigned char)) / 4);
vBuf = (unsigned char*)malloc((frameWidth * frameHeight*sizeof(unsigned char)) / 4);
BMP2YUV(File_header,Info_header, bmpFile,rgbBuf);
之后转成yuv(防止溢出的方法与实验一相同)
RGB2YUV(frameWidth, frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip)
同一图片循环10次写入yuv文件
for(fp=0;fp<10;fp++)
{
fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
printf("\r...%d", ++videoFramesWritten);
}
最后清理空间
fclose(yuvFile);
fclose(bmpFile);
free(rgbBuf);
free(yBuf);
free(uBuf);
free(vBuf);
2.bmp2rgb2yuv.cpp
主要函数void BMP2YUV(BITMAPFILEHEADER & file_h, BITMAPINFOHEADER & info_h, FILE * pFile, unsigned char * rgbDataOut)
定义变量
unsigned long Loop;
unsigned int width,height,w,h;
unsigned char mask, *Data;
获得长与宽(保证是4的整数倍)
if ((info_h.biWidth % 4) == 0)
w = info_h.biWidth;
else
w = (info_h.biWidth*info_h.biBitCount + 31) / 32 * 4;
if ((info_h.biHeight % 2) == 0)
h = info_h.biHeight;
else
h = info_h.biHeight + 1;
width = w / 8 * info_h.biBitCount;
height = h;
开辟空间并写入图像数据
Data = (unsigned char *)malloc(height*width);
fread(Data, 1,height*width,pFile);
if(info_h.biBitCount==24)
{ memcpy(rgbDataOut, Data, height*width);
free(Data);
}
else if(info_h.biBitCount==16)
{ for (Loop = 0;Loop < height * width;Loop +=2)
{
*rgbDataOut = (Data[Loop]&0x1F)<<3;
*(rgbDataOut + 1) = ((Data[Loop]&0xE0)>>2) + ((Data[Loop+1]&0x03)<<6);
*(rgbDataOut + 2) = (Data[Loop+1]&0x7C)<<1;
rgbDataOut +=3;
}
}
else{ RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned int)pow(2,info_h.biBitCount));
if(!MakePalette(pFile,file_h,info_h,pRGB))
printf("No palette!");
for (Loop = 0; Loop<height*width; Loop++)
{
switch (info_h.biBitCount)
{
case 1:
mask = 0x80;
break;
case 2:
mask = 0xC0;
break;
case 4:
mask = 0xF0;
break;
case 8:
mask = 0xFF;
break;
}
int shiftCnt = 1;
while (mask)
{
unsigned char index = mask == 0xFF?Data[Loop] : ((Data[Loop] & mask)>>(8 - shiftCnt * info_h.biBitCount));
* rgbDataOut = pRGB[index].rgbBlue;
* (rgbDataOut+1) = pRGB[index].rgbGreen;
* (rgbDataOut+2) = pRGB[index].rgbRed;
if(info_h.biBitCount == 8) mask = 0;
else mask >>= info_h.biBitCount;
rgbDataOut+=3;
shiftCnt ++;
}
free(pRGB);
}
}
调色板函数
bool MakePalette(FILE * pFile,BITMAPFILEHEADER &file_h,BITMAPINFOHEADER & info_h,RGBQUAD *pRGB_out)
{
if ((file_h.bfOffBits - sizeof(BITMAPFILEHEADER) - info_h.biSize) == sizeof(RGBQUAD)*pow(2,info_h.biBitCount))
{
fseek(pFile,sizeof(BITMAPFILEHEADER)+info_h.biSize,0);
fread(pRGB_out,sizeof(RGBQUAD),(unsigned int)pow(2,info_h.biBitCount),pFile);
return true;
}else
return false;
}
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip)函数与实验一相同