资源描述
摘要 图片的合成和缩放我们实现了其基本的算法,本文通过bmp位图数据源,通过采用双线形插值,后处理操作和合成算法实现了对图片的基本操作,并以此为基础实现了对bmp位图的读写。试验结果表明,我们所实现的算法适用于对BMP位图文件的基本操作。在实现数字图象处理的过程中,主要是通过对图像中的每一个像素点运用各种图像处理算法来达到预期的效果,所以进行图像处理的第一步,也是我们最关心的问题,是如何得到图像中每一个像素点的亮度值;为了观察和验证处理的图像效果,另一个需要解决的问题是如何将处理前后的图像正确的显示出来。我们就是解决这些问题。关键词:图片的合成和缩放;双线形插值;bmp位图;数字图象处理 vc+ picture processing implementationAbstract The picture synthesis we have realized its basic algorithm, this article through the bmp position chart data pool, through used the double linear interpolation, the post-processing operation and the synthesis algorithm has realized to the picture elementary operation, and has realized read-write to the bmp position chart take this as the foundation. The test result indicated, we realize the algorithm is suitable for to the BMP position chart document elementary operation.In the realization digital image processing process, mainly is through utilizes each kind of picture processing algorithm to in the picture each picture to achieve the anticipated effect, therefore carries on picture processing the first step, also is our most issue of concern, is how obtains in the picture each picture brightness value; In order to observe with the confirmation processing picture effect, around another needs to solve how will the question is process the picture correct demonstration. We solve these problems.Key words: Picture synthesis; Double linear interpolation; Bmp position chart; Digital image processing目录1图像缩放合成处理的意义国内外的情况综述 12设备无关位图文件的读写存和缩放22.1 BMP文件的格式2 2.1.1 BMP文件组成22.1.2 BMP文件头22.1.3 位图信息头32.1.4 颜色表32.1.5 位图数据42.2 BMP文件的读写 42.3 BMP文件的保存 102.4 BMP文件的缩放 153图像合成的具体方法 213.1 图片合成算法详解213.2 图片合成算法集的实现 223.3 图片合成检查步骤244试验数据与展望 254.1试验数据 254.2展望 26参考文献28致谢 30引 言数字图像处理技术与理论是计算机应用的一个重要领域,许多工程应用都涉及到图像处理,一直有一个强烈的愿望,想系统的写一个关于数字图像处理的讲座,由于工作学习很忙,时至今日才得以实现。1 图片缩放合成的意义,国内外的情况综述 “图”是物体透射光或反射光的分布,“像”是人的视觉系统对图的接收在大脑中形成的印象或认识。图像是两者的结合。人类获取外界信息是靠听觉、视觉、触觉、嗅觉、味觉等,但绝大部分(约80%左右)来自视觉所接收的图像信息。图像处理就是对图像信息进行加工处理,以满足人的视觉心理和实际应用的需要。简单的说,依靠计算机对图像进行各种目的的处理我们就称之为数字图像处理。早期的数字图像处理的目的是以人为对象,为了满足人的视觉效果而改善图像的质量,处理过程中输入的是质量差的图像,输出的是质量好的图像,常用的图像处理方法有图像增强、复原等。随着计算机技术的发展,有一类图像处理是以机器为对象,处理的目的是使机器能够自动识别目标,这称之为图像的识别,因为这其中要牵涉到一些复杂的模式识别的理论,讨论其中最基本的内容。由于在许多实际应用的编程中往往都要涉及到数字图像处理,涉及到图像合成算法,讨论如何利用微软的Visual C+开发工具来实现一些常用的数字图像处理算法,论述了图像处理的理论,同时给出了VC实现的源代码。传统的电脑只能处理文字、数字,最多是简单的图形。近年来,随着电脑硬件技术的飞速发展和更新,使得计算机处理图形图像的能力大大增强。以前要用大型图形工作站来运行的图形应用软件,或是特殊文件格式的生成及对图形所作的各种复杂的处理和转换;如今,很普遍的家用电脑就完全可以胜任,我们可以轻易的使用PhotoShop、CorelDraw、3D MAX或是别的什么软件做出精美的图片或是逼真的三维物体,你甚至可以自己去做一个有趣的动画。 在当今信息社会,以多媒体为代表的信息技术和信息产业的发展和应用对人类社会产生的影响和作用愈来愈明显,愈来愈重要。多媒体的发展和应用,极大地推动了诸多工业的相互渗透和飞速发展,逐步改变了整个人类社会的工作结构和生活方式。可以毫不夸张地说,多媒体产业的形成和发展,将不仅引起计算机工业的一次革命,也将影响人类社会发生一场巨大的变革。我们知道,所谓多媒体,即多种信息媒介,通常包括以下几种:文本、图形、影像、声音、视频、动画。可以看出,多媒体的应用在很大程度当依赖于丰富多彩的图形和图像。也就是说,图形图像技术的飞速发展也将是必然趋势,掌握图形图像处理技术对一个计算机操作人员是必要的。计算机图形学是研究用计算机生成、处理和显示图形的一门科学。为了生成图形,首先要有原始数据或数学模型(如工程人员构思的草图、地形航测数据、飞机的,总体方案模型等),这些数字化的输入信息经过计算机处理后变成图形输出。图形从原始数据生成图象数据经过了一系列变换过程,每个变换过程都可能产生不同于输入数据的输出数据,这些数据需要按一定的结构进行组织,形成一系列描述图形数据的文件,我们把这类文件称为图形文件(也称为图形图象文件),而图象文件是描述图象数据的文件,它是图形文件的一种特例。在图形生成过程中有多种类型的数据,如模型数据、场景数据和图象数据等,因此,图形文件所描述的图形层次就不一样,这也是产生多种图形文件的一个重要原因。另一方面,在同一个描述层上,由于每种图形软件包使用自己的格式保存图形数据,随着图形应用软件包的不断增多,图形文件的格式也会越来越多,虽然国际标准化组织(ISO)为解决图形信息的共享问题,建立了一系列图形文件标准(如CGM),但是这些标准较难得到广大用户和厂商的支持,从而形成了目前这种多种图形文件共存的局面.图形文件有以下特点:(1)数据量大。由于现在数据获取手段日趋先进,可以得到的数据越来越复杂,数据量也增大。(2)结构性强。数据在本质上分为数字化的和模拟的两种。模拟信息可以转换为数字信息。数字系统中的最基本单位为位(bit),其他结构单位都以位为础。在较低层次上可以是“构造块”(如浮点数、整数和字符);在较高层次上可以是记录(如Pascal中)或结构(如C语言中),而图形文件就是由特定的结构或记录组成的。每种图形文件都按自己的方式组织图形信息,由于图形文件包含的数据量大,所以很多图形文件都使用一定的压缩算法来压缩图形数据。2 设备无关位图文件的读写存和缩放2.1 BMP文件的格式2.1.1 BMP文件组成 BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。文件头主要包含文件的大小、文件类型、图像数据偏离文件头的长度等信息;位图信息头包含图象的尺寸信息、图像用几个比特数值来表示一个像素、图像是否压缩、图像所用的颜色数等信息。颜色信息包含图像所用到的颜色表,显示图像时需用到这个颜色表来生成调色板,但如果图像为真彩色,既图像的每个像素用24个比特来表示,文件中就没有这一块信息,也就不需要操作调色板。文件中的数据块表示图像的相应的像素值,需要注意的是:图像的像素值在文件中的存放顺序为从左到右,从下到上,也就是说,在BMP文件中首先存放的是图像的最后一行像素,最后才存储图像的第一行像素,但对与同一行的像素,则是按照先左边后右边的的顺序存储的;另外一个需要读者朋友关注的细节是:文件存储图像的每一行像素值时,如果存储该行像素值所占的字节数为4的倍数,则正常存储,否则,需要在后端补0,凑足4的倍数。 2.1.2 BMP文件头 BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。其结构定义如下: typedef struct tagBITMAPFILEHEADER WORD bfType; / 位图文件的类型,必须为BM DWORD bfSize; / 位图文件的大小,以字节为单位 WORD bfReserved1; / 位图文件保留字,必须为0 WORD bfReserved2; / 位图文件保留字,必须为0 DWORD bfOffBits; / 位图数据的起始位置,以相对于位图文件头的偏移量表示,以字节为单位 BITMAPFILEHEADER;该结构占据14个字节。 2.1.3 位图信息头 BMP位图信息头数据用于说明位图的尺寸等信息。其结构如下: typedef struct tagBITMAPINFOHEADER DWORD biSize; / 本结构所占用字节数 LONG biWidth; / 位图的宽度,以像素为单位 LONG biHeight; / 位图的高度,以像素为单位 WORD biPlanes; / 目标设备的平面数不清,必须为1 WORD biBitCount/ 每个像素所需的位数,必须是1(双色), 4(16色),8(256色)或24(真彩色)之一 DWORD biCompression; / 位图压缩类型,必须是 0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一 DWORD biSizeImage; / 位图的大小,以字节为单位 LONG biXPelsPerMeter; / 位图水平分辨率,每米像素数 LONG biYPelsPerMeter; / 位图垂直分辨率,每米像素数 DWORD biClrUsed;/ 位图实际使用的颜色表中的颜色数 DWORD biClrImportant;/ 位图显示过程中重要的颜色数 BITMAPINFOHEADER;该结构占据40个字节。 注意:对于BMP文件格式,在处理单色图像和真彩色图像的时候,无论图象数据多么庞大,都不对图象数据进行任何压缩处理,一般情况下,如果位图采用压缩格式,那么16色图像采用RLE4压缩算法,256色图像采用RLE8压缩算法。 2.1.4 颜色表 颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下: typedef struct tagRGBQUAD BYTErgbBlue;/ 蓝色的亮度(值范围为0-255) BYTErgbGreen; / 绿色的亮度(值范围为0-255) BYTErgbRed; / 红色的亮度(值范围为0-255) BYTErgbReserved;/ 保留,必须为0 RGBQUAD; 颜色表中RGBQUAD结构数据的个数由BITMAPINFOHEADER 中的biBitCount项来确定,当biBitCount=1,4,8时,分别有2,16,256个颜色表项,当biBitCount=24时,图像为真彩色,图像中每个像素的颜色用三个字节表示,分别对应R、G、B值,图像文件没有颜色表项。位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下: typedef struct tagBITMAPINFO BITMAPINFOHEADER bmiHeader; / 位图信息头 RGBQUAD bmiColors1; / 颜色表 BITMAPINFO; 注意:RGBQUAD数据结构中,增加了一个保留字段rgbReserved,它不代表任何颜色,必须取固定的值为0,同时, RGBQUAD结构中定义的颜色值中,红色、绿色和蓝色的排列顺序与一般真彩色图像文件的颜色数据排列顺序恰好相反,既:若某个位图中的一个像素点的颜色的描述为00,00,ff,00,则表示该点为红色,而不是蓝色。 2.1.5 位图数据 位图数据记录了位图的每一个像素值或该对应像素的颜色表的索引值,图像记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。这种格式我们又称为Bottom_Up位图,当然与之相对的还有Up_Down形式的位图,它的记录顺序是从上到下的,对于这种形式的位图,也不存在压缩形式。位图的一个像素值所占的字节数:当biBitCount=1时,8个像素占1个字节;当biBitCount=4时,2个像素占1个字节;当 biBitCount=8时,1个像素占1个字节;当biBitCount=24时,1个像素占3个字节,此时图像为真彩色图像。当图像不是为真彩色时,图像文件中包含颜色表,位图的数据表示对应像素点在颜色表中相应的索引值,当为真彩色时,每一个像素用三个字节表示图像相应像素点彩色值,每个字节分别对应R、G、B分量的值,这时候图像文件中没有颜色表。上面我已经讲过了,Windows规定图像文件中一个扫描行所占的字节数必须是4的倍数(即以字为单位),不足的以0填充,图像文件中一个扫描行所占的字节数计算方法: DataSizePerLine= (biWidth* biBitCount+31)/8;/ 一个扫描行所占的字节位图数据的大小按下式计算(不压缩情况下): DataSize= DataSizePerLine* biHeight。2.2 BMP文件的读写如今Windows(3.x以及95,98,NT)系列已经成为绝大多数用户使用的操作系统,它比DOS成功的一个重要因素是它可视化的漂亮界面。那么Windows是如何显示图象的呢?这就要谈到位图(bitmap)。我们知道,普通的显示器屏幕是由许许多多点构成的,我们称之为象素。显示时采用扫描的方法:电子枪每次从左到右扫描一行,为每个象素着色,然后从上到下这样扫描若干行,就扫过了一屏。为了防止闪烁,每秒要重复上述过程几十次。例如我们常说的屏幕分辨率为640480,刷新频率为70Hz,意思是说每行要扫描640个象素,一共有480行,每秒重复扫描屏幕70次。我们称这种显示器为位映象设备。所谓位映象,就是指一个二维的象素矩阵,而位图就是采用位映象方法显示和存储的图象。举个例子,图1.1是一幅普通的黑白位图,图1.2是被放大后的图,图中每个方格代表了一个象素。我们可以看到:整个骷髅就是由这样一些黑点和白点组成的。先来说说三元色RGB概念。我们知道,自然界中的所有颜色都可以由红、绿、蓝(R,G,B)组合而成。有的颜色含有红色成分多一些,如深红;有的含有红色成分少一些,如浅红。针对含有红色成分的多少,可以分成0到255共256个等级,0级表示不含红色成分;255级表示含有100%的红色成分。同样,绿色和蓝色也被分成256级。这种分级概念称为量化。这样,根据红、绿、蓝各种不同的组合我们就能表示出256256256,约1600万种颜色。这么多颜色对于我们人眼来说已经足够丰富了。表1.1 常见颜色的RGB组合值颜色RGB红25500蓝02550绿00255黄2552550紫2550255青0255255白255255255黑000灰128128128在实现数字图象处理的过程中,主要是通过对图像中的每一个像素点运用各种图像处理算法来达到预期的效果,所以进行图像处理的第一步,也是我们最关心的问题,是如何得到图像中每一个像素点的亮度值;为了观察和验证处理的图像效果,另一个需要解决的问题是如何将处理前后的图像正确的显示出来。我们这章内容就是解决这些问题。 随着科技的发展,图像处理技术已经渗透到人类生活的各个领域并得到越来越多的应用,但是突出的一个矛盾是图像的格式也是越来越多,目前图像处理所涉及的主要的图像格式就有很多种,如TIF、JEMP、BMP等等,一般情况下,为了处理简单方便,进行数字图像处理所采用的都是BMP格式的图像文件(有时也称为DIB格式的图像文件),并且这种格式的文件是没有压缩的。我们通过操作这种格式的文件,可以获取正确显示图像所需的调色板信息,图像的尺寸信息,图像中各个像素点的亮度信息等等,有了这些数据,开发人员就可以对图像施加各种处理算法,进行相应的处理。如果特殊情况下需要处理其它某种格式的图像,如GIF、JEMP等格式的图象文件,可以首先将该格式转换为BMP格式,然后再进行相应的处理。这一点需要读者清楚。 BMP格式的图像文件又可以分为许多种类,如真彩色位图、256色位图,采用RLE(游程编码)压缩格式的BMP位图等等。由于在实际的工程应用和图像算法效果验证中经常要处理的是256级并且是没有压缩的BMP灰度图像,例如通过黑白采集卡采集得到的图像就是这种格式,所以我们在整个讲座中范例所处理的文件格式都是BMP灰度图像。如果读者对这种格式的位图能够作到熟练的操作,那么对于其余形式的BMP位图的操作也不会很困难。 BMP灰度图像作为Windows环境下主要的图像格式之一,以其格式简单,适应性强而倍受欢迎。正如我们在上一讲中介绍过的那样,这种文件格式就是每一个像素用8bit表示,显示出来的图像是黑白效果,最黑的像素的灰度(也叫作亮度)值为0,最白的像素的灰度值为255,整个图像各个像素的灰度值随机的分布在0到255的区间中,越黑的像素,其灰度值越接近于0,越白(既越亮)的像素,其灰度值越接近于255;与此对应的是在该文件类型中的颜色表项的各个RGB分量值是相等的,并且颜色表项的数目是256个。 在进行图像处理时,操作图像中的像素值就要得到图像阵列;经过处理后的图像的像素值需要存储起来;显示图像时要正确实现调色板、得到位图的尺寸信息等。可以根据BMP位图文件的结构,操作BMP位图文件并读入图像数据,为此我们充分利用了VC的对话框结构,自己新建ReadDIBFile()函数,这样用户就可以在自动生成程序的打开文件对话框中选择所要打开的位图文件,然后程序将自动调用该函数执行读取数据的操作。该函数的实现代码如下所示HDIB WINAPI ReadDIBFile(CFile& file)BITMAPFILEHEADER bmfHeader;DWORD dwBitsSize;HDIB hDIB;LPSTR pDIB;/ 获取DIB(文件)长度(字节)dwBitsSize = file.GetLength();/ 尝试读取DIB文件头if (file.Read(LPSTR)&bmfHeader, sizeof(bmfHeader) != sizeof(bmfHeader)/ 大小不对,返回NULL。return NULL;/ 判断是否是DIB对象,检查头两个字节是否是BMif (bmfHeader.bfType != DIB_HEADER_MARKER)/ 非DIB对象,返回NULL。return NULL;/ 为DIB分配内存hDIB = (HDIB) :GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwBitsSize);if (hDIB = 0)/ 内存分配失败,返回NULL。return NULL;/ 锁定pDIB = (LPSTR) :GlobalLock(HGLOBAL) hDIB);/ 读象素if (file.ReadHuge(pDIB, dwBitsSize - sizeof(BITMAPFILEHEADER) !=dwBitsSize - sizeof(BITMAPFILEHEADER) )/ 大小不对。/ 解除锁定:GlobalUnlock(HGLOBAL) hDIB);/ 释放内存:GlobalFree(HGLOBAL) hDIB);/ 返回NULL。return NULL;/ 解除锁定:GlobalUnlock(HGLOBAL) hDIB);/ 返回DIB句柄return hDIB;呼叫两个函数之一来显示DIB时,您需要几个关于图像的信息。如果除了文件表头外,整个文件被储存在内存的连续区块中,指向该内存块开始处(也就是信息表头的开头)的指标被称为指向packed DIB的指标。因为整个DIB由单个指标(如pPackedDib)引用,所以packed DIB是在内存中储存DIB的方便方法,您可以把指标定义为指向BYTE的指标。使用本章前面所示的结构定义,能得到所有储存在DIB内的信息,包括色彩对照表和个别图素位。然而,要想得到这么多信息,还需要一些程序代码。例如,您不能通过以下叙述简单地取得DIB的图素宽度:iWidth = (PBITMAPINFOHEADER) pPackedDib)-biWidth ; DIB有可能是OS/2兼容格式的。在那种格式中,packed DIB以BITMAPCOREHEADER结构开始,并且DIB的图素宽度和高度以16位WORD,而不是32位LONG储存。因此,首先必须检查DIB是否为旧的格式,然后进行相对应的操作:if(PBITMAPCOREHEADER) pPackedDib)-bcSize = sizeof (BITMAPCOREHEADER) iWidth = (PBITMAPCOREHEADER) pPackedDib)-bcWidth ; else iWidth = (PBITMAPINFOHEADER) pPackedDib)-biWidth ; SetDIBitsToDevice和StretchDIBits函数需要两个指向DIB的指标,因为这两个部分不在连续的内存块内。确实,把DIB分成两个内存块是很有用的,只是我们更喜欢与整个DIB储存在单个内存块的packed DIB打交道。除了这两个指标,SetDIBitsToDevice和StretchDIBits函数通常也需要DIB的图素宽度和高度。如只想显示DIB的一部分,就不必明确地知道这些值,但它们会定义您在DIB图素位数组内定义的矩形的上限。点对点图素显示 SetDIBitsToDevice函数显示没有延伸和缩小的DIB。DIB的每个图素对应到输出设备的一个图素上,而且DIB中的图像一定会被正确显示出来也就是说,图像的顶列在上方。任何会影响设备内容的坐标转换都影响了显示DIB的开始位置,但不影响显示出来的图片大小和方向。不要对参数的数量感到厌烦,在多数情况下,函数用起来比看起来要简单。不过在其它用途上来说,它的用法真的是乱七八糟,不过我们将学会怎么用它。和GDI显示函数一样,SetDIBitsToDevice的第一个参数是设备内容句柄,它指出显示DIB的设备。下面两个参数xDst和yDst,是输出设备的逻辑坐标,并指出了显示DIB图像左上角的坐标(上端指的是视觉上的上方,并不是DIB图素的第一行)。注意,这些都是逻辑坐标,因此它们附属于实际上起作用的任何坐标转换方式或在Windows NT的情况下设定的任何空间转换。在内定的MM_TEXT映像方式下,可以把这些参数设为0,从显示平面上向左向上显示DIB图像。 可以显示整个DIB图像或仅显示其中的一部分,这就是后四个参数的作用。但是DIB图素数据的由上而下的方向产生了许多误解,待会儿会谈到这些。现在应该清楚当显示整个DIB时,应把xSrc和ySrc设定为0,并且cxSrc和cySrc应分别等于DIB的图素宽度和高度。注意,因为BITMAPINFOHEADER结构的biHeight字段对于由上而下的DIB来说是负的,cySrc应设定为biHeight字段的绝对值。pBits参数是指向DIB图素位的指针。pInfo参数是指向DIB的BITMAPINFO结构的指针。虽然BITMAPINFO结构的地址与BITMAPINFOHEADER结构的地址相同,但是SetDIBitsToDevice结构被定义为使用BITMAPINFO结构,暗示着:对于1位、4位和8位DIB,位图信息表头后必须跟着色彩对照表。尽管pInfo参数被定义为指向BITMAPINFO结构的指针,它也是指向BITMAPCOREINFO、BITMAPV4HEADER或BITMAPV5HEADER结构的指针。2.3 BMP文件的保存/* * * 函数名称: * SaveDIB() * * 参数: * HDIB hDib - 要保存的DIB * CFile& file - 保存文件CFile * * 返回值: * BOOL - 成功返回TRUE,否则返回FALSE或者CFileException * * 说明: * 该函数将指定的DIB对象保存到指定的CFile中。该CFile由调用程序打开和关闭。 * */BOOL WINAPI SaveDIB(HDIB hDib, CFile& file)/ Bitmap文件头BITMAPFILEHEADER bmfHdr;/ 指向BITMAPINFOHEADER的指针LPBITMAPINFOHEADER lpBI;/ DIB大小DWORD dwDIBSize;if (hDib = NULL)/ 如果DIB为空,返回FALSEreturn FALSE;/ 读取BITMAPINFO结构,并锁定lpBI = (LPBITMAPINFOHEADER) :GlobalLock(HGLOBAL) hDib);if (lpBI = NULL)/ 为空,返回FALSEreturn FALSE;/ 判断是否是WIN3.0 DIBif (!IS_WIN30_DIB(lpBI)/ 不支持其它类型的DIB保存/ 解除锁定:GlobalUnlock(HGLOBAL) hDib);/ 返回FALSEreturn FALSE;/ 填充文件头/ 文件类型BMbmfHdr.bfType = DIB_HEADER_MARKER;/ 计算DIB大小时,最简单的方法是调用GlobalSize()函数。但是全局内存大小并/ 不是DIB真正的大小,它总是多几个字节。这样就需要计算一下DIB的真实大小。/ 文件头大小颜色表大小/ (BITMAPINFOHEADER和BITMAPCOREHEADER结构的第一个DWORD都是该结构的大小)dwDIBSize = *(LPDWORD)lpBI + :PaletteSize(LPSTR)lpBI);/ 计算图像大小if (lpBI-biCompression = BI_RLE8) | (lpBI-biCompression = BI_RLE4)/ 对于RLE位图,没法计算大小,只能信任biSizeImage内的值dwDIBSize += lpBI-biSizeImage;else/ 象素的大小DWORD dwBmBitsSize;/ 大小为Width * HeightdwBmBitsSize = WIDTHBYTES(lpBI-biWidth)*(DWORD)lpBI-biBitCount) * lpBI-biHeight;/ 计算出DIB真正的大小dwDIBSize += dwBmBitsSize;/ 更新biSizeImage(很多BMP文件头中biSizeImage的值是错误的)lpBI-biSizeImage = dwBmBitsSize;/ 计算文件大小:DIB大小BITMAPFILEHEADER结构大小bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER);/ 两个保留字bmfHdr.bfReserved1 = 0;bmfHdr.bfReserved2 = 0;/ 计算偏移量bfOffBits,它的大小为Bitmap文件头大小DIB头大小颜色表大小bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI-biSize + PaletteSize(LPSTR)lpBI);/ 尝试写文件TRY/ 写文件头file.Write(LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER);/ 写DIB头和象素file.WriteHuge(lpBI, dwDIBSize);CATCH (CFileException, e)/ 解除锁定:GlobalUnlock(HGLOBAL) hDib);/ 抛出异常THROW_LAST();END_CATCH/ 解除锁定:GlobalUnlock(HGLOBAL) hDib);/ 返回TRUEreturn TRUE;2.4 BMP文件的缩放由于放大图象时产生了新的象素,以及浮点数的操作,得到的坐标可能并不是整数,这一点我们在介绍旋转时就提到了。我们采用的做法是找与之最临近的点。实际上,更精确的做法是采用插值(interpolation),即利用邻域的象素来估计新的象素值。其实我们前面的做法也是一种插值,称为最邻近插值(Nearest Neighbour Interpolation)。下面先介绍线形插值(Linear Interpolation)。线形插值使用原图中两个值来构造所求坐标处的值。举一个一维的例子。如图2.16所示,如果已经知道了两点x0,x2处的函数值f(x0),f(x2),现在要求x1处的函数值f(x1)。我们假设函数是线形的,利用几何知识可以知道f(x1)=(f(x2)-f(x0)(x1-x0)/(x2-x0)+f(x0)在图象处理中需要将线形插值扩展到二维的情况,即采用双线形插值(Bilinear Intrepolation).图2.17为双线形插值的示意图。图2.16 线形插值的示意图图.217 双线形插值的示意图已知a、b、c、d四点的灰度,要求e点的灰度,可以先在水平方向上由a,b线形插值求出g、c、d线形插值求出f,然后在垂直方向上由g,f线形插值求出e。线形插值基于这样的假设:原图的灰度在两个象素之间是线形变化的。一般情况下,这种插值的效果还不错。更精确的方法是采用曲线插值(Curvilinear Interpolation),即认为象素之间的灰度变化规律符合某种曲线,但这种处理的计算量是很大的。/* * * 函数名称: * ZoomDIB() * * 参数: * LPSTR lpDIB- 指向源DIB的指针 * float fXZoomRatio- X轴方向缩放比率 * float fYZoomRatio- Y轴方向缩放比率 * * 返回值: * HGLOBAL - 缩放成功返回新DIB句柄,否则返回NULL。 * * 说明: * 该函数用来缩放DIB图像,返回新生成DIB的句柄。 * */HGLOBAL WINAPI ZoomDIB(LPSTR lpDIB, float fXZoomRatio, float fYZoomRatio)/ 源图像的宽度和高度LONGlWidth;LONGlHeight;/ 缩放后图像的宽度和高度LONGlNewWidth;LONGlNewHeight;/ 缩放后图像的宽度(lNewWidth,必须是4的倍数)LONGlNewLineBytes;/ 指向源图像的指针LPSTRlpDIBBits;/ 指向源象素的指针LPSTRlpSrc;/ 缩放后新DIB句柄HDIBhDIB;/ 指向缩放图像对应象素的指针LPSTRlpDst;/ 指向缩放图像的指针LPSTRlpNewDIB;LPSTRlpNewDIBBits;/ 指向BITMAPINFO结构的指针(Win3.0)LPBITMAPINFOHEADER lpbmi;/ 指向BITMAPCOREINFO结构的指针LPBITMAPCOREHEADER lpbmc;/ 循环变量(象素在新DIB中的坐标)LONGi;LONGj;/ 象素在源DIB中的坐标LONGi0;LONGj0;/ 图像每行的字节数LONG lLineBytes;/ 找到源DIB图像象素起始位置lpDIBBits = :FindDIBBits(lpDIB);/ 获取图像的宽度lWidth = :DIBWidth(lpDIB);/ 计算图像每行的字节数lLineBytes = WIDTHBYTES(lWidth * 8);/ 获取图像的高度lHeight = :DIBHeight(lpDIB);/ 计算缩放后的图像实际宽度/ 此处直接加0.5是由于强制类型转换时不四舍五入,而是直接截去小数部分lNewWidth = (LONG) (:DIBWidth(lpDIB) * fXZoomRatio + 0.5);/ 计算新图像每行的字节数lNewLineBytes = WIDTHBYTES(lNewWidth * 8);/ 计算缩放后的图像高度lNewHeight = (LONG) (lHeight * fYZoomRatio + 0.5);/ 分配内存,以保存新DIBhDIB = (HDIB) :GlobalAlloc(GHND, lNewLineBytes * lNewHeight + *(LPDWORD)lpDIB + :PaletteSize(lpDIB);/ 判断是否内存分配失败if (hDIB = NULL)/ 分配内存失败return NULL;/ 锁定内存lpNewDIB = (char * ):GlobalLock(HGLOBAL) hDIB);/ 复制DIB信息头和调色板memcpy(lpNewDIB, lpDIB, *(LPDWORD)lpDIB + :PaletteSize(lpDIB);/ 找到新DIB象素起始位置lpNewDIBBits = :FindDIBBits(lpNewDIB);/ 获取指针lpbmi = (LPBITMAPINFOHEADER)lpNewDIB;lpbmc = (LPBITMAPCOREHEADER)lpNewDIB;/ 更新DIB中图像的高度和宽度if (IS_WIN30_DIB(lpNewDIB)/ 对于Windows 3.0 DIBlpbmi-biWidth = lNewWidth;lpbmi-biHeight = lNewHeight;else/ 对于其它格式的DIBlpbmc-bcWidth = (unsigned short) lNewWidth;lpbmc-bcHeight = (unsigned short) lNewHeight;/ 针对图像每行进行操作for(i = 0; i lNewHeight; i+)/ 针对图像每列进行操作for(j = 0; j = 0) & (j0 = 0) & (i0 GetDC();CRect rc;pDC-GetWindow()-GetClientRect(&rc);CRect rcDIB;rcDIB.top = rcDIB.left=0;rcDIB.right = cxDIB;rcDIB.bottom = cyDIB;HBITMAP hBitmap_front=DIBToBitmap(m_dibFront, m_palette);HBITMAP hBitmap_back=DIBToBitmap(m_dibBack, m_palette);CBitmap bmp1,bmp2;BITMAP bmpX,bmpY;bmp1.Attach(hBitmap_front);bmp2.Attach(hBitmap_back);bmp1.GetBitmap(&bmpX);UINT* bmpBuffer=(UINT*)GlobalAlloc(GPTR,bmpX.bmWidthBytes*bmpX.bmHeight);bmp1.GetBitmapB
展开阅读全文