NO IMAGE

轉載自http://875880923.iteye.com/blog/1905265

 邊緣檢索與區域提取是我們的課程設計的一個課題。看到這個課題感到十分新穎,因為以前從未接觸過這方面的知識。而且由於課程作業要求的語言是c ,所以選擇了 c 的一個介面類函式庫easyX  下載可見官網 http://www.easyx.cn/ 

   我們將這個課題分為兩部分,一是邊緣檢測,一是區域提取。 
   關於邊緣檢測:影象的邊緣形成的原因是影象的灰度在某一區域的突然變化使得人眼才有了識別輪廓的功能。所以對於計算機識別邊緣我們也可以用數學的方法定量的找出影象中灰度階躍不連續或是線條不連續的地方,比如說一階導數的極值點或二階導數的零點的方法找到邊緣。(圖片摘自百度)。 


 

當我們繼續往下學時,越來越多的概念湧了出來,各種運算元、幅度、差分、閾值、卷積運算。這對我這種數學渣渣的確是一種考驗。其實我們沒必要將邊緣檢索的數學推導都看懂。我們只要瞭解它的基本原理後,在運用它的結論就足以達到我們的目的。 
我們總結了一下:什麼Prewitt運算元、Sobel運算元,都是對灰度圖片的一個變換,我們稱這個變換為卷積模板變換,模板變換後,邊緣就會被檢索出來。首先讓我們必須瞭解什麼叫做卷積模板變換。這個部落格說的很清楚,大家可以看看。 
http://www.cnblogs.com/a-toad/archive/2008/10/24/1318921.html 


所以我們根據這個運算規則,將它封裝成一個方法,程式碼及註釋如下 

C 程式碼  收藏程式碼
  1. //模板操作,包括影象邊緣的檢測,影象的平滑都會用到這個函式  
  2. /*引數的意義 
  3. * img 指向影象的指標 
  4. * tempWidth tempHeight 模板的寬高 
  5. * tempX tempY 模板的中心的x y 座標 
  6. * temp 指向模板陣列的指標 tempCoef 模板的係數 
  7. * img2 為轉化後的照片 
  8. */  
  9. bool templateChange(IMAGE *img,int tempWidth,int tempHeight,int tempX,int tempY,float *temp,float tempCoef,IMAGE *img2){  
  10.     long imgWidth = img->getwidth();  
  11.     long imgHeight = img->getheight();  
  12.     //得到原圖的內容  
  13.     DWORD *p = GetImageBuffer(img);  
  14.     (*img2).Resize(imgWidth,imgHeight);  
  15.     DWORD *p2 = GetImageBuffer(img2);  
  16.     //用於暫存模板值  
  17.     float result;  
  18.     int endResult;  
  19.     for (long i=tempY;i<imgHeight-tempHeight tempY;i ){  
  20.         for (long j=tempX;j<imgWidth-tempWidth tempX;j ){  
  21.             result=0;  
  22.             //計算模板  
  23.             for (int k=0;k<tempHeight;k ){  
  24.                 for (int l=0;l<tempWidth;l ){  
  25.                       //邏輯表示式較複雜(橫座標 i-tempy k 縱座標 j-tempx 1)  
  26.                     //得到灰度(RGB相同)  
  27.                     int z=GetRValue(p[imgWidth*(i tempY-k) j-tempX l]);  
  28.                     result  =z*temp[k*tempWidth l];  
  29.                 }  
  30.             }  
  31.             result *=tempCoef;  
  32.             endResult =abs((int)result);  
  33.             if (endResult>255){  
  34.                 endResult=255;  
  35.             }  
  36.             p2[i*imgWidth j]=RGB(endResult,endResult,endResult);  
  37.         }  
  38.     }  
  39.     return true;  
  40. }  


瞭解了這個模板運算,我們反過頭來再看各種運算元 

Sobel運算元: 

 

進行的橫豎兩個方向的模板運算後,遍歷整個圖片取相同畫素點模板運算的最大值。 
Prewitt運算元: 

 

同Sobel運算元,進行的橫豎兩個方向的模板運算後,遍歷整個圖片取相同畫素點模板運算的最大值。 

給出了演算法,寫程式就很容易了。這裡只展示prewitt運算元的程式碼: 

C 程式碼  收藏程式碼
  1. void GraphicCut(IMAGE *img){  
  2.     IMAGE img1,img2;  
  3.     float temp1[9]={1,0,-1,1,0,-1,1,0,-1};  
  4.     float tempCoef1=1;  
  5.     templateChange(img,3,3,1,1,temp1,tempCoef1,&img1);  
  6.     float temp2[9]={-1,-1,-1,0,0,0,1,1,1};  
  7.     float tempCoef2=1;  
  8.     templateChange(img,3,3,1,1,temp2,tempCoef2,&img2);  
  9.     DWORD *p = GetImageBuffer(img);  
  10.     DWORD *p1 = GetImageBuffer(&img1);  
  11.     DWORD *p2 = GetImageBuffer(&img2);  
  12.     for (long i=0;i<img->getheight();i )  
  13.         for (long j=0;j<img->getwidth();j )  
  14.             if (p1[i*img->getwidth() j]<p2[i*img->getwidth() j]){  
  15.                p[i*img->getwidth() j]=p2[i*img->getwidth() j];  
  16.             }else{  
  17.                p[i*img->getwidth() j]=p1[i*img->getwidth() j];  
  18.             }  
  19. }  


效果圖如下: 

 

 


啃完了邊緣檢索這塊硬骨頭後,我們又開始考慮區域要怎麼提取。我們最初的想法是做一個類似於搜尋的演算法,遍歷某個區域中的所有點,將這個區域中的畫素點提取出來儲存到另一幅圖片中,就完成了區域提取。 
我們最開始想到的是深度搜尋,但對於一張500*500的圖片來說,這種搜尋對時間和空間的消耗都是特別大的,搞不好還會棧溢位。所以我們馬上丟棄了這個想法。另一種搜尋方法就是廣度搜尋了,理論上是可行的,所以我們決定試試這種演算法。(其實網上有一些經過優化過的搜尋演算法,如掃描線演算法)。 
程式碼如下: 

C 程式碼  收藏程式碼
  1. void graphicsFill(IMAGE *img1,IMAGE *img2,IMAGE *img3,int mouseX,int mouseY){  
  2.     long imgWidth = img1->getwidth();  
  3.     long imgHeight = img1->getheight();  
  4.     DWORD *p1 = GetImageBuffer(img1);  
  5.     DWORD *p2 = GetImageBuffer(img2);  
  6.     DWORD *p3 = GetImageBuffer(img3);  
  7.     list<Point> stk;  
  8.     Point point;  
  9.     point.x = mouseX;  
  10.     point.y = mouseY;  
  11.     stk.push_back(point);  
  12.     int a[]={0,1,1,1,0,-1,-1,-1};  
  13.     int b[]={1,1,0,-1,-1,-1,0,1};  
  14.     while (!stk.empty()){  
  15.         Point pt;  
  16.         //獲取連結串列頭元素 ,並將其彈出  
  17.         pt=stk.front();  stk.pop_front();  
  18.         long y=pt.y;  
  19.         long x=pt.x;  
  20.         long z;  
  21.         int x1,y1;  
  22.         for (int i=0;i<8;i ){  
  23.             x1=x a[i]; y1=y b[i];  
  24.             z=abs(long(GetRValue(p1[y*imgWidth x])-GetRValue(p1[y1*imgWidth x1])));  
  25.             if (p2[y1*imgWidth x1]!=p3[y1*imgWidth x1]&&z<4){  
  26.                p3[y1*imgWidth x1]=p2[y1*imgWidth x1];  
  27.                Point pt;  
  28.                pt.x=x1; pt.y=y1;  
  29.                stk.push_back(pt);  
  30.             }  
  31.         }  
  32.     }  
  33. }  


提取效果圖片如下: 

 


 


 


整的來說,演算法的效率還算高。但的確有更好的演算法可以去解決這個問題,更好的演算法就等讀者自己去研究發現了。