在该系列的第八篇文章中,咱们曾介绍过一阶导数和二阶导数对剖析边际的结论:

  • 一阶导数通常在图画中发生较粗的边际;

  • 二阶导数对精细细节,如细线、孤立点和噪声有较强的呼应;

  • 二阶导数在灰度斜坡和灰度台阶过渡处会发生双边际呼应;

  • 二阶导数的符号可用于确认边际的过渡是从亮到暗还是从暗到亮。

一阶导数算子(例如 Sobel 算子)经过对图画求导来确认图画的边际,数值绝对值较高的点对应了图画的边际。假如持续求二阶导,原先数值绝对值较高的点对应了过零点。因而,也能够经过找到二阶导数的过零点来检测边际。在某些情况下,找二阶导数的过零点可能更简单。

OpenCV 笔记(10):常用的边际检测算子—— Laplace、LoG

1. Laplace 算子

之前咱们曾介绍过二阶导数的 Laplace 算子能够经过差分近似来简化,其公式为

∇2f(x,y)=f(x+1,y)+f(x−1,y)+f(x,y+1)+f(x,y−1)−4f(x,y)nabla^2f(x,y)=f(x+1,y)+f(x-1,y)+ f(x,y+1)+f(x,y-1)-4f(x,y)

它的卷积核:

OpenCV 笔记(10):常用的边际检测算子—— Laplace、LoG

这是它的 4 邻域卷积核。

1.1 Laplace 算子的扩展

Laplace 算子是具有旋转不变性的各向同性的算子。

将 4 邻域的 Laplace 算子旋转 45 后,与原算子相加,就能够得到 8 邻域的算子。

OpenCV 笔记(10):常用的边际检测算子—— Laplace、LoG

这是它的 8 邻域卷积核。这个算子表明一个像素周围一圈 8 个像素的和与中间像素 8 倍的差,作为拉普拉斯核算结果。

别的,还有两个拉普拉斯卷积核,分别是对 4 邻域卷积核和 8 邻域卷积核取反。

OpenCV 笔记(10):常用的边际检测算子—— Laplace、LoG

1.2 图画的含糊检测

运用拉普拉斯改换对图画进行含糊检测的进程大致如下:

  • 对图画进行拉普拉斯改换,检测水平和垂直边际

  • 然后对拉普拉斯改换后输出的图画求方差

  • 假如图画足够清晰,输出图画的方差会大于给定阈值

  • 假如图画相对含糊,则拉普拉斯改换在图画中并不能检测到足够的细节,边际就越少,然后导致输出图画的方差小于给定阈值

该进程需求挑选适宜的阈值。

拉普拉斯算子能突出显示图画中包含快速梯度改变的区域,这些区域往往与边际有关。因而,假如一幅图画的方差较高,阐明图画中存在广泛的边际呼应,包含类边和非类边,这是一幅正常聚集图画的代表。但假如方差很低,那么表明图画中的边际呼应很小,几乎没有边际存在。因而,经过比较方差与预设阈值的巨细,能够判断图画是否含糊。

依照上面的进程完成了一个含糊检测的函数:

bool isImageBlurry(const char* inputFile, double threshold)
{
    Mat src = imread(inputFile);
    if (src.empty()) {
        printf("Image not loadedn");
        return false;
    }
    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    Mat dst, absDst;
    cv::Laplacian(gray, dst, CV_16S, 3);
    cv::convertScaleAbs(dst, absDst);
    Mat mean, stddev;
    double m = 0, sd = 0;
    meanStdDev(absDst, mean, stddev);
    m = mean.at<double>(0, 0);
    sd = stddev.at<double>(0, 0);
    double result = sd * sd;
    std::cout << "m: " << m << std::endl;
    std::cout << "StdDev: " << result << std::endl;
    return result <= threshold;
}

然后写一个程序来判断一下这张图是否是含糊的

OpenCV 笔记(10):常用的边际检测算子—— Laplace、LoG

int main(int argc,char *argv[])
{
    string fileName = ".../test.jpeg";
    bool result = isImageBlurry(fileName.c_str(),11.0);
    cout << "result =" << result <<endl;
    return 0;
}

输出结果:

m: 2.5213
StdDev: 6.31374
result = 1

阐明是含糊的图片。

Laplace 算子对噪声敏感,通常不适用于存在噪声的图画。

2. LoG 算子

LoG(Laplacian of Gaussian)边际检测算子是 David Courtnay Marr 和 Ellen Hildreth 在 1980 年一起提出的,也称为 Marr-Hildreth 算子,它依据图画的信噪比来求检测边际的最优滤波器。该算法先对图画进行高斯滑润处理,然后再与 Laplacian 算子进行卷积。稍后来解释为何是这样的。

先来回忆一下二维高斯函数的公式:

G(x,y)=122e−x2+y222G(x,y)=frac{1}{2pidelta^2}e^{-frac{x^2+y^2}{2delta^2}}

高斯函数的一阶导数和二阶导数,在许多算子中都会用到。例如一阶导数应用在 Canny 算子,二阶导数应用在 LoG 算子等等。

简单推导一下它的一阶导数:

∂G∂x=122∂e−x2+y222∂x=122e−x2+y222∗(−2×22)=(−124)xe−x2+y222frac{partial G}{partial x}=frac{1}{2pidelta^2}frac{partial e^{-frac{x^2+y^2}{2delta^2}}}{partial x}\=frac{1}{2pidelta^2}e^{-frac{x^2+y^2}{2delta^2}}*(-frac{2x}{2delta^2}) \=(-frac{1}{2pidelta^4})xe^{-frac{x^2+y^2}{2delta^2}}

同理:

∂G∂y=(−124)ye−x2+y222frac{partial G}{partial y}=(-frac{1}{2pidelta^4})ye^{-frac{x^2+y^2}{2delta^2}}

还有推导一下它的二阶导数:

∂2G∂x2=∂(−124)xe−x2+y222∂x=(−124)∂xe−x2+y222∂x=(−124)(1∗e−x2+y222−xe−x2+y222∗2×22)=(−124)(1−x22)e−x2+y222frac{partial^2 G}{partial x^2}=frac{partial (-frac{1}{2pidelta^4})xe^{-frac{x^2+y^2}{2delta^2}}}{partialx} \=(-frac{1}{2pidelta^4})frac{partialxe^{-frac{x^2+y^2}{2delta^2}}}{partial x} \=(-frac{1}{2pidelta^4})(1*e^{-frac{x^2+y^2}{2delta^2}} – xe^{-frac{x^2+y^2}{2delta^2}}*frac{2x}{2delta^2}) \=(-frac{1}{2pidelta^4})(1-frac{x^2}{delta^2})e^{-frac{x^2+y^2}{2delta^2}}

同理:

∂2G∂y2=(−124)(1−y22)e−x2+y222frac{partial^2 G}{partial y^2}=(-frac{1}{2pidelta^4})(1-frac{y^2}{delta^2})e^{-frac{x^2+y^2}{2delta^2}}

将高斯函数代入拉普拉斯算子,可得 LoG 算子:

∇2G(x,y)=∂2G∂x2+∂2G∂y2=(−124)(1−x22)e−x2+y222+(−124)(1−y22)e−x2+y222=x2+y2−2226e−x2+y222nabla^2G(x,y)=frac{partial^2 G}{partial x^2}+frac{partial^2 G}{partial y^2} \=(-frac{1}{2pidelta^4})(1-frac{x^2}{delta^2})e^{-frac{x^2+y^2}{2delta^2}}+(-frac{1}{2pidelta^4})(1-frac{y^2}{delta^2})e^{-frac{x^2+y^2}{2delta^2}} \=frac{x^2+y^2-2delta^2}{2pidelta^6}e^{-frac{x^2+y^2}{2delta^2}}

Marr-Hildreth 算法如下:

  1. 首先让 LoG 核与一幅输入图画卷积:

g(x,y)=[∇2G(x,y)]★f(x,y)g(x,y)=[nabla^2G(x,y)]bigstar f(x,y)

  1. 寻觅 g(x,y) 的过零点来确认 f(x,y) 的边际位置。因为拉普拉斯改换和卷积都是线性运算,因而上式能够改成

g(x,y)=∇2[G(x,y)★f(x,y)]g(x,y)=nabla^2[G(x,y)bigstar f(x,y)]

其间,f(x,y) 是输入图画,g(x,y) 是输出图画。

这样正好解释了之前说的,该算法先对图画进行高斯滑润处理,然后再与 Laplacian 算子进行卷积。因为先运用高斯滤波器对图画进行滑润处理,能够削减噪声和细节,然后运用拉普拉斯算子对滤波后的图画进行边际检测。

它的长处是能够有用去除噪声,同时保存图画中的真实边际。比较 Laplace 算子,LoG 算子具有更好的边际定位才能和抗噪声。可是它也存在一些缺点,核算量相对较大。

下图是负 LoG 算子的三维图画,看上去很像“墨西哥草帽”。所以,在业界也被称为墨西哥草帽小波(Mexican hat wavelet)。

OpenCV 笔记(10):常用的边际检测算子—— Laplace、LoG

OpenCV 笔记(10):常用的边际检测算子—— Laplace、LoG

负 LoG 算子可用 5*5 的模版近似表明

OpenCV 笔记(10):常用的边际检测算子—— Laplace、LoG

下面用高斯含糊和拉普拉斯改换来完成 LoG :

int main(int argc,char *argv[])
{
    Mat src = imread(".../street.jpg");
    imshow("src",src);
    Mat dst, gray, edge;
    cv::GaussianBlur(src, dst, cv::Size(3, 3), 0 ,0); // 高斯含糊 去除噪声
    cv::cvtColor(dst, gray, cv::COLOR_BGR2GRAY); // 灰度化
    cv::Laplacian(gray, edge, CV_16S, 3); // 运用拉普拉斯算子提取边际
    cv::convertScaleAbs(edge, edge);
    imshow("LoG", edge);
    waitKey(0);
    return 0;
}

OpenCV 笔记(10):常用的边际检测算子—— Laplace、LoG

3. 总结

本文介绍了 Laplace 算子、LoG 算子,它们都是二阶导数的边际算子。

特别是 LoG 算子在 Laplace 算子的基础上引入了高斯滤波,能够在必定程度上克服噪声的影响。但它仍旧有必定的局限性,不过这种思维的引入对后续图画特征研讨起到了积极作用,被许多后续的算法所采纳。