1. 程式人生 > >影象分割演算法簡介及常用邊緣檢測演算法

影象分割演算法簡介及常用邊緣檢測演算法

                                                                              影象分割演算法概述及常用邊緣檢測演算法

一、概述  用計算機進行數字影象處理的目的有兩個,一是產生更適合人類視覺觀察和識別的影象,二是希望計算機能夠自動進行識別和理解影象。無論是為了何種目的,影象處理的關鍵一步是對包含有大量各式各樣景物資訊的影象進行分解。分解的最終結果就是影象被分成一些具有各種特徵的最小成分,這些成分就稱為影象的基元。產生這些基元的過程就是影象分割的過程。影象分割作為影象處理領域中極為重要的內容之一,是實現影象分析與理解的基礎。從概念上來說,所謂影象分割就是按照一定的原則將一幅影象或景物分為若干個部分或子集的過程。目前影象處理系統中我們只能得到二維影象資訊,因此只能進行影象分割而不是景物分割(景物是三維資訊);影象分割也可以理解為將影象中有意義的特徵區域或者需要應用的特徵區域提取出來,這些特徵區域可以是畫素的灰度值、物體輪廓曲線、紋理特性等,也可以是空間頻譜或直方圖特徵等。在影象中用來表示某一物體的區域,其特徵都是相近或相同的,但是不同物體的區域之間,特徵就會急劇變化。目前已經提出的影象分割方法很多,從分割依據的角度來看,影象的分割方法可以分為相似性分割和非連續性分割。相似性分割就是將具有同一灰度級或相同組織結構的畫素聚集在一起,形成影象的不同區域;非連續性分割就是首先檢測區域性不連續性,然後將它們連線在一起形成邊界,這些邊界將影象分成不同的區域。由於不同種類的影象,不同的應用場合,需要提取的影象特徵是不同的,當然對應的影象特徵提取方法也就不同,因此並不存在一種普遍適應的最優方法。

  影象分割方法又可分為結構分割方法和非結構分割方法兩大類。結構分割方法是根據影象的區域性區域象素的特徵來實現影象分割,如閾值分割、區域生長、邊緣檢測、紋理分析等,這些方法假定事先知道這些區域的特性,或者在處理過程中能夠求得這些特性,從而能夠尋找各種形態或研究各畫素群。非結構分割法包括統計模式識別、神經網路方法或其它利用景物的先驗知識實現的方法等等。這些內容由於專業性很強,就不在本文討論內容中了,有興趣的讀者可以參考影象處理的專業書籍。總之,影象分割可以分為影象的邊緣提取和影象的二值化二部分內容,下面我們首先來討論一下各種常用的影象邊緣提取的方法。二、影象邊緣檢測  數字影象的邊緣檢測是影象分割、目標區域的識別、區域形狀提取等影象分析領域十分重要的基礎,是影象識別中提取影象特徵的一個重要屬性,影象理解和分析的第一步往往就是邊緣檢測,目前它以成為機器視覺研究領域最活躍的課題之一,在工程應用中佔有十分重要的地位。物體的邊緣是以影象的區域性特徵不連續的形式出現的,也就是指影象區域性亮度變化最顯著的部分,例如灰度值的突變、顏色的突變、紋理結構的突變等,同時物體的邊緣也是不同區域的分界處。影象邊緣有方向和幅度兩個特性,通常沿邊緣的走向灰度變化平緩,垂直於邊緣走向的畫素灰度變換劇烈,根據灰度變化的特點,可分為階躍型、房頂型和凸緣型,

  由於邊緣是影象上灰度變化最劇烈的地方,傳統的邊緣檢測就是利用了這個特點,對影象各個畫素點進行微分或求二階微分來確定邊緣畫素點。一階微分影象的峰值處對應著影象的邊緣點;二階微分影象的過零點處對應著影象的邊緣點。根據數字影象的特點,處理影象過程中常採用差分來代替導數運算,對於影象的簡單一階導數運算,影象邊緣提取的常用梯度運算元有Robert運算元、Sobel運算元、Prewitt運算元、Krisch運算元等。下面以邊緣檢測Sobel運算元為例來講述數字影象處理中邊緣檢測的實現:對於數字影象,可以用一階差分代替一階微分;

 △xf(x,y)=f(x,y)-f(x-1,y);

 △yf(x,y)=f(x,y)-f(x,y-1);

  求梯度時對於平方和運算及開方運算,可以用兩個分量的絕對值之和表示,Sobel梯度運算元是先做成加權平均,再微分,然後求梯度,即: 

△xf(x,y)= f(x-1,y+1) + 2f(x,y+1) + f(x+1,y+1)-f(x-1,y-1) - 2f(x,y-1) - f(x+1,y-1);    △yf(x,y)= f(x-1,y-1) + 2f(x-1,y) +f(x-1,y+1)- f(x+1,y-1) - 2f(x+1,y) - f(x+1,y+1);    G[f(x,y)]=|△xf(x,y)|+|△yf(x,y)|;  上述各式中的畫素之間的關係見下圖

      圖一:Sober運算元中各個畫素點的關係圖

常用的檢測實現公式列出如下(檢測模版可以從相應的演算法很容易的得到):  Roberts運算元:G[i,i]=|f[i,j]-f[i+1,j+1]|+|f[i+1,j]-f[i,j+1]|;  Sobe運算元:G[i,i]=|f[i-1,j+1]+2f[i,j+1]+f[i+1,j+1]-f[i-1,j-1]-2f[i,j-1]-f[i+1,j-1]|           +|f[i-1,j-1]+2f[i-1,j]+f[i-1,j+1]-f[i+1,j-1]-2f[i+1,j]-f[i+1,j+1]|;  其中G[i,j]表示處理後(i,j)點的灰度值,f[i,j]表示處理前該點的灰度值。

在檢視類中定義了響應選單命令的邊緣檢測Sobel運算元實現灰度影象邊緣檢測的函式:

void CDibView::OnMENUSobel()
{
 CClientDC pDC(this);
HDC hDC=pDC.GetSafeHdc();//
獲取當前裝置上下文的控制代碼;
SetStretchBltMode(hDC,COLORONCOLOR);
HANDLE data1handle;
 LPDIBHDRTMAPINFOHEADER lpDIBHdr; CDibDoc *pDoc=GetDocument(); HDIB hdib; unsigned char *lpDIBBits; unsigned char *data; hdib=pDoc->m_hDIB;//得到圖象資料; lpDIBHdr=(LPDIBHDRTMAPINFOHEADER)GlobalLock((HGLOBAL)hdib); lpDIBBits= lpDIBHdr +* (LPDWORD)lpDIBHdr +256*sizeof(RGBQUAD); //得到指向點陣圖畫素值的指標;
data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpDIBHdr->biWidth*8)*lpDIBHdr->biHeight);
 //申請存放處理後的畫素值的緩衝區
data=(unsigned char*)GlobalLock((HGLOBAL)data1handle);
 AfxGetApp()->BeginWaitCursor(); int i,j,buf,buf1,buf2; for( j=0; jbiHeight; j++)//以下迴圈求(x,y)位置的灰度值  for( i=0; ibiWidth; i++) {   if(((i-1)>=0)&&((i+1)biWidth)&&((j-1)>=0)&&((j+1)biHeight))   {//對於影象四周邊界處的向素點不處理   buf1=(int)*(lpDIBBits+(i+1)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j-1))     +2*(int)*(lpDIBBits+(i+1)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j))     +(int)(int)*(lpDIBBits+(i+1)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j+1));    buf1=buf1-(int)(int)*(lpDIBBits+(i-1)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j-1))     -2*(int)(int)*(lpDIBBits+(i-1)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j))     -(int)(int)*(lpDIBBits+(i-1)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j+1));    //以上是對影象進行水平(x)方向的加權微分    buf2=(int)(int)*(lpDIBBits+(i-1)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j+1))     +2*(int)(int)*(lpDIBBits+(i)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j+1))     +(int)(int)*(lpDIBBits+(i+1)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j+1));     buf2=buf2-(int)(int)*(lpDIBBits+(i-1)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j-1))     -2*(int)(int)*(lpDIBBits+(i)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j-1))     -(int)(int)*(lpDIBBits+(i+1)*WIDTHBYTES(lpDIBHdr->biWidth*8)+(j-1));    //以上是對影象進行垂直(y)方向加權微分    buf=abs(buf1)+abs(buf2);//求梯度   if(buf>255) buf=255;     if(buf<0)buf=0;   *(data+i*WIDTHBYTES(lpDIBHdr->biWidth*8)+j)=(BYTE)buf;   }   else*(data+i*lpDIBHdr->biWidth+j)=(BYTE)0;  } for( j=0; jbiHeight; j++) for( i=0; ibiWidth; i++) *(lpDIBBits+i*WIDTHBYTES(lpDIBHdr->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpDIBHdr->biWidth*8)+j);      //處理後的資料寫回原緩衝區
StretchDIBits(hDC,0,0,lpDIBHdr->biWidth,lpDIBHdr->biHeight,0,0,
lpDIBHdr->biWidth,lpDIBHdr->biHeight,
lpDIBBits,(LPDIBHDRTMAPINFO)lpDIBHdr,
DIB_RGB_COLORS,
SRCCOPY);

對於邊緣檢測,只要知道有若干個檢測模板(既邊緣檢測矩陣),就可以直接實現檢測功能了,常用的檢測實現公式列出如下(檢測模版可以從相應的演算法很容易的得到):  Roberts運算元:G[i,i]=|f[i,j]-f[i+1,j+1]|+|f[i+1,j]-f[i,j+1]|;

  Sobe運算元:G[i,i]=|f[i-1,j+1]+2f[i,j+1]+f[i+1,j+1]-f[i-1,j-1]-2f[i,j-1]-f[i+1,j-1]|+|f[i-1,j-1]+2f[i-1,j]+f[i-1,j+1]-f[i+1,j-1]-2f[i+1,j]-f[i+1,j+1]|;

  其中G[i,j]表示處理後(i,j)點的灰度值,f[i,j]表示處理前該點的灰度值。

Kirsch運算元實現起來相對來說稍微麻煩一些,它採用8個模板對影象上的每一個畫素點進行卷積求導數,這8個模板代表8個方向,對影象上的8個特定邊緣方向作出最大響應,運算中取最大值作為影象的邊緣輸出(上述演算法中用到的8個模板在下面的實現程式碼中給出)。為了便於讀者理解該演算法的實現,這裡我們給出實現該演算法的函式程式碼,讀者可以稍加改動應用到自己的專案中去。

BOOL Kirsch(BYTE *pData,int Width,int Height)
{//
定義實現Kirsch演算法的8個模板;
int i,j,s,t,k,max,sum[8];
statica[3][3]={{+5,+5,+5},{-3,0,-3},{-3,-3,-3}};
statica1[3][3]={{-3,+5,+5},{-3,0,+5},{-3,-3,-3}};
statica2[3][3]={{-3,-3,+5},{-3,0,+5},{-3,-3,+5}};
statica3[3][3]={{-3,-3,-3},{-3,0,+5},{-3,+5,+5}};
statica4[3][3]={{-3,-3,-3},{-3,0,-3},{+5,+5,+5}};
statica5[3][3]={{-3,-3,-3},{+5,0,-3},{+5,+5,-3}};
statica6[3][3]={{+5,-3,-3},{+5,0,-3},{+5,-3,-3}};
statica7[3][3]={{+5,+5,-3},{+5,0,-3},{-3,-3,-3}};
BYTE *pData1;
if(pData==NULL)
{
AfxMessageBox("
影象資料為空,請讀取影象資料!");
return FALSE;
}
pData1=(BYTE*)new char[Width*Height];
if(pData1==NULL)
{
AfxMessageBox("
影象緩衝資料區申請失敗,請重新申請影象資料緩衝區!");
return FALSE ;
}
memcpy(pData1,pData, Width*8*Height);
//kirsch
運算元處理,對每一畫素點求取八個方向的導數;;
for(i=1;i<Height-1;i++)
for(j=1;j<Width-1;j++)

sum[1]=sum[2]=sum[3]=sum[4]=sum[5]=sum[6]=sum[7]=sum[8]=0;
for(t=-1;t<2;t++)
{
for(s=-1;s<2;s++)
{sum[1]+=*(pData+WIDTHBYTES(Width*8)*(i+t)+j+s)*a[1+t][1+s];
sum[2]+=*(pData+WIDTHBYTES(Width*8)*(i+t)+j+s)*a1[1+t][1+s];sum[3]+=*(pData+WIDTHBYTES(Width*8)*(i+t)+j+s)*a2[1+t][1+s];sum[4]+=*(pData+WIDTHBYTES(Width*8)*(i+t)+j+s)*a3[1+t][1+s];sum[5]+=*(pData+WIDTHBYTES(Width*8)*(i+t)+j+s)*a4[1+t][1+s];sum[6]+=*(pData+WIDTHBYTES(Width*8)*(i+t)+j+s)*a5[1+t][1+s]; sum[7]+=*(pData+WIDTHBYTES(Width*8)*(i+t)+j+s)*a6[1+t][1+s];sum[8]+=*(pData+WIDTHBYTES(Width*8)*(i+t)+j+s)*a7[1+t][1+s];
}
}
//取最大方向的導數;
for(k=0;k<8;k++)

max=0; 
if(max<sum[k])
max=sum[k];
}
if(max<0)
max=0;
if(max>255)
max=255;
*(pData1+ Width*8*i+j)=max;
}
memcpy(pData,pData1, Width*8*Height);
delete pData1;
return TRUE;

  另外還有一種稱為拉普拉斯的運算元是不依賴於邊緣方向的二階微分運算元,對於數字影象來說拉普拉斯運算元可以簡單表示為:G[I,j]=|f[i+1,j]+f[i-1,j]+f(i,j+1)+f[i,j-1]-4f[i,j]|;它是一個標量而不是向量,具有旋轉不變,既各向同性的性質,它常常用在影象處理的過程中。梯度運算元和拉普拉斯運算元對噪聲敏感,它們都使噪聲成份加強,因此在處理含有較大噪聲的影象時,常常先對影象進行平滑操作,然後再進行二階微分,這就產生了所謂的LOG(又稱為Marr方法)邊緣檢測方法。它先用高斯函式對影象進行平滑,然後再用拉普拉斯運算元進行運算。總的來說,傳統的邊緣檢測運算元的噪聲平滑能力和邊緣定位能力是矛盾的,為了克服這個不足,正確地得到影象的邊緣資訊,人們提出了很多方法,如多尺度空間濾波、Facet模型檢測邊緣、模板匹配、Hough變換、小波變換、人工神經網路、模糊推理等演算法。但這些方法絕大多數沒有經典的演算法精簡,要麼難以獲得合理的計算複雜度,要麼需要人為的調節各種引數,有時甚至難以實時執行。因為傳統邊緣的定義為影象中灰度的突變,所以這樣定義邊緣既失去了邊緣的部分資訊,又把噪聲的影響包含在了邊緣中。其實,邊緣往往具有以下特徵:

1)灰度突變;

2)是不同區域的邊界;

3)具有方向性;

  根據邊緣的這三個特徵,可以判斷所關心的區域其特徵是否存在差異來判斷是否存在邊緣的可能性。如果特徵沒有差異,則認為是平滑區;如果特徵有差異,則判斷為邊緣點。