http://blog.csai.cn/user1/
不同圖像灰度不同,邊界處一般會有明顯的邊緣,
在實際的圖像分割中,往往只用到一階和二階導數,雖然,原理上,
各種算子的存在就是對這種導數分割原理進行的實例化計算,
Roberts算子:邊緣定位准,但是對噪聲敏感。
Prewitt算子:對噪聲有抑制作用,
Sobel算子:Sobel算子和Prewitt算子都是加權平
Isotropic Sobel算子:加權平均算子,權值反比於鄰點與中心點的距離,
在邊沿檢測中,常用的一種模板是Sobel 算子。Sobel
由於Sobel算子是濾波算子的形式,用於提取邊緣,
上面的算子是利用一階導數的信息,屬於梯度算子范疇。
Laplacian算子:這是二階微分算子。其具有各向同性,
Laplacian算子一般不以其原始形式用於邊緣檢測,
微分算子在圖像處理中扮演重要的角色,其算法實現簡單,
1.Sobel
其主要用於邊緣檢測,在技術上它是以離散型的差分算子,
2.Robert算子
根據任一相互垂直方向上的差分都用來估計梯度,
3.Prewitt算子
該算子與Sobel算子類似,只是權值有所變化,
4.Laplacian算子
拉普拉斯算子是一種二階微分算子,
5.Canny算子
該算子功能比前面幾種都要好,但是它實現起來較為麻煩,
下面算法是基於的算法不可能直接運行,
該算子具體實現方法:
// anny.cpp: implementation of the Canny class.
//
//
#include "anny.h"
#include "math.h"
//#include "algorithms.h"
//#include "algorithm.h"
#include "stdlib.h"
//#include "maths.h"
//using namespace std;
//
// Construction/Destruction
//
Canny::Canny(int PicHeight,int PicWidth,double ** PicData,double PicSigma,double PicRatioLow,double PicRatioHigh)
{
iHeight=PicHeight;
iWidth=PicWidth;
iData=PicData;
sigma=PicSigma;
dRatioLow=PicRatioLow;
dRatioHigh=PicRatioHigh;
}
Canny::~Canny()
{
}
void Canny::CannyArith(int **iEdgePoint)
{
int i;
int **iGradX ; // 指向x方向導數的指針
int **iGradY ; // 指向y方向導數的指針
int **iExtent ; // 梯度的幅度
iGradX=new int *[iHeight];
for(i=0;i<iHeight;i++)
iGradX[i]=new int[iWidth];
iGradY=new int *[iHeight];
for(i=0;i<iHeight;i++)
iGradY[i]=new int[iWidth];
iExtent=new int *[iHeight];
for(i=0;i<iHeight;i++)
iExtent[i]=new int[iWidth];
// 對原圖象進行濾波
GaussionSmooth();
// 計算X,Y方向上的方向導數
DirGrad(iGradX,iGradY);
// 計算梯度的幅度
GradExtent(iGradX,iGradY,
// 應用non-maximum 抑制
NonMaxSuppress(iExtent,iGradX,
// 應用Hysteresis,找到所有的邊界
Hysteresis(iExtent,
// 釋放內存
for(i=0;i<iHeight;i++)
delete []*(iGradX+i);
delete iGradX;
for(i=0;i<iHeight;i++)
delete []*(iGradY+i);
delete iGradY;
for(i=0;i<iHeight;i++)
delete []*(iExtent+i);
delete iExtent;
}
void Canny::GaussionSmooth()
{
int i,j,k;
int iWindowSize;
int iHalfLen;
double *pdKernel;
double dDotMul;
double dWeightSum;
double **dTemp;
//開辟空間
dTemp=new double *[iHeight];
for(i=0;i<iHeight;i++)
dTemp[i]=new double[iWidth];
//獲得模板長度和模板的各個權值
MakeGauss(&pdKernel,&
//得到模板的一半長度
iHalfLen=iWindowSize/2;
//對圖像對水方向根據模板進行平滑
for(i=0;i<iHeight;i++)
{
for(j=0;j<iWidth;j++)
{
dDotMul=0;
dWeightSum=0;
for(k=(-iHalfLen);k<=
{
if((k+j>=0)&&(k+j<iWidth))
{
dDotMul+=iData[i][j+k]*
dWeightSum+=pdKernel[k+
}
}
dTemp[i][j]=dDotMul/
}
}
//對圖像垂直方向上根據模板的轉置進行平滑(
for(i=0;i<iWidth;i++)
{
for(j=0;j<iHeight;j++)
{
dDotMul=0;
dWeightSum=0;
for(k=(-iHalfLen);k<=
{
if((k+j>=0)&&(k+j<iHeight)
{
dDotMul+=dTemp[j+k][i]*
dWeightSum+=pdKernel[k+
}
}
iData[j][i]=dDotMul/
}
}
//空間釋放
delete []pdKernel;
pdKernel=NULL;
for(i=0;i<iHeight;i++)
delete []*(dTemp+i);
delete dTemp;
}
void Canny::MakeGauss(double **pdKernel,int *iWindowSize)
{
int i; //循環變量
int nCenter; //確定高斯模板的一半長度
double dDistance; //一維高斯模板各點離中心點的距離
double PI=3.1415926; //圓周率
double dValue; //中間變量,記錄高斯模板各點的權值(未經歸一化)
double dSum=0; //中間變量,記錄高斯模板各點權值的總和
*iWindowSize=int(1+2*int(3*
nCenter=(*iWindowSize)/2;
*pdKernel=new double[*iWindowSize];//
//利用高斯分布函數(正太分布)確定各點的權值,
//相似,離中心點越遠,對中心點的影響就越小。
for(i=0;i<(*iWindowSize);i++)
{
dDistance=double(i-nCenter);
//高斯分布函數求值
dValue=exp((-1/2)*dDistance*
(*pdKernel)[i]=dValue;
dSum+=dValue;
}
//歸一化(因為要不改變原圖像的灰度區域,
for(i=0;i<(*iWindowSize);i++)
{
(*pdKernel)[i] /= dSum;
}
}
void Canny::DirGrad(int **iGradX,int **iGradY)
{
int i,j,temp1,temp2;
//水平方向的方向導數(
for(i=0;i<iHeight;i++)
{
for(j=0;j<iWidth;j++)
{
if(iWidth-1<j+1)
temp1=iWidth-1;
else
temp1=j+1;
if(0<j-1)
temp2=j-1;
else
temp2=0;
iGradX[i][j]=int(iData[i][
}
}
//垂直方向的方向導數
for(i=0;i<iWidth;i++)
{
for(j=0;j<iHeight;j++)
{
if(iHeight-1<j+1)
temp1=iHeight-1;
else
temp1=j+1;
if(0<j-1)
temp2=j-1;
else
temp2=0;
iGradY[j][i]=int(iData[
}
}
}
void Canny::GradExtent(int **iGradX,int **iGradY,int **iExtent)
{
int i,j;
double iTemp1,iTemp2;
for(i=0;i<iHeight;i++)
{
for(j=0;j<iWidth;j++)
{
iTemp1=iGradX[i][j]*iGradX[
iTemp2=iGradY[i][j]*iGradY[
iExtent[i][j]=int(sqrt(
}
}
}
void Canny::NonMaxSuppress(int **iExtent,int **iGradX,int **iGradY,int **dUnchRst)
{
int i,j;
int gx,gy; //記錄像素點X,Y 方向的方向導數值
int g1,g2,g3,g4; //各個領域的梯度值
double weight; //比重
double dTemp1,dTemp2,dTemp; //中間變量
//處理邊緣值(邊緣點不可能是邊界點
for(i=0;i<iHeight;i++)
{
dUnchRst[i][0]=0;
dUnchRst[i][iWidth-1]=0;
}
for(j=0;j<iWidth;j++)
{
dUnchRst[0][j]=0;
dUnchRst[iHeight-1][j]=0;
}
//標記有可能是邊界點的像素點
for(i=1;i<iHeight-1;i++)
{
for(j=1;j<iWidth-1;j++)
{
//梯度值是0的像素點不可能是邊界點
if(iExtent[i][j]==0)
dUnchRst[i][j]=0;
else
{
dTemp=iExtent[i][j];
gx=iGradX[i][j];
gy=iGradY[i][j];
//
if(abs(gy)>abs(gx))
{
weight=double(abs(gx)/abs(gy))
g2=iExtent[i-1][j];
g4=iExtent[i+1][j];
if(gx*gy>0)
{
g1=iExtent[i-1][j-1];
g3=iExtent[i+1][j+1];
}
else
{
g1=iExtent[i-1][j+1];
g3=iExtent[i+1][j-1];
}
}
else
{
weight=double(abs(gy)/
g2=iExtent[i][j+1];
g4=iExtent[i][j-1];
if(gx*gy>0)
{
g1=iExtent[i+1][j+1];
g3=iExtent[i-1][j-1];
}
else
{
g1=iExtent[i-1][j+1];
g3=iExtent[i+1][j-1];
}
}
dTemp1=weight*g1+(1-
dTemp2=weight*g3+(1-
//當大於的時候就有可能是邊界點
if(dTemp>=dTemp1&&dTemp>=
{
dUnchRst[i][j] = 128 ;
}
else
{
dUnchRst[i][j]=0 ;
}
}
}
}
}
void Canny::Hysteresis(int **iExtent,int **iEdgePoint)
{
int i,j;
int iThreHigh;
int iThreLow;
SetThreshold(iExtent,&
for(i=0;i<iHeight;i++)
{
for(j=0;j<iWidth;j++)
{
if((iEdgePoint[i][j]==128)&
{
iEdgePoint[i][j]=255;
TraceEdge(i,j,iThreLow,
}
}
}
// 那些還沒有被設置為邊界點的象素已經不可能成為邊界點
for(i=0;i<iHeight;i++)
{
for(j=0;j<iWidth;j++)
{
if(iEdgePoint[i][j]!=255)
{
// 設置為非邊界點
iEdgePoint[i][j] = 0 ;
}
}
}
}
void Canny::SetThreshold(int **iExtent,int *iThreHigh,int *iThreLow,int **iEdgePoint)
{
int i,j,k;
int GradHist[1024];
int iEdgeNum;
int iGradMax=0;
int iHighCount;
//初始化
for(i=0;i<1024;i++)
GradHist[i]=0;
//梯度直方圖統計
for(i=0;i<iHeight;i++)
{
for(j=0;j<iWidth;j++)
{
if(iEdgePoint[i][j]==128)
{
GradHist[iExtent[i][j]]++;
}
}
}
iEdgeNum=0;
//找出最大梯度和統計邊界點的個數
for(i=0;i<1024;i++)
{
if(GradHist[i]!=0)
iGradMax=i;
iEdgeNum+=GradHist[i];
}
//獲得小於高閾值的個數
iHighCount=int(iEdgeNum*
k=1;
iEdgeNum=GradHist[1];
//求出高閾值
while((k<=(iGradMax-1))&&(
{
k++;
iEdgeNum+=GradHist[k];
}
*iThreHigh=k;
//根據高閾值和比例關系求得低閾值
*iThreLow=int((*iThreHigh)*
}
void Canny::TraceEdge(int y,int x,int iThreLow,int **iEdgePoint,int **iExtent)
{
// 對8鄰域象素進行查詢
int xNb[8] = {1, 1, 0,-1,-1,-1, 0, 1} ;
int yNb[8] = {0, 1, 1, 1,0 ,-1,-1,-1} ;
int yy ;
int xx ;
int k ;
for(k=0;k<8;k++)
{
yy=y+yNb[k] ;
xx=x+xNb[k] ;
// 如果該象素為可能的邊界點,又沒有處理過, 並且梯度大於閾值
if(iEdgePoint[yy][xx]==128&&
{
// 把該點設置成為邊界點
iEdgePoint[yy][xx]=255 ;
// 以該點為中心進行跟蹤
//TraceEdge(yy,xx,iThreLow,
}
}
}
http://blog.sina.com.cn/s/