BMP文件及直方图均衡化处理
BMP文件格式
一、BMP简介
BMP(Bitmap-File)是一种与硬件设备无关的图像文件格式,由于它不采用压缩的方式,所以BMP文件所占用的空间很大。BMP文件的图像位深可选1bit、4bit、8bit、24bit,文件存储的时候图像的扫描方式是从左到右,从下到上的顺序。接下来主要看看BMP文件的结构:
二、BMP文件的结构
BMP文件由文件头(BITMAPFILEHEADER)、位图信息头(BITMAPINFOHEADER)、调色板(Rgbquad)以及图像数据(BYTEDATA)。在C语言中,内置了结构体以说明BMP信息,具体如下面所示,但在说明具体信息之前我们必须得知道一些关键词的信息,有助于我们对之后
的读取BMP文件信息的操作:
| 关键词 | 实际表示 | 比特数 | 字节数 |
|---|---|---|---|
| WORD | unsigned short | 16 | 2 |
| DWORD | unsigned long | 32 | 4 |
| LONG | long | 32 | 4 |
| BYTE | unsigned char | 8 | 1 |
- 文件头(BITMAPFILEHEADER)
图像的文件头包括文件类型、文件大小、两个保留字以及关键的偏移量,具体如下结构体所示:
typedef struct BITMAPFILEHEADER
{
WORD bfType; //文件类型
DWORD bfSize; //文件大小
WORD bfReserved1; //保留字,必须为0
WORD bfReserved2; //保留字,必须为0
DWORD bfOffBits; //图像数据的起始位置以相对于文件头的偏移量表示,字节为单位
}BITMAPFILEHEADER;
如果你想通过C语言读取文件头的信息并输出,你可以在你打开的文件中这样操作:
BITMAPFILEHEADER bmpFileHeader; //定义文件头
fseek(fpbmp, 0L, SEEK_SET);//将文件指针移至开头
fread(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, fpbmp);
//输出文件头信息
printf("************************************************\n");
printf("*************tagBITMAPFILEHEADER info***********\n");
printf("************************************************\n");
printf("bfType is %d.\n", bmpFileHeader.bfType);
printf("bfSize is %d.\n", bmpFileHeader.bfSize);
printf("bfReserved1 is %d.\n", bmpFileHeader.bfReserved1);
printf("bfReserved2 is %d.\n", bmpFileHeader.bfReserved2);
printf("bfOffBits is %d.\n", bmpFileHeader.bfOffBits);
运行结果是这样的(一定要先打开文件并且写指针之类的,下面都是以256×256像素为例展开说明):

在这里我想说的是这个 偏移量 真的是很重要的东西,在读取文件具体数据的时候,因为图像信息头以及调色板的长度会根据不同情况而变化,可根据偏移量迅速读取图像数据。关于图像数据将会在接下来的文章中进行说明。
- 位图信息头(BITMAPINFOHEADER)
位图信息头包括BMP图像的占用字节数、位宽、位高、目标设备级别、位深、压缩数据、大小、水平分辨率、垂直分辨率、实际使用调色板的颜色数、重要颜色数:
typedef struct BITMAPINFOHEADER
{
DWORD biSize; //本结构占用的字节数
LONG biWidth; //位图的宽度,以像素为单位
LONG biHeight; //位图的高度,以像素为单位
WORD biPlanes; //目标设备的级别,必须为
WORD biBitCount; //每个像素所需的位数
DWORD biCompression; //位图压缩类型必须是0
DWORD biSizeImage; //位图的大小,以字节为单位
LONG biXPelsPerMeter; //位图水平分辨率,每米像素数
LONG biYPelsPerMeter; //位图垂直分辨率,每米像素数
DWORD biClrUsed; //位图实际使用调色板的颜色数
DWORD biClrImportant; //位图显示过程中重要的颜色数/
}BITMAPINFOHEADER;
其中我想说明的是以下几点:
- 一张图的像素=biWidth×biHeight,这点在接下来的直方图处理中很关键,因为要定义一下内存的大小以及位图数据读取时的循环次数。
- 每个像素的位数,也就是我们常说的位深biBitCount,下面介绍一下1位、4位以及8位的情况:
- 如果位深为1,则说明位图最多只有两种颜色;
- 如果位深为4,则说明位图最多有16种颜色,每个像素用4位来表示,并用这4位作为彩色表的表项来查找该像素的颜色,比如说位图第一个字节是0x1F,表示有两个表项,第一像素的颜色就在调色板的第2表项就找,第二像素的颜色就在彩色表的第16表项中查找。此时调色板在缺省的情况下会有16个RGB,对应于索引0~15;
- 如果位深是8,表示位图最多有256种颜色,每个像素用8位表示,我们在接下来的均衡中用的也是这种8位的图;
BY THE WAY,我们在接下来的均衡中,像我老师说的24位的图像不采用索引图像格式,没有调色板部分,像素数据直接用BGR值,导致我在读取其他位数(除了8位)时会出现数据错误,或者读到的是一些错误的数据。当然这些也都是图像数据的内容,我在这里也就先提前声明了
接下来我们看一下如何在C语言程序中调用信息头信息
BITMAPINFOHEADER bmpInfoHeader;
fseek(fpbmp, 14L, SEEK_SET); //将指针移至离开头14L处
fread(&bmpInfoHeader, sizeof(BITMAPINFOHEADER), 1, fpbmp);
//输出BMP文件头的详细信息
printf("************************************************\n");
printf("*************tagBITMAPINFOHEADER info***********\n");
printf("************************************************\n");
printf("biSize is %d.\n", bmpInfoHeader.biSize);
printf("biWidth is %d.\n", bmpInfoHeader.biWidth);
printf("biHeight is %d.\n", bmpInfoHeader.biHeight);
printf("biPlanes is %d.\n", bmpInfoHeader.biPlanes);
printf("biBitCount is %d.\n", bmpInfoHeader.biBitCount);
printf("biCompression is %d.\n", bmpInfoHeader.biCompression);
printf("biSizeImage is %d.\n", bmpInfoHeader.biSizeImage);
printf("biXPelsPerMeter is %d.\n", bmpInfoHeader.biXPelsPerMeter);
printf("biYPelsPerMeter is %d.\n", bmpInfoHeader.biYPelsPerMeter);
printf("biClrUsed is %d.\n", bmpInfoHeader.biClrUsed);
printf("biClrImportant is %d.\n", bmpInfoHeader.biClrImportant);
运行结果如下图所示:

- 调色板(Rgbquad)
调色板应予说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构定义一种颜色,RGBQUAD结构数据的个数由位图信息头的biBitCount来确定,当biBitCount=1,4,8时,分别有2,16,256个表项,而biBitCount=24时没有彩色表项。
typedef struct RGBQUAD
{
BYTE rgbBlue; //蓝色的亮度(0~255)
BYTE rgbGreen; //绿色的亮度(0~255)
BYTE rgbRed; //红色的亮度(0~255)
BYTE rgbReserved; //保留,必须为0
}RGBQUAD;
我们可以通过下面的操作读取调色板的数据:
RGBQUAD bmpColorTable[256];
fread(bmpColorTable, sizeof(RGBQUAD), 256, fpbmp);
- 图像数据构成(BYTEDATA)
位图数据按我的理解是一种索引号,为每个像素点在调色板中寻找出对应的颜色。在编程中喜欢把位图信息头和调色板合为位图信息:
typedef struct BITMAPINFO
{
BITMAPINFOHEADER bmiHeader; //位图信息头
RGBQUAD bmiColors[256]; //彩色表
}BITMAPINFO;
在程序中我是这样调用的:
fseek(fpbmp, bmpFileHeader.bfOffBits, SEEK_SET);//偏移量
fread(bmpValue, total_xiangsu, 1, fpbmp);//total_xiangsu是总的像素数=位宽*位高
位图数据极为重要,在接下来的直方图中我们将主要对位图数据进行处理。
三、读取数据的顺序
按照下面的流程图才能一步一步读取成功BMP文件的数据,这样BMP文件的所有信息才能读出来:

本文深入解析BMP文件格式,涵盖文件结构、读取顺序及直方图均衡化处理。通过C语言实现均衡化算法,对比MATLAB验证结果,探讨图像处理技巧。
最低0.47元/天 解锁文章

938





