opencv变换:Laplacian 算子

199 阅读3分钟

1. 目标

在本文中,将学习如何:

  • 使用 OpenCV 函数Laplacian()来实现Laplacian operator的离散模拟。

2. 理论

  1. 在之前的文章中,我们学习了如何使用Sobel Operator。这是基于这样一个事实,即在边缘区域,像素强度显示出“跳跃”或强度的大的变化。得到强度的一阶导数,观察到边缘的特征是最大值,如图所示:

    Laplace_Operator_Tutorial_Theory_Previous.jpg

  2. 但... 如果使用二阶导数会发生什么?

    Laplace_Operator_Tutorial_Theory_ddIntensity.jpg

    可以观察到二阶导数为零!因此,也可以使用这个标准来检测图像中的边缘。但是,请注意,零不仅会出现在边缘(它们实际上可以出现在其他无意义的位置);这可以通过在需要的地方应用过滤来解决。

Laplacian算子

  1. 从上面的解释,二阶导数可以用来检测边缘。由于图像是“2D”,需要在两个维度上求导。在这里,拉普拉斯算子就派上用场了。

  2. Laplacian算子定义为:

Laplace(f)=2fx2+2fy2 Laplace(f)= \frac{\partial^2f}{\partial x^2} +\frac{\partial^2f}{\partial y^2}

可以解释为梯度的散度

  1. 拉普拉斯算子在 OpenCV 中由函数Laplacian()实现。事实上,由于 Laplacian 使用图像的梯度,它在内部调用Sobel 算子来执行计算。

3. 代码

  1. 这个程序有什么作用?

    • 加载图像
    • 通过应用高斯模糊去除噪声,然后将原始图像转换为灰度
    • 将拉普拉斯算子应用于灰度图像并存储输出图像
    • 在窗口中显示结果
  2. 教程代码如下所示。也可以从这里下载

#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
int main( int argc, char** argv )
{
    // Declare the variables we are going to use
    Mat src, src_gray, dst;
    int kernel_size = 3;
    int scale = 1;
    int delta = 0;
    int ddepth = CV_16S;
    const char* window_name = "Laplace Demo";
    const char* imageName = argc >=2 ? argv[1] : "lena.jpg";
    src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
    // Check if image is loaded fine
    if(src.empty()){
        printf(" Error opening image\n");
        printf(" Program Arguments: [image_name -- default lena.jpg] \n");
        return -1;
    }
    // Reduce noise by blurring with a Gaussian filter ( kernel size = 3 )
    GaussianBlur( src, src, Size(3, 3), 0, 0, BORDER_DEFAULT );
    cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to grayscale
    Mat abs_dst;
    Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
    // converting back to CV_8U
    convertScaleAbs( dst, abs_dst );
    imshow( window_name, abs_dst );
    waitKey(0);
    return 0;
}

4. 代码解释

声明变量

 // Declare the variables we are going to use
    Mat src, src_gray, dst;
    int kernel_size = 3;
    int scale = 1;
    int delta = 0;
    int ddepth = CV_16S;
    const char* window_name = "Laplace Demo";

加载源图像

 const char* imageName = argc >=2 ? argv[1] : "lena.jpg";
 src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
    // Check if image is loaded fine
    if(src.empty()){
        printf(" Error opening image\n");
        printf(" Program Arguments: [image_name -- default lena.jpg] \n");
        return -1;
    }

平滑噪音

 // Reduce noise by blurring with a Gaussian filter ( kernel size = 3 )
    GaussianBlur( src, src, Size(3, 3), 0, 0, BORDER_DEFAULT );

灰度

 cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to grayscale

拉普拉斯算子

Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
  • 参数是:

    • src_gray:输入图像。
    • dst:目标(输出)图像
    • ddepth:目标图像的深度。由于我们的输入是CV_8U,我们定义ddepth = CV_16S以避免溢出
    • kernel_size:要在内部应用的 Sobel 算子的内核大小。我们在这个例子中使用 3。
    • scaledeltaBORDER_DEFAULT:我们将它们保留为默认值。

将输出转换为CV_8U图像

// converting back to CV_8U
    convertScaleAbs( dst, abs_dst );

显示结果

 imshow( window_name, abs_dst );
    waitKey(0);

5. 结果

  1. 编译上面的代码,运行,将图像的路径作为参数。例如,用作输入:

    Laplace_Operator_Tutorial_Original_Image.jpg

  2. 得到以下结果。注意树木和奶牛的轮廓是如何大致清晰地定义的(除了强度非常相似的区域,即奶牛头部周围)。另外,请注意,树木后面的房屋屋顶(右侧)是显著的标记。这是因为该区域的对比度更高。

    Laplace_Operator_Tutorial_Result.jpg