第2章图象的几何变换

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

第2章图象的几何变换这一章我们将介绍图象的几何变换,包括图象的平移、旋转、镜象变换、转置、放缩等。如果你熟悉矩阵运算,你将发现,实现这些变换是非常容易的。2.1平移平移(translation)变换大概是几何变换中最简单的一种了。如图2.1所示,初始坐标为(x0,y0)的点经过平移(tx,ty)(以向右,向下为正方向)后,坐标变为(x1,y1)。这两点之间的关系是x1=x0+tx,y1=y0+ty。图2.1平移的示意图以矩阵的形式表示为(2.1)我们更关心的是它的逆变换:(2.2)这是因为:我们想知道的是平移后的图象中每个象素的颜色。例如我们想知道,新图中左上角点的RGB值是多少?很显然,该点是原图的某点经过平移后得到的,这两点的颜色肯定是一样的,所以只要知道了原图那点的RGB值即可。那么到底新图中的左上角点对应原图中的哪一点呢?将左上角点的坐标(0,0)入公式(2.2),得到x0=-tx,y0=-ty;所以新图中的(0,0)点的颜色和原图中(-tx,-ty)的一样。这样就存在一个问题:如果新图中有一点(x1,y1),按照公式(2.2)得到的(x0,y0)不在原图中该怎么办?通常的做法是,把该点的RGB值统一设成(0,0,0)或者(255,255,255)。另一个问题是:平移后的图象是否要放大?一种做法是不放大,移出的部分被截断。例如,图2.2为原图,图2.3为移动后的图。这种处理,文件大小不会改变。图2.2移动前的图图2.3移动后的图还有一种做法是:将图象放大,使得能够显示下所有部分,如图2.4所示。图2.4移动后图象被放大这种处理,文件大小要改变。设原图的宽和高分别是w1,h1则新图的宽和高变为w1+|tx|和h1+|ty|,加绝对值符号是因为tx,ty有可能为负(即向左,向上移动)。下面的函数Translation采用的是第一种做法,即移出的部分被截断。在给出源代码之前,先说明一个问题。如果你用过Photoshop,CorelPhotoPaint等图象处理软件,可能听说过“灰度图”(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)-widthtx≤0:如图2.5所示。容易看出,图象区域的x范围从0到width-|tx|,对应原图的范围从|tx|到width;图2.5tx≤0,ty≤0的情况(3)0txwidth:如图2.6所示。容易看出,图象区域的x范围从tx到width,对应原图的范围从0到width-tx;图2.60txwidth,0tyheight的情况(4)tx≥width:很显然,图象完全移出了屏幕,不用做任何处理。y方向是对应的(height表示图象的高度):(1)ty≤-height,图象完全移出了屏幕,不用做任何处理;(2)-heightty≤0,图象区域的y范围从0到height-|ty|,对应原图的范围从|ty|到height;(3)0tyheight,图象区域的y范围从ty到height,对应原图的范围从0到height-ty;(4)ty≥height,图象完全移出了屏幕,不用做任何处理。这种做法利用了位图存储的连续性,即同一行的象素在内存中是相邻的。利用memcpy函数,从(x0,y0)点开始,一次可以拷贝一整行(宽度为x1-x0),然后将内存指针移到(x0,y0+1)处,拷贝下一行。这样拷贝(y1-y0)行就完成了全部操作,避免了一个一个象素的计算,提高了效率。Translation的源代码如下:intxOffset=0,yOffset=0;BOOLTranslation(HWNDhWnd){DLGPROCdlgInputBox=NULL;DWORDOffBits,BufSize;LPBITMAPINFOHEADERlpImgData;LPSTRlpPtr;HLOCALhTempImgData;LPBITMAPINFOHEADERlpTempImgData;LPSTRlpTempPtr;intSrcX0,SrcY0,SrcX1,SrcY1;intDstX0,DstY0,DstX1,DstY1;intRectWidth,RectHeight;BOOLxVisible,yVisible;HDChDc;HFILEhf;inti;//出现对话框,输入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,Errorallocmemory!,ErrorMessage,MB_OK|MB_ICONEXCLAMATION);returnFALSE;//失败,返回}//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;elseif(xOffset=0){DstX0=0;//表示移动后,有图区域的左上角点的x坐标DstX1=bi.biWidth+xOffset;//表示移动后,有图区域的右下角点的x坐标}elseif(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;elseif(yOffset=0){DstY0=0;//表示移动后,有图区域的左上角点的y坐标DstY1=bi.biHeight+yOffset;//表示移动后,有图区域的右下角点的y坐标}elseif(yOffsetbi.biHeight){DstY0=yOffset;DstY1=bi.biHeight;}elseyVisible=FALSE;SrcY0=DstY0-yOffset;//对应DstY0在原图中的y坐标SrcY1=DstY1-yOffset;//对应DstY1在原图中的y坐标RectHeight=DstY1-DstY0;//有图区域的高度if(xVisible&&yVisible){//x,y方向都没有完全移出可显示的范围for(i=0;iRectHeight;i++){//拷贝每一行//lpPtr指向要拷贝的那一行的最左边的象素对应在原图中的位//置。特别要注意的是,由于.bmp是上下颠倒的,偏移是//(BufSize-LineBytes-(i+SrcY0)*LineBytes)+SrcX0,而不是//(i+SrcY0)*LineBytes)+SrcX0,你试着举个例子就明白了。lpPtr=(char*)lpImgData+(BufSize-LineBytes-(i+SrcY0)*LineBytes)+SrcX0;//lpTempPtr指向要拷贝的那一行的最左边的象素对应在新图中//的位置。同样要注意上面//的问题。

1 / 32
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功