图像的膨胀与腐蚀、细化
生活随笔
收集整理的這篇文章主要介紹了
图像的膨胀与腐蚀、细化
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
轉自:http://www.opencvchina.com/forum.php?mod=viewthread&tid=1124
原理:在特殊領域運算形式——結構元素(Sturcture Element),在每個像素位置上與二值圖像對應的區域進行特定的邏輯運算。運算結構是輸出圖像的相應像素。運算效果取決于結構元素大小內容以及邏輯運算性質。
結構元素:膨脹和腐蝕操作的最基本組成部分,用于測試輸出圖像,通常要比待處理的圖像小還很多。二維平面結構元素由一個數值為0或1的矩陣組成。結構元素的原點指定了圖像中需要處理的像素范圍,結構元素中數值為1的點決定結構元素的鄰域像素在進行膨脹或腐蝕操作時是否需要參與計算。
先來定義一些基本符號和關系。 1. 元素 設有一幅圖象X,若點a在X的區域以內,則稱a為X的元素,記作a∈X,如圖6.1所示。 2. B包含于X 設有兩幅圖象B,X。對于B中所有的元素ai,都有ai∈X,則稱B包含于(included in)X,記作B X,如圖6.2所示。 3. B擊中X 設有兩幅圖象B,X。若存在這樣一個點,它即是B的元素,又是X的元素,則稱B擊中(hit)X,記作B↑X,如圖6.3所示。 4. B不擊中X 設有兩幅圖象B,X。若不存在任何一個點,它即是B的元素,又是X的元素,即B和X的交集是空,則稱B不擊中(miss)X,記作B∩X=Ф;其中∩是集合運算相交的符號,Ф表示空集。如圖6.4所示。| 圖6.1 元素 | 圖6.2 包含 |
| 圖6.3 擊中 | 圖6.4 不擊中 |
| 圖6.6 對稱集的示意圖 | 圖6.7 平移的示意圖 |
6.1 腐蝕
把結構元素B平移a后得到Ba,若Ba包含于X,我們記下這個a點,所有滿足上述條件的a點組成的集合稱做X被B腐蝕(Erosion)的結果。用公式表示為:E(X)={a| Ba X}=X B,如圖6.8所示。 圖6.8 腐蝕的示意圖 圖6.8中X是被處理的對象,B是結構元素。不難知道,對于任意一個在陰影部分的點a,Ba 包含于X,所以X被B腐蝕的結果就是那個陰影部分。陰影部分在X的范圍之內,且比X小,就象X被剝掉了一層似的,這就是為什么叫腐蝕的原因。 值得注意的是,上面的B是對稱的,即B的對稱集Bv=B,所以X被B腐蝕的結果和X被 Bv腐蝕的結果是一樣的。如果B不是對稱的,讓我們看看圖6.9,就會發現X被B腐蝕的結果和X被 Bv腐蝕的結果不同。 圖6.9 結構元素非對稱時,腐蝕的結果不同 圖6.8和圖6.9都是示意圖,讓我們來看看實際上是怎樣進行腐蝕運算的。 在圖6.10中,左邊是被處理的圖象X(二值圖象,我們針對的是黑點),中間是結構元素B,那個標有origin的點是中心點,即當前處理元素的位置,我們在介紹模板操作時也有過類似的概念。腐蝕的方法是,拿B的中心點和X上的點一個一個地對比,如果B上的所有點都在X的范圍內,則該點保留,否則將該點去掉;右邊是腐蝕后的結果。可以看出,它仍在原來X的范圍內,且比X包含的點要少,就象X被腐蝕掉了一層。 圖6.10 腐蝕運算 圖6.11為原圖,圖6.12為腐蝕后的結果圖,能夠很明顯地看出腐蝕的效果。 圖6.11 原圖 圖6.12 腐蝕后的結果圖 下面的這段程序,實現了上述的腐蝕運算,針對的都是黑色點。參數中有一個BOOL變量,為真時,表示在水平方向進行腐蝕運算,即結構元素B為 ;否則在垂直方向上進行腐蝕運算,即結構元素B為 。 腐蝕源碼 1 BOOL Erosion(HWND hWnd,BOOL Hori) 2 { 3 DWORD OffBits,BufSize; 4 LPBITMAPINFOHEADER lpImgData; 5 LPSTR lpPtr; 6 HLOCAL hTempImgData; 7 LPBITMAPINFOHEADER lpTempImgData; 8 LPSTR lpTempPtr; 9 HDC hDc; 10 HFILE hf; 11 LONG x,y; 12 unsigned char num; int i; //為了處理方便,仍采用256級灰度圖,不過只用調色板中0和255兩項 if( NumColors!=256){ 13 MessageBox(hWnd,"Must be a mono bitmap with grayscale palette!", "Error Message",MB_OK|MB_ICONEXCLAMATION); return FALSE; 14 } 15 16 OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER); //BufSize為緩沖區大小 BufSize=OffBits+bi.biHeight*LineBytes; //為新的緩沖區分配內存 if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL) 17 { 18 MessageBox(hWnd,"Error alloc memory!","Error Message", 19 MB_OK|MB_ICONEXCLAMATION); return FALSE; 20 } 21 lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData); 22 lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData); //拷貝頭信息和位圖數據 memcpy(lpTempImgData,lpImgData,BufSize); if(Hori) 23 { //在水平方向進行腐蝕運算 for(y=0;y<bi.biHeight;y++){ //lpPtr指向原圖數據,lpTempPtr指向新圖數據 lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes)+1; 24 lpTempPtr=(char*)lpTempImgData+ 25 (BufSize-LineBytes-y*LineBytes)+1; for(x=1;x<bi.biWidth-1;x++){ //注意為防止越界,x的范圍從1到寬度-2 num=(unsigned char)*lpPtr; if (num==0){ //因為腐蝕掉的是黑點,所以只對黑點處理 *lpTempPtr=(unsigned char)0; //先置成黑點 for(i=0;i<3;i++){ 26 num=(unsigned char)*(lpPtr+i-1); if(num==255){ //自身及上下鄰居中若有一個不是黑點,則將該點腐 //蝕成白點 *lpTempPtr=(unsigned char)255; break; 27 } 28 } 29 } //原圖中就是白點的,新圖中仍是白點 else *lpTempPtr=(unsigned char)255; //指向下一個象素 lpPtr++; 30 lpTempPtr++; 31 } 32 } 33 } else{ //在垂直方向進行腐蝕運算 for(y=1;y<bi.biHeight-1;y++){ //注意為防止越界,y的范圍從1到高度-2 //lpPtr指向原圖數據,lpTempPtr指向新圖數據 lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes); 34 lpTempPtr=(char *)lpTempImgData+(BufSize-LineBytes-y*LineBytes); for(x=0;x<bi.biWidth;x++){ 35 num=(unsigned char)*lpPtr; if (num==0){ //因為腐蝕掉的是黑點,所以只對黑點處理 *lpTempPtr=(unsigned char)0; //先置成黑點 for(i=0;i<3;i++){ 36 num=(unsigned char)*(lpPtr+(i-1)*LineBytes); if(num==255){ //自身及上下鄰居中若有一個不是黑點,則將該點腐 //蝕成白點 *lpTempPtr=(unsigned char)255; break; 37 } 38 } 39 } //原圖中就是白點的,新圖中仍是白點 else *lpTempPtr=(unsigned char)255; //指向下一個象素 lpPtr++; 40 lpTempPtr++; 41 } 42 } 43 } if(hBitmap!=NULL) 44 DeleteObject(hBitmap); 45 hDc=GetDC(hWnd); //產生新的位圖 hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData, 46 (LONG)CBM_INIT, 47 (LPSTR)lpTempImgData+ sizeof(BITMAPINFOHEADER)+ 48 NumColors*sizeof(RGBQUAD), 49 (LPBITMAPINFO)lpTempImgData, DIB_RGB_COLORS); //起不同的結果文件名 if(Hori) 50 hf=_lcreat("c:\\herosion.bmp",0); else hf=_lcreat("c:\\verosion.bmp",0); 51 _lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER)); 52 _lwrite(hf,(LPSTR)lpTempImgData,BufSize); 53 _lclose(hf); //釋放內存及資源 ReleaseDC(hWnd,hDc); 54 LocalUnlock(hTempImgData); 55 LocalFree(hTempImgData); 56 GlobalUnlock(hImgData); return TRUE; 57 } 膨脹(dilation)可以看做是腐蝕的對偶運算,其定義是:把結構元素B平移a后得到Ba,若Ba擊中X,我們記下這個a點。所有滿足上述條件的a點組成的集合稱做X被B膨脹的結果。用公式表示為:D(X)={a | Ba↑X}=X B,如圖6.13所示。圖6.13中X是被處理的對象,B是結構元素,不難知道,對于任意一個在陰影部分的點a,Ba擊中X,所以X被B膨脹的結果就是那個陰影部分。陰影部分包括X的所有范圍,就象X膨脹了一圈似的,這就是為什么叫膨脹的原因。 同樣,如果B不是對稱的,X被B膨脹的結果和X被 Bv膨脹的結果不同。 讓我們來看看實際上是怎樣進行膨脹運算的。在圖6.14中,左邊是被處理的圖象X(二值圖象,我們針對的是黑點),中間是結構元素B。膨脹的方法是,拿B的中心點和X上的點及X周圍的點一個一個地對,如果B上有一個點落在X的范圍內,則該點就為黑;右邊是膨脹后的結果。可以看出,它包括X的所有范圍,就象X膨脹了一圈似的。 圖6.13 膨脹的示意圖 圖6.14 膨脹運算 圖6.15為圖6.11膨脹后的結果圖,能夠很明顯的看出膨脹的效果。 圖6.15 圖6.11膨脹后的結果圖 下面的這段程序,實現了上述的膨脹運算,針對的都是黑色點。參數中有一個BOOL變量,為真時,表示在水平方向進行膨脹運算,即結構元素B為 ;否則在垂直方向上進行膨脹運算,即結構元素B為 。膨脹源碼
1 BOOL Dilation(HWND hWnd,BOOL Hori) 4 { 7 DWORD OffBits,BufSize; 10 LPBITMAPINFOHEADER lpImgData; 13 LPSTR lpPtr; 16 HLOCAL hTempImgData; 19 LPBITMAPINFOHEADER lpTempImgData; 22 LPSTR lpTempPtr; 25 HDC hDc; 28 HFILE hf; 31 LONG x,y; 34 unsigned char num; int i; //為了處理的方便,仍采用256級灰度圖,不過只調色板中0和255兩項 if( NumColors!=256){ 37 MessageBox(hWnd,"Must be a mono bitmap with grayscale palette!", "Error Message",MB_OK|MB_ICONEXCLAMATION); return FALSE; 40 } 43 OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER); //BufSize為緩沖區大小 BufSize=OffBits+bi.biHeight*LineBytes; //為新的緩沖區分配內存 if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL) 46 { 49 MessageBox(hWnd,"Error alloc memory!","Error Message", 52 MB_OK|MB_ICONEXCLAMATION); return FALSE; 55 } 58 lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData); 60 lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData); //拷貝頭信息和位圖數據 memcpy(lpTempImgData,lpImgData,BufSize); if(Hori) 63 { //在水平方向進行膨脹運算 for(y=0;y<bi.biHeight;y++){ //lpPtr指向原圖數據,lpTempPtr指向新圖數據 lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes)+1; 66 lpTempPtr=(char*)lpTempImgData+ 69 (BufSize-LineBytes-y*LineBytes)+1; for(x=1;x<bi.biWidth-1;x++){ //注意為防止越界,x的范圍從1到寬度-2 num=(unsigned char)*lpPtr; //原圖中是黑點的,新圖中肯定也是,所以要考慮的是那些原圖 //中的白點,看是否有可能膨脹成黑點 if (num==255){ 72 *lpTempPtr=(unsigned char)255; //先置成白點 for(i=0;i<3;i++){ 74 num=(unsigned char)*(lpPtr+i-1); //只要左右鄰居中有一個是黑點,就膨脹成黑點 if(num==0){ 77 *lpTempPtr=(unsigned char)0; break; 80 } 83 } 86 } //原圖中就是黑點的,新圖中仍是黑點 else *lpTempPtr=(unsigned char)0; //指向下一個象素 lpPtr++; 89 lpTempPtr++; 92 } 95 } 98 } else{ //在垂直方向進行腐蝕運算 for(y=1;y<bi.biHeight-1;y++){ //注意為防止越界,y的范圍從1到高度-2 lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes); 101 lpTempPtr=(char *)lpTempImgData+(BufSize-LineBytes-y*LineBytes); for(x=0;x<bi.biWidth;x++){ 104 num=(unsigned char)*lpPtr; if (num==255){ 107 *lpTempPtr=(unsigned char)255; for(i=0;i<3;i++){num=(unsigned char)*(lpPtr+(i-1)*LineBytes); //只要上下鄰居中有一個是黑點,就膨脹成黑點 if(num==0){ 113 *lpTempPtr=(unsigned char)0; break; 116 } 119 } 122 } else *lpTempPtr=(unsigned char)0; 125 lpPtr++; 128 lpTempPtr++; 131 } 134 } 137 } if(hBitmap!=NULL) 140 DeleteObject(hBitmap); 143 hDc=GetDC(hWnd); //產生新的位圖 hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData, 146 (LONG)CBM_INIT, 149 (LPSTR)lpTempImgData+ sizeof(BITMAPINFOHEADER)+ 152 NumColors*sizeof(RGBQUAD), 155 (LPBITMAPINFO)lpTempImgData, 158 DIB_RGB_COLORS); //起不同的結果文件名 if(Hori) 161 hf=_lcreat("c:\\hdilation.bmp",0); else hf=_lcreat("c:\\vdilation.bmp",0); 164 _lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER)); 166 _lwrite(hf,(LPSTR)lpTempImgData,BufSize); 169 _lclose(hf); //釋放內存及資源 ReleaseDC(hWnd,hDc); 172 LocalUnlock(hTempImgData); 175 LocalFree(hTempImgData); 178 GlobalUnlock(hImgData); return TRUE; 181 }
?
腐蝕運算和膨脹運算互為對偶的,用公式表示為(X B)c=(Xc B),即X 被B腐蝕后的補集等于X的補集被B膨脹。這句話可以形象的理解為:河岸的補集為河面,河岸的腐蝕等價于河面的膨脹。你可以自己舉個例子來驗證一下這個關系。在有些情況下,這個對偶關系是非常有用的。例如:某個圖象處理系統用硬件實現了腐蝕運算,那么不必再另搞一套膨脹的硬件,直接利用該對偶就可以實現了。
開 先腐蝕后膨脹稱為開(open),即OPEN(X)=D(E(X))。 讓我們來看一個開運算的例子(見圖6.16): 圖6.16開運算 在圖16上面的兩幅圖中,左邊是被處理的圖象X(二值圖象,我們針對的是黑點),右邊是結構元素B,下面的兩幅圖中左邊是腐蝕后的結果;右邊是在此基礎上膨脹的結果。可以看到,原圖經過開運算后,一些孤立的小點被去掉了。一般來說,開運算能夠去除孤立的小點,毛刺和小橋(即連通兩塊區域的小點),而總的位置和形狀不變。這就是開運算的作用。要注意的是,如果B是非對稱的,進行開運算時要用B的對稱集Bv膨脹,否則,開運算的結果和原圖相比要發生平移。圖6.17和圖6.18能夠說明這個問題。 圖6.17 用B膨脹后,結果向左平移了 圖6.18 用Bv膨脹后位置不變 圖6.17是用B膨脹的,可以看到,OPEN(X)向左平移了。圖18是用Bv膨脹的,可以看到,總的位置和形狀不變。 圖6.19為圖6.11經過開運算后的結果。 圖6.19 圖6.11經過開運算后的結果 開運算的源程序可以很容易的根據上面的腐蝕,膨脹程序得到,這里就不給出了。 閉 先膨脹后腐蝕稱為閉(close),即CLOSE(X)=E(D(X))。 讓我們來看一個閉運算的例子(見圖6.20): 圖6.20 閉運算 在圖6.20上面的兩幅圖中,左邊是被處理的圖象X(二值圖象,我們針對的是黑點),右邊是結構元素B,下面的兩幅圖中左邊是膨脹后的結果,右邊是在此基礎上腐蝕的結果可以看到,原圖經過閉運算后,斷裂的地方被彌合了。一般來說,閉運算能夠填平小湖(即小孔),彌合小裂縫,而總的位置和形狀不變。這就是閉運算的作用。同樣要注意的是,如果B是非對稱的,進行閉運算時要用B的對稱集Bv膨脹,否則,閉運算的結果和原圖相比要發生平移。 圖6.21為圖6.11經過閉運算后的結果。 圖6.21 圖.611經過閉運算后的結果 閉運算的源程序可以很容易的根據上面的膨脹,腐蝕程序得到,這里就不給出了。 你大概已經猜到了,開和閉也是對偶運算,的確如此。用公式表示為(OPEN(X))c=CLOSE((Xc)),或者(CLOSE(X))c =OPEN((Xc))。即X 開運算的補集等于X的補集的閉運算,或者X 閉運算的補集等于X的補集的開運算。這句話可以這樣來理解:在兩個小島之間有一座小橋,我們把島和橋看做是處理對象X,則X的補集為**。如果漲潮時將小橋和島的外圍淹沒(相當于用尺寸比橋寬大的結構元素對X進行開運算),那么兩個島的分隔,相當于小橋兩邊海域的連通(對Xc做閉運算)。 細化 細化(thinning)算法有很多,我們在這里介紹的是一種簡單而且效果很好的算法,用它就能夠實現從文本抽取骨架的功能。我們的對象是白紙黑字的文本,但在程序中為了處理的方便,還是采用256級灰度圖,不過只用到了調色板中0和255兩項。 所謂細化,就是從原來的圖中去掉一些點,但仍要保持原來的形狀。實際上,是保持原圖的骨架。所謂骨架,可以理解為圖象的中軸,例如一個長方形的骨架是它的長方向上的中軸線;正方形的骨架是它的中心點;圓的骨架是它的圓心,直線的骨架是它自身,孤立點的骨架也是自身。文本的骨架嘛,前言中的例子顯示的很明白。那么怎樣判斷一個點是否能去掉呢?顯然,要根據它的八個相鄰點的情況來判斷,我們給幾個例子(如圖6.22所示)。 圖6.22 根據某點的八個相鄰點的情況來判斷該點是否能刪除 圖6.22中,(1)不能刪,因為它是個內部點,我們要求的是骨架,如果連內部點也刪了,骨架也會被掏空的;(2)不能刪,和(1)是同樣的道理;(3)可以刪,這樣的點不是骨架;(4)不能刪,因為刪掉后,原來相連的部分斷開了;(5)可以刪,這樣的點不是骨架;(6)不能刪,因為它是直線的端點,如果這樣的點刪了,那么最后整個直線也被刪了,剩不下什么;(7)不能刪,因為孤立點的骨架就是它自身。 總結一下,有如下的判據:(1)內部點不能刪除;(2)孤立點不能刪除;(3)直線端點不能刪除;(4)如果P是邊界點,去掉P后,如果連通分量不增加,則P可以刪除。 我們可以根據上述的判據,事先做出一張表,從0到255共有256個元素,每個元素要么是0,要么是1。我們根據某點(當然是要處理的黑色點了)的八個相鄰點的情況查表,若表中的元素是1,則表示該點可刪,否則保留。 查表的方法是,設白點為1,黑點為0;左上方點對應一個8位數的第一位(最低位),正上方點對應第二位,右上方點對應的第三位,左鄰點對應第四位,右鄰點對應第五位,左下方點對應第六位,正下方點對應第七位,右下方點對應的第八位,按這樣組成的8位數去查表即可。例如上面的例子中(1)對應表中的第0項,該項應該為0;(2)對應37,該項應該為0;(3)對應173,該項應該為1;(4)對應231,該項應該為0;(5)對應237,該項應該為1;(6)對應254,該項應該為0;(7)對應255,該項應該為0。 這張表我已經替大家做好了,可花了我不少時間呢! static int erasetable[256]={ 0,0,1,1,0,0,1,1, 1,1,0,1,1,1,0,1, 1,1,0,0,1,1,1,1, 0,0,0,0,0,0,0,1, 0,0,1,1,0,0,1,1, 1,1,0,1,1,1,0,1, 1,1,0,0,1,1,1,1, 0,0,0,0,0,0,0,1, 1,1,0,0,1,1,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 1,1,0,0,1,1,0,0, 1,1,0,1,1,1,0,1, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,1,1,0,0,1,1, 1,1,0,1,1,1,0,1, 1,1,0,0,1,1,1,1, 0,0,0,0,0,0,0,1, 0,0,1,1,0,0,1,1, 1,1,0,1,1,1,0,1, 1,1,0,0,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,0,0,1,1,0,0, 0,0,0,0,0,0,0,0, 1,1,0,0,1,1,1,1, 0,0,0,0,0,0,0,0, 1,1,0,0,1,1,0,0, 1,1,0,1,1,1,0,0, 1,1,0,0,1,1,1,0, 1,1,0,0,1,0,0,0 }; 有了這張表,算法就很簡單了,每次對一行一行的將整個圖象掃描一遍,對于每個點(不包括邊界點),計算它在表中對應的索引,若為0,則保留,否則刪除該點。如果這次掃描沒有一個點被刪除,則循環結束,剩下的點就是骨架點,如果有點被刪除,則進行新的一輪掃描,如此反復,直到沒有點被刪除為止。 實際上,該算法有一些缺陷。舉個簡單的例子,有一個黑色矩形,如圖6.23所示。 圖6.23經過細化后,我們預期的結果是一條水平直線,且位于該黑色矩形的中心。實際的結果確實是一條水平直線,但不是位于黑色矩形的中心,而是最下面的一條邊。 為什么會這樣,我們來分析一下:在從上到下,從左到右的掃描過程中,我們遇到的第一個黑點就是黑色矩形的左上角點,經查表,該點可以刪。下一個點是它右邊的點,經查表,該點也可以刪,如此下去,整個一行被刪了。每一行都是同樣的情況,所以都被刪除了。到了最后一行時,黑色矩形已經變成了一條直線,最左邊的黑點不能刪,因為它是直線的端點,它右邊的點也不能刪,因為如果刪除,直線就斷了,如此下去,直到最右邊的點,也不能刪,因為它是直線的右端點。所以最下面的一條邊保住了,但這并不是我們希望的結果。 解決的辦法是,在每一行水平掃描的過程中,先判斷每一點的左右鄰居,如果都是黑點,則該點不做處理。另外,如果某個黑點被刪除了,那么跳過它的右鄰居,處理下一個點。這樣就避免了上述的問題。| 圖6.23 黑色矩形 | 圖6.24 圖6.23細化后的結果 |
細化源碼
?
1 BOOL Thinning(HWND hWnd) 4 { 7 DWORD OffBits,BufSize; 10 LPBITMAPINFOHEADER lpImgData; 13 LPSTR lpPtr; 16 HLOCAL hTempImgData; 19 LPBITMAPINFOHEADER lpTempImgData; 22 LPSTR lpTempPtr; 25 HDC hDc; 28 HFILE hf; 31 LONG x,y; int num; 34 BOOL Finished; int nw,n,ne,w,e,sw,s,se; //為了處理的方便,仍采用256級灰度圖,不過只用調色板中0和255兩項 if( NumColors!=256){ 37 MessageBox(hWnd,"Must be a mono bitmap with grayscale palette!", "Error Message",MB_OK|MB_ICONEXCLAMATION); return FALSE; 40 } 43 OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER); //BufSize為緩沖區大小 BufSize=OffBits+bi.biHeight*LineBytes; //為新的緩沖區分配內存 if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL) 46 { 49 MessageBox(hWnd,"Error alloc memory!","Error Message", 52 MB_OK|MB_ICONEXCLAMATION); return FALSE; 55 } 58 lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData); 60 lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData); //拷貝頭信息和位圖數據 memcpy(lpTempImgData,lpImgData,BufSize); //結束標志置成假 Finished=FALSE; while(!Finished){ //還沒有結束 //結束標志置成假 Finished=TRUE; //先進行水平方向的細化 for (y=1;y<bi.biHeight-1;y++){ //注意為防止越界,y的范圍從1到高度-2 //lpPtr指向原圖數據,lpTempPtr指向新圖數據 lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes); 63 lpTempPtr=(char *)lpTempImgData+(BufSize-LineBytes-y*LineBytes); 66 x=1; //注意為防止越界,x的范圍從1到寬度-2 while(x<bi.biWidth-1){ if(*(lpPtr+x)==0){ //是黑點才做處理 w=(unsigned char)*(lpPtr+x-1); //左鄰點 e=(unsigned char)*(lpPtr+x+1); //右鄰點 if( (w==255)|| (e==255)){ //如果左右兩個鄰居中至少有一個是白點才處理 nw=(unsigned char)*(lpPtr+x+LineBytes-1); //左上鄰點 n=(unsigned char)*(lpPtr+x+LineBytes); //上鄰點 ne=(unsigned char)*(lpPtr+x+LineBytes+1); //右上鄰點 sw=(unsigned char)*(lpPtr+x-LineBytes-1); //左下鄰點 s=(unsigned char)*(lpPtr+x-LineBytes); //下鄰點 se=(unsigned char)*(lpPtr+x-LineBytes+1); //右下鄰點 //計算索引 num=nw/255+n/255*2+ne/255*4+w/255*8+e/255*16+ 69 sw/255*32+s/255*64+se/255*128; if(erasetable[num]==1){ //經查表,可以刪除 //在原圖緩沖區中將該黑點刪除 *(lpPtr+x)=(BYTE)255; //結果圖中該黑點也刪除 *(lpTempPtr+x)=(BYTE)255; 71 Finished=FALSE; //有改動,結束標志置成假 x++; //水平方向跳過一個象素 } 74 } 77 } 80 x++; //掃描下一個象素 } 83 } //再進行垂直方向的細化 for (x=1;x<bi.biWidth-1;x++){ //注意為防止越界,x的范圍從1到寬度-2 y=1; //注意為防止越界,y的范圍從1到高度-2 while(y<bi.biHeight-1){ 86 lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes); 89 lpTempPtr=(char*)lpTempImgData+ 92 (BufSize-LineBytes-y*LineBytes); if(*(lpPtr+x)==0){ //是黑點才做處理 n=(unsigned char)*(lpPtr+x+LineBytes); 95 s=(unsigned char)*(lpPtr+x-LineBytes); if( (n==255)|| (s==255)){ //如果上下兩個鄰居中至少有一個是白點才處理 nw=(unsigned char)*(lpPtr+x+LineBytes-1); 98 ne=(unsigned char)*(lpPtr+x+LineBytes+1); 101 w=(unsigned char)*(lpPtr+x-1); 104 e=(unsigned char)*(lpPtr+x+1); 107 sw=(unsigned char)*(lpPtr+x-LineBytes-1); 110 se=(unsigned char)*(lpPtr+x-LineBytes+1); //計算索引 num=nw/255+n/255*2+ne/255*4+w/255*8+e/255*16+ 113 sw/255*32+s/255*64+se/255*128; if(erasetable[num]==1){ //經查表,可以刪除 //在原圖緩沖區中將該黑點刪除 *(lpPtr+x)=(BYTE)255; //結果圖中該黑點也刪除 *(lpTempPtr+x)=(BYTE)255; 115 Finished=FALSE; //有改動,結束標志置成假 y++;//垂直方向跳過一個象素 } 118 } 121 } 124 y++; //掃描下一個象素 } 127 } 129 } if(hBitmap!=NULL) 132 DeleteObject(hBitmap); 135 hDc=GetDC(hWnd); //產生新的位圖 hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData, 138 (LONG)CBM_INIT, 141 (LPSTR)lpTempImgData+ sizeof(BITMAPINFOHEADER)+ 144 NumColors*sizeof(RGBQUAD), 147 (LPBITMAPINFO)lpTempImgData, 150 DIB_RGB_COLORS); 153 hf=_lcreat("c:\\thinning.bmp",0); 156 _lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER)); 158 _lwrite(hf,(LPSTR)lpTempImgData,BufSize); 161 _lclose(hf); //釋放內存及資源 ReleaseDC(hWnd,hDc); 164 LocalUnlock(hTempImgData); 167 LocalFree(hTempImgData); 170 GlobalUnlock(hImgData); return TRUE; 173 }?
?
?
轉載于:https://www.cnblogs.com/ranlx/p/3200152.html
總結
以上是生活随笔為你收集整理的图像的膨胀与腐蚀、细化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (转载)[MySQL技巧]INSERT
- 下一篇: javascript 对象详解