基于Windows的图像处理(VC).doc_第1页
基于Windows的图像处理(VC).doc_第2页
基于Windows的图像处理(VC).doc_第3页
基于Windows的图像处理(VC).doc_第4页
基于Windows的图像处理(VC).doc_第5页
已阅读5页,还剩112页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第1章 Windows位图和调色板1.1 位图和调色板的概念如今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 骷髅图1.2 放大后的骷髅位图颜色RGB红25500蓝02550绿00255黄2552550紫2550255青0255255白255255255黑000灰128128128表1.1 常见颜色的RGB组合值因此,当一幅图中每个象素赋予不同的RGB值时,能呈现出五彩缤纷的颜色了,这样就形成了彩色图。的确是这样的,但实际上的做法还有些差别。让我们来看看下面的例子。有一个长宽各为200个象素,颜色数为16色的彩色图,每一个象素都用R、G、B三个分量表示。因为每个分量有256个级别,要用8位(bit),即一个字节(byte)来表示,所以每个象素需要用3个字节。整个图象要用2002003,约120k字节,可不是一个小数目呀!如果用下面的方法,就能省的多。因为是16色图,也就是说这幅图中最多只有16种颜色,我们可以用一个表:表中的每一行记录一种颜色的R、G、B值。这样当我们表示一个象素的颜色时,只需要指出该颜色是在第几行,即该颜色在表中的索引值。举个例子,如果表的第0行为255,0,0(红色),那么当某个象素为红色时,只需要标明0即可。再来计算一下:16种状态可以用4位(bit)表示,所以一个象素要用半个字节。整个图象要用2002000.5,约20k字节,再加上表占用的字节为316=48字节.整个占用的字节数约为前面的1/6,省很多。这张R、G、B表,就是我们常说的调色板(Palette),另一种叫法是颜色查找表LUT(Look Up Table),似乎更确切一些。Windows位图中便用到了调色板技术。其实不光是Windows位图,许多图象文件格式如pcx、tif、gif等都用到了。所以很好地掌握调色板的概念是十分有用的。有一种图,它的颜色数高达256256256种,也就是说包含我们上述提到的R、G、B颜色表示方法中所有的颜色,这种图叫做真彩色图(true color)。真彩色图并不是说一幅图包含了所有的颜色,而是说它具有显示所有颜色的能力,即最多可以包含所有的颜色。表示真彩色图时,每个象素直接用R、G、B三个分量字节表示,而不采用调色板技术。原因很明显:如果用调色板,表示一个象素也要用24位,这是因为每种颜色的索引要用24位(因为总共有224种颜色,即调色板有224行),和直接用R,G,B三个分量表示用的字节数一样,不但没有任何便宜,还要加上一个2562562563个字节的大调色板。所以真彩色图直接用R、G、B三个分量表示,它又叫做24位色图。1.2 bmp文件格式介绍完位图和调色板的概念,下面就让我们来看一看Windows的位图文件(.bmp文件)的格式是什么样子。bmp文件大体上分成四个部分,如图1.3所示。位图文件头BITMAPFILEHEADER位图信息头BITMAPINFOHEADER调色板Palette实际的位图数据ImageDate图1.3 Windows位图文件结构示意图第一部分为位图文件头BITMAPFILEHEADER,是一个结构,其定义如下:typedef struct tagBITMAPFILEHEADER WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; BITMAPFILEHEADER; 这个结构的长度是固定的,为14个字节(WORD为无符号16位整数,DWORD为无符号32位整数),各个域的说明如下:bfType指定文件类型,必须是0x424D,即字符串“BM”,也就是说所有.bmp文件的头两个字节都是“BM”。bfSize指定文件大小,包括这14个字节。bfReserved1,bfReserved2 为保留字,不用考虑bfOffBits为从文件头到实际的位图数据的偏移字节数,即图1.3中前三个部分的长度之和。第二部分为位图信息头BITMAPINFOHEADER,也是一个结构,其定义如下:typedef struct tagBITMAPINFOHEADERDWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; BITMAPINFOHEADER; 这个结构的长度是固定的,为40个字节(LONG为32位整数),各个域的说明如下:biSize指定这个结构的长度,为40。biWidth指定图象的宽度,单位是象素。biHeight指定图象的高度,单位是象素。biPlanes必须是1,不用考虑。biBitCount 指定表示颜色时要用到的位数,常用的值为1(黑白二色图), 4(16色图), 8(256色), 24(真彩色图)(新的.bmp格式支持32位色,这里就不做讨论了)。biCompression指定位图是否压缩,有效的值为BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS(都是一些Windows定义好的常量)。要说明的是,Windows位图可以采用RLE4,和RLE8的压缩格式,但用的不多。我们今后所讨论的只有第一种不压缩的情况,即biCompression为BI_RGB的情况。biSizeImage指定实际的位图数据占用的字节数,其实也可以从以下的公式中计算出来:biSizeImage=biWidth biHeight要注意的是:上述公式中的biWidth必须是4的整倍数(所以不是biWidth,而是biWidth,表示大于或等于biWidth的,最接近4的整倍数。举个例子,如果biWidth=240,则biWidth=240;如果biWidth=241,biWidth=244)。如果biCompression为BI_RGB,则该项可能为零biXPelsPerMeter指定目标设备的水平分辨率,单位是每米的象素个数,关于分辨率的概念,我们将在第4章详细介绍。biYPelsPerMeter指定目标设备的垂直分辨率,单位同上。biClrUsed指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2biBitCount。biClrImportant指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的。第三部分为调色板Palette,当然,这里是对那些需要调色板的位图文件而言的。有些位图,如真彩色图,是不需要调色板的,BITMAPINFOHEADER后直接是位图数据。调色板实际上是一个数组,共有biClrUsed个元素(如果该值为零,则有2biBitCount个元素)。数组中每个元素的类型是一个RGBQUAD结构,占4个字节,其定义如下:typedef struct tagRGBQUAD BYTE rgbBlue; /该颜色的蓝色分量BYTE rgbGreen; /该颜色的绿色分量BYTE rgbRed; /该颜色的红色分量BYTE rgbReserved; /保留值 RGBQUAD; 第四部分就是实际的图象数据了。对于用到调色板的位图,图象数据就是该象素颜色在调色板中的索引值。对于真彩色图,图象数据就是实际的R、G、B值。下面针对2色、16色、256色位图和真彩色位图分别介绍。对于2色位图,用1位就可以表示该象素的颜色(一般0表示黑,1表示白),所以一个字节可以表示8个象素。对于16色位图,用4位可以表示一个象素的颜色,所以一个字节可以表示2个象素。对于256色位图,一个字节刚好可以表示1个象素。对于真彩色图,三个字节才能表示1个象素,哇,好费空间呀!没办法,谁叫你想让图的颜色显得更亮丽呢,有得必有失嘛。要注意两点:(1) 每一行的字节数必须是4的整倍数,如果不是,则需要补齐。见前面介绍biSizeImage。(2) 一般来说,.bMP文件的数据从下到上,从左到右的。也就是说,从文件中最先读到的是图象最下面一行的左边第一个象素,然后是左边第二个象素接下来是倒数第二行左边第一个象素,左边第二个象素依次类推 ,最后得到的是最上面一行的最右一个象素。1.3 显示一个bmp文件的C程序下面的函数LoadBmpFile,其功能是从一个.bmp文件中读取数据(包括BITMAPINFOHEADER,调色板和实际图象数据),将其存储在一个全局内存句柄hImgData中,这个hImgData将在以后的图象处理程序中用到。同时填写一个类型为HBITMAP的全局变量hBitmap和一个类型为HPALETTE的全局变量hPalette。这两个变量将在处理WM_PAINT消息时用到,用来显示位图。该函数的两个参数分别是用来显示位图的窗口句柄,和.bmp文件名(全路径)。当函数成功时,返回TRUE,否则返回FALSE。BITMAPFILEHEADER bf;BITMAPINFOHEADER bi;BOOL LoadBmpFile (HWND hWnd,char *BmpFileName) HFILE hf; /文件句柄/指向BITMAPINFOHEADER结构的指针LPBITMAPINFOHEADER lpImgData; LOGPALETTE *pPal; /指向逻辑调色板结构的指针LPRGBQUAD lpRGB; /指向RGBQUAD结构的指针HPALETTE hPrevPalette; /用来保存设备中原来的调色板HDC hDc; /设备句柄HLOCAL hPal; /存储调色板的局部内存句柄DWORD LineBytes; /每一行的字节数DWORD ImgSize; /实际的图象数据占用的字节数/实际用到的颜色数 ,即调色板数组中的颜色个数DWORD NumColors; DWORD i;if(hf=_lopen(BmpFileName,OF_READ)=HFILE_ERROR)MessageBox(hWnd,File c:test.bmp not found!,Error Message,MB_OK|MB_ICONEXCLAMATION);return FALSE; /打开文件错误,返回/将BITMAPFILEHEADER结构从文件中读出,填写到bf中_lread(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER); /将BITMAPINFOHEADER结构从文件中读出,填写到bi中_lread(hf,(LPSTR)&bi,sizeof(BITMAPINFOHEADER);/我们定义了一个宏 #define WIDTHBYTES(i) (i+31)/32*4)上面曾经/提到过,每一行的字节数必须是4的整倍数,只要调用/WIDTHBYTES(bi.biWidth*bi.biBitCount)就能完成这一换算。举一个例/子,对于2色图,如果图象宽是31,则每一行需要31位存储,合3个/字节加7位,因为字节数必须是4的整倍数,所以应该是4,而此时的/biWidth=31,biBitCount=1,WIDTHBYTES(31*1)=4,和我们设想的一样。/再举一个256色的例子,如果图象宽是31,则每一行需要31个字节存/储,因为字节数必须是4的整倍数,所以应该是32,而此时的/biWidth=31,biBitCount=8,WIDTHBYTES(31*8)=32,我们设想的一样。你可/以多举几个例子来验证一下/LineBytes为每一行的字节数LineBytes=(DWORD)WIDTHBYTES(bi.biWidth*bi.biBitCount);/ImgSize为实际的图象数据占用的字节数ImgSize=(DWORD)LineBytes*bi.biHeight;/NumColors为实际用到的颜色数 ,即调色板数组中的颜色个数if(bi.biClrUsed!=0)/如果bi.biClrUsed不为零,即为实际用到的颜色数NumColors=(DWORD)bi.biClrUsed; else /否则,用到的颜色数为2biBitCount。switch(bi.biBitCount)case 1:NumColors=2; break; case 4:NumColors=16; break; case 8: NumColors=256; break; case 24: NumColors=0; /对于真彩色图,没用到调色板 break;default: /不处理其它的颜色数,认为出错。MessageBox(hWnd,Invalid color numbers!,Error Message,MB_OK|MB_ICONEXCLAMATION);_lclose(hf); return FALSE; /关闭文件,返回FALSEif(bf.bfOffBits!=(DWORD)(NumColors*sizeof(RGBQUAD)+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)/计算出的偏移量与实际偏移量不符,一定是颜色数出错 MessageBox(hWnd,Invalid color numbers!,Error Message,MB_OK|MB_ICONEXCLAMATION);_lclose(hf);return FALSE; /关闭文件,返回FALSEbf.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD)+ImgSize;/分配内存,大小为BITMAPINFOHEADER结构长度加调色板+实际位图if(hImgData=GlobalAlloc(GHND,(DWORD)(sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD)+ImgSize)=NULL)/分配内存错误MessageBox(hWnd,Error alloc memory!,ErrorMessage,MB_OK| MB_ICONEXCLAMATION);_lclose(hf);return FALSE; /关闭文件,返回FALSE/指针lpImgData指向该内存区lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData); /文件指针重新定位到BITMAPINFOHEADER开始处_llseek(hf,sizeof(BITMAPFILEHEADER),SEEK_SET);/将文件内容读入lpImgData_hread(hf,(char *)lpImgData,(long)sizeof(BITMAPINFOHEADER)+(long)NumColors*sizeof(RGBQUAD)+ImgSize);_lclose(hf); /关闭文件if(NumColors!=0) /NumColors不为零,说明用到了调色板/为逻辑调色板分配局部内存,大小为逻辑调色板结构长度加/NumColors个PALETTENTRYhPal=LocalAlloc(LHND,sizeof(LOGPALETTE)+NumColors* sizeof(PALETTEENTRY);/指针pPal指向该内存区pPal =(LOGPALETTE *)LocalLock(hPal); /填写逻辑调色板结构的头pPal-palNumEntries = NumColors; pPal-palVersion = 0x300;/lpRGB指向的是调色板开始的位置lpRGB = (LPRGBQUAD)(LPSTR)lpImgData +(DWORD)sizeof(BITMAPINFOHEADER);/填写每一项for (i = 0; i palPalEntryi.peRed=lpRGB-rgbRed;pPal-palPalEntryi.peGreen=lpRGB-rgbGreen;pPal-palPalEntryi.peBlue=lpRGB-rgbBlue;pPal-palPalEntryi.peFlags=(BYTE)0;lpRGB+; /指针移到下一项/产生逻辑调色板,hPalette是一个全局变量hPalette=CreatePalette(pPal);/释放局部内存LocalUnlock(hPal);LocalFree(hPal);/获得设备上下文句柄hDc=GetDC(hWnd);if(hPalette) /如果刚才产生了逻辑调色板/将新的逻辑调色板选入DC,将旧的逻辑调色板句柄保存在/hPrevPalettehPrevPalette=SelectPalette(hDc,hPalette,FALSE);RealizePalette(hDc);/产生位图句柄hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpImgData, (LONG)CBM_INIT,(LPSTR)lpImgData+sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD),(LPBITMAPINFO)lpImgData, DIB_RGB_COLORS);/将原来的调色板(如果有的话)选入设备上下文句柄if(hPalette & hPrevPalette)SelectPalette(hDc,hPrevPalette,FALSE);RealizePalette(hDc);ReleaseDC(hWnd,hDc); /释放设备上下文GlobalUnlock(hImgData); /解锁内存区return TRUE; /成功返回对上面的程序要说明两点:(1) 对于需要调色板的图,要想正确地显示,必须根据bmp文件,产生逻辑调色板。产生的方法是:为逻辑调色板指针分配内存,大小为逻辑调色板结构(LOGPALETTE)长度加NumColors个PALETTENTRY大小(调色板的每一项都是一个PALETTEENTRY结构);填写逻辑调色板结构的头pPal-palNumEntries = NumColors; pPal-palVersion = 0x300;从文件中读取调色板的RGB值,填写到每一项中;产生逻辑调色板:hPalette=CreatePalette(pPal)。(2) 产生位图(BITMAP)句柄,该项工作由函数CreateDIBitmap来完成。hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpImgData, (LONG)CBM_INIT,(LPSTR)lpImgData+sizeof(BITMAPINFOHEADER)+NumColors*sizeof(RGBQUAD),(LPBITMAPINFO)lpImgData, DIB_RGB_COLORS);CreateDIBitmap的作用是产生一个和Windows设备无关的位图。该函数的第一项参数为设备上下文句柄。如果位图用到了调色板,要在调用CreateDIBitmap之前将逻辑调色板选入该设备上下文中,产生hBitmap后,再把原调色板选入该设备上下文中,并释放该上下文;第二项为指向BITMAPINFOHEADER的指针;第三项就用常量CBM_INI,不用考虑;第四项为指向调色板的指针;第五项为指向BITMAPINFO(包括BITMAPINFOHEADER,调色板,及实际的图象数据)的指针;第六项就用常量DIB_RGB_COLORS,不用考虑。上面提到了设备上下文,Windows操作系统统一管理着诸如显示,打印等操作,将它们看作是一个个的设备,每一个设备都有一个复杂的数据结构来维护。所谓设备上下文就是指这个数据结构。然而,我们不能直接和这些设备上下文打交道,只能通过引用标识它的句柄(实际上是一个整数),让Windows去做相应的处理。产生的逻辑调色板句柄hPalette和位图句柄hBitmap要在处理WM_PAINT消息时使用,这样才能在屏幕上显示出来,处理过程如下面的程序。Static HDC hDC,hMemDC;PAINTSTRUCT ps;case WM_PAINT:hDC = BeginPaint(hwnd, &ps); /获得屏幕设备上下文if (hBitmap) /hBitmap一开始是NULL,当不为NULL时表示有图hMemDC = CreateCompatibleDC(hDC); /建立一个内存设备上下文if (hPalette) /有调色板/将调色板选入屏幕设备上下文SelectPalette (hDC, hPalette, FALSE); /将调色板选入内存设备上下文SelectPalette (hMemDC, hpalette, FALSE);RealizePalette (hDC);/将位图选入内存设备上下文SelectObject(hMemDC, hBitmap);/显示位图BitBlt(hDC, 0, 0, bi.biWidth, bi.biHeight, hMemDC, 0, 0, SRCCOPY);/释放内存设备上下文DeleteDC(hMemDC);/释放屏幕设备上下文EndPaint(hwnd, &ps);break;在上面的程序中,我们调用CreateCompatibleDC创建一个内存设备上下文。SelectObject函数将与设备无关的位图选入内存设备上下文中。然后我们调用BitBlt函数在内存设备上下文和屏幕设备上下文中进行位拷贝。由于所有操作都是在内存中进行,所以速度很快。BitBlt函数的参数分别为:1.目标设备上下文,在上面的程序里,为屏幕设备上下文,如果改成打印设备上下文,就不是显示位图,而是打印;2.目标矩形左上角点x坐标;3. 目标矩形左上角点y坐标,在上面的程序中,2和3为(0,0),表示显示在窗口的左上角;4.目标矩形的宽度;5. 目标矩形的高度;6. 源设备上下文,在上面的程序里,为内存设备上下文;7. 源矩形左上角点x坐标;8. 源矩形左上角点y坐标;9.操作方式,在这里为SRCCOPY,表示直接将源矩形拷贝到目标矩形。还可以是反色,擦除,做“与”运算等操作,具体细节见VC+帮助。你可以试着改改第2、3、4、5、7、8、9项参数,就能体会到它们的含义了。在本书的附盘中包含所有的源程序,包括头文件和资源文件和例图。特别要注意的是,退出时,别忘了释放内存和资源,这是每个程序员应该养成的习惯。这些个程序并不是很完善,例如,如果一幅图很大,屏幕显示不下怎么办?你可以试着自己加上滚动条。另外,为了节省篇幅,.bmp文件名被固定为c:test.bmp,可以自己加入打开文件对话框,任意选择你要显示的文件。图1.4为程序运行时的画面。图1.4 运行时的画面最后,再介绍一个命令行编译的窍门。为什么要用命令行编译呢?主要有两个好处:第一,不用进入IDE(集成开发环境),节省了时间,而且编译速度也比较快;第二,对于简单的程序,不用生成项目文件.mdp或.mak,直接就能生成.exe文件,这一点,在下面的例子中可以看到。在安装完Visual C+时,在bin目录下会产生一个VCVARS32.BAT文件,它的作用是在命令行编译时设置正确的环境变量,如存放头文件的INCLUDE目录,存放库文件的LIB目录等。如果你没找到这个批处理文件,可以参考下面的例子,自己做一个批处理。echo offset MSDevDir=d:MSDEVset VcOsDir=WIN95set PATH=%MSDevDir%BIN;%MSDevDir%BIN%VcOsDir%;%PATH%set INCLUDE=%MSDevDir%INCLUDE;%MSDevDir%MFCINCLUDE;%INCLUDE%set LIB=%MSDevDir%LIB;%MSDevDir%MFCLIB;%LIB%set VcOsDir=只要把上面的“d:MSDEV”改成你自己的VC目录就可以了。在DOS PROMPT下执行该批处理文件,执行set命令,你就能看到新设置的环境变量了。如下所示:PATH=D:MSDEVBIN;D:MSDEVBINWIN95;C:WIN95;C:WIN95COMMAND;C:WIN95SYSTEM;INCLUDE=d:msdevINCLUDE;d:msdevMFCINCLUDE;LIB=d:msdevLIB;d:msdevMFCLIB;现在我们就可以进行命令行编译了。首先编译资源文件,输入rc bmp.rc,将生成bmp.res文件,接着输入cl bmp.c bmp.res user32.lib gdi32.lib,就生成bmp.exe 了。可以看到,我们并没有用到项目文件,所以,对于这种简单的程序来说,使用命令行编译还是非常方便的。有时命令行编译会出现“Out of enviroment space”的错误,那是因为缺省的初始环境变量内存太小,首先执行command /e:2048 (或更大)命令即可解决改问题。使用ide的方法是:new project,类型是win32 application-empty project,然后把.h,.rc,.c文件add to project编译即可。好了,运行bmp.exe,欣赏一下你今天的劳动成果。第2章 图象的几何变换这一章我们将介绍图象的几何变换,包括图象的平移、旋转、镜象变换、转置、放缩等。如果你熟悉矩阵运算,你将发现,实现这些变换是非常容易的。2.1 平移平移(translation)变换大概是几何变换中最简单的一种了。如图2.1所示,初始坐标为(x0,y0)的点经过平移(tx,ty)(以向右,向下为正方向)后,坐标变为(x1,y1)。这两点之间的关系是x1=x0+tx ,y1=y0+ty。图2.1 平移的示意图以矩阵的形式表示为(2.1)我们更关心其逆变换(2.2)(2.1),其逆变换:这是因为:我们想知道的是平移后的图象中每个象素的颜色。例如我们想知道,新图中左上角点的RGB值是多少?很显然,该点是原图的某点经过平移后得到的,这两点的颜色肯定是一样的,所以只要知道了原图那点的RGB值即可。那么到底新图中的左上角点对应原图中的哪一点呢?将左上角点的坐标(0,0)入公式(2.2),得到x0=-tx ,y0=-ty;所以新图中的(0,0)点的颜色和原图中(-tx , -ty)的一样。这样就存在问题1:如果新图中有一点(x1,y1),按照公式(2.2)得到的(x0,y0)不在原图中该怎么办?通常的做法是,把该点的RGB值统一设成(0,0,0)或者(255,255,255)。问题2是:平移后的图象是否要放大?一种做法是不放大,移出的部分被截断。例如,图2.2为原图,图2.3为移动后的图。这种处理,文件大小不会改变。还有一种做法是:将图象放大,使得能够显示下所有部分,如图2.4所示。这种处理,文件大小要改变。设原图的宽和高分别是w1,h1则新图的宽和高变为w1+|tx|和h1+|ty|,加绝对值符号是因为tx, ty有可能为负(即向左,向上移动)。图2.2 移动前的图 图2.3 移动后的图图 2.4 移动后图象被放大下面的函数Translation采用的是第一种做法,即移出的部分被截断。在给出源代码之前,先说明一个问题。如果你用过Photoshop,Corel PhotoPaint等图象处理软件,可能听说过“灰度图”(grayscale)这个词。灰度图是指只含亮度信息,不含色彩信息的图象,就象我们平时看到的黑白照片:亮度由暗到明,变化是连续的。因此,要表示灰度图,就需要把亮度值进行量化。通常划分成0到255共256个级别,其中0最暗(全黑),255最亮(全白)。.bmp格式的文件中,并没有灰度图这个概念,但是,我们可以很容易在.bmp文件中表示灰度图。方法是用256色的调色板,只不过这个调色板有点特殊,每一项的RGB值都是相同的。也就是说RGB值从(0,0,0),(1,1,1)一直到(255,255,255)。(0,0,0)是全黑色,(255,255,255)是全白色,中间的是灰色。这样,灰度图就可以用256色图来表示了。为什么会这样呢?难道是一种巧合?其实并不是。在表示颜色的方法中,除了RGB外,还有一种叫YUV的表示方法,应用也很多。电视信号中用的就是一种类似于YUV的颜色表示方法。在这种表示方法中,Y分量的物理含义就是亮度,U和V分量代表了色差信号(你不必了解什么是色差,只要知道有这么一个概念就可以了)。使用这种表示方法有很多好处,最主要的有两点:(1) 因为Y代表了亮度,所以Y分量包含了灰度图的所有信息,只用Y分量就能完全能够表示出一幅灰度图来。当同时考虑U,V分量时,就能够表示出彩色信息来。这样,用同一种表示方法可以很方便的在灰度和彩色图之间切换,而RGB表示方法就做不到这一点了。(2) 人眼对于亮度信号非常敏感,而对色差信号的敏感程度相对较弱。也就是说,图象的主要信息包含在Y分量中。这就提示我们:如果在对YUV信号进行量化时,可以“偏心”一点,让Y的量化级别多一些(谁让它重要呢?)而让UV的量化级别少一些,就可以实现图象信息的压缩。这一点将在第9章介绍图象压缩时仔细研究,这里就不深入讨论了。而RGB的表示方法就做不到这一点,因为RGB三个分量同等重要,缺了谁也不行。YUV和RGB之间有着如下的对应关系 (2.3) (2.4)当RGB三个分量的大小一样时,假设都是a,代入公式(2.3),得到Y=a,U=0,V=0 。不是巧合的原因在此。使用灰度图有一个好处,那就是方便。首先RGB的值都一样;其次,图象数据即调色板索引值,也就是实际的RGB值,也就是亮度值;另外,因为是256色调色板,所以图象数据中一个字节代表一个象素,很整齐。如果是2色图或16色图,还要拼凑字节,很麻烦。如果是彩色的256色图,由于图象处理后有可能会产生不属于这256种颜色的新颜色,就更麻烦了;这一点,今后你就会有深刻体会的。所以,做图象处理时,一般采用灰度图。为了将重点放在算法本身上,今后给出的程序如不做特殊说明,都是针对256级灰度图的。其它颜色的情况,你可以自己想一想,把算法补全。若想得到一幅灰度图,可以使用Sea或者PhotoShop等软件提供的颜色转换功能将彩色图转换成灰度图。好了,言归正传,下面给出Translation的源代码。算法的思想是先将所有区域填成白色,然后找平移后显示区域的左上角点(x0,y0) 和右下角点(x1,y1) ,分几种情况进行处理。先看x方向(width指图象的宽度)(1) tx-width:很显然,图象完全移出了屏幕,不用做任何处理;(2) -widthtx0:如图2.5所示。容易看出,图象区域的x范围从0到width-|tx|,对应原图的范围从|tx|到width;(3) 0 tx width:如图2.6所示。容易看出,图象区域的x范围从tx 到width,对应原图的范围从0到width - tx ;(4) tx width:很显然,图象完全移出了屏幕,不用做任何处理。y方向是对应的(height表示图象的高度):(1) ty-height,图象完全移出了屏幕,不用做任何处理;(2) -heightty0,图象区域的y范围从0到height-|ty|,对应原图的范围从|ty|到height;(3) 0tyheight ,图象区域的y范围从ty到height,对应原图的范围从0到height-ty;(4) tyheight,图象完全移出了屏幕,不用做任何处理。这种做法利用了位图存储的连续性,即同一行的象素在内存中是相邻的。利用memcpy函数,从(x0,y0)点开始,一次可以拷贝一整行(宽度为x1x0),然后将内存指针移到(x0,y0+1)处,拷贝下一行。这样拷贝(y1-y0)行就完成了全部操作,避免了一个一个象素的计算,提高了效率。Translation的源代码如下:图2.5 tx0,ty0的情况图 2.6 0 txwidth,0tyheight的情况int xOffset=0,yOffset=0;BOOL Translation(HWND hWnd)DLGPROC dlgInputBox = NULL;DWORD OffBits,BufSize;LPBITMAPINFOHEADER lpImgData;LPSTR lpPtr;HLOCAL hTempImgData;LPBITMAPINFOHEADER lpTempImgData;LPSTR lpTempPtr;int SrcX0,SrcY0,SrcX1,SrcY1;int DstX0,DstY0,DstX1,DstY1;int RectWidth,RectHeight;BOOL xVisible,yVisible;HDC hDc;HFILE hf;int i;/出现对话框,输入x偏移量xOffset,和y偏移量yOffsetdlgInputBox = (DLGPROC) MakeProcInstance ( (FARPROC)InputBox,ghInst );DialogBox (ghInst, INPUTBOX, hWnd, dlgInputBox);FreeProcInstance ( (FARPROC) dlgInputBox );/OffBits为BITMAPINFOHEADER结构长度加调色板的大小OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER); BufSize=OffBits+bi.biHeight*LineBytes;/要开的缓冲区的大小/为新产生的位图分配缓冲区内存if(hTempImgData=LocalAlloc(LHND,BufSize)=NULL)MessageBox(hWnd,Error alloc memory!,Error Message,MB_OK|MB_ICONEXCLAMATION);return FALSE; /失败,返回/lpImgData为指向原来位图数据的指针lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);/lpTempImgData为指向新产生位图数据的指针lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);lpPtr=(char *)lpImgData;lpTempPtr=(char *)lpTempImgData;/将新的缓冲区中的每个字节都填成255,这样以后未处理的象素就是白色memset(lpTempPtr,(BYTE)255,BufSize);/两幅图之间的头信息,包括调色板都是相同的,所以直接拷贝头和调色板memcpy(lpTempPtr,lpPtr,OffBits);/xVisible为FALSE时,表示x方向已经移出了可显示的范围xVisible=TRUE; if( xOffset= -bi.biWidth )xVisible=FALSE; else if( xOffset=0)DstX0=0; /表示移动后,有图区域的左上角点的x坐标DstX1=bi.biWidth+xOffset; /表示移动后,有图区域的右下角点的x坐标else if ( xOffsetbi.biWidth)DstX0=xOffset;DstX1=bi.biWidth;elsexVisible=FALSE;SrcX0=DstX0-xOffset; /对应DstX0在原图中的x坐标SrcX1=DstX1-xOffset; /对应DstX1在原图中的x坐标RectWidth=DstX1-DstX0; /有图区域的宽度/yVisible为FALSE时,表示y方向已经移出了可显示的范围yVisible=TRUE; if( yOffset= -bi.biHeight )yVisible=FALSE;else

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论