Bootstrap

OpenCV 卷积 Robert算子,Laplance算子,Sobel算子,Canny边缘检测原理

提取边缘原理

OpenCV 提取边缘的原理主要依赖于图像的梯度变化。边缘是图像中灰度值发生显著变化的区域,通常对应于物体的边界。在图像特征提权,对象检测,模式识别都有重要作用。判断灰度值发生显著变化通常通过计算图像的梯度来实现。梯度是图像灰度值变化的速率或方向,即垂直方向和水平方向的一阶导数,delta = f(x)-f(x-1),delta越大,说明像素在x方向变化越大,边缘信号越强,说明是极有可能是边界。

在这里插入图片描述

如何得到梯度值呢? 使用卷积操作

Robert算子的基本原理

Robert算子是一种基于图像梯度的边缘检测算法,其主要原理是通过计算图像中每个像素的局部灰度变化来识别图像的边缘。它是边缘检测算法中的一种经典方法,广泛应用于图像处理领域。

梯度计算

  • Robert算子通过计算图像中灰度值的局部变化来估计梯度。边缘通常表示为灰度值的突变,因此,边缘检测算法的核心是计算图像中灰度变化的大小和方向。

  • Robert算子使用两个简单的卷积核(或掩模)来计算图像在水平和垂直方向上的梯度。这两个卷积核分别是:

    在这里插入图片描述

卷积操作

  • 卷积是图像处理中用于计算图像局部特征的操作。在Robert算子中,我们将上述卷积核与图像进行卷积,计算每个像素的局部梯度。
  • 对于每个图像像素,将卷积核与像素及其邻域的灰度值进行乘法和求和,得到水平和垂直方向上的梯度分量。

梯度幅值

  • 计算图像中每个像素的梯度幅值,以获取边缘强度。梯度幅值表示灰度变化的强度,计算公式为:

  • G = G x 2 + G y 2 G = \sqrt{G_x^2 + G_y^2} G=Gx2+Gy2

  • 其中,Gx和 Gy 分别是水平和垂直方向上的梯度分量。幅值高的地方通常表示图像的边缘。

边缘检测

  • 根据梯度幅值进行边缘检测。通常会应用一个阈值,将幅值高于阈值的像素标记为边缘像素,从而得到二值化的边缘图像。

示例代码

int main()
{
    // 读取图像
    Mat image = imread("C:\\Users\\Marxist\\Pictures\\coco\\wanxi1.png", IMREAD_GRAYSCALE);
    if (image.empty()) {
        cout << "Could not open or find the image" << endl;
        return -1;
    }
    // 定义Robert算子的卷积核
    Mat kernel_x = (Mat_<float>(2, 2) << 1, 0, 0, -1);
    Mat kernel_y = (Mat_<float>(2, 2) << 0, 1, -1, 0);
    // 应用Robert算子
    Mat grad_x, grad_y;
    filter2D(image, grad_x, CV_32F, kernel_x);
    filter2D(image, grad_y, CV_32F, kernel_y);
    // 计算梯度幅值
    Mat grad_magnitude;
    magnitude(grad_x, grad_y, grad_magnitude);
    // 将结果转换为8位图像
    Mat grad_magnitude_8u;
    grad_magnitude.convertTo(grad_magnitude_8u, CV_8U);
    // 显示结果
    imshow("Original Image", image);
    imshow("Robert Gradient Magnitude", grad_magnitude_8u);
    waitKey(0);
    return 0;
}

效果如图:图像的边缘线条就被勾勒出来了

在这里插入图片描述

Sobel算子的基本原理

Sobel算子是一种常用的边缘检测算子,它通过计算图像在水平和垂直方向上的梯度来检测图像中的边缘。在OpenCV中,提供了Sobel函数轻松实现Sobel算子的应用。

Sobel算子使用两个卷积核来计算图像的水平和垂直梯度:

水平梯度核(Gx):
[ − 1 0 1 − 2 0 2 − 1 0 1 ] \begin {bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{bmatrix} 121000121
垂直梯度核(Gy):
[ − 1 − 2 − 1 0 0 0 1 2 1 ] \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1 \end{bmatrix} 101202101
代码实现:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
    // 读取图像并转换为灰度图像
    Mat image = imread("C:\\Users\\Marxist\\Pictures\\coco\\example.jpg");
    if (image.empty()) {
        cout << "Could not open or find the image" << endl;
        return -1;
    }
    Mat gray;
    cvtColor(image, gray, COLOR_BGR2GRAY);

    // 使用Sobel算子计算水平和垂直方向的梯度
    Mat grad_x, grad_y;
    Mat abs_grad_x, abs_grad_y;

    // Sobel函数参数解释:
    // 第一个参数:输入图像
    // 第二个参数:输出图像
    // 第三个参数:图像深度,通常使用CV_16S以避免梯度计算溢出 因为值很有可能超过8位
    // 第四个参数:x方向上的导数阶数
    // 第五个参数:y方向上的导数阶数
    // 第六个参数:内核大小(一般为3)
    Sobel(gray, grad_x, CV_16S, 1, 0, 3);
    Sobel(gray, grad_y, CV_16S, 0, 1, 3);

    // 将梯度转换为绝对值,然后转换回8位
    convertScaleAbs(grad_x, abs_grad_x);
    convertScaleAbs(grad_y, abs_grad_y);

    // 计算梯度的总和
    Mat grad;
    addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad);

    // 显示结果
    imshow("Original Image", image);
    imshow("Gray Image", gray);
    imshow("Sobel - Grad X", abs_grad_x);
    imshow("Sobel - Grad Y", abs_grad_y);
    imshow("Sobel - Combined Gradient", grad);

    waitKey(0);
    return 0;
}

效果如图:明显强过Robert算子

在这里插入图片描述

拉普拉斯算子的基本原理

拉普拉斯算子(Laplacian operator)是一种用于图像处理和边缘检测的二阶导数算子。它通过计算图像中像素的二阶导数来检测边缘,特别适用于检测图像中的快速变化区域。拉普拉斯算子具有方向无关性,即它在所有方向上都计算图像的变化,因此能够检测出所有方向的边缘。

拉普拉斯算子基于二阶导数,主要用于检测图像中灰度值的变化。对于二维函数 ( f(x, y) )(图像中的灰度值),拉普拉斯算子定义为:
∇ 2 f = ∂ 2 f ∂ x 2 + ∂ 2 f ∂ y 2 \nabla^2 f = \frac{\partial^2 f}{\partial x^2} + \frac{\partial^2 f}{\partial y^2} 2f=x22f+y22f
在图像处理中,离散的拉普拉斯算子通常使用卷积核来实现。这些卷积核近似于计算二阶导数。常用的卷积核包括:

  • 4-邻域:
    [ 0 1 0 1 − 4 1 0 1 0 ] \begin{bmatrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{bmatrix} 010141010

  • 8-邻域:
    [ 1 1 1 1 − 8 1 1 1 1 ] \begin{bmatrix} 1 & 1 & 1 \\ 1 & -8 & 1 \\ 1 & 1 & 1 \end{bmatrix} 111181111

使用这些卷积核对图像进行卷积运算,可以得到图像的二阶导数,从而检测出图像中的边缘。

拉普拉斯算子的特点

  1. 方向无关性:拉普拉斯算子计算的是图像在所有方向上的变化,因此能够检测出所有方向的边缘。
  2. 增强边缘:由于计算的是二阶导数,拉普拉斯算子对图像中的边缘增强效果明显,能够突出快速变化的区域。
  3. 敏感性拉普拉斯算子对噪声敏感,因此在应用拉普拉斯算子之前,通常需要对图像进行平滑处理(例如高斯模糊)以减少噪声。

代码实现

OpenCV提供了Laplacian函数,可以方便地对图像应用拉普拉斯算子。

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
    // 读取图像并转换为灰度图像
    Mat image = imread("C:\\Users\\Marxist\\Pictures\\coco\\example.jpg");
    if (image.empty()) {
        cout << "Could not open or find the image" << endl;
        return -1;
    }
    Mat gray;
    cvtColor(image, gray, COLOR_BGR2GRAY);

    // 使用高斯模糊平滑图像以减少噪声
    Mat blurred;
    GaussianBlur(gray, blurred, Size(3, 3), 0);

    // 使用拉普拉斯算子检测边缘
    Mat laplacian;
    Laplacian(blurred, laplacian, CV_16S, 3);
    Mat abs_laplacian;
    convertScaleAbs(laplacian, abs_laplacian);

    // 显示结果
    imshow("Original Image", image);
    imshow("Gray Image", gray);
    imshow("Laplacian Edge Detection", abs_laplacian);

    waitKey(0);
    return 0;
}

效果如图

在这里插入图片描述

Canny边缘检测原理

Canny边缘检测是一种多级边缘检测算法,由John F. Canny在1986年开发。它在边缘检测中广泛应用,因为它能够显著提高图像中的边缘检测精度,同时降低噪声的影响。Canny边缘检测算法包括五个主要步骤:高斯滤波、计算梯度、非极大值抑制、双阈值检测和边缘连接

1.高斯滤波

为了减少图像中的噪声,首先对图像进行高斯滤波。高斯滤波是一种平滑滤波器,它通过卷积运算来减弱图像中的噪声,而不会显著影响边缘。

2.计算梯度

使用Sobel算子计算图像的梯度。Sobel算子计算图像在水平方向(Gx)和垂直方向(Gy)上的一阶导数。通过这两个梯度,可以计算梯度幅值和方向

3.非极大值抑制

非极大值抑制用于细化边缘,即抑制非边缘的像素。对于每个像素点,检查其在梯度方向上的邻域,并保留局部极大值点。具体来说,如果一个像素点的梯度幅值大于其梯度方向上的两个相邻像素点的梯度幅值,则保留该点,否则将其抑制。

4.双阈值检测

双阈值检测使用两个阈值(高阈值和低阈值)来区分强边缘、弱边缘和非边缘:

  • 强边缘:梯度幅值大于高阈值的像素点。
  • 弱边缘:梯度幅值介于高阈值和低阈值之间的像素点。
  • 非边缘:梯度幅值小于低阈值的像素点。

5.边缘连接

通过连接强边缘和相邻的弱边缘来形成连续的边缘。如果一个弱边缘像素与强边缘像素连接,则将其视为边缘的一部分,否则将其抑制。

示例代码

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
    // 读取图像并转换为灰度图像
    Mat image = imread("C:\\Users\\Marxist\\Pictures\\coco\\example.jpg");
    if (image.empty()) {
        cout << "Could not open or find the image" << endl;
        return -1;
    }
    Mat gray;
    cvtColor(image, gray, COLOR_BGR2GRAY);

    // 高斯滤波以减少噪声
    Mat blurred;
    GaussianBlur(gray, blurred, Size(5, 5), 1.5);

    // 应用Canny边缘检测
    Mat edges;
    Canny(blurred, edges, 50, 100);
	//推荐低阈值 和高阈值 1:2 或者1:3  ,如果发现图像线条丢失严重,建议减低高阈值,得到更多线条。
    // 显示结果
    imshow("Original Image", image);
    imshow("Gray Image", gray);
    imshow("Canny Edge Detection", edges);

    waitKey(0);
    return 0;
}

Canny函数原型

void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false);

参数说明:

  1. image:输入图像。通常是一个单通道的灰度图像。输入图像的类型应为 CV_8U
  2. edges:输出图像。该图像将包含检测到的边缘。输出图像的类型为 CV_8U
  3. threshold1:第一个阈值,用于边缘连接的低阈值。任何小于此值的梯度强度将被认为不是边缘。
  4. threshold2:第二个阈值,用于边缘连接的高阈值。任何大于此值的梯度强度将被认为是强边缘。
  5. apertureSize:Sobel算子的孔径大小,用于计算图像梯度。默认值为3,常见取值有3、5、7。
  6. L2gradient:布尔值,用于指定是否使用更精确的L2范数(即欧几里得距离)来计算图像梯度幅值。如果为false,则使用L1范数(即曼哈顿距离)。默认值为false。

效果如图:
在这里插入图片描述

;