车牌识别项目之图像预处理一:C语言读取bmp图像信息
- 一、什么是bmp文件
- 二、BMP格式结构
- 1、文件信息头
- 2、图像描述信息块
- 3、BMP调色板
- 4、BMP图像数据区
-
- 三、原理实现:
- 1、打开一张bmp文件
- 2、查看图像属性
- 3、用ULtraEdit打开bmp文件。显示的是16进制的代码。
- 四、完整代码实现
本文是将用SoC FPGA 板子实现的车牌识别项目,用的改写的LeNet网络。主要实现分俩个大的部分:1、是用fpga实现的卷积、偏置激活、池化和全连接操作。2、是在ARM端实现摄像头读进来的图像数据的预处理操作。我们先介绍的是在ARM对图像的操作,后续继续更新。
一、什么是bmp文件
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文件的数据按照从文件头开始的先后顺序分为四个部分:
◆ 位图文件头(bmp file header): 提供文件的格式、大小等信息
◆ 位图信息头(bitmap information):提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息
◆ 调色板(color palette):可选,如使用索引来表示图像,调色板就是索引与其对应的颜色的映射表
◆ 位图数据(bitmap data):图像数据区
BMP图片文件数据表如下:
1、文件信息头
文件信息头 (14字节)存储着文件类型,文件大小等信息。
typedef struct tagBITMAPFILEHEADER
{
/unsigned short bfType;
unsigned int bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned int bfOffBits;
} BITMAPFILEHEADER;
2、图像描述信息块
图片信息头 (40字节)存储着图像的尺寸,颜色索引,位平面数等信息
typedef struct tagBITMAPINFOHEADER
{
unsigned int biSize;
long biWidth;
long biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} BITMAPINFOHEADER;
3、BMP调色板
调色板 (由颜色索引数决定)【可以没有此信息】这部分定义了图像中所用的颜色。如上所述,位图图像一个像素接着一个像素储存,每个像素使用一个或者多个字节的值表示,所以调色板的目的就是要告诉应用程序这些值所对应的实际颜色。
典型的位图文件使用RGB彩色模型。在这种模型中,每种颜色都是由不同强度(从0到最大强度)的红色(R)、绿色(G)和蓝色(B)组成的,也就是说,每种颜色都可以使用红色、绿色和蓝色的值所定义。
在位图文件的实现中,调色板可以包含很多条目,条目个数就是图像中所使用的颜色的个数。
typedef struct tagRGBQUAD{
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} RGBQUAD;
每个条目用来描述一种颜色,包含4个字节,其中三个表示蓝色、绿色和红色,第四个字节没有使用(大多数应用程序将它设为0);对于每个字节,数值0表示该颜色分量在当前的颜色中没有使用,而数值255表示这种颜色分量使用最大的强度。
注意:
- 1,4,8位图像才会使用调色板数据,16,24,32位图像不需要调色板数据,即调色板最多只需要256项(索引0 - 255)。
- 颜色表的大小根据所使用的颜色模式而定:2色图像为8字节;16色图像位64字节;256色图像为1024字节。其中,每4字节表示一种颜色,并以B(蓝色)、G(绿色)、R(红色)、alpha(32位位图的透明度值,一般不需要)。即首先4字节表示颜色号1的颜色,接下来表示颜色号2的颜色,依此类推。
- 颜色表中RGBQUAD结构数据的个数有biBitCount来确定,当biBitCount=1,4,8时,分别有2,16,256个表项。
- 当biBitCount=1时,为2色图像,BMP位图中有2个数据结构RGBQUAD,一个调色板占用4字节数据,所以2色图像的调色板长度为2*4为8字节。
- 当biBitCount=4时,为16色图像,BMP位图中有16个数据结构RGBQUAD,一个调色板占用4字节数据,所以16像的调色板长度为16*4为64字节。
- 当biBitCount=8时,为256色图像,BMP位图中有256个数据结构RGBQUAD,一个调色板占用4字节数据,所以256色图像的调色板长度为256*4为1024字节。
- 当biBitCount=16,24或32时,没有颜色表。
4、BMP图像数据区
①像素存储
表示位图中像素的比特是以行为单位对齐存储的,每一行的大小都向上取整为4字节(32位DWORD)的倍数。如果图像的高度大于1,多个经过填充实现对齐的行就形成了像素数组。
完整存储的一行像素所需的字节数可以通过这个公式计算:
Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充,
DataSizePerLine= (biWidth* biBitCount+31)/8;
DataSizePerLine= DataSizePerLine/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)表示。
- 每像素占1位(色深为1位,1bpp)的格式支持2种不同颜色。像素值直接对应一个位的值,最左像素对应第一个字节的最高位。使用该位的值用来对色表的索引:为0表示色表中的第一项,为1表示色表中的第二项(即最后一项)。
- 每像素占2位(色深为2位,2bpp)的格式支持4种不同颜色。每个字节对应4个像素,最左像素为最高的两位(仅在WindowsCE中有效)。需要使用像素值来对一张含有4个颜色值的色表进行索引。
- 每像素占4位(色深为4位,4bpp)的格式支持16种不同的颜色。每个字节对应2个像素,最左像素为最高的四位。需要使用像素值来对一张含有16个颜色值的色表进行索引。
- 每像素占8位(色深为8位,8bpp)的格式支持256种不同的颜色。每个字节对应1个像素。需要使用像素值来对一张含有256个颜色值的色表进行索引。
- 每像素占16位(色深为16位,16bpp)的格式支持65536种不同的颜色,每2个字节(byte)对应一个像素。该像素的不透明度(英语:alpha)、红、绿、蓝采样值即存储在该2个字节中。
- 每像素占24位(色深为24位,24bpp)的格式支持16777216种不同的颜色,每3个字节对应一个像素。
- 每像素占32位(色深为32位,32bpp)的格式支持4294967296种不同的颜色,每4个字节对应一个像素。
简单来说就是如下:
位图的一个像素值所占的字节数:
当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图像实现,后续便于我们给你更好的理解车牌。
1、打开一张bmp文件
2、查看图像属性
3、用ULtraEdit打开bmp文件。显示的是16进制的代码。
用其他软件打开也是可以的
现在我们来读取这些代码,看看他们到底保存了一些啥东西。 在这里要注意的是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 ;
unsigned char rgbGreen = 0xA5583E 其中R:0X3E G:0X58 B:0XA5 ;
unsigned char rgbRed = 0x7A5A3E 其中R:0X3E G:0X5A B:0X7A ;
unsigned char rgbReserved;
四、完整代码实现
BmpFormat.h
# ifndef BMP_H
# define BMP_H
typedef struct tagBITMAPFILEHEADER
{
unsigned int bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned int bfOffBits;
} BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER
{
unsigned int biSize;
long biWidth;
long biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD{
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} RGBQUAD;
#endif
read_bmp.c
#include <stdio.h>
#include <malloc.h>
#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");
if(fp == NULL)
{
printf("打开'lena.bmp'失败!\n");
return -1;
}
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);
}
}
运行结果:
原理:参考百度百科和维基百科
完整的车牌识别项目若有需要请私聊博主。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)