Canny算子
Canny边缘检测算子是John F.Canny于 1986 年开发出来的一个多级边缘检测算法。
三个主要评价标准
其中,Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的评价标准如下:
- 低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
- 高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。
- 最小响应: 图像中的边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘。
Canny算子的步骤
- 消除噪声。一般情况下,使用高斯平滑滤波器卷积降噪。
- 计算梯度幅值和方向,此处,按照Sobel滤波器的步骤
- 运用一对卷积阵列(分别作用于 x 和 y 方向)
- 使用下列公式计算梯度幅值和方向,梯度方向近似到四个可能角度之一(一般为0, 45, 90, 135)
1 2 3
| G = \sqrt{Gx^2+Gy^2}
θ = arctan(\frac {Gy}{Gx})
|
- 非极大值抑制。这一步排除非边缘像素,仅仅保留了一些细线条(候选边缘)。
- 滞后阈值。最后一步,Canny 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值):
- 如果某一像素位置的幅值超过高阈值, 该像素被保留为边缘像素。
- 如果某一像素位置的幅值小于低阈值, 该像素被排除。
- 如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一高于 高阈值的像素时被保留。
函数原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
void Canny( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false );
|
实例
1 2 3
| Mat mat_base = imread("fish.png"); Mat mat_dst; Canny(mat_base, mat_dst, 3, 9);
|
![]()
Sobel算子
Sobel 算子是一个主要用作边缘检测的离散微分算子,他结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。
步骤
假设被操作的图像为I
- 分别在x和y两个方向求导
- 水平变化: 将 I 与一个奇数大小的内核进行卷积。
- 垂直变化:将I与一个奇数大小的内核进行卷积。比如,当内核大小为3时, 的计算结果为:
1 2 3 4 5 6 7 8 9 10 11 12 13
| Gx = \begin{bmatrix} -1&0&+1\\ -2&0&+2\\ -1&0&+1\\ \end{bmatrix} * I
Gy = \begin{bmatrix} -1&-2&-1\\ 0&0&0\\ -1&-2&-1\\ \end{bmatrix} * I
|
2.在图像的每一点,结合以上两个结果求出近似梯度:
函数原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
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 );
|
补充
- 当内核大小为 3 时, 我们的 Sobel 内核可能产生比较明显的误差(毕竟, Sobel 算子只是求取了导数的近似值而已)。 为解决这一问题,OpenCV 提供了 Scharr 函数,但该函数仅作用于大小为3的内核。该函数的运算与 Sobel 函数一样快,但结果却更加精确,其内核是这样的:
1 2 3 4 5 6 7 8 9 10
| Gx = \begin{bmatrix} -3&0&+3\\ -10&0&+10\\ -3&0&+3\\ \end{bmatrix} Gy = \begin{bmatrix} -3&-10&-3\\ 0&0&0\\ +3&+10&+3\\ \end{bmatrix}
|
- 因为 Sobel 算子结合了高斯平滑和分化,因此结果会具有更多的抗噪性。大多数情况下,我们使用 Sobel 函数时,取[dx = 1,dy = 0,ksize = 3]来计算图像 x 方向的导数,[dx = 0,dy = 1,ksize = 3]来计算图像 y方向的导数。
实例
1 2 3 4 5 6 7
| Mat mat_base = imread("fish.png"); Mat grad_x, grad_y, abs_grad_x, abs_grad_y, mat_dst; Sobel(mat_base, grad_x, CV_16S, 1, 0); convertScaleAbs(grad_x, abs_grad_x); Sobel(mat_base, grad_y, CV_16S, 0, 1); convertScaleAbs(grad_y, abs_grad_y); addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, mat_dst);
|
![]()
Laplace算子
Laplacian 算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度 grad()的散度 div()。因此如果f是二阶可微的实函数,则f的 Laplacian 算子定义为:
f的拉普拉斯算子也是笛卡儿坐标系xi中的所有非混合二阶偏导数求和:
作为一个二阶微分算子,拉普拉斯算子把C函数映射到C函数,对于 k ≥ 2。表达式(1)(或(2))定义了一个算子 Δ :C(R) → C(R),或更一般地,定义了一个算子 Δ :C(Ω) → C(Ω),对于任何开集 Ω。
需要点破的是,由于 Laplacian 使用了图像梯度,它内部的代码其实是调用了 Sobel 算子的。
另附一个小tips:让一幅图像减去它的Laplacian可以增强对比度。
Laplacian 算子的定义:
1
| Laplacian(f) = \frac{∂^2f}{∂^2x} + \frac{∂^2f}{∂^2y}
|
函数原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
void Laplacian( InputArray src, OutputArray dst, int ddepth, int ksize = 1, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
|
补充
Laplacian 函数其实主要是利用 sobel 算子的运算。它通过加上 sobel 算子运算出的图像 x 方向和 y 方向上的导数,来得到我们载入图像的拉普拉斯变换结果。
其中,sobel算子(ksize>1)如下
1
| dst = Δ src = \frac{∂^2src}{∂^x} + \frac{∂^2src}{∂^y}
|
而当ksize = 1 时,Laplacian 函数采用以下 3 × 3 的孔径
1 2 3 4 5
| \begin{bmatrix} 0&1&0\\ 1&-4&1\\ 0&1&0\\ \end{bmatrix}
|
实例
1 2 3 4 5 6
| Mat mat_base = imread("fish.png"); Mat mat_gray, mat_dst; GaussianBlur(mat_base, mat_base, Size(3, 3), 0); cvtColor(mat_base, mat_gray, COLOR_RGB2GRAY); Laplacian(mat_gray, mat_dst, CV_16S); convertScaleAbs(mat_dst, mat_dst);
|
![]()
Scharr滤波器
scharr 一般直接称为滤波器,而不是算子。它在OpenCV中主要是配合 Sobel 算子的运算而存在的。
函数原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
void Scharr( InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
|
实例
1 2 3 4 5 6 7
| Mat mat_base = imread("fish.png"); Mat grad_x, grad_y, abs_grad_x, abs_grad_y, mat_dst; Scharr(mat_base, grad_x, CV_16S, 1, 0); convertScaleAbs(grad_x, abs_grad_x); Scharr(mat_base, grad_y, CV_16S, 0, 1); convertScaleAbs(grad_y, abs_grad_y); addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, mat_dst);
|
![]()