边缘检测

边缘检测

简介

  • 边缘检测是一种图像处理技术,用于识别对象的边界(边缘)或图像中的区域

  • 图像的边缘指的是图像中像素灰度值突然发生变化的区域

  • 图像的边缘有可能是由高像素值变为低像素值,也有可能是由低像素值变成高像素值,因此通过计算得到的边缘信息会有正数值和负数值。为了在图像中同时表示出这两种边缘信息,需要将计算的结果求取绝对值。OpenCV提供了converScaleAbs()函数用于计算矩阵中所有数据的绝对值

converScaleAbs(src, dst)
  • 另外,由于求取边缘的结果可能会有负数,不在原始图像的CV_8U的数据类型内,因此滤波后的图像数据类型不要用 “-1” ,而应该改为CV_16S
    以下所有代码均用此图作为示例

Sobel算子

简介

  • Sobel算子通过寻找图像一阶导数中的最大值来检测边界,然后利用计算结果估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值


  • Sobel算子在实际应用中效率比Canny算法要高,但是边缘不如Canny检测的准确,但是很多实际应用的场合,Sobel算子却是首选
  • Sobel算子是高斯平滑与微分操作的结合体,所以抗噪声能力很强,用途较多,尤其是效率要求较高,而对细纹理不太关心的时候

实现

以下是 OpenCV 4 应用Sobel边缘检测的语法

Sobel(src, dst, ddepth, dx, dy, ksize)
  • src:待提取边缘的图像

  • dst:输出图像,与输入图像具有相同的尺寸和通道数,数据类型由第三个参数 ddepth 控制

  • ddepth:输出图像的数据类型(深度),根据输入图像数据类型的不同拥有不同的取值范围,具体如下表

    输入图像数据类型 输出图像可选数据类型
    CV_8U -1 / CV_16S / CV_32F / CV_64F
    CV_16U / CV_16S -1 / CV_32F / CV_64F
    CV_12F -1 / CV_32F / CV_64F
    CV_64F -1 / CV_64F
    • 当赋值为-1时,输出图像的数据类型自动选择
  • dx:X方向的差分阶数。如果dx = 1和dy = 0,我们提取X方向的边缘信息

  • dy:Y方向的差分阶数。如果两者dx = 1和dy = 1,我们提取两个方向也就是整幅图像的边缘信息

  • ksize:Sobel算子的尺寸,必须是1、3、5或7,默认为3

:任意一个方向的差分阶数都需要小于算子的尺寸。特殊情况是,当ksize = 1时,任意一个方向的阶数都需要小于3。在一般情况下,当差分阶数的最大值为1时,算子尺寸选3;当差分阶数的最大值为2时,算子尺寸选5;当差分阶数最大值为3时,算子尺寸选7

,由于提取边缘信息时有可能出现负数,因此不要使用CV_8U类型的输出图像

示例代码如下:

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

using namespace cv;
using namespace std;

int main()
{
    // 读取图像,黑白图像边缘检测结果较为明显
    Mat img = imread("/home/kslas/OpenCV/edge.jpg", IMREAD_ANYCOLOR);
    if (img.empty())
    {
        cout << "Could not read the image" << endl;
        return -1;
    }

    // X方向一阶边缘
    Mat resultX;
    Sobel(img, resultX, CV_16S, 2, 0, 1);
    convertScaleAbs(resultX, resultX);

    // Y方向一阶边缘
    Mat resultY;
    Sobel(img, resultY, CV_16S, 0, 1, 3);
    convertScaleAbs(resultY, resultY);

    // 整幅图像的一阶边缘
    Mat resultXY = resultX + resultY;

    // 显示图像
    imshow("resultX", resultX);
    imshow("resultY", resultY);
    imshow("resultXY", resultXY);
    waitKey(0);
    destroyAllWindows();
    return 0;
}

运行结果:

Laplacian算子

简介

  • Laplacian算子通过寻找图像二阶导数零穿越来寻找边界

  • Laplacian算子具有各方向同性的特点,能够对任意方向的边缘进行提取,具有无方向性的优点
  • Laplacian算子是一种二阶导数算子,对噪声比较敏感,因此常需要配合高斯滤波一起使用

实现

以下是 OpenCV 4 应用Laplacian边缘检测的语法

Laplacian(src, dst, ddepth, ksize)
  • scr:输入原始图像,可以是灰度图像或彩色图像
  • dst:输出图像,与输入图像具有相同的尺寸和通道数
  • ddepth:同上
  • ksize:滤波器的大小,必须为正奇数

,由于提取边缘信息时有可能出现负数,因此不要使用CV_8U类型的输出图像,否则会使得图像边缘提取不准确

示例代码如下:

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

using namespace cv;
using namespace std;

int main()
{
    // 读取图像,黑白图像边缘检测结果较为明显
    Mat img = imread("/home/kslas/OpenCV/edge.jpg", IMREAD_ANYCOLOR);
    if (img.empty())
    {
        cout << "Could not read the image" << endl;
        return -1;
    }

    // 未进行滤波提取Laplacian边缘
    Mat result;
    Laplacian(img, result, CV_16S, 3);
    convertScaleAbs(result, result);

    // 滤波后提取Laplacian边缘
    Mat result_g, result_G;
    GaussianBlur(img ,result_g, Size(3, 3), 5, 0);  // 高斯滤波
    Laplacian(result_g, result_G, CV_16S, 3);
    convertScaleAbs(result_G, result_G);

    // 显示图像
    imshow("result", result);
    imshow("result_G", result_G);
    waitKey(0);
    destroyAllWindows();
    return 0;
}

运行结果:

Canny算法

简介

  • Canny算法是目前最优越的边缘检测算法之一

  • Canny算法不容易受到噪声的影响,能够识别图像中的弱边缘和强边缘,并结合强弱边缘的位置关系,综合给出图像整体的边缘信息

  • 该方法的检测过程可分为以下5个步骤:

    • 使用高斯滤波平滑图像
    • 计算图像中每个像素的梯度方向和幅值
    • 应用非极大值抑制算法消除边缘检测带来的杂散相应
    • 应用双阈值法划分强边缘和弱边缘
    • 消除孤立的弱边缘

实现

OpenCV 4 提供了Canny()函数用于实现Canny算法检测图像中的边缘

Canny(image, edges, threshold1, threshold2, apertureSize, L2gradient)
  • image:输入图像,必须是CV_8U的单通道或者三通道图像
  • edges:输出图像,与输入图像具有相同尺寸的单通道图像,且数据类型为CV_8U
  • threshold1:第一个滞后阈值
  • threshold2:第二个滞后阈值
  • apertureSize:Sobel算子的直径,默认为3
  • L2gradient:计算图像梯度幅度方法的标志,默认为false

补1:在一般情况下,较大阈值与较小阈值的比值在 2 : 1 到 3 : 1 之间

补2:较高的阈值会降低噪声信息对图像提取边缘结果的影响,但同时也会减少结果中的边缘信息

补3:高斯模糊在边缘纹理较多的区域能减少边缘检测的结果,但对纹理较少的区域影响较小

示例代码:

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

using namespace cv;
using namespace std;

int main()
{
    // 读取图像,黑白图像边缘检测结果较为明显
    Mat img = imread("/home/kslas/OpenCV/edge.jpg", IMREAD_ANYCOLOR);
    if (img.empty())
    {
        cout << "Could not read the image" << endl;
        return -1;
    }

    // 高阈值检测图像边缘
    Mat resultHigh;
    Canny(img, resultHigh, 100, 200, 3);

    // 低阈值检测图像边缘
    Mat resultLow;
    Canny(img, resultLow, 20, 40, 3);

    // 高斯模糊后检测图像边缘
    Mat resultG, resultGC;
    GaussianBlur(img, resultG, Size(3, 3), 5);
    Canny(resultG, resultGC, 100, 200, 3);

    // 显示图像
    imshow("resultHigh", resultHigh);
    imshow("resultLow", resultLow);
    imshow("resultG", resultGC);
    waitKey(0);
    destroyAllWindows();
    return 0;
}

运行结果:

posted @ 2022-01-22 22:04  TNTksals  阅读(616)  评论(0编辑  收藏  举报