《opencv实用探索·十三》opencv之canny边缘检测

2023-12-14 08:43:42

1、canny边缘检测应用场景

目标检测:
Canny边缘检测可以用于检测图像中的目标边缘,从而帮助识别和定位物体。在目标检测的流程中,边缘通常是检测的第一步。
图像分割:
Canny边缘检测可用于图像分割,即将图像划分为具有相似属性的区域。图像分割是许多计算机视觉任务的基础,如图像分析、物体识别等。
图像增强:
Canny边缘检测可以突出图像中的边缘特征,从而增强图像的结构信息。这对于图像处理任务,如图像重建和特征提取,非常有帮助。
边缘跟踪:
Canny边缘检测是一种强边缘跟踪器,可以用于跟踪图像中的曲线和边缘。
机器视觉和自动驾驶: 在机器视觉和自动驾驶系统中,Canny边缘检测可用于检测道路边缘、车辆和行人等物体。
医学图像处理:
在医学图像中,Canny边缘检测用于检测器官的边缘,从而协助医生进行诊断和手术规划。
光学字符识别(OCR):
在文字识别领域,Canny边缘检测可用于提取文本的边缘,以帮助进行光学字符识别。
图像分析:
Canny边缘检测在图像分析中是一种常见的工具,用于提取图像的重要特征。

2、canny边缘检测步骤
(1)噪声抑制:
在进行边缘检测之前,通常会对图像进行平滑以抑制噪声。这通常使用高斯滤波器来实现,可以通过cv::GaussianBlur函数完成。
(2)计算图像梯度:
使用Sobel算子计算图像在水平和垂直方向上的梯度。这个过程可以通过cv::Sobel函数实现。
(3)计算梯度的幅值和方向:
根据梯度的水平和垂直分量,计算梯度的幅值和方向。这可以通过cv::magnitude和cv::phase实现。
(4)非极大值抑制:
对梯度幅值图进行非极大值抑制,保留梯度方向上的局部最大值。这一步骤有助于细化边缘,使其更接近实际的边界。
(5)双阈值边缘跟踪:
根据设定的两个阈值(高阈值和低阈值),对非极大值抑制后的图像进行阈值处理,得到二值图像。高阈值用于确定强边缘,低阈值用于确定可能的边缘。通常,高阈值选取的较高,低阈值选取的较低。
(6)边缘连接:
对低阈值下的边缘像素进行连接,使其连接到高阈值下的边缘。这一步骤可以使用递归或迭代方法实现。

对于高斯滤波和梯度计算在之前的章节已经阐述过,请点击高斯滤波梯度计算进行回顾。

这里解释下什么是梯度幅值和方向:
边缘检测算子返回水平方向的Gx和垂直方向的Gy。梯度的幅度𝐺和方向𝛩(用角度值表示)为:
在这里插入图片描述
式中,atan2(?)表示具有两个参数的 arctan 函数。

梯度幅值是梯度矢量的长度,表示图像在某一点的强度变化的大小,梯度的方向总是与边缘垂直的,通常就近取值为水平(左、右)、垂直(上、下)、对角线(右上、左上、左下、右下)等 8 个不同的方向。

因此,在计算梯度时,我们会得到梯度的幅度和角度(代表梯度的方向)两个值。

下图展示了梯度的表示法。其中,每一个梯度包含幅度和角度两个不同的值。为了方便观察,这里使用了可视化表示方法。例如,左上角顶点的值“2↑”实际上表示的是一个二元数对“(2, 90)”,表示梯度的幅度为 2,角度为 90°。
在这里插入图片描述

非极大值像素梯度抑制:
在具体实现时,逐一遍历像素点,判断当前像素点是否是周围像素点中具有相同梯度方向的最大值,并根据判断结果决定是否抑制该点。如果当前像素点是周围像素点中最大值,我们保留该像素值,否则将其抑制为零。

例如A、B、C 三点具有相同的方向(梯度方向垂直于边缘)如下图。判断这三个点是否为各自的局部最大值:如果是,则保留该点;否则,抑制该点(归零)。
在这里插入图片描述
从图中可以看到A点在图像边缘,所以A 点具有最大的局部值,所以保留 A 点(称为边缘),其余两点(B和 C)被抑制(归零)。

再比如下图中,黑色背景的点都是向上方向梯度(边缘处于水平)的局部最大值。因此,这些点会被保留;其余点被抑制(处理为 0)。这意味着,这些黑色背景的点最终会被处理为边缘点,而其他点都被处理为非边缘点。
在这里插入图片描述
经过上述处理后,对于同一个方向的若干个边缘点,基本上仅保留了一个,使得检测到的边缘更狭窄,更接近实际的边界。

当完成非极大值抑制后,只保留了效果明显的边缘,然而,对于这些边缘,有些确实是图像的边缘,而有些却是由噪声产生的。之前用高斯滤波只是降噪,并不能消除噪声。而这些噪声造成的边缘被称为虚边缘,我们需要通过双值域将其消除。

双阈值设置:
首先设置两个阈值:高阈值(highThreshold)和低阈值(lowThreshold),高阈值用于确定强边缘,低阈值用于确定可能的边缘。对于所有的像素点,逐一遍历,如果当前边缘像素的梯度值 大于高阈值定为强边缘, 低于高阈值但是高于低阈值被定为虚边缘, 低于低阈值的像素点(抑制)置零。
所有的强边缘保留,而对于虚边缘,我们要做个判断。如果这些虚边缘是连接着强边缘的,那么保留,如果不是,那么抑制(置零)。

下面以一个示例图来说明。
图中Max和Min表示高低阈值,A在Max上面为强边缘直接保留,B在Max和Min之间为虚边缘,但B连着B,故也认定为边缘,保留。C也处于Max和Min之间,但C和A没有连接,故认定C不是边缘,被抑制。D之间小于低阈值,之间被抑制为0。

最后进行边缘连接,其过程如下:
(1)遍历经过非极大值抑制后的梯度幅值图像(图像经过非极大值抑制后只有边缘上的像素点的梯度幅值被保留,其他像素点的梯度幅值为零。)
(2)将大于高阈值的像素点标记为强边缘,高低阈值之间的像素值标记为弱边缘。
(3)从弱边缘像素出发,沿着其梯度方向前进,如果相邻像素中有强边缘像素(梯度幅值大于高阈值),则将其标记为强边缘,并继续追踪。追踪过程中,可能的边缘像素逐渐被标记为强边缘,形成一个连接的边缘。
(4)重复以上步骤,直到没有新的像素被添加到连接的边缘。最终,连接的边缘像素集合就是Canny边缘检测算法的输出。

3、opencv之canny接口调用

void cv::Canny(
    InputArray image,         // 输入图像(单通道8位灰度图像)
    OutputArray edges,         // 输出边缘图像
    double threshold1,        // 低阈值
    double threshold2,        // 高阈值
    int apertureSize = 3,      // Sobel算子核的大小,默认为3
    bool L2gradient = false    // 是否使用更精确的L2范数梯度计算,默认为false
);

双阈值怎么设定?

通常建议高阈值与低阈值的比例在2:1到3:1之间。
例如,如果设置高阈值为100,则低阈值可以设置为50或33。
根据图像特性:
可以根据图像的特性来调整阈值。如果图像中的边缘比较弱,可以适当降低阈值;如果图像中的边缘比较强,可以适当提高阈值。

参考代码:

int main() 
{  
    // 读取灰度图像
    cv::Mat image = cv::imread("1.jpg", cv::IMREAD_GRAYSCALE);

    // 检查图像是否成功读取
    if (image.empty()) {
        std::cerr << "Error: Could not read the image." << std::endl;
        return -1;
    }

    // 应用Canny边缘检测
    cv::Mat edges;
    double lowThreshold = 50;  // 低阈值
    double highThreshold = 150;  // 高阈值
    //高斯滤波去噪后检测图像边缘
    GaussianBlur(image, image, Size(3, 3), 0);
    cv::Canny(image, edges, lowThreshold, highThreshold);

    // 显示原始图像和Canny边缘检测的输出
    cv::imshow("Original Image", image);
    cv::imshow("Canny Edges", edges);
    // 等待用户按键
    cv::waitKey(0);

    return 0;
}

效果图如下:
左边是之间通过canny检测的边缘,右边是先通过高斯滤波去噪后再使用canny检测的边缘,可以明显的发现先去噪再检测边缘效果会更好。
在这里插入图片描述

参考文章:opencv基础44- Canny边缘检测详解-cv.Canny()

在这里插入图片描述

文章来源:https://blog.csdn.net/cs1395293598/article/details/134856697
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。