C#程序实现Canny边缘检测算法 边缘检测算法实现

转载自:http://blog.csdn.net/yjz_uestc/article/details/6664937

Canny边缘检测是被公认的检测效果最好的边缘检测方法,是由JohnF.Canny于1986年提出,算法目标是找出一个最优的边缘检测的方法,所谓最优即:1.好的检测:算法能够尽可能的标识出图像的边缘;2.好的定位:标识出的边缘要尽可能的与实际边缘相接近;3.最小响应:图像中的边缘只能标识一次,并且不能把噪声标识成边缘。同时我们也要满足3个准则:信噪比准则、定位精度准则、单边缘响应准则。

Canny边缘检测算法可分为4步:

高斯滤波器平滑、计算梯度、非极大值抑制、双阈值边缘检测和边缘连接。

(经典不会随着时间褪色,算法也是一样)

下面将逐步讲解并给出程序:

第一步:高斯平滑

为什么要对图像(灰度图像)进行高斯平滑预处理呢?高斯滤波器对去除服从正态分布的的噪声很有效,我做过实验,随着高斯模板的增大,被识别的边缘会逐渐减少,所以通过选着适合大小的高斯模板平滑,可以比较有效的去除一些伪边缘点。

第二步:计算梯度

首先,由一阶导数算子(一般用sobel模板)计算灰度图像每个像素点在水平和竖直方向上的导数Gx、Gy,得出梯度向量(Gx,Gy),计算梯度的值G和方向theta:

G=sqrt(Gx*Gx+Gy*Gy)theta=arctan(Gy/Gx)

然后,将每个像素点的梯度的值和方向分别放入两个数组中,程序如下:

[csharp]viewplaincopy
  1. <span>byte[]orients=newbyte[width*height];//梯度方向数组
  2. float[,]gradients=newfloat[width,height];//梯度值数组
  3. doublegx,gy;
  4. for(inti=1;i<(height-1);i++)
  5. {
  6. for(intj=1;j<(width-1);j++)
  7. {
  8. //求水平和竖直导数
  9. gx=bufdata[(i-1)*width+j]+bufdata[(i+1)*width+j]-bufdata[(i-1)*width+j-1]-bufdata[(i+1)*width+j-1]+2*(bufdata[i*width+j+1]-bufdata[i*width+j-1]);
  10. gy=bufdata[(i-1)*width+j-1]+bufdata[(i+1)*width+j+1]-bufdata[(i+1)*width+j-1]-bufdata[(i+1)*width+j+1]+2*(bufdata[(i-1)*width+j]-bufdata[(i+1)*width+j-1]);
  11. gradients[j,i]=(float)Math.Sqrt(gx*gx+gy*gy);
  12. if(gx==0)
  13. {
  14. orientation=(gy==0)?0:90;
  15. }
  16. else
  17. {
  18. doublediv=(double)gy/gx;
  19. if(div<0)
  20. {
  21. orientation=180-Math.Atan(-div)*toAngle;
  22. }
  23. else
  24. {
  25. orientation=Math.Atan(div)*toAngle;
  26. }
  27. //只保留成4个方向
  28. if(orientation<22.5)
  29. orientation=0;
  30. elseif(orientation<67.5)
  31. orientation=45;
  32. elseif(orientation<112.5)
  33. orientation=90;
  34. elseif(orientation<157.5)
  35. orientation=135;
  36. elseorientation=0;
  37. }
  38. orients[i*width+j]=(byte)orientation;
  39. }
  40. }</span>

第三步:非极大值抑制

如果直接把梯度作为边缘的话,将得到一个粗边缘的图像,这不满足上面提到的准则,我们希望得到定位准确的单像素的边缘,所以将每个像素点的梯度与其梯度方向上的相邻像素比较,如果不是极大值,将其置0,否则置为某一不大于255的数,程序如下:

[csharp]viewplaincopy
  1. <span>floatleftPixel=0,rightPixel=0;
  2. for(inty=1;y<height-1;y++)
  3. {
  4. for(intx=1;x<width-1;x++)
  5. {
  6. //获得相邻两像素梯度值
  7. switch(orients[y*width+x])
  8. {
  9. case0:
  10. leftPixel=gradients[x-1,y];
  11. rightPixel=gradients[x+1,y];
  12. break;
  13. case45:
  14. leftPixel=gradients[x-1,y+1];
  15. rightPixel=gradients[x+1,y-1];
  16. break;
  17. case90:
  18. leftPixel=gradients[x,y+1];
  19. rightPixel=gradients[x,y-1];
  20. break;
  21. case135:
  22. leftPixel=gradients[x+1,y+1];
  23. rightPixel=gradients[x-1,y-1];
  24. break;
  25. }
  26. if((gradients[x,y]<leftPixel)||(gradients[x,y]<rightPixel))
  27. {
  28. dis[y*disdata.Stride+x]=0;
  29. }
  30. else
  31. {
  32. dis[y*disdata.Stride+x]=(byte)(gradients[x,y]/maxGradient*255);//maxGradient是最大梯度
  33. }
  34. }
  35. }</span>

第四步:双阈值边缘检测

由上一步得到的边缘还有很多伪边缘,我们通过设置高低双阈值的方法去除它们,具体思想是:梯度值大于高阈值的像素点认为其一定是边缘,置为255,梯度值小于低阈值的像素点认为其一定不是边缘置为0,介于两阈值之间的点像素点为待定边缘。然后,考察这些待定边缘点,如果其像素点周围8邻域的梯度值都小于高阈值,认为其不是边缘点,置为0;至于,如何设定双阈值大小,我们可以根据实际情况设定,如设成100和20,也可以根据图像梯度值的统计信息设定,一般小阈值是大阈值的0.4倍即可。程序如下:

[csharp]viewplaincopy
  1. <span>fmean=fmean/maxGradient*255;//某统计信息
  2. highThreshold=(byte)(fmean);//高阈值
  3. lowThreshold=(byte)(0.4*highThreshold);//低阈值
  4. for(inty=0;y<height;y++)
  5. {
  6. for(intx=0;x<width;x++)
  7. {
  8. if(dis[y*disdata.Stride+x]<highThreshold)
  9. {
  10. if(dis[y*disdata.Stride+x]<lowThreshold)
  11. {
  12. dis[y*disdata.Stride+x]=0;
  13. }
  14. else
  15. {
  16. if((dis[y*disdata.Stride+x-1]<highThreshold)&&
  17. (dis[y*disdata.Stride+x+1]<highThreshold)&&
  18. (dis[(y-1)*disdata.Stride+x-1]<highThreshold)&&
  19. (dis[(y-1)*disdata.Stride+x]<highThreshold)&&
  20. (dis[(y-1)*disdata.Stride+x+1]<highThreshold)&&
  21. (dis[(y+1)*disdata.Stride+x-1]<highThreshold)&&
  22. (dis[(y+1)*disdata.Stride+x]<highThreshold)&&
  23. (dis[(y+1)*disdata.Stride+x+1]<highThreshold))
  24. {
  25. dis[y*disdata.Stride+x]=0;
  26. }
  27. }
  28. }
  29. }
  30. }</span>

最后,效果图如下:

原图:

灰度图:

边缘图:

C#程序实现Canny边缘检测算法 边缘检测算法实现
  

爱华网本文地址 » http://www.aihuau.com/a/25101010/24492.html

更多阅读

DCT变换编码及C语言实现 haar变换c语言实现

离散余弦变换(Discrete CosineTransform,简称DCT变换)是一种与傅立叶变换紧密相关的数学运算。在傅立叶级数展开式中,如果被展开的函数是实偶函数,那么其傅立叶级数中只包含余弦项,再将其离散化可导出余弦变换,因此称之为离散余弦变换。离

pid算法的c语言实现和升级 fuzzypid的c语言算法

struct _pid{ floatSetSpeed;//定义设定值 floatActualSpeed;//定义实际值 floaterr;//定义偏差值 floaterr_last;//定义上一个偏差值 floatKp,Ki,Kd;//定义比例、积分、微分系数 floatvoltage;//定义电压值(控制执行器的变量) floati

C程序设计---练习题四

第9章编译预处理1.以下程序的输出结果是()A、9B、6C、36D、18#define f(x) x*xmain( ){ int a=6,b=2,c;c=f(a) / f(b);

声明:《C#程序实现Canny边缘检测算法 边缘检测算法实现》为网友怡寶分享!如侵犯到您的合法权益请联系我们删除