?? segapi.cpp
字號:
* int nWidth - 圖象數據寬度
* int nHeight - 圖象數據高度
* unsigned char *pUnchRst - 經過NonmaxSuppress處理后的結果
*
* \返回值:
* 無
*
* \說明:
* 抑止梯度圖中非局部極值點的象素。
*
*************************************************************************
*/
void NonmaxSuppress(int *pnMag, int *pnGradX, int *pnGradY, int nWidth,
int nHeight, unsigned char *pUnchRst)
{
// 循環控制變量
int y ;
int x ;
int nPos;
// x方向梯度分量
int gx ;
int gy ;
// 臨時變量
int g1, g2, g3, g4 ;
double weight ;
double dTmp1 ;
double dTmp2 ;
double dTmp ;
// 設置圖象邊緣部分為不可能的邊界點
for(x=0; x<nWidth; x++)
{
pUnchRst[x] = 0 ;
pUnchRst[nHeight-1+x] = 0;
}
for(y=0; y<nHeight; y++)
{
pUnchRst[y*nWidth] = 0 ;
pUnchRst[y*nWidth + nWidth-1] = 0;
}
for(y=1; y<nHeight-1; y++)
{
for(x=1; x<nWidth-1; x++)
{
nPos = y*nWidth + x ;
// 如果當前象素的梯度幅度為0,則不是邊界點
if(pnMag[nPos] == 0 )
{
pUnchRst[nPos] = 0 ;
}
else
{
// 當前象素的梯度幅度
dTmp = pnMag[nPos] ;
// x,y方向導數
gx = pnGradX[nPos] ;
gy = pnGradY[nPos] ;
// 如果方向導數y分量比x分量大,說明導數的方向更加“趨向”于y分量。
if (abs(gy) > abs(gx))
{
// 計算插值的比例
weight = fabs(gx)/fabs(gy);
g2 = pnMag[nPos-nWidth] ;
g4 = pnMag[nPos+nWidth] ;
// 如果x,y兩個方向的方向導數的符號相同
// C是當前象素,與g1-g4的位置關系為:
// g1 g2
// C
// g4 g3
if (gx*gy > 0)
{
g1 = pnMag[nPos-nWidth-1] ;
g3 = pnMag[nPos+nWidth+1] ;
}
// 如果x,y兩個方向的方向導數的符號相反
// C是當前象素,與g1-g4的位置關系為:
// g2 g1
// C
// g3 g4
else
{
g1 = pnMag[nPos-nWidth+1] ;
g3 = pnMag[nPos+nWidth-1] ;
}
}
// 如果方向導數x分量比y分量大,說明導數的方向更加“趨向”于x分量
// 這個判斷語句包含了x分量和y分量相等的情況
else
{
// 計算插值的比例
weight = fabs(gy)/fabs(gx);
g2 = pnMag[nPos+1] ;
g4 = pnMag[nPos-1] ;
// 如果x,y兩個方向的方向導數的符號相同
// C是當前象素,與g1-g4的位置關系為:
// g3
// g4 C g2
// g1
if (gx*gy > 0)
{
g1 = pnMag[nPos+nWidth+1] ;
g3 = pnMag[nPos-nWidth-1] ;
}
// 如果x,y兩個方向的方向導數的符號相反
// C是當前象素,與g1-g4的位置關系為:
// g1
// g4 C g2
// g3
else
{
g1 = pnMag[nPos-nWidth+1] ;
g3 = pnMag[nPos+nWidth-1] ;
}
}
// 下面利用g1-g4對梯度進行插值
{
dTmp1 = weight*g1 + (1-weight)*g2 ;
dTmp2 = weight*g3 + (1-weight)*g4 ;
// 當前象素的梯度是局部的最大值
// 該點可能是個邊界點
if(dTmp>=dTmp1 && dTmp>=dTmp2)
{
pUnchRst[nPos] = 128 ;
}
else
{
// 不可能是邊界點
pUnchRst[nPos] = 0 ;
}
}
} //else
} // for
}
}
/*************************************************************************
*
* \函數名稱:
* TraceEdge()
*
* \輸入參數:
* int x - 跟蹤起點的x坐標
* int y - 跟蹤起點的y坐標
* int nLowThd - 判斷一個點是否為邊界點的低閾值
* unsigned char *pUnchEdge - 記錄邊界點的緩沖區
* int *pnMag - 梯度幅度圖
* int nWidth - 圖象數據寬度
*
* \返回值:
* 無
*
* \說明:
* 遞歸調用
* 從(x,y)坐標出發,進行邊界點的跟蹤,跟蹤只考慮pUnchEdge中沒有處理并且
* 可能是邊界點的那些象素(=128),象素值為0表明該點不可能是邊界點,象素值
* 為255表明該點已經被設置為邊界點,不必再考慮
*
*
*************************************************************************
*/
void TraceEdge (int y, int x, int nLowThd, unsigned char *pUnchEdge, int *pnMag, int nWidth)
{
// 對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(pUnchEdge[yy*nWidth+xx] == 128 && pnMag[yy*nWidth+xx]>=nLowThd)
{
// 把該點設置成為邊界點
pUnchEdge[yy*nWidth+xx] = 255 ;
// 以該點為中心進行跟蹤
TraceEdge(yy, xx, nLowThd, pUnchEdge, pnMag, nWidth);
}
}
}
/*************************************************************************
*
* \函數名稱:
* EstimateThreshold()
*
* \輸入參數:
* int *pnMag - 梯度幅度圖
* int nWidth - 圖象數據寬度
* int nHeight - 圖象數據高度
* int *pnThdHigh - 高閾值
* int *pnThdLow - 低閾值
* double dRatioLow - 低閾值和高閾值之間的比例
* double dRatioHigh - 高閾值占圖象象素總數的比例
* unsigned char *pUnchEdge - 經過non-maximum處理后的數據
*
* \返回值:
* 無
*
* \說明:
* 經過non-maximum處理后的數據pUnchEdge,統計pnMag的直方圖,確定閾值。
* 本函數中只是統計pUnchEdge中可能為邊界點的那些象素。然后利用直方圖,
* 根據dRatioHigh設置高閾值,存儲到pnThdHigh。利用dRationLow和高閾值,
* 設置低閾值,存儲到*pnThdLow。dRatioHigh是一種比例:表明梯度小于
* *pnThdHigh的象素數目占象素總數目的比例。dRationLow表明*pnThdHigh
* 和*pnThdLow的比例,這個比例在canny算法的原文里,作者給出了一個區間。
*
*************************************************************************
*/
void EstimateThreshold(int *pnMag, int nWidth, int nHeight, int *pnThdHigh,int *pnThdLow,
unsigned char * pUnchEdge, double dRatioHigh, double dRationLow)
{
// 循環控制變量
int y;
int x;
int k;
// 該數組的大小和梯度值的范圍有關,如果采用本程序的算法,那么梯度的范圍不會超過pow(2,10)
int nHist[1024] ;
// 可能的邊界數目
int nEdgeNb ;
// 最大梯度值
int nMaxMag ;
int nHighCount ;
nMaxMag = 0 ;
// 初始化
for(k=0; k<1024; k++)
{
nHist[k] = 0;
}
// 統計直方圖,然后利用直方圖計算閾值
for(y=0; y<nHeight; y++)
{
for(x=0; x<nWidth; x++)
{
// 只是統計那些可能是邊界點,并且還沒有處理過的象素
if(pUnchEdge[y*nWidth+x]==128)
{
nHist[ pnMag[y*nWidth+x] ]++;
}
}
}
nEdgeNb = nHist[0] ;
nMaxMag = 0 ;
// 統計經過“非最大值抑止(non-maximum suppression)”后有多少象素
for(k=1; k<1024; k++)
{
if(nHist[k] != 0)
{
// 最大梯度值
nMaxMag = k;
}
// 梯度為0的點是不可能為邊界點的
// 經過non-maximum suppression后有多少象素
nEdgeNb += nHist[k];
}
// 梯度比高閾值*pnThdHigh小的象素點總數目
nHighCount = (int)(dRatioHigh * nEdgeNb +0.5);
k = 1;
nEdgeNb = nHist[1];
// 計算高閾值
while( (k<(nMaxMag-1)) && (nEdgeNb < nHighCount) )
{
k++;
nEdgeNb += nHist[k];
}
// 設置高閾值
*pnThdHigh = k ;
// 設置低閾值
*pnThdLow = (int)((*pnThdHigh) * dRationLow+ 0.5);
}
/*************************************************************************
*
* \函數名稱:
* Hysteresis()
*
* \輸入參數:
* int *pnMag - 梯度幅度圖
* int nWidth - 圖象數據寬度
* int nHeight - 圖象數據高度
* double dRatioLow - 低閾值和高閾值之間的比例
* double dRatioHigh - 高閾值占圖象象素總數的比例
* unsigned char *pUnchEdge - 記錄邊界點的緩沖區
*
* \返回值:
* 無
*
* \說明:
* 本函數實現類似“磁滯現象”的一個功能,也就是,先調用EstimateThreshold
* 函數對經過non-maximum處理后的數據pUnchSpr估計一個高閾值,然后判斷
* pUnchSpr中可能的邊界象素(=128)的梯度是不是大于高閾值nThdHigh,如果比
* 該閾值大,該點將作為一個邊界的起點,調用TraceEdge函數,把對應該邊界
* 的所有象素找出來。最后,當整個搜索完畢時,如果還有象素沒有被標志成
* 邊界點,那么就一定不是邊界點。
*
*************************************************************************
*/
void Hysteresis(int *pnMag, int nWidth, int nHeight, double dRatioLow,
double dRatioHigh, unsigned char *pUnchEdge)
{
// 循環控制變量
int y;
int x;
int nThdHigh ;
int nThdLow ;
int nPos;
// 估計TraceEdge需要的低閾值,以及Hysteresis函數使用的高閾值
EstimateThreshold(pnMag, nWidth, nHeight, &nThdHigh,
&nThdLow, pUnchEdge,dRatioHigh, dRatioLow);
// 這個循環用來尋找大于nThdHigh的點,這些點被用來當作邊界點,然后用
// TraceEdge函數來跟蹤該點對應的邊界
for(y=0; y<nHeight; y++)
{
for(x=0; x<nWidth; x++)
{
nPos = y*nWidth + x ;
// 如果該象素是可能的邊界點,并且梯度大于高閾值,該象素作為
// 一個邊界的起點
if((pUnchEdge[nPos] == 128) && (pnMag[nPos] >= nThdHigh))
{
// 設置該點為邊界點
pUnchEdge[nPos] = 255;
TraceEdge(y, x, nThdLow, pUnchEdge, pnMag, nWidth);
}
}
}
// 那些還沒有被設置為邊界點的象素已經不可能成為邊界點
for(y=0; y<nHeight; y++)
{
for(x=0; x<nWidth; x++)
{
nPos = y*nWidth + x ;
if(pUnchEdge[nPos] != 255)
{
// 設置為非邊界點
pUnchEdge[nPos] = 0 ;
}
}
}
}
/*************************************************************************
*
* \函數名稱:
* Canny()
*
* \輸入參數:
* unsigned char *pUnchImage- 圖象數據
* int nWidth - 圖象數據寬度
* int nHeight - 圖象數據高度
* double sigma - 高斯濾波的標準方差
* double dRatioLow - 低閾值和高閾值之間的比例
* double dRatioHigh - 高閾值占圖象象素總數的比例
* unsigned char *pUnchEdge - canny算子計算后的分割圖
*
* \返回值:
* 無
*
* \說明:
* canny分割算子,計算的結果保存在pUnchEdge中,邏輯1(255)表示該點為
* 邊界點,邏輯0(0)表示該點為非邊界點。該函數的參數sigma,dRatioLow
* dRatioHigh,是需要指定的。這些參數會影響分割后邊界點數目的多少
*************************************************************************
*/
void Canny(unsigned char *pUnchImage, int nWidth, int nHeight, double sigma,
double dRatioLow, double dRatioHigh, unsigned char *pUnchEdge)
{
// 經過高斯濾波后的圖象數據
unsigned char * pUnchSmooth ;
// 指向x方向導數的指針
int * pnGradX ;
// 指向y方向導數的指針
int * pnGradY ;
// 梯度的幅度
int * pnGradMag ;
pUnchSmooth = new unsigned char[nWidth*nHeight] ;
pnGradX = new int [nWidth*nHeight] ;
pnGradY = new int [nWidth*nHeight] ;
pnGradMag = new int [nWidth*nHeight] ;
// 對原圖象進行濾波
GaussianSmooth(pUnchImage, nWidth, nHeight, sigma, pUnchSmooth) ;
// 計算方向導數
DirGrad(pUnchSmooth, nWidth, nHeight, pnGradX, pnGradY) ;
// 計算梯度的幅度
GradMagnitude(pnGradX, pnGradY, nWidth, nHeight, pnGradMag) ;
// 應用non-maximum 抑制
NonmaxSuppress(pnGradMag, pnGradX, pnGradY, nWidth, nHeight, pUnchEdge) ;
// 應用Hysteresis,找到所有的邊界
Hysteresis(pnGradMag, nWidth, nHeight, dRatioLow, dRatioHigh, pUnchEdge);
// 釋放內存
delete pnGradX ;
pnGradX = NULL ;
delete pnGradY ;
pnGradY = NULL ;
delete pnGradMag ;
pnGradMag = NULL ;
delete pUnchSmooth ;
pUnchSmooth = NULL ;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -