2024年2月21日发(作者:)

南通大学计算机科学与技术学院

《数字图像处理》课程实验

报 告 书

实 验 名 BMP文件的读写(8位和24位)

班 级 计 121

姓 名 张 进

学 号 1213022016

2014年6月 16 日

一、实验内容

1、了解BMP文件的结构

2、8位位图和24位位图的读取

二、BMP图形文件简介

BMP(Bitmap-File)图形文件是Windows采用的图形文件格式,在Windows环境下运行的所有图象处理软件都支持BMP图象文件格式。Windows系统内部各图像绘制操作都是以BMP为基础的。Windows 3.0以前的BMP图文件格式与显示设备有关,因此把这种BMP图象文件格式称为设备相关位图DDB(device-dependent bitmap)文件格式。Windows 3.0以后的BMP图象文件与显示设备无关,因此把这种BMP图象文件格式称为设备无关位图DIB(device-independent bitmap)格式(注:Windows 3.0以后,在系统中仍然存在DDB位图,象BitBlt()这种函数就是基于DDB位图的,只不过如果你想将图像以BMP格式保存到磁盘文件中时,微软极力推荐你以DIB格式保存),目的是为了让Windows能够在任何类型的显示设备上显示所存储的图象。BMP位图文件默认的文件扩展名是BMP或者bmp(有时它也会以.DIB或.RLE作扩展名)。

位图文件可看成由4个部分组成:位图文件头(bitmap-file header)、位图信息头(bitmap-information header)、彩色表(color table)和定义位图的字节阵列,它具有如下所示的形式。

位图文件的组成

位图文件头(bitmap-file header)

结构名称

BITMAPFILEHEADER bmfh

bmih

aColors[]

aBitmapBits[]

符号

位图信息头(bitmap-information header)

BITMAPINFOHEADER

彩色表(color table)

图象数据阵列字节

RGBQUAD

BYTE

位图文件结构内容摘要

偏移量

域的名称

大小

内容

两字节的内容用来识别位图的类型:

图象文件

0000h

文件标识

2 bytes

‘BM’ : Windows 3.1x, 95, NT, …

‘BA’ :OS/2 Bitmap Array

‘CI’ :OS/2 Color Icon

‘CP’ :OS/2 Color Pointer

‘IC’ : OS/2 Icon

‘PT’ :OS/2 Pointer

注:因为OS/2系统并没有被普及开,所以在编程时,你只需判断第一个标识“BM”就行。

0002h File Size

0006h Reserved

1 dword

1 dword

用字节表示的整个文件的大小

保留,必须设置为0

从文件开始到位图数据开始之间的数据(bitmap data)之间的偏移量

位图信息头(Bitmap Info Header)的长度,用来描述位图的颜色、压缩方法等。下面的长度表示:

28h - Windows 3.1x, 95, NT, …

0Ch - OS/2 1.x

000Eh Bitmap Header Size 1 dword

F0h - OS/2 2.x

注:在Windows95、98、2000等操作系统中,位图信息头的长度并不一定是28h,因为微软已经制定出了新的BMP文件格式,其中的信息头结构变化比较大,长度加长。所以最好不要直接使用常数28h,而是应该从具体的文件中读取这个值。这样才能确保程序的兼容性。

0012h Width

0016h Height

001Ah Planes

1 dword

1 dword

1 word

位图的宽度,以象素为单位

位图的高度,以象素为单位

位图的位面数(注:该值将总是1)

每个象素的位数

图象

信息

001Ch Bits Per Pixel 1 word

1 - 单色位图(实际上可有两种颜色,缺省情况下是黑色和白色。你可以自己定义这两种颜色)

4 - 16 色位图

8 - 256 色位图

16 - 16bit 高彩色位图

24 - 24bit 真彩色位图

32 - 32bit 增强型真彩色位图

压缩说明:

0 - 不压缩 (使用BI_RGB表示)

001Eh Compression 1 dword

1 - RLE 8-使用8位RLE压缩方式(用BI_RLE8表示)

2 - RLE 4-使用4位RLE压缩方式(用BI_RLE4表示)

3 - Bitfields-位域存放方式(用BI_BITFIELDS表示)

0022h Bitmap Data Size

0026h HResolution

002Ah VResolution

002Eh Colors

0032h Important Colors

1 dword

1 dword

1 dword

1 dword

1 dword

用字节数表示的位图数据的大小。该数必须是4的倍数

用象素/米表示的水平分辨率

用象素/米表示的垂直分辨率

位图使用的颜色数。如8-比特/象素表示为100h或者 256.

指定重要的颜色数。当该域的值等于颜色数时(或者等于000Ah Bitmap Data Offset 1 dword

0时),表示所有颜色都一样重要

根据调色BMP版板数本的不Palette

据 同而不同

调色板规范。对于调色板中的每个表项,这4个字节用下述方法来描述RGB的值:

N * 4 byte

1字节用于蓝色分量

1字节用于绿色分量

1字节用于红色分量

1字节用于填充符(设置为0)

根据BMP版图象数据

本及调色板尺Bitmap Data

寸的不同而不同

xxx bytes

该域的大小取决于压缩方法及图像的尺寸和图像的位深度,它包含所有的位图数据字节,这些数据可能是彩色调色板的索引号,也可能是实际的RGB值,这将根据图像信息头中的位深度值来决定。

三、读写涉及的原理

1、图像的二值化的基本原理

图像的二值化处理就是讲图像上的点的灰度置为0或255,也就是讲整个图像呈现出明显的黑白效果。即将256个亮度等级的灰度图像通过适当的阀值选取而获得仍然可以反映图像整体和局部特征的二值化图像。在数字图像处理中,二值图像占有非常重要的地位,特别是在实用的图像处理中,以二值图像处理实现而构成的系统是很多的,要进行二值图像的处理与分析,首先要把灰度图像二值化,得到二值化图像,这样子有利于再对图像做进一步处理时,图像的集合性质只与像素值为0或255的点的位置有关,不再涉及像素的多级值,使处理变得简单,而且数据的处理和压缩量小。为了得到理想的二值图像,一般采用封闭、连通的边界定义不交叠的区域。所有灰度大于或等于阀值的像素被判定为属于特定物体,其灰度值为255表示,否则这些像素点被排除在物体区域以外,灰度值为0,表示背景或者例外的物体区域。如果某特定物体在内部有均匀一致的灰度值,并且其处在一个具有其他等级灰度值的均匀背景下,使用阀值法就可以得到比较的分割效果。如果物体同背景的差别表现不在灰度值上(比如纹理不同),可以将这个差别特征转换为灰度的差别,然后利用阀值选取技术来分割该图像。动态调节阀值实现图像的二值化可动态观察其分割图像的具体结果。

2、图像的反色原理

对于彩色图像的R、G、B各彩色分量取反的技术就是图像的反色处理,这在处理二值化图像的连通区域选取的时候非常重要。如物体连通域用黑色表示,而二值化后的物体连通域图像可那是白色的,而背景是黑色的,这时应手动选取图像的反色处理或有程序根据背景和物体连通域两种颜色的数量所占比例而自动选择是否选择选取图像的反色处理

四、读写转换代码

#include

#include

#include

#include

#include

#include

#include

#include

//---------------------------------------------------------------------------------------

//以下该模块是完成BMP图像(彩色图像是24bit RGB各8bit)的像素获取,并存在文件名为xiang_su_中

unsigned char *pBmpBuf;//读入图像数据的指针

int bmpWidth;//图像的宽

int bmpHeight;//图像的高

RGBQUAD *pColorTable;//颜色表指针

int biBitCount;//图像类型,每像素位数

//-------------------------------------------------------------------------------------------

//读图像的位图数据、宽、高、颜色表及每像素位数等数据进内存,存放在相应的全局变量中

bool readBmp(char *bmpName)

{

FILE *fp=fopen(bmpName,"rb");//二进制读方式打开指定的图像文件

if(fp==0)

return 0;

//跳过位图文件头结构BITMAPFILEHEADER

fseek(fp, sizeof(BITMAPFILEHEADER),0);

//定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中

BITMAPINFOHEADER head;

fread(&head, sizeof(BITMAPINFOHEADER), 1,fp); //获取图像宽、高、每像素所占位数等信息

bmpWidth = h;

bmpHeight = ht;

biBitCount = ount;//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)

int lineByte=(bmpWidth * biBitCount/8+3)/4*4;//灰度图像有颜色表,且颜色表表项为256

if(biBitCount==8)

{

//申请颜色表所需要的空间,读颜色表进内存

pColorTable=new RGBQUAD[256];

fread(pColorTable,sizeof(RGBQUAD),256,fp);

}

//申请位图数据所需要的空间,读位图数据进内存

pBmpBuf=new unsigned char[lineByte * bmpHeight];

fread(pBmpBuf,1,lineByte * bmpHeight,fp);

fclose(fp);//关闭文件

return 1;//读取文件成功

}

//-----------------------------------------------------------------------------------------

//给定一个图像位图数据、宽、高、颜色表指针及每像素所占的位数等信息,将其写到指定文件中

bool saveBmp(char *bmpName, unsigned char *imgBuf, int width, int height, int biBitCount,

RGBQUAD *pColorTable)

{

//如果位图数据指针为0,则没有数据传入,函数返回

if(!imgBuf)

return 0;

//颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0

int colorTablesize=0;

if(biBitCount==8)

colorTablesize=1024;

//待存储图像数据每行字节数为4的倍数

int lineByte=(width * biBitCount/8+3)/4*4;

//以二进制写的方式打开文件

FILE *fp=fopen(bmpName,"wb");

if(fp==0)

return 0;

//申请位图文件头结构变量,填写文件头信息

BITMAPFILEHEADER fileHead;

= 0x4D42;//bmp类型

//bfSize是图像文件4个组成部分之和

= sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +

colorTablesize + lineByte*height;

rved1 = 0;

rved2 = 0;

//bfOffBits是图像文件前3个部分所需空间之和

its=54+colorTablesize;

//写文件头进文件

fwrite(&fileHead, sizeof(BITMAPFILEHEADER),1, fp);

//申请位图信息头结构变量,填写信息头信息

BITMAPINFOHEADER head;

ount=biBitCount;

mportant=0;

sed=0;

ression=0;

ht=height;

es=1;

=40;

Image=lineByte*height;

h=width;

sPerMeter=0;

sPerMeter=0;

//写位图信息头进内存

fwrite(&head, sizeof(BITMAPINFOHEADER),1, fp);

//如果灰度图像,有颜色表,写入文件

if(biBitCount==8)

fwrite(pColorTable, sizeof(RGBQUAD),256, fp);

//写位图数据进文件

fwrite(imgBuf, height*lineByte, 1, fp);

//关闭文件

fclose(fp);

return 1;

}

//----------------------------------------------------------------------------------------

//以下为像素的读取函数

void doIt()

{

//读入指定BMP文件进内存

char readPath[]="D:";

readBmp(readPath);

//输出图像的信息

cout<<"width="<

biBitCount="<

//循环变量,图像的坐标

//每行字节数

int lineByte=(bmpWidth*biBitCount/8+3)/4*4;

//循环变量,针对彩色图像,遍历每像素的三个分量

int m=0,n=0,count_xiang_su=0;

//将图像左下角1/4部分置成黑色

ofstream outfile("图像像素.txt",ios::in|ios::trunc);

if(biBitCount==8) //对于灰度图像

{

//------------------------------------------------------------------------------------

//以下完成图像的分割成8*8小单元,并把像素值存储到指定文本中。由于BMP图像的像素数据是从

//左下角:由左往右,由上往下逐行扫描的

int L1=0;

int hang=63;

int lie=0;

//int L2=0;

//int fen_ge=8;

for(int fen_ge_hang=0;fen_ge_hang<8;fen_ge_hang++)//64*64矩阵行循环

{

for(int fen_ge_lie=0;fen_ge_lie<8;fen_ge_lie++)//64*64列矩阵循环

{

//--------------------------------------------

for(L1=hang;L1>hang-8;L1--)//8*8矩阵行

{

for(int L2=lie;L2

{

m=*(pBmpBuf+L1*lineByte+L2);

outfile<

count_xiang_su++;

if(count_xiang_su%8==0)//每8*8矩阵读入文本文件

{

outfile<

}

}

}

//---------------------------------------------

hang=63-fen_ge_hang*8;//64*64矩阵行变换

lie+=8;//64*64矩阵列变换

//该一行(64)由8个8*8矩阵的行组成

}

hang-=8;//64*64矩阵的列变换

lie=0;//64*64juzhen

}

}

//double xiang_su[2048];

//ofstream outfile("xiang_su_",ios::in|ios::trunc);

if(!outfile)

{

cout<<"open error!"<

exit(1);

}

else if(biBitCount==24)

{//彩色图像

for(int i=0;i

{

for(int j=0;j

{

for(int k=0;k<3;k++)//每像素RGB三个分量分别置0才变成黑色

{

//*(pBmpBuf+i*lineByte+j*3+k)-=40;

m=*(pBmpBuf+i*lineByte+j*3+k);

outfile<

count_xiang_su++;

if(count_xiang_su%8==0)

{

outfile<

}

//n++;

}

n++;

}

}

cout<<"总的像素个素为:"<

cout<<"----------------------------------------------------"<

}

//将图像数据存盘

char writePath[]="D:";//图片处理后再存储

saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable);

//清除缓冲区,pBmpBuf和pColorTable是全局变量,在文件读入时申请的空间

delete []pBmpBuf;

if(biBitCount==8)

delete []pColorTable;

}

void main()

{

doIt();

}

五、运行结果

24位位图 如下:

原图像的属性:

读取结果:

生成的新图片:

8位位图读取 如下

图片属性:

读取的结果:

生成的新图片:

六、心得体会

大一学的c++,现在却发现忘得差不多了,在程序实现上遇到了困难。但是本次实验确实又再一次的让我接触了c语言。

这次实验掌握了BMP文件的读取,第一次对图像有这么深的认识了解。对图像的组成不再是以前那种错误的认识,有收获。