canny算法的理解
传统边缘检测方法的主要缺点:
1、没有很好的利用梯度方向
2、最后得到的二值图像,只是简单的利用单一阈值做处理
canny算法的优势:
1、基于边缘梯度方向的非极大值抑制
2、双阈值的滞后阈值处理
canny算法的主要步骤如下:
- 对输入图像进行高斯平滑,降低错误率(主要目的是降低噪声,剔除伪边缘)
- 计算梯度幅度和方向来估计每一点处的边缘强度与方向
- 幅度和角度的理论计算方法,梯度的具体计算在上一篇博客中已经讲过,这里就不做过多的解读
- 根据梯度方向,对梯度幅值进行非极大值抑制。本质上是对Sobel、Prewitt等算子结果的进一步细化
- 用双阈值处理和连接边缘
边缘抑制算法讲解:
核心代码如下:
/* 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; } } } } }