BMP文件数据
数据按照从文件头开始的先后顺序分为四个部分:
1、bmp文件头(bmp file header):提供文件的格式、大小等信息
2、位图信息头(bitmap information):提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息
3、调色板(color palette):可选,如使用索引来表示图像,调色板就是索引与其对应的颜色的映射表
4、位图数据(bitmap data):就是图像数据
bmp文件头
Windows为bmp文件头定义了如下结构体:
typedef struct tagBITMAPFILEHEADER
{
UINT16 bfType;
DWORD bfSize;
UINT16 bfReserved1;
UINT16 bfReserved2;
DWORD bfOffBits;
}BITMAPFILEHEADER;
位图信息头
调色板
调色板其实是一张映射表,标识颜色索引号与其代表的颜色的对应关系。
索引:(蓝,绿,红,Alpha)
0号:(fe,fa,fd,00)
1号:(fd,f3,fc,00)
2号:(f4,f3,fc,00)
3号:(fc,f2,f4,00)
4号:(f6,f2,f2,00)
5号:(fb,f9,f6,00) 等等。
一共有256种颜色,每个颜色占用4个字节,就是一共1024个字节,再加上前面的文件信息头和位图信息头的54个字节加起来一共是1078个字节。
位图数据
由于位图信息头中的图像高度是正数,所以位图数据在文件中的排列顺序是从左下角到右上角,以行为主序排列的。
也即我们见到的第一个像素是图像最左下角的数据,第二个人像素为图像最后一行第二列的数据,…一直到最后一行的最后一列数据,后面紧接的是倒数第二行的第一列的数据,依此类推。
如果图像是24位或是32位数据的位图的话,位图数据区就不是索引而是实际的像素值了。下面说明一下,此时位图数据区的每个像素的RGB颜色阵列排布:
24位RGB按照BGR的顺序来存储每个像素的各颜色通道的值,一个像素的所有颜色分量值都存完后才存下一个下一个像素,不进行交织存储。
32位数据按照BGRA的顺序存储,其余与24位位图的方式一样。
像素的排布规则与前述一致。
对齐规则
我们知道Windows默认的扫描的最小单位是4字节,如果数据对齐满足这个值的话对于数据的获取速度等都是有很大的增益的。因此,BMP图像顺应了这个要求,要求每行的数据的长度必须是4的倍数,如果不够需要进行比特填充(以0填充),这样可以达到按行的快速存取。这时,位图数据区的大小就未必是 图片宽×每像素字节数×图片高 能表示的了,因为每行可能还需要进行比特填充。
填充后的每行的字节数为:
在程序中,我们可以表示为:
int iLineByteCnt = (((m_iImageWidth * m_iBitsPerPixel) + 31) >> 5) << 2;
这样,位图数据区的大小为:
m_iImageDataSize = iLineByteCnt * m_iImageHeight;
我们在扫描完一行数据后,也可能接下来的数据并不是下一行的数据,可能需要跳过一段填充数据:
skip = 4 - ((m_iImageWidth * m_iBitsPerPixel)>>3) & 3;
读写代码
struct bitmapfileheader { // bmfh
unsigned short bfType;
int bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
int bfOffBits;
};
struct bitmapinfoheader { // bmih
int biSize;
int biWidth;
int biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
int biCompression;
int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
int biClrUsed;
int biClrImportant;
};
bitmapfileheader bmfh; //头文件
bitmapinfoheader bmih; //位图信息
unsigned char *imgdata;//位图像素数据
int ReadFileHeader(char *filepath,bitmapfileheader *bmfh);
int ReadInfoHeader(char *filepaht,bitmapinfoheader *bmih);
int ReadImage(char *filepath); //读取图像
void SaveImage(unsigned char *saveimgdata,char *filepath);
//读取像素数据
int ReadImage(char *filepath)
{
FILE *fp;
int n;
int width;
int height;
int bitCount;
int dwLineBytes;
//读入文件头
n = ReadFileHeader(filepath, &bmfh);
if (n == -1)
{
printf("Can not read the file header of BMP file.\n");
return -1;
}
//读入信息头
n = ReadInfoHeader(filepath, &bmih);
if (n == -1)
{
printf("Can not read the info header of BMP file.\n");
return -1;
}
//获取信息头有用信息
width = bmih.biWidth;
height = bmih.biHeight;
bitCount = bmih.biBitCount;
//biSizeImage=bmih.biSizeImage;
dwLineBytes = GetLineBytes(width, bitCount);
fp = fopen(filepath, "rb");
if (!fp)
{
printf("Can not open the file:%s\n", filepath);
return -1;
}
if (bitCount == 8 || bitCount == 24)
{
fseek(fp, bmfh.bfOffBits, SEEK_SET);//直接跳到像素数据
}
else
{
printf("此位图为%d位!", bitCount);
printf("只支持8或24位");
fclose(fp);
return -1;
}
//给imgdata分配内存
imgdata = (unsigned char*)malloc(dwLineBytes*height*sizeof(char));
if (!imgdata)
{
printf("Can not allocate memory for the pixel data.\n");
return -1;
}
//读入像素数据,大小为高度乘上每行所占字节数
n = fread(imgdata, dwLineBytes*height*sizeof(char), 1, fp);
if (n == 0)
{
if (ferror(fp))
{
printf("Can not read the pixel data.\n");
free(imgdata);
fclose(fp);
return -1;
}
}
fclose(fp);
return 1;
}
//读入文件头
int ReadFileHeader(char *filepath, bitmapfileheader *bmfh)
{
FILE *fp;
//打开文件
fp = fopen(filepath, "rb");
//fp = fopen( "G:\\DSP\\BMP\\road.bmp", "rb" );
if (!fp)
{ //如果打开失败
printf("Can not open the file:%s\n", filepath);
return -1;
}
//读入bfType
if (fread(&bmfh->bfType, sizeof(unsigned short), 1, fp) != 1)
{
printf("Can not read bfType in the file header.\n");
fclose(fp);
return -1;
}
//读入bfSize
if (fread(&bmfh->bfSize, sizeof(int), 1, fp) != 1)
{
printf("Can not read bfSize in the file header.\n");
fclose(fp);
return -1;
}
//读入bfReserved1
if (fread(&bmfh->bfReserved1, sizeof(unsigned short), 1, fp) != 1)
{
printf("Can not read bfReserved1 in the file header.\n");
fclose(fp);
return -1;
}
//读入bfReserved2
if (fread(&bmfh->bfReserved2, sizeof(unsigned short), 1, fp) != 1)
{
printf("Can not read bfReserved2 in the file header.\n");
fclose(fp);
return -1;
}
//读入bfOffBits
if (fread(&bmfh->bfOffBits, sizeof(int), 1, fp) != 1)
{
printf("Can not read bfOffBits in the file header.\n");
fclose(fp);
return -1;
}
//关闭文件指针
fclose(fp);
return 0;
}
//读入信息头
int ReadInfoHeader(char *filepath, bitmapinfoheader *bmih)
{
FILE *fp;
//打开文件
fp = fopen(filepath, "rb");
if (!fp)
{
printf("Can not open the file:%s\n", filepath);
return -1;
}
//使文件指针跳过文件头(14字节)
fseek(fp, 14, SEEK_SET);
//读入biSize
if (fread(&bmih->biSize, sizeof(int), 1, fp) != 1)
{
printf("Can not read biSize in the info header.\n");
fclose(fp);
return -1;
}
//读入biWidth
if (fread(&bmih->biWidth, sizeof(int), 1, fp) != 1)
{
printf("Can not read biWidth in the info header.\n");
fclose(fp);
return -1;
}
//读入biHeight
if (fread(&bmih->biHeight, sizeof(int), 1, fp) != 1)
{
printf("Can not read biHeight in the info header.\n");
fclose(fp);
return -1;
}
//读入biPlanes
if (fread(&bmih->biPlanes, sizeof(unsigned short), 1, fp) != 1)
{
printf("Can not read biPlanes in the info header.\n");
fclose(fp);
return -1;
}
//读入biBitCount
if (fread(&bmih->biBitCount, sizeof(unsigned short), 1, fp) != 1)
{
printf("Can not read biBitCount in the info header.\n");
fclose(fp);
return -1;
}
//读入biCompression
if (fread(&bmih->biCompression, sizeof(int), 1, fp) != 1)
{
printf("Can not read biCompression in the info header.\n");
fclose(fp);
return -1;
}
//读入biSizeImage
if (fread(&bmih->biSizeImage, sizeof(int), 1, fp) != 1)
{
printf("Can not read biSizeImage in the info header.\n");
fclose(fp);
return -1;
}
//读入biXPelsPerMeter
if (fread(&bmih->biXPelsPerMeter, sizeof(int), 1, fp) != 1)
{
printf("Can not read biXPelsPerMeter in the info header.\n");
fclose(fp);
return -1;
}
//读入biYPelsPerMeter
if (fread(&bmih->biYPelsPerMeter, sizeof(int), 1, fp) != 1)
{
printf("Can not read biYPelsPerMeter in the info header.\n");
fclose(fp);
return -1;
}
//读入biClrUsed
if (fread(&bmih->biClrUsed, sizeof(int), 1, fp) != 1)
{
printf("Can not read biClrUsed in the info header.\n");
fclose(fp);
return -1;
}
//读入biClrImportant
if (fread(&bmih->biClrImportant, sizeof(int), 1, fp) != 1)
{
printf("Can not read biClrImportant in the info header.\n");
fclose(fp);
return -1;
}
//关闭文件
fclose(fp);
return 0;
}
//保存图像
void SaveImage(unsigned char *saveimgdata,char *filepath)
{
FILE *fp;
int width;
int height;
int bitCount;
int dwLineBytes;
fp=fopen(filepath,"wb");
if(!fp)
{
printf("Can not open the file:%s\n",filepath);
}
//fwrite(&bmfh,sizeof(bitmapfileheader),1,fp);//写文件头
fwrite(&bmfh.bfType,sizeof(unsigned short),1,fp);
fwrite(&bmfh.bfSize,sizeof(int),1,fp);
fwrite(&bmfh.bfReserved1,sizeof(unsigned short),1,fp);
fwrite(&bmfh.bfReserved2,sizeof(unsigned short),1,fp);
fwrite(&bmfh.bfOffBits,sizeof(int),1,fp);
fwrite(&bmih,sizeof(bitmapinfoheader),1,fp);//写信息头
width=bmih.biWidth;
height=bmih.biHeight;
bitCount=bmih.biBitCount;
dwLineBytes=GetLineBytes(width,bitCount);
//读入像素数据,大小为高度乘上每行所占字节数
fwrite(saveimgdata,dwLineBytes*height*sizeof(char),1,fp);//写像素数据
fclose(fp);
}