关闭

bmp

930人阅读 评论(0) 收藏 举报
分类:

    BMP(全称Bitmap)是Window操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB)。两者的主要区别:

    24位的DDB图像只能绘制到每像素24位的显示系统上(二值图像例外,可以绘制到任何配置的显示系统上);

    24位的DIB则不一样,可以绘制到每像素8位、16位、24位和32位的显示系统上。当颜色深度不匹配时,颜色匹配算法被使用,以获取最好的颜色匹配。例如被绘制到8位显示系统上的24位DIB必须减少为显示系统中可用的256色。当DIB被绘制到屏幕上时,颜色减少操作在绘图过程中执行,并不影响内存中的DIB数据,唯一的缺点就是性能大大降低。因此,当性能是主要关注的对象时,不应该使用DIB,最好使用DDB。另外,DIB图像一般保存为BMP格式的文件!因此,一下讨论的是DIB。

    位图采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选lbit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。

    BMP文件主要有4部分组成:1、文件头;2、位图信息头;3、颜色表;4、图形数据。

1、文件头(14字节)

typedef struct tagBITMAPFILEHEADER
{
    WORD bfType; // 位图文件的类型,必须为BM(1-2字节)
    DWORD bfSize; // 位图文件的大小,以字节为单位(3-6字节)
    WORD bfReserved1; // 位图文件保留字,必须为0(7-8字节)
    WORD bfReserved2; // 位图文件保留字,必须为0(9-10字节)
    DWORD bfOffBits; // 位图数据的起始位置,以相对于位图文件头的偏移量表示,以字节为单位(11-14字节)
} BITMAPFILEHEADER;

2、位图信息头(40字节)——用于说明位图的尺寸等信息。

typedef struct tagBITMAPINFOHEADER{
    DWORD biSize; // 本结构所占用字节数(15-18字节)
    LONG biWidth; // 位图的宽度,以像素为单位(19-22字节)
    LONG biHeight; // 位图的高度,以像素为单位(23-26字节)
    WORD biPlanes; // 目标设备的级别,必须为1(27-28字节)
    WORD biBitCount;// 每个像素所需的位数,必须是1(双色),  4(16色),8(256色)16(高彩色)或24(真彩色)之一(29-30字节)
    DWORD biCompression; // 位图压缩类型,必须是 0(不压缩), 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一(31-34字节)
    DWORD biSizeImage; // 位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)
    LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数(39-42字节)
    LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数(43-46字节)
    DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数(47-50字节)
    DWORD biClrImportant;// 位图显示过程中重要的颜色数(51-54字节)
} BITMAPINFOHEADER;

3、颜色表(4*N字节)

    颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。也就是说,颜色表中有多少个表项,就有多少种颜。RGBQUAD结构的定义如下:

typedef struct tagRGBQUAD {
    BYTE rgbBlue;// 蓝色的亮度(值范围为0-255)
    BYTE rgbGreen; // 绿色的亮度(值范围为0-255)
    BYTE rgbRed; // 红色的亮度(值范围为0-255)
    BYTE rgbReserved;// 保留,必须为0
} RGBQUAD;

    颜色表中表项的个数有biBitCount来确定:当biBitCount=1,4,8时,分别有2,16,256个表项;当biBitCount=24(真彩色)时,没有颜色表项

    位图信息头颜色表组成位图信息,BITMAPINFO结构定义如下:

typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER bmiHeader; // 位图信息头
    RGBQUAD bmiColors[1]; // 颜色表
} BITMAPINFO;

    可以看到,BITMAPINFO中bmiColors[1]虽然是个数组,但是只有1个成员。这是不是说明只能保存一种颜色呢?答案是否定的。由于bmp文件中信息头后面就是颜色表,连续存储。因此bmiColors[1]存放的是颜色表的第一个成员。由于数组的存放是连续的,因此可以通过bmiColors继续找到第2个、3个成员(当然,这时候数组越界了,但是没关系,达到目的就好!)。因此,BITMAPINFO中的bmiColors[1]仅仅是起到了一个定义类型的作用!而不是表示所有的颜色信息!(详见参考资料[5])

4、图形数据

    颜色表已经定义了本图像的所有颜色,图形数据中,各个像素的值就代表那个像素选择颜色表中的哪一种颜色,即表示像素颜色在调色板(颜色表)中的索引号;图像为真彩色时,图形数据直接表示红、绿、蓝的相对亮度。

    

5、实例分析

    下图是使用HexEditor2.exe打开的一个bmp文件的截图:


                                图1 


1-2:42 4D——BM

3-6:3E 39 01 00——0X00 01 39 3E=80190 byte=78.3KB(小端模式),表示本bmp文件的大小为78.31KB。下面是本bmp文件的属性截图。在“大小”一项中可以知道,本bmp文件的大小为78.3KB,和上面的数据一致!

7-8:00 00——保留,必须为0

9-10:00 00 ——保留,必须为0


                        图2


11-14:36 00 00 00 ——0X00 00 00 36=54,从文件开始到图形数据之间的偏移量。计算公式:14+40+4*(2^biBitCount),这是颜色表存在的情况。但是本文件的偏移量只是54,说明不符合本计算公式。经过查阅发现本文件的biBitCount=24(真彩色)。这说明真彩色是不需要颜色表的!

15-18:28 00 00 00——0X00 00 00 28=40,位图信息头长度,按照预定,本bmp文件的位图信息头长度为40字节。

19-22:FC 00 00 00——0X00 00 00 FC=252,位图宽度,以像素为单位。本bmp图像的宽度是252像素。图3是利用exiftool工具查看本bmp文件的信息的截图。从中可以看到本文件的“Image Width”为252,和这里的数据一致。

23-26:6A 00 00 00——0X00 00 00 6A=106,位图高度。同样可以和图3中的“Image Height”比较,发现两者是一致的。


                                                图3 利用exiftool工具查看本bmp文件的信息的截图

27-28:01 00 ——0X00 01,位图的位面数,该值总是1。

29-30:18 00 ——0X00 18=24,每个像素的位数,本bmp文件是真彩色。

31-34:00 00 00 00——0X00 00 00 00=0,压缩说明:0表示不压缩。

6、编程实例_修改自百度百科,在VC6.0中编译通过,测试正确。

#include <stdio.h>
#include <windows.h>
#include <stdlib.h>

//注意:这个结构并非颜色表,只是方便于下面保存24位真彩色图像的R、G、B值而已。
typedef struct{
    BYTE b;
    BYTE g;
    BYTE r;
}RGB;

int main( void )
{
	//VC 6.0中,要求对变量的定义要放置前面!
	RGB *img;
	int size;
	int i;

    BITMAPFILEHEADER fileHeader;
    BITMAPINFOHEADER infoHeader;
    FILE* pfin =fopen("bmp.bmp","rb");
    FILE* pfout = fopen( "修改后的图像.bmp" , "wb");
    //Read the Bitmap file header;
    fread(&fileHeader,sizeof(BITMAPFILEHEADER),1,pfin);
    //Read the Bitmap info header;
    fread(&infoHeader,sizeof(BITMAPINFOHEADER),1,pfin);
    //为简化代码,只处理24位彩色
	if( infoHeader.biBitCount == 24 )
	{
		//int size = infoHeader.biWidth*infoHeader.biHeight;
		size = infoHeader.biWidth*infoHeader.biHeight;

		//RGB img[infoHeader.biHeight][infoHeader.biWidth];
		img = (RGB *)malloc(infoHeader.biHeight*infoHeader.biWidth*sizeof(RGB)); //虽然原本的img是一个二维的数组,但是这里并不能将它定义为二维的指针,因为二维的指针需要进行两次的内存分配!

		fread( img , sizeof(RGB) , size , pfin );
		//把第50行染成黑色
		//int i = 0;
		for(i=0 ; i < infoHeader.biWidth ; i++ )
		{
		//   img[50][i].b =img[50][i].g=img[50][i].r= 0;
			(img+50*infoHeader.biWidth+i)->b=(img+50*infoHeader.biWidth+i)->g=(img+50*infoHeader.biWidth+i)->r=0; //一维的指针,自然有一维的读写方法啦。
		}
		//将修改后的图片保存到文件
		fwrite( &fileHeader , sizeof(fileHeader) , 1 , pfout );
		fwrite( &infoHeader , sizeof(infoHeader) , 1 , pfout );
		fwrite( img , sizeof(RGB) , size , pfout );
	}
	free(img);
	fclose(pfin);
	fclose(pfout);
}


7、读取bmp文件_matlab

clear
clc
i=fopen('slice_350.bmp'); %CT断层图像
A=fread(i);
A=A(1079:end); % 前面1078字节是信息头,1079是根据实际情况计算得到的
img= uint8(zeros(1024,1024)); % 若想使用imshow()对其进行显示,则必须转换为uint8类型,否则,显示的效果颗粒感很强
for m=1:1024
    for n=1:1024
        img(1025-m, n) = A((m-1)*1024 + n); % bmp图像的扫描方式是从左到右,从下到上,因此需要写成1025-m的形式。
    end
end
imshow(img)


8、参考资料


[1]BMP_百度百科
[3]位映射_百度百科
[4]真彩色_百度百科

[5] 处理 BITMAPINFO 结构体中的 bmiColors 这个成员 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1081261次
    • 积分:17145
    • 等级:
    • 排名:第606名
    • 原创:641篇
    • 转载:2篇
    • 译文:11篇
    • 评论:112条
    文章分类
    最新评论