canny算子边缘提取分为四个步骤
1、去噪:
利用高斯滤波对图像卷积进行去噪处理;
2、求梯度:
采用梯度滤波模板对图像进行卷积,求取图像X方向和Y方向的梯度,以及对应的夹角;
3、非极大值抑制:
将各像素的梯度模值与梯度方向上相邻两个像素进行比较,保留梯度为极大值点的像素灰度;
算法设计
算法目标:完成像素梯度方向相邻两点的梯度比较。
方式:C点梯度一致,dTemp1和dTemp2梯度未知,采用相邻像素梯度,通过插值求得dTemp1和dTemp2的梯度,进行比较。
方法:根据C点的梯度角度确定相邻像素,通过相邻像素进行插值得到dTemp的梯度。
1、首先需要选取插值像素g1,g2,再选取插值比例K。
参数设定依据,尽量选择一致的不随条件改变的规则,简化算法。
2、插值比例K与C点斜率相关,tanU=dx/dy——K=短边/长边,tanU与K存在条件转换关系
插值比例与斜率的条件转换关系
1、以角度45°,90°,135°,180°作为边界判断条件。
<45°,tanU=dx/dy=K
45°<U<90°,tanU=dx/dy=1/K
90°<U<135°,tanU=dx/dy=-1/K
135°<U<180°,tanU=dx/dy=-K
2、以K=短边/长边
tanU=dx/dy
abs(dx)?abs(dy):比较大小
K=小/大
第二种方法不需要判断条件,这是取决于斜率=dx/dy;插值比例是短边占整边的比例(等价于占长边的比例)所以只需要在斜率中提取出短长边的比值信息即可。
3、插值规则:temp=g1*K+g2*(1-K),g1是对角像素,g2是轴向像素。
插值像素选取依据:
g1是对角像素,g2是轴向像素,g1,g2的定义源于插值条件,但对角和轴向是相对于C点的抽象的概念,当C点的斜率不相同时,对角和轴向对应不同的坐标,计算机无法自动识别,所以需要将不同情况下的坐标赋值给g1,g2。
继承长短边的判断条件:
当dx是长边,代表斜率距x轴更近,当dx,dy同号在第一三象限,g1=C(x+1,y+1),g2=C(x+1,y),反之在二四象限g1=C(x-1,y-1),g2=C(x-1,y)
当dy是长边,代表斜率距y轴更近,当dx,dy同号在第一三象限,g1=C(x+1,y+1),g2=C(x,y+1),反之在二四象限g1=C(x-1,y-1),g2=C(x,y-1)
象限问题和网上搜的程序有出入,还望大佬们帮我指出问题。
canny算子及边缘提取原理_jiachen0212的博客_canny算子阈值怎么取
边缘检测(Edge Detection)_长江之声的博客_边缘测试
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分量,即更贴近y轴
if (abs(gy)>abs(gx))
{
// calculate the factor of interplation
weight=fabs(gx)/fabs(gy);
g2 = pMag[nPos-sz.cx]; // 确定g2出现在c(中心点)的上一行
g4 = pMag[nPos+sz.cx]; // 确定g4出现在c(中心点)的下一行
//C 为当前像素,与g1-g4 的位置关系为:
//g1 g2
// C
// g4 g3
if(gx*gy>0) //如果x,y两个方向导数的符号相同
{
g1 = pMag[nPos-sz.cx-1];
g3 = pMag[nPos+sz.cx+1];
} //对的,画个图可以很好的理解。 左上角为原点,x,y导数相同即导数线跨越2 4象限 如上图1
//如果x,y两个方向的方向导数方向相反
//C是当前像素,与g1-g4的关系为:
// g2 g1
// C
// g3 g4
else //左上角为原点,x,y导数相反即导数线跨越1 3象限 如上图2
{
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;
}
}
}
}
}
4、双阈值绘制边缘:
以高阈值提取出的边缘为主边缘,在边缘残缺部分补充与主边缘相连接的低阈值边缘,最终构成完整的边缘信息。
这个先挖坑,后边再补。
5、算法设计思路总结:
首先清楚算法的目标,以及想要实现的方式。
细化方法:
将方法中的每一个对象,进行具体定位;
考虑全所有的情况,进行对象在不同条件下的行为匹配;
将对象所要实施的行为进行细化拆解,转化为电脑可理解的,可实行的基础动作;
整合简化判断条件,变量规则一致化设置,起到简化算法的效果。
如果我的博文有帮到你,请点赞给我加油呀,也谢谢在解决问题过程中参考的诸位大佬们啦~