互联网@时事传播图像处理OpenCv

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

2019-04-25  本文已影响11人  307656af5a04

上一篇我们学习了边缘检测相关的Sobel算了,经过学习之后,我们应该可以对Sobel算子进行熟悉的运用,那么今天,我们一起来学习一下边缘检测相关的Laplacian(拉普拉斯)算子以及scharr滤波器。

一、理论

Laplacian 算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度grad()的散度div(),因此如果f是二阶可微的实函数,则f的拉普拉斯算子定义为:

(1) f的拉普拉斯算子也是笛卡儿坐标系xi中的所有非混合二阶偏导数求和;

(2) 作为一个二阶微分算子,拉普拉斯算子把C函数映射到C函数,对于k ≥ 2。表达式(1)(或(2))定义了一个算子Δ :C(R) → C(R),或更一般地,定义了一个算子Δ : C(Ω) → C(Ω),对于任何开集Ω。

根据图像处理的原理我们知道,二阶导数可以用来进行检测边缘 。 因为图像是 “二维”,我们需要在两个方向进行求导,使用Laplacian算子将会使求导过程变得简单。

由于 Laplacian使用了图像梯度,它内部的代码其实是调用了 Sobel 算子的。

另附一个小tips:让一幅图像减去它的Laplacian可以增强对比度

离散函数导数
离散函数的导数退化成了差分,一维一阶差分公式和二阶差分公式分别为

一维一阶差分公式和二阶差分公式

Laplace算子的差分形式
分别对Laplace算子x,y两个方向的二阶导数进行差分就得到了离散函数的Laplace算子
在一个二维函数f(x,y)中,x,y两个方向的二阶差分分别为,

x,y两个方向的二阶差分

所以Laplace算子的差分形式为,

Laplacian算子的差分形式

函数的拉普拉斯算子也是该函数的Hessian 矩阵的迹,可以证明,它具有各向同性,即与坐标轴方向无关,坐标轴旋转后梯度结果不变。如果邻域系统是4 邻域,Laplacian 算子的模板为:

4邻域Laplacian 算子的模板

如果邻域系统是8 邻域,Laplacian 算子的模板为:

8邻域Laplacian 算子的模板

Laplacian 算子对噪声比较敏感,所以图像一般先经过平滑处理,因为平滑处理也是用模板进行的,所以,通常的分割算法都是把Laplacian 算子和平滑算子结合起来生成一个新的模板。

二、OpenCV中Laplacian()函数详解

1、函数原型

void Laplacian(InputArray src, 
    OutputArray dst, 
    int ddepth, 
    int ksize = 1, 
    double scale = 1, 
    double delta = 0, 
    intborderType = 
    BORDER_DEFAULT);

2、函数功能
Laplacian函数可以计算出图像经过拉普拉斯变换后的结果。

3、参数详解

ksize

当ksize = 1,拉普拉斯算子默认采用下面的卷积核

kszie =1 卷积核

4、实例:实现对摄像头的图像进行Laplacian边缘检测

#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <ctype.h>
#include <stdio.h>
#include <iostream>

using namespace cv;
using namespace std;

static void help()
{
    cout <<
        "\nThis program demonstrates \
        Laplace point/edge detection \
        using OpenCV function Laplacian()\n"

        "It captures from the camera of \
        your choice: 0, 1, ... default 0\n"
        "Call:\n"

        "./laplace -c=<camera #, default 0> \
        -p=<index of the frame to be \
        decoded/captured next>\n" << endl;
}

// 三种滤波
enum { GAUSSIAN, BLUR, MEDIAN };

int sigma = 3;

// 默认滤波为高斯滤波
int smoothType = GAUSSIAN;

const char* keys =
{
    "{ c | 0 | }{ p | | }"
};

int main(int argc, char** argv)
{
    cv::CommandLineParser parser(argc, 
        argv, keys );

    help();

    VideoCapture cap;  // 摄像机接口

    // 获取摄像机参数
    string camera = parser.get<string>("c");
    if (camera.size() == 1 && isdigit(camera[0]))
        cap.open(parser.get<int>("c"));
    else
        cap.open(samples::findFileOrKeep(camera));

    // 判断相机是否打开
    if (!cap.isOpened())
    {
        cerr << "Can't open camera/video stream: " 
            << camera << endl;

        return 1;
    }
    // 打开相机后,输出相机的相关信息
    cout << "Video " << parser.get<string>("c") <<
        ": width=" << cap.get(CAP_PROP_FRAME_WIDTH) <<
        ", height=" << cap.get(CAP_PROP_FRAME_HEIGHT) <<
        ", nframes=" << cap.get(CAP_PROP_FRAME_COUNT) << endl;

    int pos = 0;

    if (parser.has("p"))
    {
        pos = parser.get<int>("p");
    }

    if (!parser.check())
    {
        parser.printErrors();
        return -1;
    }

    if (pos != 0)
    {
        cout << "seeking to frame #" << pos << endl;

        // 设置视频的播放位置到pos
        if (!cap.set(CAP_PROP_POS_FRAMES, pos))
        {
            cerr << "ERROR: seekeing is not supported" << endl;
        }
    }

    namedWindow("Laplacian", 
        WINDOW_AUTOSIZE);

    // 创建滑动条
    createTrackbar("Sigma", 
        "Laplacian", 
        &sigma, 
        15,
        0);

    Mat smoothed, laplace, result;

    for (;;)
    {
        Mat frame;
        // 将相机中的每一帧图像存储到frame中
        cap >> frame;

        if (frame.empty())
            break;

        // 卷积核大小
        int ksize = (sigma * 5) | 1;

        // 高斯滤波
        if (smoothType == GAUSSIAN)
            GaussianBlur(frame, smoothed, 
                Size(ksize, ksize), 
                sigma, sigma);

        // 均值滤波
        else if (smoothType == BLUR)
            blur(frame, smoothed, 
                Size(ksize, ksize));

        // 中值滤波
        else
            medianBlur(frame, smoothed, ksize);

        // 进行拉普拉斯边缘检测处理
        Laplacian(smoothed, laplace, CV_16S, 5);

        convertScaleAbs(laplace, result, 
            (sigma + 1)*0.25);

        imshow("Laplacian", result);

        // 等待30毫秒在读取下一帧图像
        // 即帧率为30
        char c = (char)waitKey(30);
        
        // 单击空格切换各种滤波模式
        if (c == ' ')
            smoothType = smoothType == GAUSSIAN ? BLUR : 
            smoothType == BLUR ? MEDIAN : GAUSSIAN;

        // 退出循环
        if (c == 'q' || c == 'Q' || c == 27)
            break;
    }
    return 0;
}

三、实例中一些函数的解释

1、CommandLineParser类

功能:对命令行进行解析。
构造函数:

CommandLineParser(int argc, 
    const char* const argv[], 
    const String& keys);

参数详解:
第一个参数:argc 命令行参数的数量;
第二个参数:命令行参数的命令;
第三个参数:第3个就是刚刚定义的keys了,keys的结构有一定规律,比如说"{c |camera |false | use camera or not}" 都是用大括号和双引号引起来,然后中间的内容分成4断,用”|”分隔开,分别表示简称,文件来源,文件值和帮助语句。第二行和第三行表示打开摄像头和打开文件,文件的文件名等都在keys指针中了。

2、convertScaleAbs()函数
函数原型:

void convertScaleAbs(InputArray src,
    OutputArray dst,
    double alpha = 1, 
    double beta = 0);

函数功能:
计算图像每个像素的绝对值,并将结果转换为8位无符号整数。

参数详解:

转换公式

convertScaleAbs公式

四、scharr滤波器

scharr滤波器在OpenCV中主要是配合Sobel算子的运算而存在的。

函数原型

void Scharr(InputArray src, 
    OutputArray dst, 
    int ddepth,
    int dx, 
    int dy, 
    double scale = 1, 
    double delta = 0,
    int borderType = 
    BORDER_DEFAULT);

函数功能
该函数的功能与sobel算子基本上上一样,但是Scharr滤波器仅作用于大小为3的内核,具有和sobel算子一样的速度,但结果更为精确。

参数详解

实例

#include <opencv2/opencv.hpp>

using namespace cv;

int main()
{
    //创建 grad_x 和 grad_y 矩阵
    Mat grad_x, grad_y;
    Mat abs_grad_x, abs_grad_y, dst;

    //载入原始图  
    Mat src = imread("lena.png");

    //显示原始图 
    imshow("【原始图】Scharr滤波器", src);

    //求 X方向梯度
    Scharr(src, grad_x, CV_16S, 
        1, 0, 1, 0, BORDER_DEFAULT);

    convertScaleAbs(grad_x, abs_grad_x);

    imshow("【效果图】 X方向Scharr", abs_grad_x);

    //求Y方向梯度
    Scharr(src, grad_y, CV_16S,
        0, 1, 1, 0, BORDER_DEFAULT);

    convertScaleAbs(grad_y, abs_grad_y);

    imshow("【效果图】Y方向Scharr", abs_grad_y);

    //合并梯度(近似)
    addWeighted(abs_grad_x, 0.5, 
        abs_grad_y, 0.5, 
        0, dst);

    //显示效果图
    imshow("【效果图】合并梯度后Scharr", dst);

    waitKey(0);
    return 0;
}

实验结果:

Scharr原图 Scharr效果图

好了,今天的OpenCV学到这里就结束了,喜欢的朋友可以给我点个赞哦!!!

上一篇 下一篇

猜你喜欢

热点阅读