BMP文件及直方图均衡化处理

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

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
  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像素为例展开说明):
在这里插入图片描述
在这里我想说的是这个 偏移量 真的是很重要的东西,在读取文件具体数据的时候,因为图像信息头以及调色板的长度会根据不同情况而变化,可根据偏移量迅速读取图像数据。关于图像数据将会在接下来的文章中进行说明。

  1. 位图信息头(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); 

运行结果如下图所示:
在这里插入图片描述

  1. 调色板(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);
  1. 图像数据构成(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文件的所有信息才能读出来:

Created with Raphaël 2.2.0
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值