opencv4 学习 12 边缘检测

1、Sobel 算子和 Scharr 算子
原理:都是先计算当前像素的水平方向梯度和垂直方向梯度,然后取两梯度的平方和,再开方;也可以直接取两梯度的绝对值的和。

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;

int main(int argc, char* argv[])
{
    Mat image, src, src_gray;
    Mat grad;
    const String window_name = "Sobel Demo - Simple Edge Detector";
    int ksize = 1;
    int scale = 1;
    int delta = 0;
    int ddepth = CV_16S;

    String imageName = "lena.jpg";
    image = imread(imageName, IMREAD_COLOR);

    for(;;){
        GaussianBlur(image, src, Size(3,3), 0, 0, BORDER_DEFAULT);
        cvtColor(src, src_gray, COLOR_BGR2GRAY);

        Mat grad_x, grad_y;
        Mat abs_grad_x, abs_grad_y;

        Sobel(src_gray, grad_x, ddepth, 1, 0, ksize, scale, delta, BORDER_DEFAULT);
        Sobel(src_gray, grad_y, ddepth, 0, 1, ksize, scale, delta, BORDER_DEFAULT);

        convertScaleAbs(grad_x, abs_grad_x);
        convertScaleAbs(grad_y, abs_grad_y);

        addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad);

        imshow(window_name, grad);

        char key = (char) waitKey(0);
        if(key == 27) return EXIT_SUCCESS;
        if(key == 'k' || key == 'K') ksize = ksize < 30 ? ksize+2: -1;
        if(key == 's' || key == 'S') scale++;
        if(key == 'd' || key == 'D') delta++;
        if(key == 'r' || key == 'R') {scale=1, ksize=-1, delta = 0;} // when ksize = -1, it equal to Scharr function
    }
    return EXIT_SUCCESS;
}

2、Laplacian 算子

前面的算子都是计算像素的一阶导数,Laplacian 算子计算像素的二阶导数,其内部实际上也是调用Soble算子实现的。

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;

int main(int argc, char* argv[])
{
    Mat image, src, src_gray, dst;
    Mat grad;
    const String window_name = "Laplacian Demo - Simple Edge Detector";
    int kernel_size = 3;
    int scale = 1;
    int delta = 0;
    int ddepth = CV_16S;

    String imageName = "lena.jpg";
    image = imread(imageName, IMREAD_COLOR);

    GaussianBlur(image, src, Size(3,3), 0, 0, BORDER_DEFAULT);
    cvtColor(src, src_gray, COLOR_BGR2GRAY);

    Mat abs_dst;
    Laplacian(src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_CONSTANT);
    convertScaleAbs(dst, abs_dst);

    imshow(window_name, abs_dst);

    char key = (char) waitKey(0);

    return EXIT_SUCCESS;
}

3、 Canny 算子

步骤:

  1. 对图像进行高斯模糊
  2. 分别计算水平和垂直方向的梯度以及方向梯度之间的夹角
  3. 使用 Non-maximum suppression 去除冗余像素
  4. 设置两个阈值(upper, lower)过滤像素
    • 当像素梯度大于upper时,则为边缘
    • 当像素梯度小于lower时,则剔除
    • 当像素梯度位于lower和upper之间时,如果该像素和一个梯度大于upper的像素连接,则该像素则为边缘;否则,剔除
  5. canny 算法推荐的 upper:lower 比率在 2:1 和 3:1 之间
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
Mat src, src_gray;
Mat dst, detected_edges;

int lowThreshold = 0;
const int max_lowThreshold = 100;
const int ratio = 3;
const int kernel_size = 3;
const char* window_name = "Edge Map";

static void cannyThreshold(int, void*){
    blur(src_gray, detected_edges, Size(3,3));
    Canny(detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size);
    dst = Scalar::all(0);
    src.copyTo(dst, detected_edges);
    imshow(window_name, dst);
}

int main(int argc, char* argv[])
{
    src = imread("lena.jpg", IMREAD_COLOR);
    dst.create(src.size(), src.type());
    cvtColor(src,src_gray,COLOR_BGR2GRAY);
    namedWindow(window_name, WINDOW_AUTOSIZE);
    createTrackbar("Min Threshold:", window_name, &lowThreshold, max_lowThreshold, cannyThreshold);
    cannyThreshold(0,0);
    char key = (char) waitKey(0);

    return EXIT_SUCCESS;
}

 

参考:

https://docs.opencv.org/3.4/d2/d2c/tutorial_sobel_derivatives.html

https://docs.opencv.org/3.4/d5/db5/tutorial_laplace_operator.html

https://docs.opencv.org/3.4/da/d5c/tutorial_canny_detector.html

 

posted @ 2020-08-12 16:42  blackx  阅读(104)  评论(0编辑  收藏  举报