Chinaunix首页 | 论坛 | 博客
  • 博客访问: 156520
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 360
  • 用 户 组: 普通用户
  • 注册时间: 2017-02-28 08:37
个人简介

没有绝活,怎能风骚.....

文章分类

全部博文(31)

文章存档

2017年(31)

我的朋友

分类: C/C++

2017-05-14 11:09:51

    自己一直做串口液晶显示屏上位机的开发工作,遇到了一个问题就是上位机软件做好的工程,下载到屏里,屏里显示白屏,没有显示对应的图片,自己也就很纳闷了,因为自己一直以为bmp图片都是一样,从未去纠结过其格式,后来深入了解了一下bmp图片格式,才知道bmp格式图片还有这么深的学问。       
     BMP图片格式,又称Bitmap(位图)或是DIB(Device-Independent Device,设备无关位图
),是Windows中使用非常广泛的图像格式。它是不做任何变换地保存图像像素域的数据,所以是我们取得原始数据的来源。Windows的图形用户界面(graphical user interfaces)也在它的内建图像子系统GDI中对BMP格式提供了支持。
    首先结合Windows的位图数据结构对BMP图片的数据格式进行剖析,其按照文件头开始的先后顺序分为四个部分:bmp文件头、位图信息头、调色板和位图数据。具体内容如下图所示:
    
    一般我们见到的图像都是以24位色图像为主,即R、G、B三种颜色各用8bit来表示,我们称这样的图像为真彩色图像,这种情况下是不需要调色板的,即位图信息头后面紧跟的就是位图数据了。所以,常有这样的说法:位图文件从文件头偏移54个字节就是位图数据了,这其实说的是16、24位或32位图的情况。这种偏移54个字节就是位图数据的程序为什么对某些位图文件没用,比如对8位的位图或16位的565格式的位图。
    下面针对我之前在项目遇到一副特定的图像进行分析,我使用的显示图像如下:
    
    这是一幅16位的位图文件,因此它是不含调色板的,我们一起来看一下这个位图文件的四个数据段的排布以及组成。
    在对位图图像数据进行分析前,首先大家得了解什么是字节序,字节序分为大小端,不了解的同学,可以参考我之前写的另一篇文章:http://blog.chinaunix.net/uid-31439230-id-5763342.html
    大家还得了解的两个知识:
    ①在BMP图片文件,如果一个数据需要同几个字节来表示的话,那么该数据的存放字节顺序为“低地址存放低位数据,高地址存放高位数据”,即小端存储方式,如数据0x3412在内存中的存储顺序为:
    
    ②下面所有分析均以字节为序号单位进行。下面是图片文件的数据(我个人使用的是VS2008查看的):
    

一、bmp文件头
    Windows为bmp文件头定义了如下结构体:
  1. typedef struct tagBITMAPFILEHEADER
  2. {
  3. UINT16 bfType;
  4. DWORD bfSize;
  5. UINT16 bfReserved1;
  6. UINT16 bfReserved2;
  7. DWORD bfOffBits;
  8. } BITMAPFILEHEADER;
    其中结构体中每项的内容如下:
    
    对照文件数据我们可以看到:
    
    0-1 : 424dH = "BM",表示这是Windows支持的位图格式。很多人说开头两个字节必须为“BM”才是位图文件,从上表来看应为开头两个字节必须是“BM”才是Windows位图文件。
    2-5 : 0003FC38H = 261176B = 255.05KB,通过查看文件属性发现一致。
    6-9 : 这是两个保留段,为0。
    A-D : 00000036H = 54。即从文件头到位图数据需偏移54字节。一会会验证这个数据。
    从0-D,结构体一共14个字节。

二、位图信息头
    同样的,Windows位位图信息头定义如下结构体:
  1. typedef struct tagBITMAPINFOHEADER { /* bmih */
  2. DWORD biSize;
  3. LONG biWidth;
  4. LONG biHeight;
  5. WORD biPlanes;
  6. WORD biBitCount;
  7. DWORD biCompression;
  8. DWORD biSizeImage;
  9. LONG biXPelsPerMeter;
  10. LONG biYPelsPerMeter;
  11. DWORD biClrUsed;
  12. DWORD biClrImportant;
  13. } BITMAPINFOHEADER;
    其中结构体中每项的内容如下:
    
    对照数据文件我们可以看到:
    
    0E-11 : 00000028H = 40,这是说明我们这个位图信息头的大小为40个字节。前面我们也讲了位图信息头一般有40个字节,既然是这也,为什么这里还要给一个字段来说明呢?这里涉及都一些历史,,其实为位图信息头原本有很多大小的版本的,看一下下面这个表:
    
    出于兼容性的考虑,大多数应用使用了旧版的位图信息图来保存文件。而OS/2已经过时了,因此现在最常用的格式就仅有Windows V3版本文件头了。所以,我们在前面说了位图信息头的大小为40字节。
    12-15 : 000001E0H = 480,图像宽位480像素,与文件属性一致。
    16-19 : 00000110H = 272,图像高为272像素,与文件属性一致;且这是个正数,说明图像数据是从图像左下角到右上角排列的。
    1A-1B : 0001H = 1,这个值总为1。
    1C-1D : 0010H = 16,表示每个像素占16个比特,即该图像共有65536种颜色。
    1E-21 : 00000000H,BI_RGB,说明本图像不压缩。
    22-25 : 0003FC02H = 261122B = 255KB,图像的大小。
    26-29 : 00000B12H = 2834,水平分辨率为2834像素/米。
    2A-2D : 00000B12H = 2834,垂直分辨率为2834像素/米。
    2E-31 : 00000000H,为0,本位图实际使用的索引数为所有调色板项,即2^biBitCount = 65536,与1C-1D得到相同的结论。
    32-35 : 00000000H,为0,表示本位图的颜色索引数目都重要。

三、调色板
    调色板是单色、16色何256色图像文件所特有的,相应的调色板大小是2、16和256,调色板以4字节为单位,每4个字节存放一个颜色值,图像的数据是指向调色板的索引。
    可以将调色板想象成一个数组,每个数组元素的大小为4字节,假设有一256色的BMP图像的调色数据为:
    调色板[0] = 黑、调色板[1] = 白、调色板[2] = 红、调色板[3] = 蓝...调色板[255] = 黄
    图像数据01 00 02 FF表示调用调色板[1]
调色板[0]调色板[2]调色板[255]中的数据来显示图像颜色。
    在早期的计算机中,显卡相对落后,不一定能保证显示所有颜色,所以在调色板中的颜色数据尽可能将图像中的主要颜色按顺序排列在前面,位图信息头的biClrImportant字段指出了有多少种颜色是重要的。
    每个调色板的大小为4字节,按蓝、绿、红存储一个颜色值。
    BMP文件的调色板的数据结构定义:
  1. typedef struct tagRGBQUAD {
  2. BYTE rgbBlue; //蓝色值,1字节
  3. BYTE rgbGreen//绿色值,1字节
  4. BYTE rgbRed//红色值,1字节
  5. BYTE rgbReserved//保留,总为0
  6. } RGBQUAD;
    如果图像是单色(1位)、16色(4位)和256色(8位),则紧跟着调色板的是位图数据,位图数据是指向调色板的索引序号
    如果位图是16位、24位和32位色,则图像文件中不保留调色板,即不存在调色板,图像的颜色直接在位图数据中给出。
    16位图像使用2字节保存颜色值,常见有两种格式:5位红5位绿5位蓝和5位红6位绿5位蓝,即555格式何565格式。555格式只使用了15位,最后一位保留,设为0。16位BMP在结构上与其他的几种位深的BMP有一定不同,由以下几个部分组成:
    BITMAPFILEHEADER
    BITMAPINFOHEADER
    BITMASK
    IMAGEDATA
    这里的BITMASK是24位或8位以下色所没有的,它表明了后面的数据分各颜色分量所使用的蒙版。值得注意的是555格式是个特例,它没有BITMASK部分的数据,并且其位图信息结构中的biCompression为BI_RGB,而其他几种格式都为BI_bitfields。还有一点就是555格式的文件头的biSize为40,其他的都为56。
    24位图像使用3字节保存颜色值,每一个字节代表一种颜色,按红、绿、蓝排列。
    32位图像使用4字节保存颜色值,每一个字节代表一种颜色,除了原来的红、绿、蓝,还有Alpha通道,即透明色。
    如果图像带有调色板,则位图数据可以根据需要选择压缩与不压缩,如果选择压缩,则根据BMP图像是16或256色,采用RLE4或RLE8压缩算法压缩。
    
    1:单色图,调色板中含有两种颜色,也就是我们通常说的黑白位图;
    4:16色图;
    8:256色图,通常说的灰度图;
    16:64K图,一般没有调色板,图像数据中每两个字节表示一个像素,5个或6个表示一个RGB分量;
    24:16M真彩色图,一般没有调色板,图像数据中每3个字节表示一个像素,每个字节表示一个RGB分量;
    32:4G真彩色,一般没有调色板,每4个字节表示一个像素,相对24位真彩图而言,加入了一个透明度,即RGBA模式。

四、位图数据
    下面是对上面位图数据的讲解,由于上图是16位位图,所以没有调色板,且其位图信息结构中的biCompression为BI_RGB,所以它没有BITMASK部分的数据,所以从文件头到位图数据需偏移54字节,每一个像素占两个字节,如下图所示:
    
    从地址00000036H处开始便是位图数据,16位的位图,一个像素两个字节,所以第一个像素为0x1DFB,这个像素表示什么颜色呢?大家先保留这个疑问,后面会进行讲解。
    注意:由于位图信息头中的图像高度是正数,所以位图数据在文件中的排序是从左下角到右上角,以行为主序排列。
    也即我们见到的第一个像素1DFB是图像最下角的数据,第二个像素1DFB为图像最后一行第二列的数据,...一直到最后一行最后一列数据,后面紧跟着的是倒数第二行第一列的数据,以此类推。

五、对齐规则
    我们知道Windows默认扫描的最小单位是4字节,如果数据对齐满足这个值的话对于数据的获取速度等是有很大的增益的。因此BMP图像顺应了这个要求,要求每行的数据的长度都必须是4的倍数,如果不够需要进行比特填充(以0填充),这样可以达到按行的快速提取。这时,位图数据区的大小就未必是图片宽x每像素字节素x图片高能表示的了,因为每行可能还需要进行比特填充。
    填充后的每行字节数为:
    RowSize = 4*((BPP*Width)/32),其中BPP(Bits Per Pixel)为每像素的比特数。
    在程序中,我们可以表示为:
    int iLineByteCnt = (((m_iImageWidth*m_iBitsPerPixel)+31)>>5)<<2;
    这样,位图数据区的大小为:
    m_iImageDataSize = iLineByteCnt*m_iImageHeight;
    我们扫描完一行数据后,野可能接下来的数据并不是下一行的数据,可能需要跳过一段填充数据:
    skip = 4 - ((m_iImageWidth*m_iBitsPerPixel)>>3)
&3;

六、嵌入式设备使用16位位图
    上面我没分析了一个具体的位图文件例子详细剖析了位图文件的组成。需要注意的是:上面讲的主要是PC机上的位图文件构成,对于嵌入式平台,如我们公司的串口屏,使用的就是这种常见的16位555格式或565格式的位图,565格式位图采用的掩码的方式来表示图像,而555格式前面提到是特殊的,没有BITMASK。此时565格式调色板数据段共有四个部分,每个部分分4个字节,实际表示的是彩色版规范,即:
    第一部分是红色分量的掩码
    第二部分是绿色分量的掩码
    第三部分是蓝色分量的掩码
    第四部分是Alpha分量的掩码(缺省为0)
    典型的调色板规范在文件中的顺序为这里以(565格式为例):
        00 F8 00 00 E0 07 00 00 1F 00 00 00 00 00 00 00
    其中
    00 F8 00 00为 F800H = 1111100000000000(二进制),红色分量的掩码
    E0 07 00 00为 07E0H = 0000011111100000(二进制),绿色分量的掩码
    1F 00 00 00为 001FH = 0000000000011111(二进制),蓝色分量的掩码
    00 00 00 00设置为0。
    如下图所示为565格式位图的数据:
    
    红色框中的数据就是各颜色分类的掩码,所以565格式位图的位图信息头位56字节,比555格式位图的40字节多了16字节,这16字节就是BITMASK。
    将掩码跟像素值进行“与”运算再进行移位操作就可以得到各色分量值。看看掩码,就可以明白事实上在每个像素值的两个字节16位中,从高到低取5、6、5位分别就是R、G、B分量值。取出分量值后把R、G分别除以256、8,B值乘以8,就可以补齐每个分量为一个字节,再把这三个字节按BGR组合,放入存储器,就可以转换为24位标准BMP格式了。
    如取上述位图数据区的其中FF 9F这个像素,这个数据实际上为9FFF:
    R = (9FFF&F800)>>8 = 98H = 152
    G = (9FFF&07E0)>>3 = FCH = 252
    B = (9FFF&001F)<<3 = F8H = 248
    原图为:
    
    将算好的R=152 G=252 B=248,放到画图工具里查看,如下图所示:
    
    对比原图左下角处的像素颜色,可知道算出的值是完全正确的。
    
    最后再算一下,我们开始举例的555格式位图的数据对应的24位标准BMP的颜色,看是否正确,当时记的第一个像素是0x1DFB,因为是555格式,所以最高位为0,其他各色5位。
    R = (1DFB&7C00)>>7 = 38H = 56
    G = (1DFB&03E0)>>2 = 78H = 120
    B = (1DFB&001F)<<3 = D8H = 216
    得到的颜色如下图所示:
    
    对比前面图像可以发现,和原图左下角处的像素点的颜色基本上是一样的。

阅读(5088) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~