BMP文件的格式:BMP文件在实际存储中包含以下的数据:
1.位图文件头 占用14个字节
//文件信息
unsigned short bfType;//文件类型,ASCII字符BM;
//位图文件头
typedef struct{
unsigned long bfSize;//文件大小
unsigned short bfReserved1;//备用
unsigned short bfReserved2;//备用
unsigned long bfOffBits;//图像开始处的字节偏移
}BitMapFileHeader;
本来 那个unsigned short bfType数据是在位图文件头之中的,在实际的编程中,需要考虑到结构的对齐原则,呵呵,本人在vs2013中没有学会如何设置结构体的对齐原则(用代码进行设置不行,#pragme pack(2),,,,#pragme pop),所以只好将那个数据提出位图信息头了.所以结构体占用了12个字节,bfType占用了2个字节
2.位图信息 位图信息包含两部分内容一个是位图信息头,另一个是调色板
位图信息头 占用40个字节
//文图信息头
typedef struct{
unsigned long biSize;//信息头大小
unsigned long biWidth;//图像宽度,以像素为单位
unsigned long biHeight;//图像高度,以像素为单位
unsigned short biPlanes;//位平面数,为1;
unsigned short biBitCount;//每像素位数,1,4,8,24
unsigned long biCompression;//压缩类型,0为非压缩
unsigned long biSizeImage;//压缩图像大小
unsigned long biXpelspermeter;//水平分辨率
unsigned long biYpelspermeter;//垂直分辨率
unsigned long biClrUsed;//使用的色彩数
unsigned long biClrImportant;//重要色彩数
}BitMapInforHeader;
接下来是调色板了,占用了的字节数是不确定的.2位灰度图占用8个字节,4为灰度图占用64个字节,8位灰度图占用1024个字节.算法如下:
/调色板
typedef struct
{
unsigned char rgbBlue; //该颜色的蓝色分量
unsigned char rgbGreen; //该颜色的绿色分量
unsigned char rgbRed; //该颜色的红色分量
unsigned char rgbReserved; //保留值
} RGBQuad;
在调色板数据结构中,有4个分量分别是红绿蓝和一个保留值,每个分量占用一个字节,8位灰度图实际上有0~255个数据,每个数据4个字节,所以是1024个字节.(白色是由三种颜色绘出来的)
3.像素数据,实际上就是0~255的数值,
//像素数据
unsigned char * * ImgData;//二维数组
这儿定义了一个二维数组来存储数据
4. 其他数据. 在实际的图像存储过程中会有以下其他数据
//其他数据
typedef struct OtherData
{
unsigned char extradata;
struct OtherData *next;
}OtherData;
这儿用一个链表来存储,但是有一个缺点就是在链表中只有一个有用的数据,所以结构体的利用率有点低,解决办法是在一个结构体数组中多设置几个数据项.
接下来是第二部分C语言来读取8位BMP灰度图
int main()
{
FILE* fp;
errno_t err;
unsigned int j, k;
BitMapFileHeader FileHeader;
BitMapInforHeader InfoHeader;
RGBQuad *IpRGBQuad;
char FileName[30];
OtherData *Otherdata, *temp,*SavePreCursor;
/*图像的读取顺序是从下到上,从左到右*/
IpRGBQuad = (RGBQuad *)malloc(256*sizeof(RGBQuad));//灰度图为8位的调色板数据为256个结构,1024个字节
Otherdata = (OtherData*)malloc(sizeof(OtherData));
SavePreCursor = Otherdata;//保存当前指针游标,目的就是保存链表头结点
Otherdata->extradata = NULL;
Otherdata->next = NULL;
strcpy_s(FileName, "图像灰度.bmp");
err = fopen_s(&fp,FileName, "rb");
if( err!=0 )
{
printf_s("cannot open this file");
return 0;
}
//拥有1024个调色板数据
//printf("%d\n", sizeof(FileHeader));
//fread(&FileHeader, sizeof(FileHeader), 1, fp);//读取位图文件头
fread(&bfType, sizeof(unsigned short), 1, fp);//读取位图文件头
if (bfType == 0x4D42)
{
printf_s("\n\n------------------------位图文件头----------------------------------------\n\n");
fread(&FileHeader, sizeof(FileHeader), 1, fp);//读取位图文件头
printf_s("文件类型%X\n",bfType);
//printf_s("%X\n", FileHeader.bfType);
printf_s("文件大小(单位字节):%X\n", FileHeader.bfSize);
printf_s("备用:%X\n", FileHeader.bfReserved1);
printf_s("备用:%X\n", FileHeader.bfReserved2);
printf_s("图像开始处的字节偏移:%X\n", FileHeader.bfOffBits);
//读取位图信息头
printf_s("\n\n-------------------------位图信息头----------------------------------------\n\n");
fread(&InfoHeader, sizeof(InfoHeader), 1, fp);
printf_s("信息头大小:%X\n", InfoHeader.biSize);
printf_s("图像宽度:%d\n", InfoHeader.biWidth);
printf_s("图像高度:%d\n", InfoHeader.biHeight);
printf_s("位平面数:%X\n", InfoHeader.biPlanes);
printf_s("每像素位数:%X\n", InfoHeader.biBitCount);
printf_s("压缩类型:%X\n", InfoHeader.biCompression);
printf_s("压缩图像大小的字节数:%X\n", InfoHeader.biSizeImage);
printf_s("水平分辨率:%X\n", InfoHeader.biXpelspermeter);
printf_s("垂直分辨率:%X\n", InfoHeader.biYpelspermeter );
printf_s("使用的色彩数:%X\n", InfoHeader.biClrUsed);
printf_s("重要色彩数:%X\n", InfoHeader.biClrImportant);
//读取调色板数据
printf_s("\n\n-----------------------------调色板------------------------------------------\n\n");
printf_s("\t红\t绿\t蓝\n");
fread(IpRGBQuad, sizeof(RGBQuad), 256, fp);
for (int i = 0; i < 256; i++)
{
printf("\t%X\t%X\t%X\n",IpRGBQuad[i].rgbBlue , IpRGBQuad[i].rgbGreen , IpRGBQuad[i].rgbRed );
}
//读取像素数据
InfoHeader.biHeight++;
InfoHeader.biWidth += 2;
ImgData = new unsigned char *[InfoHeader.biHeight];
for (j = 0; j < InfoHeader.biHeight; j++)
{
ImgData[j] = new unsigned char [InfoHeader.biWidth];//定义像素数组ImgData[InfoHeader.biHeight][InfoHeader.biWidth]
}
for ( j = 0; j < InfoHeader.biHeight; j++)
{
for ( k = 0; k < InfoHeader.biWidth; k++)
{
fread(&ImgData[j][k], sizeof(unsigned char), 1, fp);
//
}
}
//读取其他数据
temp = (OtherData*)malloc(sizeof(OtherData));
while (!feof(fp))
{
temp = (OtherData*)malloc(sizeof(OtherData));
fread(&temp->extradata, sizeof(unsigned char), 1, fp);
Otherdata->next = temp;
Otherdata = Otherdata->next;
}
Otherdata->next = NULL;//置末尾为空,判断条件
Otherdata = SavePreCursor;//使Otherdata重新指向头部
}
fclose(fp);
system("pause");
return 0;
}