本人最近正在研究"图象边缘检测"相关问题,希望各位GG,JJ能够提供程序源代码,或者介绍一下相关流程~~~(分不够可以再加)谢谢!~~~~~

解决方案 »

  1.   

    http://contact.ys168.com/
    我这里有 来下吧
      

  2.   

    边缘指的是图像中在且仅在某个方向上灰度急剧变化、而在另一个与其正交的方向灰度变化平缓的像素。边缘特征可以更好的保持图像的结构信息,同时很多角点提取算法也是基于边缘轮廓特征的。
    在一维连续信号中,边缘定义为一阶导数最大或者二阶导数为零的地方。在二维连续图像f中,边缘是用图像梯度来定义的。而在数字图像中,像素是离散的,此时我们使用两种思路来求梯度。第一种思路是用具有差分性质的算子来近似代替求导运算,如Sobel、Prewitt、Roberts边缘检测算子。第二种方法是使用参数化模型方法,由离散图像拟和一个连续图像进而求梯度,并确定边缘位置所在。
      

  3.   

    二值化+边缘提取
    原文更全,地址
    http://www.3pcode.com/article/article_153/76933.htmcanny算子代码,希望有所帮助void CreatGauss(double sigma, double **pdKernel, int *pnWidowSize);
    void GaussianSmooth(SIZE sz, LPBYTE pGray, LPBYTE pResult, double sigma);
    void Grad(SIZE sz, LPBYTE pGray, int *pGradX, int *pGradY, int *pMag);
    void NonmaxSuppress(int *pMag, int *pGradX, int *pGradY, SIZE sz, LPBYTE pNSRst);
    void EstimateThreshold(int *pMag, SIZE sz, int *pThrHigh, int *pThrLow, LPBYTE pGray, 
              double dRatHigh, double dRatLow);
    void Hysteresis(int *pMag, SIZE sz, double dRatLow, double dRatHigh, LPBYTE pResult);
    void TraceEdge(int y, int x, int nThrLow, LPBYTE pResult, int *pMag, SIZE sz);
    void Canny(LPBYTE pGray, SIZE sz, double sigma, double dRatLow,
           double dRatHigh, LPBYTE pResult);#include "afx.h"
    #include "math.h"
    #include "canny.h"//  一维高斯分布函数,用于平滑函数中生成的高斯滤波系数
    void CreatGauss(double sigma, double **pdKernel, int *pnWidowSize)
    {
     LONG i;
     //数组中心点
     int nCenter;
     //数组中一点到中心点距离
     double dDis;
     //中间变量
     double dValue;
     double dSum;
     dSum = 0; // [-3*sigma,3*sigma] 以内数据,会覆盖绝大部分滤波系数
     *pnWidowSize = 1+ 2*ceil(3*sigma);
     nCenter = (*pnWidowSize)/2;
     *pdKernel = new double[*pnWidowSize];
     //生成高斯数据
     for(i=0;i<(*pnWidowSize);i++)
     {
      dDis = double(i - nCenter);
      dValue = exp(-(1/2)*dDis*dDis/(sigma*sigma))/(sqrt(2*3.1415926)*sigma);
      (*pdKernel)[i] = dValue;
      dSum+=dValue;
     }
     //归一化
     for(i=0;i<(*pnWidowSize);i++)
     {
      (*pdKernel)[i]/=dSum;
     }
    }//用高斯滤波器平滑原图像
    void GaussianSmooth(SIZE sz, LPBYTE pGray, LPBYTE pResult, double sigma)
    {
     LONG x, y;
     LONG i;
     //高斯滤波器长度
     int nWindowSize;
     //窗口长度
     int nLen;
     //一维高斯滤波器
     double *pdKernel;
     //高斯系数与图像数据的点乘
     double dDotMul;
     
     //滤波系数总和 
     double dWeightSum;
     
     double *pdTemp;
     pdTemp = new double[sz.cx*sz.cy]; //产生一维高斯数据
     CreatGauss(sigma, &pdKernel, &nWindowSize);
     nLen = nWindowSize/2;
     //x方向滤波
     for(y=0;y<sz.cy;y++)
     {
      for(x=0;x<sz.cx;x++)
      {
       dDotMul = 0;
       dWeightSum = 0;
       for(i=(-nLen);i<=nLen;i++)
       {
        //判断是否在图像内部
        if((i+x)>=0 && (i+x)<sz.cx)
        {
         dDotMul+=(double)pGray[y*sz.cx+(i+x)] * pdKernel[nLen+i];
         dWeightSum += pdKernel[nLen+i];
        }
       }
       pdTemp[y*sz.cx+x] = dDotMul/dWeightSum;
      }
     }
     //y方向滤波
     for(x=0; x<sz.cx;x++)
     {
      for(y=0; y<sz.cy; y++)
      {
       dDotMul = 0;
       dWeightSum = 0;
       for(i=(-nLen);i<=nLen;i++)
       {
        if((i+y)>=0 && (i+y)< sz.cy)
        {
         dDotMul += (double)pdTemp[(y+i)*sz.cx+x]*pdKernel[nLen+i];
         dWeightSum += pdKernel[nLen+i];
        }
       }
       pResult[y*sz.cx+x] = (unsigned char)dDotMul/dWeightSum;
      }
     }
     delete []pdKernel;
     pdKernel = NULL;
     delete []pdTemp;
     pdTemp = NULL;
    }
    // 方向导数,求梯度
    void Grad(SIZE sz, LPBYTE pGray,int *pGradX, int *pGradY, int *pMag)
    {
     LONG y,x;
     //x方向的方向导数
     for(y=1;y<sz.cy-1;y++)
     {
      for(x=1;x<sz.cx-1;x++)
      {
       pGradX[y*sz.cx +x] = (int)( pGray[y*sz.cx+x+1]-pGray[y*sz.cx+ x-1]  );
      }
     }
     //y方向方向导数
     for(x=1;x<sz.cx-1;x++)
     {
      for(y=1;y<sz.cy-1;y++)
      {
       pGradY[y*sz.cx +x] = (int)(pGray[(y+1)*sz.cx +x] - pGray[(y-1)*sz.cx +x]);
      }
     }
     //求梯度
     //中间变量
     double dSqt1;
     double dSqt2;
     for(y=0; y<sz.cy; y++)
     {
      for(x=0; x<sz.cx; x++)
      {
       //二阶范数求梯度
       dSqt1 = pGradX[y*sz.cx + x]*pGradX[y*sz.cx + x];
       dSqt2 = pGradY[y*sz.cx + x]*pGradY[y*sz.cx + x];
       pMag[y*sz.cx+x] = (int)(sqrt(dSqt1+dSqt2)+0.5);
      }
     }
    }
    //非最大抑制
    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;
         }
        }
       }
      }
     }
    }
      

  4.   


    // 统计pMag的直方图,判定阈值
    void EstimateThreshold(int *pMag, SIZE sz, int *pThrHigh, int *pThrLow, LPBYTE pGray, 
              double dRatHigh, double dRatLow)
    {
     LONG y,x,k;
     //该数组的大小和梯度值的范围有关,如果采用本程序的算法
     //那么梯度的范围不会超过pow(2,10)
     int nHist[256];
     //可能边界数
     int nEdgeNum;
     //最大梯度数
     int nMaxMag;
     int nHighCount;
     nMaxMag = 0;
     //初始化
     for(k=0;k<256;k++)
     {
      nHist[k] = 0;
     }
     //统计直方图,利用直方图计算阈值
     for(y=0;y<sz.cy;y++)
     {
      for(x=0;x<sz.cx;x++)
      {
       if(pGray[y*sz.cx+x]==128)
       {
        nHist[pMag[y*sz.cx+x]]++;
       }
      }
     }
     nEdgeNum = nHist[0];
     nMaxMag = 0; //统计经过“非最大值抑制”后有多少像素
     for(k=1;k<256;k++)
     {
      if(nHist[k] != 0)
      {
       nMaxMag = k;
      }  //梯度为0的点是不可能为边界点的
      //经过non-maximum suppression后有多少像素
      nEdgeNum += nHist[k];
     }
     //梯度比高阈值*pThrHigh 小的像素点总书目
     nHighCount = (int)(dRatHigh * nEdgeNum + 0.5);
     k=1;
     nEdgeNum = nHist[1];
     //计算高阈值
     while((k<(nMaxMag-1)) && (nEdgeNum < nHighCount))
     {
      k++;
      nEdgeNum += nHist[k];
     }
     *pThrHigh = k;
     //低阈值
     *pThrLow = (int)((*pThrHigh) * dRatLow + 0.5);
    }//利用函数寻找边界起点
    void Hysteresis(int *pMag, SIZE sz, double dRatLow, double dRatHigh, LPBYTE pResult)
    {
     LONG y,x;
     int nThrHigh,nThrLow;
     int nPos;
     //估计TraceEdge 函数需要的低阈值,以及Hysteresis函数使用的高阈值
     EstimateThreshold(pMag, sz,&nThrHigh,&nThrLow,pResult,dRatHigh,dRatLow);
     //寻找大于dThrHigh的点,这些点用来当作边界点,
     //然后用TraceEdge函数跟踪该点对应的边界
     for(y=0;y<sz.cy;y++)
     {
      for(x=0;x<sz.cx;x++)
      {
       nPos = y*sz.cx + x;
       //如果该像素是可能的边界点,并且梯度大于高阈值,
       //该像素作为一个边界的起点
       if((pResult[nPos]==128) && (pMag[nPos] >= nThrHigh))
       {
        //设置该点为边界点
        pResult[nPos] = 255;
        TraceEdge(y,x,nThrLow,pResult,pMag,sz);
       }
      }
     }
     //其他点已经不可能为边界点
     for(y=0;y<sz.cy;y++)
     {
      for(x=0;x<sz.cx;x++)
      {
       nPos = y*sz.cx + x;
       if(pResult[nPos] != 255)
       {
        pResult[nPos] = 0;
       }
      }
     }
    }
    //根据Hysteresis 执行的结果,从一个像素点开始搜索,搜索以该像素点为边界起点的一条边界的
    //一条边界的所有边界点,函数采用了递归算法
    //       从(x,y)坐标出发,进行边界点的跟踪,跟踪只考虑pResult中没有处理并且可能是边界
    //  点的像素(=128),像素值为0表明该点不可能是边界点,像素值为255表明该点已经是边界点
    void TraceEdge(int y, int x, int nThrLow, LPBYTE pResult, int *pMag, SIZE sz)
    {
     //对8邻域像素进行查询
     int xNum[8] = {1,1,0,-1,-1,-1,0,1};
     int yNum[8] = {0,1,1,1,0,-1,-1,-1};
     LONG yy,xx,k;
     for(k=0;k<8;k++)
     {
      yy = y+yNum[k];
      xx = x+xNum[k];
      if(pResult[yy*sz.cx+xx]==128 && pMag[yy*sz.cx+xx]>=nThrLow )
      {
       //该点设为边界点
       pResult[yy*sz.cx+xx] = 255;
       //以该点为中心再进行跟踪
       TraceEdge(yy,xx,nThrLow,pResult,pMag,sz);
      }
     }
    }// Canny算子
    void Canny(LPBYTE pGray, SIZE sz, double sigma, double dRatLow,
           double dRatHigh, LPBYTE pResult)
    {
     //经过高斯滤波后的图像
     LPBYTE pGaussSmooth;
     pGaussSmooth = new unsigned char[sz.cx*sz.cy];
     //x方向导数的指针
     int *pGradX;
     pGradX = new int[sz.cx*sz.cy];
     
     //y方向
     int *pGradY;
     pGradY = new int[sz.cx*sz.cy];
     
     //梯度的幅度
     int *pGradMag;
     pGradMag = new int[sz.cx*sz.cy]; //对原图高斯滤波
     GaussianSmooth(sz,pGray,pGaussSmooth,sigma);
     //计算方向导数和梯度的幅度
     Grad(sz,pGaussSmooth,pGradX,pGradY,pGradMag);
     //应用非最大抑制
     NonmaxSuppress(pGradMag,pGradX,pGradY,sz,pResult);
     //应用Hysteresis,找到所有边界
     Hysteresis(pGradMag,sz,dRatLow,dRatHigh,pResult); delete[] pGradX;
     pGradX = NULL;
     delete[] pGradY;
     pGradY = NULL;
     delete[] pGradMag;
     pGradMag = NULL;
     delete[] pGaussSmooth;
     pGaussSmooth = NULL;
    }
    /*
    void CChildWnd::OnCanny() 
    {
     if (! m_fOpenFile) 
     {
      return;
     }
     m_fDone = TRUE; 
     RGBToGray(szImg, aRGB, aGray, BPP);
     Canny(aGray,szImg,0.1,0.9,0.76,aBinImg);
      
     ShowGrayImage("l",szImg,aBinImg);
    }
    //*/