最近在做关于打印机驱动的程序,涉及了一些对于图像处理的知识,本来也知道,但又不想做完后不留下点什么,所以还是总结点自己的经验小节!:)主要是快下班了,正好够我总结!
好了,言归正传~~~
先来用通俗的语句讲解位图和调色板的概念。
我们知道,自然界中的所有颜色都可以由红、绿、蓝(R,G,B)三基色组合而成。针对含有红、绿、蓝色成分的多少,可以对其分别分成0~255个等级,而红、绿、蓝的不同组合共有256×256×256种,因此约能表示1600万种颜色。对于人眼而言,这已经是"真彩色"了。
对每个像素进行了(R,G,B)量化的图像就是位图,其在计算机中对应文件的扩展名一般为.bmp。既然用R,G,B的量化值就可以直接记录一张位图的所有像素,那我们需要调色板干什么呢?
首先,我们可以计算完全利用(R,G,B)组合来存储一个800×600的位图所需要的空间为:
800×600×3 = 1440000(字节)= 1.37M(字节)
惊人的大!因此,调色板横空出世了,它的功能在于缓解位图文件存储空间过大的问题。
假设一个位图为16色,其像素总数为800×600。我们只需要用4个bit就可以存储这个位图的每个像素在16种颜色中所处的等级,然后调色板提供了这16种等级对应的(R,G,B)值,这样,存储这个16色位图只需要:
800×600×4/8 = 240000(字节)= 0.22 M(字节)
额外的存储R,G,B表的开销(即调色板Palette,也称为颜色查找表LUT)仅仅为16×3=48字节。
存储空间被大为减少!
常见的位图有单色、16色、256色、16位及24位真彩色5种,对于前三者(即不大于256色)都可以调色板方式进行存储,而对16位及24位真彩色以调色板进行存储是不划算的,它们直接按照R,G,B分量进行存储。
在此基础上我们来分析DDB位图(Device-dependentbitmap,与设备相关的位图)与DIB位图(Device-independentbitmap,与设备无关的位图)的概念以及二者的区别。
DDB依赖于具体设备,它只能存在于内存中(视频内存或系统内存),其颜色模式必须与特定的输出设备相一致,使用系统调色板。一般只能载入色彩较简单的DDB位图,对于颜色较丰富的位图,需使用DIB才能长期保存。
DIB不依赖于具体设备,可以用来永久性地保存图象。DIB一般是以*.BMP文件的形式保存在磁盘中的,有时也会保存在*.DIB文件中。DIB位图的特点是将颜色信息储存在位图文件自身的颜色表中,应用程序要根据此颜色表为DIB创建逻辑调色板。因此,在输出一幅DIB位图之前,程序应该将其逻辑调色板选入到相关的设备上下文并实现到系统调色板中。
=================================================================================================
接下来说说位图文件的格式:(是位图最关键的部分)
位图文件头BITMAPFILEHEADER是一个结构体,长度为14字节,定义为:
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; //文件类型,必须是0x424D,即字符串"BM"
DWORD bfSize; //文件大小,包括BITMAPFILEHEADER的14个字节
WORD bfReserved1; //保留字
WORD bfReserved2; //保留字
DWORD bfOffBits; //从文件头到实际的位图数据的偏移字节数
} BITMAPFILEHEADER;
(2)位图信息头BITMAPINFOHEADER
位图信息头BITMAPINFOHEADER也是一个结构体,长度为40字节,定义为:
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; //本结构的长度,为40
LONG biWidth; //图象的宽度,单位是象素
LONG biHeight; //图象的高度,单位是象素
WORD biPlanes; //必须是1
WORD biBitCount;
//表示颜色时要用到的位数,1(单色), 4(16色), 8(256色), 24(真彩色)
DWORD biCompression;
//指定位图是否压缩,有效的值为BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS等,BI_RGB表示不压缩
DWORD biSizeImage;
//实际的位图数据占用的字节数,即 biSizeImage=biWidth’ × biHeight,biWidth’是biWidth按照4的整倍数调整后的结果
LONG biXPelsPerMeter; //目标设备的水平分辨率,单位是每米的象素个数
LONG biYPelsPerMeter; //目标设备的垂直分辨率,单位是每米的象素个数
DWORD biClrUsed; //位图实际用到的颜色数,0表示颜色数为2biBitCount
DWORD biClrImportant; //位图中重要的颜色数,0表示所有颜色都重要
} BITMAPINFOHEADER;
(3)调色板Palette
调色板Palette针对的是需要调色板的位图,即单色、16色和256色位图。对于不以调色板方式存储的位图,则无此项信息。调色板是一个数组,共有biClrUsed个元素(如果该值为0,则有2biBitCount个元素)。数组中每个元素是一个RGBQUAD结构体,长度为4个字节,定义为:
typedef struct tagRGBQUAD
{
BYTE rgbBlue; //蓝色分量
BYTE rgbGreen; //绿色分量
BYTE rgbRed; //红色分量
BYTE rgbReserved; //保留值
} RGBQUAD;
(4)实际的位图数据ImageDate
对于用到调色板的位图,实际的图象数据ImageDate为该象素的颜色在调色板中的索引值;对于真彩色图,图象数据则为实际的R、G、B值:
a.单色位图:用1bit就可以表示象素的颜色索引值;
b.16色位图:用4bit可以表示象素的颜色索引值;
c. 256色位图:1个字节表示1个象素的颜色索引值;
d.真彩色:3个字节表示1个象素的颜色R,G,B值。
此外,位图数据每一行的字节数必须为4的整倍数,如果不是,则需要补齐。奇怪的是,位图文件中的数据是从下到上(而不是从上到下)、从左到右方式存储的。
大家现在对位图有个比较清楚的了解了吧!
=================================================================================================
现在我举个例子来方便大家理解(呵呵!其实就是我怕忘说的客套话)
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000000 42 4D D6 00 00 00 00 00 00 00 36 00 00 00 28 00BM?......6...(.
00000016 00 00 0A 00 00 00 05 00 00 00 01 00 18 00 00 00................
00000032 00 00 A0 00 00 00 C4 0E 00 00 C4 0E 00 00 00 00..?..?..?....
00000048 00 00 00 00 00 00 00 00 FF 00 00 FF 00 00 FF 00........?..?..?.
00000064 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00?..?..?..?..?..
00000080 FF 00 00 FF 00 00 00 00 FF 00 00 FF 00 00 FF 00..?....?..?..?.
00000096 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00?..?..?..?..?..
00000112 FF 00 00 FF 00 00 00 00 FF 00 00 FF 00 00 FF 00..?....?..?..?.
00000128 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00?..?..?..?..?..
00000144 FF 00 00 FF 00 00 00 00 FF 00 00 FF 00 00 FF 00..?....?..?..?.
00000160 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00?..?..?..?..?..
00000176 FF 00 00 FF 00 00 00 00 FF 00 00 FF 00 00 FF 00..?....?..?..?.
00000192 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00?..?..?..?..?..
00000208 FF 00 00 FF 00 00 ?..?..
以上即基本的一幅真彩色24位宽10象素高5象素的位图的十六进制格式。我们可以把一幅
位图分为三部分来研究:位图文件头,位图信息头和位图阵列表。
一、位图文件头:
Offset 0 1 2 3 4 5 6 7 8 9 10 11 1213 14 15
00000000 42 4DD6 00 00 00 00 00 0000 36 00 00 00BM?......6...
分析:位图文件头,记录标志文件大小等一些信息。
42 4D 为位图的标志,即ASCII码为BM。
D6 00 表示位图的文件的总字节数。换算成十进制为15054字节,即这幅画的大小为150
54字节。
00 00 00 00 00 00 该几个字节为保留字节。
36 00 00 00 表示位图阵列的起始位置。36H=54即54字节开始为位图阵列。
二、位图信息头:
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000000 28 00 (.
00000016 00 00 0A 00 0000 05 00 0000 01 0018 00 00 00................
00000032 00 00 A0 00 00 00 C4 0E 00 00 C4 0E 00 00 00 00..?..?..?....
00000048 00 00 00 00 00 00 .......
分析:位图信息头,记录着和位图相关的一些信息。如位图长、宽、高等。
28 00 00 00 位图信息头的长度。28H=40,即位图信息头占用40个字节。
0A 00 00 00 位图宽度。我们建立的位图是10个象素所以这里为0AH。
05 00 00 00 位图高度 。刚好等于我们建立位图时设置的5个象素宽的值。
10 00 位图设备级别。
18 00 位图级别00 18H=24即24位真彩色。
00 00 00 00 压缩类型 为0代理不压缩。
A0 00 00 00 位图大小。A0=160 ,代表下面的图阵列表为160个字节
C4 0E 00 00 水平分辨率。
C4 0E 00 00 垂真分辨率。
00 00 00 00 位图实际使用的颜色表中的颜色变址数。
00 00 00 00 位图显示过程中被认为重要颜色变址数。
三、位图阵列表
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
00000048 00 00 FF 00 00 FF 00 00 FF 00 .?..?..?.
00000064 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00?..?..?..?..?..
00000080 FF 00 00 FF 00 00 00 00 FF 00 00 FF 00 00 FF 00..?....?..?..?.
00000096 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00?..?..?..?..?..
00000112 FF 00 00 FF 00 00 00 00 FF 00 00 FF 00 00 FF 00..?....?..?..?.
00000128 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00?..?..?..?..?..
00000144 FF 00 00 FF 00 00 00 00 FF 00 00 FF 00 00 FF 00..?....?..?..?.
00000160 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00?..?..?..?..?..
00000176 FF 00 00 FF 00 00 00 00 FF 00 00 FF 00 00 FF 00..?....?..?..?.
00000192 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00 FF 00 00?..?..?..?..?..
00000208 FF 00 00 FF 00 00 ?..?..
分析:该区域均为位图阵列表。
在24位位图阵列中,每三个字节代表画笔上的一个象素,这三个字节从左到右分别代表
蓝、绿、红,所以00 00 FF代表一个红色的象素。在位图阵列中就记载了位图显示的位
置和颜色这两个要素。在此例中地址为48开始的三个字节00 00 FF在画笔中呈出来的是
左下角的最左边的一个点。虽然和实际上的位图有所不同但它是按一定规律的。
核心成象原理:
在24位位图阵列表中,每三个字节产生屏幕上的一个象素,每个字节分别控制着蓝、绿
、红三个电子枪的发光强度。我们知道在显示器内部有三个电子枪,如果这三个电子枪
都不发光的情况下这时屏幕上就显示为黑色,此时这三个字节就应该为00 00 00;如果
三个电子枪都各自发最强的光,即产生最强的蓝、绿、红三种光混合在一起就是白色,
这时位图阵列表的三个字节为FF FF FF;如果只是其中的一个电子枪发光则产生的光就
是蓝、绿、红中的其中的一种;如果系统需要其它颜色的话就可能两个或者三个电子枪
都产生强弱不同的光线来组合成其它颜色。在位图阵列表中的这三个字节只是告诉电子
枪要发光的强弱,00代最不发光,FF代表发该电子枪最强的光,这就是为什么这么一段
位图阵列表为什么能够在画笔中呈现出多彩的颜色的基本原理。
=================================================================================================
再给大家一个分享例子:
424D 4690 0000 0000 0000 4600 0000 2800 0000 8000 00009000 0000 0100*1000 0300 0000 0090 0000 A00F 0000 A00F 0000 00000000 0000 0000*00F8 0000 E007 0000 1F00 0000 0000 0000*02F1 84F104F1 84F1 84F1 06F2 84F1 06F2 04F2 86F2 06F2 86F2 86F2 ….….
BMP文件可分为四个部分:位图文件头、位图信息头、彩色板、图像数据阵列,在上图中已用*分隔。
一、图像文件头
1)1:(这里的数字代表的是”字”,即两个字节,下同)图像文件头。424Dh=’BM’,表示是Windows支持的BMP格式。
2)2-3:整个文件大小。4690 0000,为00009046h=36934。
3)4-5:保留,必须设置为0。
4)6-7:从文件开始到位图数据之间的偏移量。46000000,为00000046h=70,上面的文件头就是35字=70字节。
5)8-9:位图图信息头长度。
6)10-11:位图宽度,以像素为单位。8000 0000,为00000080h=128。
7)12-13:位图高度,以像素为单位。9000 0000,为00000090h=144。
8)14:位图的位面数,该值总是1。0100,为0001h=1。
二、位图信息头
9)15:每个像素的位数。有1(单色),4(16色),8(256色),16(64K色,高彩色),24(16M色,真彩色),32(4096M色,增强型真彩色)。1000为0010h=16。
10)16-17:压缩说明:有0(不压缩),1(RLE 8,8位RLE压缩),2(RLE4,4位RLE压缩,3(Bitfields,位域存放)。RLE简单地说是采用像素数+像素值的方式进行压缩。T408采用的是位域存放方式,用两个字节表示一个像素,位域分配为r5b6g5。图中03000000为00000003h=3。
11)18-19:用字节数表示的位图数据的大小,该数必须是4的倍数,数值上等于位图宽度×位图高度×每个像素位数。00900000为00009000h=80×90×2h=36864。
12)20-21:用象素/米表示的水平分辨率。A00F 0000为0000 0FA0h=4000。
13)22-23:用象素/米表示的垂直分辨率。A00F 0000为0000 0FA0h=4000。
14)24-25:位图使用的颜色索引数。设为0的话,则说明使用所有调色板项。
15)26-27:对图象显示有重要影响的颜色索引的数目。如果是0,表示都重要。
三、彩色板
16)28-35:彩色板规范。对于调色板中的每个表项,用下述方法来描述RGB的值:
1字节用于蓝色分量
1字节用于绿色分量
1字节用于红色分量
1字节用于填充符(设置为0)
对于24-位真彩色图像就不使用彩色板,因为位图中的RGB值就代表了每个象素的颜色。
如,彩色板为00F8 0000 E007 0000 1F00 0000 0000 0000,其中:
00FB 0000为FB00h=1111100000000000(二进制),是红色分量的掩码。
E007 0000为 07E0h=0000011111100000(二进制),是绿色分量的掩码。
1F00 0000为001Fh=0000000000011111(二进制),是红色分量的掩码。
0000 0000总设置为0。
将掩码跟像素值进行“与”运算再进行移位操作就可以得到各色分量值。看看掩码,就可以明白事实上在每个像素值的两个字节16位中,按从高到低取5、6、5位分别就是r、g、b分量值。取出分量值后把r、g、b值分别 乘以8、4、8就可以补齐第个分量为一个字节,再把这三个字节按rgb组合,放入存储器(同样要反序),就可以转换为24位标准BMP格式了。
四、图像数据阵列
17)17-...:每两个字节表示一个像素。阵列中的第一个字节表示位图左下角的象素,而最后一个字节表示位图右上角的象素