边缘检测的一般步骤: 滤波 去除噪声–> 增强 增强梯度显著变换的点 --> 检测 阈值化检测剔除不是边缘的点
最优边缘的三个主要评价标准:
- 低错误率:标识出尽可能多的实际边缘,同时尽可能减少噪声产生的误差。
- 高定位性:标识出的边缘要与图像中的实际边缘尽可能接近。
- 最小响应 :图像中的边缘只能标识一次,并且噪声不应标识为边缘
1. Canny 算子
变分法
, 一种寻找满足特定函数功能的函数方法,它的最优检测用4个指数函数项的和表示。
- 高斯滤波降低噪声干扰
- 计算梯度幅值和方向(此处是与Sobel算子一样的计算方法)
-
非极大值抑制
排除非边缘像素
-
滞后阈值
滞后阈值有两个:高阈值+低阈值; 一般推荐高低阈值比为
2:1 或 3:1.
- 代码剖析
/*************************************************************
* Canny: image 8-bit input image ;
*
* 图像掩码: 掩码矩阵也称之为"核", 掩码矩阵实质上就是对某一区域内的像素赋予了跟原图像不一样的权值,
*
* **************************************************************/
Mat grayImage, blurNoise, CannyEdge;
GaussianBlur(srcImage, blurNoise, Size(5, 5), 0, 0 );
imshow("GaussianBlur", blurNoise);
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
Canny(grayImage, CannyEdge, 30, 10, 3);
imshow("grayEdge", CannyEdge);
Mat colorEdge;
srcImage.copyTo(colorEdge, CannyEdge); // CannyEdge is mask which added into srcImage = colorEdge
imshow("ColorEdge", colorEdge);
- 运行结果
2. Sobel 算子
主要用于边缘检测的离散微分算子,它结合了
高斯平滑
和
微分求导
,用来计算图像灰度函数的近似梯度; 在图像的任何一点使用此算子,都会产生对应的梯度矢量或是其法矢量。
- 对图像分别在x, y两个方向上求导
- 在x-y两个方向分别将图像与一个奇数内核(Gx, Gy)进行卷积
-
得到近似梯度: x,y两个方向上的内核的平方和再开根号
G = G x 2 + G y 2 G=\sqrt{G_{x}^{2}+G_{y}^{2}} G=Gx2+Gy2
- 源代码剖析
/*************************************************************
* Sobel(): void Sobel( InputArray src, OutputArray dst, int ddepth,
int dx, int dy, int ksize = 3,
double scale = 1, double delta = 0,
int borderType = BORDER_DEFAULT );
* ddepth: output image depth
* dx-dy-ksize:(X-axis) 0-1-3
* dx-dy-ksize:(Y-axis) 1-0-3
* 当内核大小(ksize == 3)时, Sobol可能产生比较明显的误差, OpenCV提供了一个替代函数Scharr()。
*
* 该函数与Sobel函数一样快,但结果更加精确,但仅限制在内核为3的情况下使用。
**************************************************************/
//Scharr();
Mat grad_x, grad_y;
Mat abs_grad_x;
Mat sobelImage;
Sobel(srcImage, grad_x, -1, 0, 1, 3, 1, 1);
//convertScaleAbs(grad_x, abs_grad_x);
imshow("grad_x", grad_x);
//imshow("abs_grad_x", abs_grad_x);
Sobel(srcImage, grad_y, -1, 1, 0, 3, 1, 1);
imshow("grad_y", grad_y);
//合并梯度
addWeighted(grad_x, 0.5, grad_y, 0.5, 0, sobelImage);
imshow("Sobel", sobelImage);
- 测试结果
从上图的对比中可以看出,在某些方面,Sobel算子明显要比canny算子获取更好的边缘的信息