最后来看看canny算子,这个是被成为最好的算子,因为过程多,有准测,后面会列出来,也是边缘检测的最后一个,所以这里作为结尾,来看看各个边缘检测的效果。
边缘检测结果比较
- Roberts算子检测方法对具有陡峭的低噪声的图像处理效果较好,但是利用roberts算子提取边缘的结果是边缘比较粗,因此边缘的定位不是很准确。
- Sobel算子检测方法对灰度渐变和噪声较多的图像处理效果较好,sobel算子对边缘定位不是很准确,图像的边缘不止一个像素。
- Prewitt算子检测方法对灰度渐变和噪声较多的图像处理效果较好。但边缘较宽,而且间断点多。
- Laplacian算子法对噪声比较敏感,所以很少用该算子检测边缘,而是用来判断边缘像素视为与图像的明区还是暗区。
- Canny方法不容易受噪声干扰,能够检测到真正的弱边缘。优点在于,使用两种不同的阈值分别检测强边缘和弱边缘,并且当弱边缘和强边缘相连时,才将弱边缘包含在输出图像中
canny对边缘检测质量进行分析时,有3个原则:
- 1、信噪比准则
- 2、定位精度准则
- 3、单边缘响应准则
canny边缘检测的基本思想是:首先对图像选择一定的Gauss滤波器进行平滑滤波,然后采用非极值抑制技术进行处理得到最后的边缘图像。
具体步骤:
1、用高斯滤波器平滑图像
对图像进行高斯滤波,听起来很玄乎,其实就是根据待滤波的像素点及其邻域点的灰度值按照一定的参数规则进行加权平均。这样可以有效滤去理想图像中叠加的高频噪声。
2、用一阶偏导的有限差分来计算梯度的幅值和方向
图像灰度值得梯度可使用一阶有限差分来进行近似,这样就可以得图像在x和y方向上偏导数的两个矩阵。常用的梯度算子就是Roberts.sobel,prewitt.,canny
3、对梯度幅值进行非极大值抑制
图像梯度幅值矩阵中的元素值越大,说明图像中该点的梯度值越大,但这不不能说明该点就是边缘(这仅仅是属于图像增强的过程)。在Canny算法中,非极大值抑制是进行边缘检测的重要步骤,通俗意义上是指寻找像素点局部最大值,将非极大值点所对应的灰度值置为0,这样可以剔除掉一大部分非边缘的点
4、用双阈值算法检测和连接边缘
Canny算法中减少假边缘数量的方法是采用双阈值法。选择两个阈值(关于阈值的选取方法在扩展中进行讨论),根据高阈值得到一个边缘图像,这样一个图像含有很少的假边缘,但是由于阈值较高,产生的图像边缘可能不闭合,未解决这样一个问题采用了另外一个低阈值。
通俗的来说:就是在进行边缘检测时,还是要用到滤波减小噪声,先通过在水平和垂直方向的一阶偏导,求得梯度的幅值和方向,这样每个点都可能有4中方向情况(0,45,90,135度),在局部范围内,保留在同一方向上,梯度最大的点,非最大就置零,最后使用2个阈值T1和T2(T1<T2),T2用来找到每条线段,T1用来在这些线段的两个方向上延伸寻找边缘的断裂处,并连接这些边缘。
<span style="font-size:18px;">C++: void Canny(InputArray image,OutputArray edges, double threshold1, double threshold2, int apertureSize=3,bool L2gradient=false ) </span>
- 第一个参数,InputArray类型的image,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位图像。
- 第二个参数,OutputArray类型的edges,输出的边缘图,需要和源图片有一样的尺寸和类型。
- 第三个参数,double类型的threshold1,第一个滞后性阈值。
- 第四个参数,double类型的threshold2,第二个滞后性阈值。
- 第五个参数,int类型的apertureSize,表示应用Sobel算子的孔径大小,其有默认值3。
- 第六个参数,bool类型的L2gradient,一个计算图像梯度幅值的标识,有默认值false。
<span style="font-size:18px;">#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <stdlib.h> #include <stdio.h> #include <iostream> using namespace cv; using namespace std; Mat src, src_gray; Mat dst, detected_edges; int edgeThresh = 1; int lowThreshold; int const max_lowThreshold = 100; int ratio = 3; int kernel_size = 3; const char* window_name = "Edge Map"; static void CannyThreshold(int, void*) { Canny( src_gray, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size ); dst = Scalar::all(0); src.copyTo( dst, detected_edges); imshow( window_name, dst ); } int main( int, char** argv ) { src = imread("D:\\lena.jpg",CV_LOAD_IMAGE_COLOR); if( !src.data ) { return -1; } dst.create( src.size(), src.type() ); cvtColor( src, src_gray, CV_BGR2GRAY ); namedWindow( window_name, CV_WINDOW_AUTOSIZE ); createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold ); CannyThreshold(0, 0); waitKey(0); return 0; } </span>
matlab
<span style="font-size:18px;">I=imread('d:\lena.jpg'); I1=rgb2gray(I); img1=edge(I1,'canny',[0.03,0.08],3); subplot(121),imshow(I); subplot(122),imshow(img1)</span>