
Canny算子中的非最大抑制分析
kezunhai@gmail.com
在常见的边缘检测算子或轮廓检测相关算法中,有一个非最大抑制步骤. 但是,在这些边缘检测算子中非最大抑制的应用可能会使理解有些混乱. 了解. 本文将介绍Canny算法中的非最大抑制. Canny算子中的非最大抑制是指沿梯度方向的非最大抑制.
首先,让我们看一下Canny运算符中用于非最大抑制的一段代码(可以跳过):
/*
fucntion: non-maximum suppression
input:
pMag: pointer to Magnitude,
pGradX: gradient of x-direction
pGradY: gradient of y-direction
sz: size of pMag (width = size.cx, height = size.cy)
output:
pNSRst: result of non-maximum suppression
*/
void NonMaxSuppress(int*pMag,int* pGradX,int*pGradY,SIZE sz,LPBYTE pNSRst)
{
LONG x,y;
int nPos;
// the component of the gradient
int gx,gy;
// the temp varialbe
int g1,g2,g3,g4;
double weight;
double dTemp,dTemp1,dTemp2;
//设置图像边缘为不可能的分界点
for(x=0;x<sz.cx;x++)
{
pNSRst[x] = 0;
pNSRst[(sz.cy-1)*sz.cx+x] = 0;
}
for(y=0;y<sz.cy;y++)
{
pNSRst[y*sz.cx] = 0;
pNSRst[y*sz.cx + sz.cx-1] = 0;
}
for (y=1;y<sz.cy-1;y++)
{
for (x=1;x<sz.cx-1;x++)
{
nPos=y*sz.cx+x;
// if pMag[nPos]==0, then nPos is not the edge point
if (pMag[nPos]==0)
{
pNSRst[nPos]=0;
}
else
{
// the gradient of current point
dTemp=pMag[nPos];
// x,y 方向导数
gx=pGradX[nPos];
gy=pGradY[nPos];
//如果方向导数y分量比x分量大,说明导数方向趋向于y分量
if (abs(gy)>abs(gx))
{
// calculate the factor of interplation
weight=fabs(gx)/fabs(gy);
g2 = pMag[nPos-sz.cx]; // 上一行
g4 = pMag[nPos+sz.cx]; // 下一行
//如果x,y两个方向导数的符号相同
//C 为当前像素,与g1-g4 的位置关系为:
//g1 g2
// C
// g4 g3
if(gx*gy>0)
{
g1 = pMag[nPos-sz.cx-1];
g3 = pMag[nPos+sz.cx+1];
}
//如果x,y两个方向的方向导数方向相反
//C是当前像素,与g1-g4的关系为:
// g2 g1
// C
// g3 g4
else
{
g1 = pMag[nPos-sz.cx+1];
g3 = pMag[nPos+sz.cx-1];
}
}
else
{
//插值比例
weight = fabs(gy)/fabs(gx);
g2 = pMag[nPos+1]; //后一列
g4 = pMag[nPos-1]; // 前一列
//如果x,y两个方向的方向导数符号相同
//当前像素C与 g1-g4的关系为
// g3
// g4 C g2
// g1
if(gx * gy > 0)
{
g1 = pMag[nPos+sz.cx+1];
g3 = pMag[nPos-sz.cx-1];
}
//如果x,y两个方向导数的方向相反
// C与g1-g4的关系为
// g1
// g4 C g2
// g3
else
{
g1 = pMag[nPos-sz.cx+1];
g3 = pMag[nPos+sz.cx-1];
}
}
dTemp1 = weight*g1 + (1-weight)*g2;
dTemp2 = weight*g3 + (1-weight)*g4;
//当前像素的梯度是局部的最大值
//该点可能是边界点
if(dTemp>=dTemp1 && dTemp>=dTemp2)
{
pNSRst[nPos] = 128;
}
else
{
//不可能是边界点
pNSRst[nPos] = 0;
}
}
}
}
}

在上面的代码中,有几个需要注意的IF-ELSE. 在John Canny提出的Canny运算符论文中,仅在0、90、45和135的四个梯度方向上执行非最大抑制. 根据相似度,每个像素梯度方向都被这四个方向替换. . 在这种情况下,通过非最大抑制比较的两个相邻像素为:
1)0: 左右
2)45: 右上和左下
3)90: 顶部和底部
4)135: 左上和右下

这样做的好处是简单,但是这种简化方法无法达到最佳效果,因为自然图像中的边缘渐变方向不一定沿着这四个方向. 因此,迫切需要进行插值以找到与梯度方向的两侧最匹配的像素上的像素值.
但是,由于实际数字图像中的像素是离散的二维矩阵,因此不一定要存在真实中心位置C上的梯度方向两侧的点,也不一定是子像素(subpixel)点,不存在的点和该点的梯度值必须通过在两侧插入点来获得.
对于上面的代码,如果| gy |> | gx |,表示该点的梯度方向更靠近Y轴方向,因此g2和g4在C的上方和下方,我们可以使用以下示例来说明这两种情况(相同方向和不同方向):
在上图中,C表示中心位置,斜直线表示梯度方向(非最大抑制是梯度方向上的最大值)canny边缘检测 非极大值抑制实现,左侧的对表示gy和gx方向相同,并且右侧此图片表示gy和gx的方向相反(请注意,原点位于左上角),并且权重为weight = |. gx | / | gy |,因此根据这种情况,其插值表示为:

dTemp1 = weight*g1 + (1-weight)*g2; dTemp2 = weight*g3 + (1-weight)*g4;
类似地,我们可以得到|的情况. gx |> | gy |,表明该点的梯度方向更靠近X轴方向,并且g2和g4在水平方向,我们可以使用下图来说明这种情况:
在上图中,C表示中心位置,斜直线表示梯度方向(非最大抑制是梯度方向上的最大值),左侧的对表示gy和gx方向相同,并且右侧此图片表示gy和gx的方向相反(请注意,原点位于左上角),并且权重为weight = |. gy | / | gx |,因此根据这种情况,其插值表示为:
dTemp1 = weight*g1 + (1-weight)*g2; dTemp2 = weight*g3 + (1-weight)*g4;

通过以上分析,我们可以了解Canny算子在进行非最大抑制之前的准备工作,即执行必要的插值. 插值的原因如下: ①由于在Canny算子中使用简化的方法来确定边缘方向,自然图像中的边缘梯度方向不一定遵循这四个方向,因此为了找到一个像素必须根据需要插值最匹配渐变方向两侧的像素值; ②
因为实际数字图像中的像素是离散的二维矩阵,所以在真实中心位置C上的渐变方向两侧的点可能不存在,或者没有子像素,并且该不存在的点和渐变必须通过在两侧插入点来获得该点的值.
下一个工作相对简单. 它将中心位置C处的dTemp的梯度幅度与两个插值点处的dTemp1和dTemp2梯度幅度进行比较,以确定其是否为极值. 代码如下:
if(dTemp>=dTemp1 && dTemp>=dTemp2)
{
pNSRst[nPos] = 128;
}
else
{
//不可能是边界点
pNSRst[nPos] = 0;
}
现在,介绍canny运算符的非最大抑制部分. 最后,需要注意的一点是: Canny算子中的非最大抑制与我们在诸如拐角检测等场景中提到的非最大抑制略有不同. Canny算子的非极大值抑制是沿着梯度方向进行的,即是否是梯度方向上的极点. 拐角检测等场景中的非最大值抑制是检测中心点at的值是否为某个邻域中的最大值. 如果是,请保留它,否则将其删除. 在这种情况下,非最大值抑制相对简单. 如果不清楚canny边缘检测 非极大值抑制实现,可以参考: 亚历山大
Neubeck的论文: 有效的非最大抑制.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-224801-1.html
反正中国门口天天被侦查
美国佬就跑路了