BMP是bitmap的缩写形式,bitmap顾名思义,就是位图也即Windows位图。它一般由4部分组成:文件头信息块、图像描述信息块、颜色表(在真彩色模式无颜色表)和图像数据区组成。在系统中以BMP为扩展名保存。
图像通常保存的颜色深度有2(1位)、16(4位)、256(8位)、65536(16位)和1670万(24位)种颜色(其中位是表示每点所用的数据位)。8位图像可以是索引彩色图像外,也可以是灰阶图像。表示透明的alpha通道也可以保存在一个类似于灰阶图像的独立文件中。带有集成的alpha通道的32位版本已经随着Windows XP出现,它在Windows系统的登录界面和系统主题中都有使用。
关键:
在读取bmp图片的时候,一定要注意内存对齐的问题,譬如文件头,否则无法读取出正确结果。
关于图片的像素数据,每一行的像素的字节数必须是4的整数倍。如果不是,则需要补齐。一般来说,bmp图像文件的数据是从下到上,从左到右的。即从文件中最先读到的是图像最下面一行的左边第一个像素,然后是坐标第二个…接下来是倒数第二行的第一个像素。
BMP文件的数据按照从文件头开始的先后顺序分为四个部分:
◆ 位图文件头(bmp file header): 提供文件的格式、大小等信息
◆ 位图信息头(bitmap information):提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息
◆ 调色板(color palette):可选,如使用索引来表示图像,调色板就是索引与其对应的颜色的映射表
◆ 位图数据(bitmap data):图像数据区
文件信息头 (14字节)存储着文件类型,文件大小等信息。
// 文件信息头结构体
typedef struct tagBITMAPFILEHEADER
{
/unsigned short bfType; // 19778,必须是BM字符串,对应的十六进制为0x4d42,十进制为19778,否则不是bmp格式文件
unsigned int bfSize; // 文件大小 以字节为单位(2-5字节)
unsigned short bfReserved1; // 保留,必须设置为0 (6-7字节)
unsigned short bfReserved2; // 保留,必须设置为0 (8-9字节)
unsigned int bfOffBits; // 从文件头到像素数据的偏移 (10-13字节)
} BITMAPFILEHEADER;
图片信息头 (40字节)存储着图像的尺寸,颜色索引,位平面数等信息
//图像信息头结构体
typedef struct tagBITMAPINFOHEADER
{
unsigned int biSize; // 此结构体的大小 (14-17字节)
long biWidth; // 图像的宽 (18-21字节)
long biHeight; // 图像的高 (22-25字节)
unsigned short biPlanes; // 表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1 (26-27字节)
unsigned short biBitCount; // 一像素所占的位数,一般为24 (28-29字节)
unsigned int biCompression; // 说明图象数据压缩的类型,0为不压缩。 (30-33字节)
unsigned int biSizeImage; // 像素数据所占大小, 这个值应该等于上面文件头结构中bfSize-bfOffBits (34-37字节)
long biXPelsPerMeter; // 说明水平分辨率,用象素/米表示。一般为0 (38-41字节)
long biYPelsPerMeter; // 说明垂直分辨率,用象素/米表示。一般为0 (42-45字节)
unsigned int biClrUsed; // 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。 (46-49字节)
unsigned int biClrImportant; // 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。(50-53字节)
} BITMAPINFOHEADER;
调色板 (由颜色索引数决定)【可以没有此信息】这部分定义了图像中所用的颜色。如上所述,位图图像一个像素接着一个像素储存,每个像素使用一个或者多个字节的值表示,所以调色板的目的就是要告诉应用程序这些值所对应的实际颜色。
典型的位图文件使用RGB彩色模型。在这种模型中,每种颜色都是由不同强度(从0到最大强度)的红色(R)、绿色(G)和蓝色(B)组成的,也就是说,每种颜色都可以使用红色、绿色和蓝色的值所定义。
在位图文件的实现中,调色板可以包含很多条目,条目个数就是图像中所使用的颜色的个数。
//24位图像素信息结构体,即调色板
typedef struct tagRGBQUAD{
unsigned char rgbBlue; //该颜色的蓝色分量 (值范围为0-255)
unsigned char rgbGreen; //该颜色的绿色分量 (值范围为0-255)
unsigned char rgbRed; //该颜色的红色分量 (值范围为0-255)
unsigned char rgbReserved;// 保留,必须为0
} RGBQUAD;
每个条目用来描述一种颜色,包含4个字节,其中三个表示蓝色、绿色和红色,第四个字节没有使用(大多数应用程序将它设为0);对于每个字节,数值0表示该颜色分量在当前的颜色中没有使用,而数值255表示这种颜色分量使用最大的强度。
注意:
表示位图中像素的比特是以行为单位对齐存储的,每一行的大小都向上取整为4字节(32位DWORD)的倍数。如果图像的高度大于1,多个经过填充实现对齐的行就形成了像素数组。
完整存储的一行像素所需的字节数可以通过这个公式计算:
Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充,
//一个扫描行所占的字节数计算方法:
DataSizePerLine= (biWidth* biBitCount+31)/8;
//一个扫描行所占的字节数
DataSizePerLine= DataSizePerLine/4*4; // 字节数必须是4的倍数
//位图数据的大小(不压缩情况下):
DataSize= DataSizePerLine* biHeight;
BMP位图要求一行的字节数为4的整数倍,4字节就是32位,根据整数除法的规则,所以加31(这其实就是数学上的取整运算的计算机实现)!
(biWidth*biBitCount+31)/32*4 公式算出来的是一行的字节数。
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。
通常,像素是从下到上、从左到右保存的。但如果使用的不是BITMAPCOREHEADER,那么未压缩的Windows位图还可以从上到下存储,此时图像高度为负值。
每一行的末尾通过填充若干个字节的数据(并不一定为0)使该行的长度为4字节的倍数。像素数组读入内存后,每一行的起始地址必须为4的倍数。这个限制仅针对内存中的像素数组,针对存储时,仅要求每一行的大小为4字节的倍数,对文件的偏移没有限制。
例如:对于24位色的位图,如果它的宽度为1像素,那么除了每一行的数据(蓝、绿、红)需要占3字节外,还会填充1字节;而如果宽为2像素,则需要2字节的填充;宽为3像素时,需要3字节填充;宽为4像素时则不需要填充。
图像相同的条件下,位图图像文件通常比使用其它压缩算法的图像文件大很多。
无论是磁盘上的位图文件还是内存中的位图图像,像素都由一组位(英语:bit)表示。
简单来说就是如下:
位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=16时,1个像素占2个字节;
当biBitCount=24时,1个像素占3个字节;
当biBitCount=32时,1个像素占4个字节;
这里我们先用Lena.bmp图像实现,后续便于我们给你更好的理解车牌。
用其他软件打开也是可以的
现在我们来读取这些代码,看看他们到底保存了一些啥东西。 在这里要注意的是Windows的数据是倒着念的,(小端存储:低地址存低数据 。如果不理解的可以参考此文连接,请点击:)这是PC电脑的特色。如果一段数据为42 4D,倒着念就是4D 42,即0x4D42。 因此,如果bfSize的数据为A2 1E 04 00,实际上就成了0x00041EA2,也就是0x41EA2。
上述圈起来的信息用代码读出来发现一一对应:
unsigned short bfType = 0x4D42 = 19778
unsigned int bfSize = 0xC0036 = 786486字节=786486/1024=768kb
unsigned short bfReserved1 = 00 00
unsigned short bfReserved2 = 00 00
unsigned int bfOffBits = 0X0000036 = 0x36 = 54字节
unsigned int biSize = 0x00000028 = 0x28 = 40字节
int biWidth = 0x00000200 = 0x200 = 512像素;
int biHeight = 0x00000200 = 0x200 = 512像素 ;
unsigned short biPlanes = 0x0001 =0x1 = 1;
unsigned short biBitCount = 0x0018 = 0x18 = 24位;
unsigned int biCompression = 0x00000000 = 0;
unsigned int biSizeImage = 0x000C0000 = 0xC0000=786,432;
int biXPelsPerMeter = 0x00000000 = 0;
int biYPelsPerMeter = 0x00000000 = 0;
unsigned int biClrUsed = 0x00000000 = 0;
unsigned int biClrImportant = 0x00000000 = 0;
unsigned char rgbBlue = 0x9E5137 其中R:0X37 G:0X51 B:0X9E ; //该颜色的蓝色分量 (值范围为0-255)
unsigned char rgbGreen = 0xA5583E 其中R:0X3E G:0X58 B:0XA5 ; //该颜色的绿色分量 (值范围为0-255)
unsigned char rgbRed = 0x7A5A3E 其中R:0X3E G:0X5A B:0X7A ; //该颜色的红色分量 (值范围为0-255)
unsigned char rgbReserved; // 保留,必须为0
BmpFormat.h
# ifndef BMP_H
# define BMP_H
/*
BMP格式
这种格式内的数据分为三到四个部分,依次是:
文件信息头 (14字节)存储着文件类型,文件大小等信息
图片信息头 (40字节)存储着图像的尺寸,颜色索引,位平面数等信息
调色板 (由颜色索引数决定)【可以没有此信息】
位图数据 (由图像尺寸决定)每一个像素的信息在这里存储
一般的bmp图像都是24位,也就是真彩。每8位为一字节,24位也就是使用三字节来存储每一个像素的信息,三个字节对应存放r,g,b三原色的数据,
每个字节的存贮范围都是0-255。那么以此类推,32位图即每像素存储r,g,b,a(Alpha通道,存储透明度)四种数据。8位图就是只有灰度这一种信息,
还有二值图,它只有两种颜色,黑或者白。
*/
// 文件信息头结构体
typedef struct tagBITMAPFILEHEADER
{
//unsigned short bfType; // 19778,必须是BM字符串,对应的十六进制为0x4d42,十进制为19778,否则不是bmp格式文件
unsigned int bfSize; // 文件大小 以字节为单位(2-5字节)
unsigned short bfReserved1; // 保留,必须设置为0 (6-7字节)
unsigned short bfReserved2; // 保留,必须设置为0 (8-9字节)
unsigned int bfOffBits; // 从文件头到像素数据的偏移 (10-13字节)
} BITMAPFILEHEADER;
//图像信息头结构体
typedef struct tagBITMAPINFOHEADER
{
unsigned int biSize; // 此结构体的大小 (14-17字节)
long biWidth; // 图像的宽 (18-21字节)
long biHeight; // 图像的高 (22-25字节)
unsigned short biPlanes; // 表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1 (26-27字节)
unsigned short biBitCount; // 一像素所占的位数,一般为24 (28-29字节)
unsigned int biCompression; // 说明图象数据压缩的类型,0为不压缩。 (30-33字节)
unsigned int biSizeImage; // 像素数据所占大小, 这个值应该等于上面文件头结构中bfSize-bfOffBits (34-37字节)
long biXPelsPerMeter; // 说明水平分辨率,用象素/米表示。一般为0 (38-41字节)
long biYPelsPerMeter; // 说明垂直分辨率,用象素/米表示。一般为0 (42-45字节)
unsigned int biClrUsed; // 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。 (46-49字节)
unsigned int biClrImportant; // 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。(50-53字节)
} BITMAPINFOHEADER;
//24位图像素信息结构体,即调色板
typedef struct tagRGBQUAD{
unsigned char rgbBlue; //该颜色的蓝色分量 (值范围为0-255)
unsigned char rgbGreen; //该颜色的绿色分量 (值范围为0-255)
unsigned char rgbRed; //该颜色的红色分量 (值范围为0-255)
unsigned char rgbReserved;// 保留,必须为0
} RGBQUAD;
#endif
read_bmp.c
#include
#include
#include "BmpFormat.h"
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
void showBmpHead(BITMAPFILEHEADER pBmpHead)
{ //定义显示信息的函数,传入文件头结构体
printf("BMP文件大小:%dkb\n", fileHeader.bfSize/1024);
printf("保留字必须为0:%d\n", fileHeader.bfReserved1);
printf("保留字必须为0:%d\n", fileHeader.bfReserved2);
printf("实际位图数据的偏移字节数: %d\n", fileHeader.bfOffBits);
}
void showBmpInfoHead(BITMAPINFOHEADER pBmpinfoHead)
{//定义显示信息的函数,传入的是信息头结构体
printf("位图信息头:\n" );
printf("信息头的大小:%d\n" ,infoHeader.biSize);
printf("位图宽度:%d\n" ,infoHeader.biWidth);
printf("位图高度:%d\n" ,infoHeader.biHeight);
printf("图像的位面数(位面数是调色板的数量,默认为1个调色板):%d\n" ,infoHeader.biPlanes);
printf("每个像素的位数:%d\n" ,infoHeader.biBitCount);
printf("压缩方式:%d\n" ,infoHeader.biCompression);
printf("图像的大小:%d\n" ,infoHeader.biSizeImage);
printf("水平方向分辨率:%d\n" ,infoHeader.biXPelsPerMeter);
printf("垂直方向分辨率:%d\n" ,infoHeader.biYPelsPerMeter);
printf("使用的颜色数:%d\n" ,infoHeader.biClrUsed);
printf("重要颜色数:%d\n" ,infoHeader.biClrImportant);
}
int main()
{
FILE* fp;
fp = fopen("lena.bmp", "rb");//读取同目录下的image.bmp文件。
if(fp == NULL)
{
printf("打开'lena.bmp'失败!\n");
return -1;
}
//如果不先读取bifType,根据C语言结构体Sizeof运算规则——整体大于部分之和,从而导致读文件错位
unsigned short fileType;
fread(&fileType,1,sizeof (unsigned short), fp);
if (fileType == 0x4d42)
{
printf("文件类型标识正确!" );
printf("\n文件标识符:%d\n", fileType);
fread(&fileHeader, 1, sizeof(BITMAPFILEHEADER), fp);
showBmpHead(fileHeader);
fread(&infoHeader, 1, sizeof(BITMAPINFOHEADER), fp);
showBmpInfoHead(infoHeader);
fclose(fp);
}
}
完整的车牌识别项目若有需要请私聊博主。