📄 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 + -