VSC++学习2BMP图像文件的处理.docx
《VSC++学习2BMP图像文件的处理.docx》由会员分享,可在线阅读,更多相关《VSC++学习2BMP图像文件的处理.docx(9页珍藏版)》请在冰豆网上搜索。
VSC++学习2BMP图像文件的处理
VS2010C++学习
(2):
BMP图像文件的处理
学习VC++编制的BMP图像文件的处理程序.。
一、主要内容:
1.BMP图像文件的结构分析;
2.BMP图像文件的读写;
3.BMP图像文件的置固定值处理、反色处理、平滑处理;
二、设计实现:
1.BMP图像文件的结构分析;
A.BMP文件结构
BMP文件是Windows操作系统所推荐和支持的图像文件格式,是一种将内存或显示器的图像数据不经过压缩而直接按位存盘的文件格式,所以称为位图(bitmap)文件,因其文件扩展名为BMP,故称为BMP文件格式,简称BMP文件。
BMP图像文件被分成4个部分:
1)位图文件头(BitmapFileHeader)、
2)位图信息头(BitmapInfoHeader)、
3)颜色表(ColorMap)
4)位图数据(即图像数据,DataBits或DataBody)。
图1BMP文件结构示意图
第1部分为位图文件头BITMAPFILEHEADER,是一个结构体类型,该结构的长度是固定的,为14个字节。
其定义如下:
typedefstructtagBITMAPFILEHEADER
{
WORDbfType;
DWORDbfSize;
WORDbfReserved1;
WORDbfReserved2;
DWORDbfOffBits;
}BITMAPFILEHEADER,FAR*LPBITMAPFILEHEADER,*PBITMAPFILEHEADER;
BITMAPFILEHEADER结构的各个域详细说明如下:
—bfType:
位图文件类型,必须是0x424D,即字符串“BM”,也就是说,所有的“*.bmp”文件的头
两个字节都是“BM”。
—bfSize:
位图文件大小,包括这14个字节。
—bfReserved1,bfReserved2:
Windows保留字,暂不用。
—bfOffBits:
从文件头到实际的位图数据的偏移字节数,图1中前3个部分的长度之和。
第2部分为位图信息头BITMAPINFOHEADER,也是一个结构体类型的数据结构,该结构的长度也是固定的,为40个字节(WORD为无符号16位整数,DWORD为无符号32位整数,LONG为32位整数)。
其定义如下:
typedefstructtagBITMAPINFOHEADER
{
DWORDbiSize;
LONGbiWidth;
LONGbiHeight;
WORDbiPlanes;
WORDbiBitCount
DWORDbiCompression;
DWORDbiSizeImage;
LONGbiXPelsPerMeter;
LONGbiYPelsPerMeter;
DWORDbiClrUsed;
DWORDbiClrImportant;
}BITMAPINFOHEADER,FAR*LPBITMAPINFOHEADER,*PBITMAPINFOHEADER;
BITMAPINFOHEADER结构的各个域的详细说明如下:
—biSize:
本结构的长度,为40个字节。
—biWidth:
位图的宽度,以像素为单位。
—biHeight:
位图的高度,以像素为单位。
—biPlanes:
目标设备的级别,必须是1。
—biBitCount:
每个像素所占的位数(bit),其值必须为1(黑白图像)、4(16色图)、8(256色)、24(真彩色图),新的BMP格式支持32位色。
—biCompresssion:
位图压缩类型,有效的值为BI_RGB(未经压缩)、BI_RLE8、BI_RLE4、BI_BITFILEDS(均为Windows定义常量)。
这里只讨论未经压缩的情况,即biCompression=BI_RGB。
—biSizeImage:
实际的位图数据占用的字节数,该值的大小在第4部分位图数据中有具体解释。
—biXPelsPerMeter:
指定目标设备的水平分辨率,单位是像素/米。
—biYPelsPerMeter:
指定目标设备的垂直分辨率,单位是像素/米。
—biClrUsed:
位图实际用到的颜色数,如果该值为零,则用到的颜色数为2的biBitCount次幂。
—biClrImportant:
位图显示过程中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的。
第3部分为颜色表。
颜色表实际上是一个RGBQUAD结构的数组,数组的长度由biClrUsed
指定(如果该值为零,则由biBitCount指定,即2的biBitCount次幂个元素)。
RGBQUAD结构是一个结构体类型,占4个字节,其定义如下:
typedefstructtagRGBQUAD
{
BYTErgbBlue;
BYTErgbGreen;
BYTErgbRed;
BYTErgbReserved;
}RGBQUAD;
RGBQUAD结构的各个域的详细说明如下:
●rgbBlue:
该颜色的蓝色分量;
●rgbGreen:
该颜色的绿色分量;
●rgbRed:
该颜色的红色分量;
●rgbReserved:
保留字节,暂不用。
有些位图需要颜色表;有些位图(如真彩色图)则不需要颜色表,颜色表的长度由BITMAPINFOHEADER结构中biBitCount分量决定。
对于biBitCount值为1的二值图像,每像素占1bit,图像中只有两种(如黑白)颜色,颜色表也就有2^1=2个表项,整个颜色表的大小为2*4=8个字节;对于biBitCount值为8的灰度图像,每像素占8bit,图像中有2^8=256种颜色,颜色表也就有256个表项,且每个表项的R、G、B分量相等,整个颜色表的大小为256*4=1024个字节;而对于biBitCount=24的真彩色图像,由于每像素3个字节中分别代表了R、G、B三分量的值,此时不需要颜色表,因此真彩色图的BITMAPINFOHEADER结构后面直接就是位图数据。
第4部分是位图数据,即图像数据,其紧跟在位图文件头、位图信息头和颜色表(如果有颜色表的话)之后,记录了图像的每一个像素值。
对于有颜色表的位图,位图数据就是该像素颜色在调色板中的索引值;对于真彩色图,位图数据就是实际的R、G、B值(三个分量的存储顺序是B、G、R)。
下面分别就2色、16色、256色和真彩色位图的位图数据进行说明:
●对于2色位图,用1位就可以表示该像素的颜色,所以1个字节能存储8个像素的颜色值。
●对于16色位图,用4位可以表示一个像素的颜色。
所以一个字节可以存储2个像素的颜色值。
●对于256色位图,1个字节刚好存储1个像素的颜色值。
●对于真彩色位图,3个字节才能表示1个像素的颜色值。
需要注意两点:
第一,Windows规定一个扫描行所占的字节数必须是4的倍数,不足4的倍数则要对其进行扩充。
假设图像的宽为biWidth个像素、每像素biBitCount个比特,其一个扫描行所占的真实字节数的计算公式如下:
DataSizePerLine=(biWidth*biBitCount/8+3)/4*4
那么,不压缩情况下位图数据的大小(BITMAPINFOHEADER结构中的biSizeImage成员)计算如下:
biSizeImage=DataSizePerLine*biHeight
第二,一般来说,BMP文件的数据是从图像的左下角开始逐行扫描图像的,即从下到上、从左到右,将图像的像素值一一记录下来,因此图像坐标零点在图像左下角。
B.GDI对象及GDI位图
GDI是图形设备接口(GraphicsDeviceInterface)的缩写,Windows的GDI对象类型是通过Microsoft基础类库(MFC)中的类来表示的,而CGdiObject正是所有GDI对象类的抽象基类,即Windows的GDI对象是通过CGdiObject派生类的C++对象来表示的。
下面我们给出了GDI派生类的列表。
—CBitmap——位图是一种位矩阵,每一个显示像素都对应于其中的一个或多个位。
—CBrush——刷子定义了一种位图形式的像素,利用它可以对区域内部填充颜色。
—CFont——字体是一种具有某种风格和尺寸的所有字符的完整集合,它常被当做资源存于磁盘中,其中有一些还依赖于某种设备。
—CPallete——调色板是一种颜色映射接口,它允许应用程序在不干扰其他应用程序的前提下,可以充分利用输出设备的颜色描绘能力。
—CPen——笔是一种用来画线及绘制有形边框的工具,可以指定它的颜色及厚度。
—CRgn——区域是由多边形、椭圆或者二者组合形成的一种范围,可利用它进行填充、裁剪以及鼠标点中测试。
CBitmap类封装了WindowsGDI位图,同时提供了一些操作位图的成员函数。
像笔和字体一样,CBitmap对象是GDI对象的一种,在使用CBitmap对象时必须创建一个CBitmap对象,然后把它选进设备环境中,再调用该类中的成员对位图进行操作,当我们对它使用完后,还必须将它从设备环境中选出来并删除掉。
C.设备无关位图(DIB)
DIB格式(Device-IndependentBitmanp)是与设备无关的BMP文件格式调入内存中的DIB位图是脱离文件而存在的,其结构可以分为三部分。
内存中的DIB实际上是BMP文件结构去掉位图文件头结构后剩下的三部分,而这三部分结构包含了位图显示和处理所需要的所有信息。
1.调色板
2.DIB访问函数
—SetDIBitsToDevice——该函数直接在显示器或打印机上显示DIB。
显示时不进行缩放,位图的每一位对应一个显示像素或一个打印点。
不能进行缩放限制了它的使用。
该函数不能像BitBlt()函数那样使用,因为BitBlt()使用的是逻辑坐标。
—StretchDIBits——该函数按照与StretchBlt()函数类似的方式将DIB直接显示在显示器或打印机上。
—GetDIBits——该函数利用申请到的内存,由GDI位图来构造DIB。
我们可以对DIB的格式进行控制,因为我们可以指定每个像素的颜色位数,并且可以指定是否对它进行压缩。
如果使用了压缩格式,就必须对GetDIBits进行两次调用,一次用于计算所需要的内存,另一次用来产生DIB数据。
—CreateDIBitmap——该函数从DIB出发来创建GDI位图。
与所有这些DIB函数一样,我们必须提供一个设备环境指针作为参数。
这里我们需要一个显示器设备环境,不需要内存设备环境。
—CreateDIBSection——该函数是一个新的Win32函数,它创建一个特殊的DIB,称为DIB项(DIBSection),然后返回一个GDI位图句柄。
该函数为我们提供了DIB和GDI位图最好的特性。
我们可以直接访问DIB的内存,而且利用位图句柄和内存设备环境,还可以在DIB中调用GDI函数画图。
2.BMP图像文件的读写;
在基于MFC的文档-视图结构应用程序中,有关文件的操作(如读、写)一般在文档类中完成,而有关显示部分一般在视图类中完成。
1)按照MFC工程向导,创建一个单文档应用程序。
2)在文档类头文件ex002Doc.h中加入公有成员变量m_pDib,用来存放打开BMP文件的DIB指针。
public:
unsignedchar*m_pDib;//存放打开文件的DIB
并在构造函数和析构函数中对m_pDib进行初始化和资源释放。
3)DIB位图的读入。
这需要重载文档类OnOpenDocument()函数。
并加入代码如下:
BOOLCChap1_4Doc:
:
OnOpenDocument(LPCTSTRlpszPathName)
{
//释放缓冲区
if(m_pDib!
=NULL){
delete[]m_pDib;
m_pDib=NULL;
}
//打开指定文件
CFilefile;
if(!
file.Open(lpszPathName,CFile:
:
modeRead|CFile:
:
shareDenyWrite))
returnFALSE;
//跳过位图文件头结构
file.Seek(sizeof(BITMAPFILEHEADER),0);
//申请DIB所需要的内存空间,将除BITMAPFILEHEADER结构以外的
//位图数据读入内存,存放在m_pDib所指向的缓冲区中
m_pDib=newBYTE[file.GetLength()-sizeof(BITMAPFILEHEADER)];
file.Read(m_pDib,file.GetLength()-sizeof(BITMAPFILEHEADER));
returnTRUE;
}
当运行应用程序并打开一个BMP文件的时候,系统执行OnOpenDocument()函数,一次性分配DIB结构所需要的所有内存缓冲区,由m_pDib指向,并将打开的图像数据(BITMAPINFOHEADER、颜色表和位图数据)读入该缓冲区中。
其缓冲区数据内容的分布如图:
图2m_pDib所指向的内存缓冲区数据分布示意图
从图中可以看出,m_pDib所指向的缓冲区的位置也就是位图BITMAPINFOHEADER结构的起始位置;而颜色表则是在m_pDib+sizeof(BITMAPINFOHEADER)处;最后,位图数据的起始位置为m_pDib+sizeof(BITMAPINFOHEADER)+颜色表大小(颜色表大小以字节为单位,可以由BITMAPINFOHEADER中的biBitCount成员求出)。
因此,程序不需要再单独为BITMAPINFOHEADER、颜色表或位图数据分配缓冲区了,也就是说,给了我们一个DIB结构,我们就可以根据需要对DIB做任何的编程操作。
4)DIB位图的显示。
图像显示的操作由CChap1_4View类的OnDraw()函数实现。
获取文档类句柄;
根据DIB的颜色表创建自己的调色板并选进设备环境中。
显示DIB到显示器。
使用StretchDIBits函数。
5)DIB的存储。
重载文档类OnSaveDocument()函数。
6)双击ResourceView窗口的StringTable,
修改IDR_MAINFRAME的标题为:
ex002\n\nex002\n(*.bmp)\n.bmp\nex002.Document\nex002.Document,它保证了打开文件对话框的文件类型为“.bmp”。
3.BMP图像文件的置固定值处理、反色处理、平滑处理;
1)置固定值处理
对DIB位图数据的访问,分为灰度图像或彩色图像分别处理。
置固定值就是对某一点进行处理,赋给一个固定的颜色值。
如黑色即赋给0值。
for(i=0;ifor(j=0;jfor(k=0;k<3;k++)//彩色图像,每像素三个分量都置0
*(pImgData+i*lineByte+j*3+k)=0;
}
2)反色处理
用255–该点的颜色值,如是彩色,三个分量都减。
3)平滑处理
采用的某点的九宫格平均值赋给该点的算法。
三、源码下载: