最优的阶梯型边缘检测算法(canny边缘检测)
     1.Canny边缘检测基本原理
     (1)图象边缘检测必须满足两个条件:一能有效地抑制噪声;二必须尽量精确确定边缘的位置。
     (2)根据对信噪比与定位乘积进行测度,得到最优化逼近算子。这就是Canny边缘检测算子。
     (3)类似与Marr(LoG)边缘检测方法,也属于先平滑后求导数的方法。
     2.Canny边缘检测算法:
     step1:用高斯滤波器平滑图象;
     step2:用一阶偏导的有限差分来计算梯度的幅值和方向;
     step3:对梯度幅值进行非极大值抑制;
     step4:用双阈值算法检测和连接边缘。
     step1:高斯平滑函数
 
 
      step3:非极大值抑制
      仅仅得到全局的梯度并不足以确定边缘,因此为确定边缘,必须保留局部梯度最大的点,而抑制非极大值。(non-maxima suppression,NMS)
解决方法:利用梯度的方向。
图2非极大值抑制
四个扇区的标号为0到3,对应3*3邻域的四种可能组合。
      在每一点上,邻域的中心象素M与沿着梯度线的两个象素相比。如果M的梯度值不比沿梯度线的两个相邻象素梯度值大,则令M=0。
即: 
       step4:阈值化
       减少假边缘段数量的典型方法是对N[i,j]使用一个阈值。将低于阈值的所有值赋零值。但问题是如何选取阈值?
       解决方法:双阈值算法。双阈值算法对非极大值抑制图象作用两个阈值τ1和τ2,且2τ1≈τ2,从而可以得到两个阈值边缘图象N1[i,j]和N2[i,j]。由于N2[i,j]使用高阈值得到,因而含有很少的假边缘,但有间断(不闭合)。双阈值法要在N2[i,j]中把边缘连接成轮廓,当到达轮廓的端点时,该算法就在N1[i,j]的8邻点位置寻找可以连接到轮廓上的边缘,这样,算法不断地在N1[i,j]中收集边缘,直到将N2[i,j]连接起来为止。
 

//非最大抑制
void NonmaxSuppress(int *pMag, int *pGradX, int *pGradY, SIZE sz, LPBYTE pNSRst)
{
 
LONG y,x;
 
int nPos;

 //梯度分量
 
int gx;
 
int gy;

 //中间变量
 
int g1,g2,g3,g4;
 
double weight;
 
double dTmp,dTmp1,dTmp2;

 //设置图像边缘为不可能的分界点
 
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;

   //如果当前像素梯度幅度为0,则不是边界点
 
  if(pMag[nPos] == 0)
 
  {
 
   pNSRst[nPos] = 0;
 
  }
 
  else
 
  {
 
   //当前点的梯度幅度
 
   dTmp = pMag[nPos];

    //x,y方向导数
 
   gx = pGradX[nPos];
 
   gy = pGradY[nPos];

    //如果方向导数y分量比x分量大,说明导数方向趋向于y分量
 
   if(abs(gy) > abs(gx))
 
   {
 
    //计算插值比例
 
    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];
 
    }
 
   }

    //如果方向导数x分量比y分量大,说明导数的方向趋向于x分量
 
   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];
 
    }
 
   }

    //利用 g1-g4 对梯度进行插值
 
   {
 
    dTmp1 = weight*g1 + (1-weight)*g2;
 
    dTmp2 = weight*g3 + (1-weight)*g4;

     //当前像素的梯度是局部的最大值
 
    //该点可能是边界点
 
    if(dTmp>=dTmp1 && dTmp>=dTmp2)
 
    {
 
     pNSRst[nPos] = 128;
 
    }
 
    else
 
    {
 
     //不可能是边界点
 
     pNSRst[nPos] = 0;
 
    }
 
   }
 
  }
 
 }
 
}
}

Posted on 2010-09-07 01:44  leivo  阅读(5240)  评论(0编辑  收藏  举报