IT@程序员猿媛互联网@时事传播图像处理

【图像处理】OpenCV系列十 --- 边缘检测

2019-04-23  本文已影响12人  307656af5a04

上一篇我们学习了图像处理形态学相关知识点,相信大家学习之后已经对形态学有了足够的理解了,那么接下来,我们一起来学习一下图像处理中的边缘检测吧!我们将会重点学习边缘检测各种算子和滤波器 --- Canny算子,Sobel算子,Laplace算子以及Scharr滤波器,本篇我们将会学习Canny算子的原理与用法!

一、理论

边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点;图像属性中的显著变化通常反映了属性的重要事件和变化。

这些包括

(i)深度上的不连续
(ii)表面方向不连续
(iii)物质属性变化
(iv)场景照明变化

边缘检测是图像处理和计算机视觉中,尤其是特征提取中的一个研究领域。

边缘检测的一般步骤

1)滤波:边缘检测的算法主要是基于图像强度的一阶和二阶导数,但导数通常对噪声很敏感,因此必须采用滤波器来改善与噪声有关的边缘检测器的性能。常见的滤波方法主要有高斯滤波,即采用离散化的高斯函数产生一组归一化的高斯核(具体见“高斯滤波原理及其编程离散化实现方法”一文),然后基于高斯核函数对图像灰度矩阵的每一点进行加权求和;

2)增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸显出来。在具体编程实现时,可通过计算梯度幅值来确定。

3)检测:经过增强的图像,往往邻域中有很多点的梯度值比较大,而在特定的应用中,这些点并不是我们要找的边缘点,所以应该采用某种方法来对这些点进行取舍。实际工程中,常用的方法是通过阈值化方法来检测。

二、Canny算子

Canny边缘检测算子是John F.Canny于 1986 年开发出来的一个多级边缘检测算法。更为重要的是 Canny 创立了边缘检测计算理论(Computational theory ofedge detection),解释了这项技术是如何工作的。Canny边缘检测算法以Canny的名字命名,被很多人推崇为当今最优的边缘检测的算法。

其中,Canny 的目标是找到一个最优的边缘检测算法,让我们看一下最优边缘检测的三个主要评价标准:

为了满足这些要求 Canny 使用了变分法,这是一种寻找满足特定功能的函数的方法。最优检测使用四个指数函数项的和表示,但是它非常近似于高斯函数的一阶导数。

(一)Canny算子的检测步骤

1.灰度化

把彩色图像变成灰度图像,该部分是按照Canny算法通常处理的图像为灰度图,如果获取的彩色图像,那首先就得进行灰度化。以RGB格式的彩图为例,通常灰度化采用的公式是:

Gray=0.299R+0.587G+0.114B;

2、高斯滤波

对图像高斯滤波,图像高斯滤波的实现可以用两个一维高斯核分别两次加权实现,也就是先一维X方向卷积,得到的结果再一维Y方向卷积。当然也可以直接通过一个二维高斯核一次卷积实现。也就是二维卷积模板,由于水平有限,只说二维卷积模板怎么算。

高斯滤波

模板中每一个点的高斯系数可以由上面的公式计算,这样得到的是不是最终的模板呢?答案不是,需要归一化,也即是每一个点的系数要除以所有系数之和,这样才是最终的二维高斯模板。

这个里面有个小知识点,要想计算上面的系数,需要知道高斯函数的标准差σ (sigma),还需要知道选3x3还是5x5的模板,也就是模板要多大,实际应用的时候,这两者是有关系的,根据数理统计的知识,高斯分布的特点就是数值分布在(μ—3σ,μ+3σ)中的概率为0.9974,也就是模板的大小其实就是6σ这么大就OK了,但是6σ可能不是奇数,因为我们一定要保证有核心。所以模板窗口的大小一般采用1+2ceil(3nSigma) ceil是向上取整函数,例如ceil(0.6)=1;

计算得到模板,那就是直接卷积就OK,卷积的意思就是图像中的点附近的模板大小区域乘以高斯模板区域,得到的结果就是该点卷积后的结果。卷积的核心意义就是获取原始图像中像模板特征的性质。

3.计算梯度幅值和方向
图像的边缘可以指向不同方向,因此经典Canny算法用了四个梯度算子来分别计算水平,垂直和对角线方向的梯度。但是通常都不用四个梯度算子来分别计算四个方向。常用的边缘差分算子(如Rober,Prewitt,Sobel)计算水平和垂直方向的差分Gx和Gy。这样就可以如下计算梯度模和方向:

梯度模和方向

梯度角度θ范围从弧度-π到π,然后把它近似到四个方向,分别代表水平,垂直和两个对角线方向(0°,45°,90°,135°)。可以以±iπ/8(i=1,3,5,7)分割,落在每个区域的梯度角给一个特定值,代表四个方向之一。

这里选择Sobel算子计算梯度,相对于其他边缘算子,Sobel算子得出来的边缘粗大明亮。

sobel算子

这里选择Sobel算子计算梯度,相对于其他边缘算子,Sobel算子得出来的边缘粗大明亮。

4.非极大值抑制

非极大值抑制是进行边缘检测的一个重要步骤,通俗意义上是指寻找像素点局部最大值。沿着梯度方向,比较它前面和后面的梯度值进行了。

非极大值抑制

上图中左右图:g1、g2、g3、g4都代表像素点,很明显它们是c的八领域中的4个,左图中c点是我们需要判断的点,蓝色的直线是它的梯度方向,也就是说c要是局部极大值,它的梯度幅值M需要大于直线与g1g2和g2g3的交点,dtmp1和dtmp2处的梯度幅值。但是dtmp1和dtmp2不是整像素,而是亚像素,也就是坐标是浮点的,那怎么求它们的梯度幅值呢?线性插值,例如dtmp1在g1、g2之间,g1、g2的幅值都知道,我们只要知道dtmp1在g1、g2之间的比例,就能得到它的梯度幅值,而比例是可以靠夹角计算出来的,夹角又是梯度的方向。

写个线性插值的公式:设g1的幅值M(g1),g2的幅值M(g2),则dtmp1可以很得到:

M(dtmp1)=wM(g2)+(1-w)M(g1)

其中w=distance(dtmp1,g2)/distance(g1,g2)

distance(g1,g2) 表示两点之间的距离。实际上w是一个比例系数,这个比例系数可以通过梯度方向(幅角的正切和余切)得到。

右边图中的4个直线就是4个不同的情况,情况不同,g1、g2、g3、g4代表c的八领域中的4个坐标会有所差异,但是线性插值的原理都是一致的。

5.双阈值的选取

6、滞后边界跟踪
强边缘点可以认为是真的边缘。弱边缘点则可能是真的边缘,也可能是噪声或颜色变化引起的。为得到精确的结果,后者引起的弱边缘点应该去掉。通常认为真实边缘引起的弱边缘点和强边缘点是连通的,而又噪声引起的弱边缘点则不会。所谓的滞后边界跟踪算法检查一个弱边缘点的8连通领域像素,只要有强边缘点存在,那么这个弱边缘点被认为是真是边缘保留下来。

这个算法搜索所有连通的弱边缘,如果一条连通的弱边缘的任何一个点和强边缘点连通,则保留这条弱边缘,否则抑制这条弱边缘。搜索时可以用广度优先或者深度优先算法。

(二)OpenCV中API函数详解

1、函数原型

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

2、函数功能
Canny函数利用Canny算法来进行图像的边缘检测。

3、参数详解

4、实例

#include <opencv2/opencv.hpp>

using namespace cv;

int main()
{
    //载入原始图  
    Mat src = imread("lena.png");
    if (src.empty())
    {
        printf("image error!");
        return -1;
    }
    Mat src1 = src.clone();

    //显示原始图 
    imshow("【原始图】Canny边缘检测", src);

    //最简单的canny用法,拿到原图后直接用。
    //将原图像转换为灰度图像
    cvtColor(src, src, COLOR_BGR2GRAY);
    Canny(src, src, 150, 100, 3);
    imshow("【效果图】Canny边缘检测", src);

    //高阶的canny用法,转成灰度图,降噪,
    //用canny,最后将得到的边缘作为掩码,
    //拷贝原图到效果图上,得到彩色的边缘图

    Mat dst, edge, gray;

    // 创建与src同类型和大小的矩阵(dst)
    dst.create(src1.size(), src1.type());

    //将原图像转换为灰度图像
    cvtColor(src1, gray, COLOR_BGR2GRAY);

    //先用使用 3x3内核来降噪
    blur(gray, edge, Size(3, 3));

    //运行Canny算子
    Canny(edge, edge, 3, 9, 3);

    //将g_dstImage内的所有元素设置为0 
    dst = Scalar::all(0);

    //使用Canny算子输出的
    //边缘图g_cannyDetectedEdges作为掩码,
    //来将原图g_srcImage拷到目标图g_dstImage中
    src1.copyTo(dst, edge);

    //显示效果图 
    imshow("【效果图】Canny边缘检测彩色图像", dst);

    waitKey(0);
    return 0;
}

实验结果:

Canny算子边缘检测
上一篇下一篇

猜你喜欢

热点阅读