Open CV 学习笔记: 边缘检测

Canny算子:

边缘检测算子是John F. Canny于1986年开发出来的一个多级边缘检测算法。更为重要的是Canny创立了边缘检测计算理论


Canny算法的步骤

1.降噪

任何边缘检测算法都不可能在未经处理的原始数据上很好地处理,所以第一步是对原始数据与高斯平滑模板作卷积,得到的图像与原始图像相比有些轻微的模糊。这样,单独的一个像素噪声在经过高斯平滑的图像上变得几乎没有影响。

2.寻找图像中的亮度梯度

图像中的边缘可能会指向不同的方向,所以Canny算法使用4个mask检测水平、垂直以及对角线方向的边缘。原始图像与每个mask所作的卷积都存储起来。对于每个点我们都标识在这个点上的最大值以及生成的边缘的方向。这样我们就从原始图像生成了图像中每个点亮度梯度图以及亮度梯度的方向。

3.在图像中跟踪边缘

较高的亮度梯度比较有可能是边缘,但是没有一个确切的值来限定多大的亮度梯度是边缘多大又不是,所以Canny使用了滞后阈值。滞后阈值需要两个阈值——高阈值与低阈值。假设图像中的重要边缘都是连续的曲线,这样我们就可以跟踪给定曲线中模糊的部分,并且避免将没有组成曲线的噪声像素当成边缘。所以我们从一个较大的阈值开始,这将标识出我们比较确信的真实边缘,使用前面导出的方向信息,我们从这些真正的边缘开始在图像中跟踪整个的边缘。在跟踪的时候,我们使用一个较小的阈值,这样就可以跟踪曲线的模糊部分直到我们回到起点。一旦这个过程完成,我们就得到了一个二值图像,每点表示是否是一个边缘点。

一个获得亚像素精度边缘的改进实现是在梯度方向检测二阶方向导数的过零点

它在梯度方向的三阶方向导数满足符号条件

其中 ... 表示用高斯核平滑原始图像得到的尺度空间表示L计算得到的偏导数。用这种方法得到的边缘片断是连续曲线,这样就不需要另外的边缘跟踪改进。滞后阈值也可以用于亚像素边缘检测。

Canny算法包含许多可以调整的参数,它们将影响到算法的计算的时间与实效。

  • 高斯滤波器的大小:第一步所用的平滑滤波器将会直接影响Canny算法的结果。较小的滤波器产生的模糊效果也较少,这样就可以检测较小、变化明显的细线。较大的滤波器产生的模糊效果也较多,将较大的一块图像区域涂成一个特定点的颜色值。这样带来的结果就是对于检测较大、平滑的边缘更加有用,例如彩虹的边缘。
  • 阈值:使用两个阈值比使用一个阈值更加灵活,但是它还是有阈值存在的共性问题。设置的阈值过高,可能会漏掉重要信息;阈值过低,将会把枝节信息看得很重要。很难给出一个适用于所有图像的通用阈值。目前还没有一个经过验证的实现方法。

Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );

输入参数:

  • detected_edges: 原灰度图像
  • detected_edges: 输出图像 (支持原地计算,可为输入图像)
  • lowThreshold: 用户通过 trackbar设定的值。
  • highThreshold: 设定为低阈值的3倍 (根据Canny算法的推荐)
  • kernel_size: 设定为 3 (Sobel内核大小,内部使用)

Sobel算子:

Sobel 算子是一个离散微分算子 (discrete differentiation operator)。 它用来计算图像灰度函数的近似梯度。

Sobel 算子结合了高斯平滑和微分求导。

  1. 在两个方向求导:

    1. 水平变化: 将 I 与一个奇数大小的内核 G_{x} 进行卷积。比如,当内核大小为3时, G_{x} 的计算结果为:

      G_{x} = \begin{bmatrix}-1 & 0 & +1  \\-2 & 0 & +2  \\-1 & 0 & +1\end{bmatrix} * I

    2. 垂直变化: 将:math:I 与一个奇数大小的内核 G_{y} 进行卷积。比如,当内核大小为3时, G_{y} 的计算结果为:

      G_{y} = \begin{bmatrix}-1 & -2 & -1  \\0 & 0 & 0  \\+1 & +2 & +1\end{bmatrix} * I

  2. 在图像的每一点,结合以上两个结果求出近似 梯度:

    G = \sqrt{ G_{x}^{2} + G_{y}^{2} }

    有时也用下面更简单公式代替:

    G = |G_{x}| + |G_{y}|

    Mat grad_x, grad_y;
    Mat abs_grad_x, abs_grad_y;


    /// 求 X方向梯度
    Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
    /// 求 Y方向梯度
    Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );


    该函数接受了以下参数:

    • src_gray: 在本例中为输入图像,元素类型 CV_8U
    • grad_x/grad_y: 输出图像.
    • ddepth: 输出图像的深度,设定为 CV_16S 避免外溢。
    • x_orderx 方向求导的阶数。
    • y_ordery 方向求导的阶数。
    • scaledelta 和 BORDER_DEFAULT: 使用默认值

Laplace算子:

Laplacian 算子 的定义:

Laplace(f) = \dfrac{\partial^{2} f}{\partial x^{2}} + \dfrac{\partial^{2} f}{\partial y^{2}}

OpenCV函数 Laplacian 实现了Laplacian算子。 实际上,由于 Laplacian使用了图像梯度,它内部调用了 Sobel 算子。

Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );

函数接受了以下参数:

  • src_gray: 输入图像。
  • dst: 输出图像
  • ddepth: 输出图像的深度。 因为输入图像的深度是 CV_8U ,这里我们必须定义 ddepth = CV_16S 以避免外溢。
  • kernel_size: 内部调用的 Sobel算子的内核大小,此例中设置为3。
  • scaledelta 和 BORDER_DEFAULT: 使用默认值。

示例:

#include 
#include 
#include 

using namespace cv;
using namespace std;


int main(int agrc,char **argv){
	//读取原始图像
	Mat src = imread("img.jpg");
	if(!src.data){
		cout<<"input error!"<

Open CV 学习笔记: 边缘检测_第1张图片

Open CV 学习笔记: 边缘检测_第2张图片

Open CV 学习笔记: 边缘检测_第3张图片

Open CV 学习笔记: 边缘检测_第4张图片

Open CV 学习笔记: 边缘检测_第5张图片

Open CV 学习笔记: 边缘检测_第6张图片

你可能感兴趣的:(Open,CV,学习笔记)