Opencv(C++)笔记--Sobel算子和laplacian算子

目录

1--Sobel算子

1-1--原理

1-2--Opencv API

1-3--代码实例

1-4--Scharr算子

1-5--扩展操作

2--laplacian算子

2-1--原理

2-2--Opencv API

2-3--代码实例提取边缘


补充:

        第 1 节的部分代码博主忘了将源图像转换为灰度图进行处理,需要手动添加以下代码:

cv::cvtColor(src, src, cv::COLOR_BGR2GRAY);

1--Sobel算子

1-1--原理

        Sobel算子的构成如下图所示:

        基于 Sobel 算子可以实现对图像的边缘检测,Gx常用于提取纵向边缘,Gy则常用于提取横向边缘;

1-2--Opencv API

         在 ddeth 参数上,使用 cv::CV_16S 处理 8 位原图像,因为经过 Sobel 算子处理,像素值会发生溢出:

1-3--代码实例

#include <opencv2/opencv.hpp>
# include<cstdio>

int main(int argc, char** argv){
    cv::Mat src;
    src = cv::imread("C:/Users/Liujinfu/Desktop/opencv_bilibili/test1.jpg");
    if (src.empty()){
        printf("could not load image..\n");
        return -1;
    }
    cv::imshow("input", src);
    
    cv::Mat dst1, dst2;
    cv::Sobel(src, dst1, 3, 1, 0, 3); // 使用Gx
    cv::Sobel(src, dst2, 3, 0, 1, 3); // 使用Gy

    cv::convertScaleAbs(dst1, dst1);
    cv::convertScaleAbs(dst2, dst2);
    cv::imshow("dst1", dst1);
    cv::imshow("dst2", dst2);

    cv::waitKey(0);

    return 0;
}

         由上图结果可知,Gx能够较好地检测纵向边缘,而Gy能够较好地检测横向边缘;

        上面的代码用到了 cv::convertScaleAbs() 函数,其作用是将任意类型的数据转化为 CV_8U;具体数据处理方法如下:

① 对于 src * alpha + beta 的结果,如果是负值且大于 -255,则取绝对值;

② 对于 src * alpha + beta 的结果,如果大于 255,则取 255;

③ 对于 src * alpha + beta 的结果,如果是负值且小于 -255,则取 255;

④ 对于 src * alpha + beta 的结果,如果在 0 - 255 之间,则保持不变;

1-4--Scharr算子

        OpenCV 提供了一个类似 Sobel 算子的函数,即 cv::Scharr(),其 API 如下:

        cv::Scharr() 对应的 Scharr 算子如下图所示:

         代码测试:

#include <opencv2/opencv.hpp>
# include<cstdio>

int main(int argc, char** argv){
    cv::Mat src;
    src = cv::imread("C:/Users/Liujinfu/Desktop/opencv_bilibili/test1.jpg");
    if (src.empty()){
        printf("could not load image..\n");
        return -1;
    }
    cv::imshow("input", src);
    
    cv::Mat dst1, dst2;
    cv::Scharr(src, dst1, 3, 1, 0, 3); // 使用Gx
    cv::Scharr(src, dst2, 3, 0, 1, 3); // 使用Gy

    cv::convertScaleAbs(dst1, dst1);
    cv::convertScaleAbs(dst2, dst2);
    cv::imshow("dst1", dst1);
    cv::imshow("dst2", dst2);

    cv::waitKey(0);
    return 0;
}

1-5--扩展操作

① 将 Gx 和 Gy 的结果进行融合

#include <opencv2/opencv.hpp>
# include<cstdio>

int main(int argc, char** argv){
    cv::Mat src;
    src = cv::imread("C:/Users/Liujinfu/Desktop/opencv_bilibili/test1.jpg");
    if (src.empty()){
        printf("could not load image..\n");
        return -1;
    }
    cv::imshow("input", src);
    
    cv::Mat dst1, dst2;
    cv::Sobel(src, dst1, 3, 1, 0, 3); // 使用Gx
    cv::Sobel(src, dst2, 3, 0, 1, 3); // 使用Gy

    cv::convertScaleAbs(dst1, dst1);
    cv::convertScaleAbs(dst2, dst2);
    cv::imshow("dst1", dst1);
    cv::imshow("dst2", dst2);

    cv::Mat dst3 = cv::Mat(dst1.size(), dst1.type()); // 初始化图像
    // // 遍历像素值,融合两幅图像
    // for(int row = 0; row < dst1.rows; row++){
    //     for(int col = 0; col < dst1.cols; col++){
    //         int x = dst1.at<uchar>(row, col);
    //         int y = dst2.at<uchar>(row, col);
    //         int xy = x + y;
    //         //printf("%d \n", xy);
    //         dst3.at<uchar>(row, col) = cv::saturate_cast<uchar>(xy);
    //     }
    // }
    cv::addWeighted(dst1, 0.5, dst2, 0.5, 0, dst3);
    cv::imshow("dst3", dst3);
    cv::waitKey(0);

    return 0;
}

② 计算梯度

        利用 Gx 和 Gy 可以计算出X方向和Y方向的梯度 Grad_x 和 Grad_y;

        则梯度可以通过以下公式进行计算:Grad = \left (Grad_x^{2} + Grad_y^{2} \right )^{\frac{1}{2}}

        上面的公式一般可以简化为:Grad = \left | Grad_x \right | + \left | Grad_y \right |

注:上面的代码都没有将图像先转化为灰度图,导致处理出现问题,需要添加以下代码:

cv::cvtColor(src, src, cv::COLOR_BGR2GRAY);

修正代码:

# include <opencv2/opencv.hpp>
# include <cstdio>
# include <iostream>

int main(int argc, char** argv){
    cv::Mat src;
    src = cv::imread("C:/Users/Liujinfu/Desktop/opencv_bilibili/test1.jpg");
    if (src.empty()){
        printf("could not load image..\n");
        return -1;
    }
    cv::imshow("input", src);
    cv::cvtColor(src, src, cv::COLOR_BGR2GRAY);
    
    cv::Mat dst1, dst2;
    cv::Sobel(src, dst1, 3, 1, 0, 3); // 使用Gx
    cv::Sobel(src, dst2, 3, 0, 1, 3); // 使用Gy
 
    cv::convertScaleAbs(dst1, dst1);
    cv::convertScaleAbs(dst2, dst2);
    cv::imshow("dst1", dst1);
    cv::imshow("dst2", dst2);
    std::cout << "type:" << dst1.type() << std::endl;
    cv::Mat dst3 = cv::Mat(dst1.size(), dst1.type()); // 初始化图像
    // 遍历像素值,融合两幅图像
    for(int row = 0; row < dst1.rows; row++){
        for(int col = 0; col < dst1.cols; col++){
            int x = dst1.at<uchar>(row, col);
            int y = dst2.at<uchar>(row, col);
            int xy = x + y;
            dst3.at<uchar>(row, col) = cv::saturate_cast<uchar>(xy);
        }
    }
    //cv::addWeighted(dst1, 0.5, dst2, 0.5, 0, dst3);
    cv::imshow("dst3", dst3);
    cv::waitKey(0);
 
    return 0;
}

2--laplacian算子

2-1--原理

        拉普拉斯算子模板函数:

         理论公式:dst = \Delta src = \frac{\delta^{2} src}{\delta^{2} x} + \frac{\delta^{2} src}{\delta^{2} y}

        上面的 Sobel 算子利用的是一阶导数,而拉普拉斯算子利用的是二阶导数;

2-2--Opencv API

        cv::Laplacian()通常结合高斯滤波来提取边缘,因为拉普拉斯算子是一种二阶导数算子,其噪声十分敏感;

2-3--代码实例提取边缘

① 代码:

#include <opencv2/opencv.hpp>
# include<cstdio>

int main(int argc, char** argv){
    cv::Mat src;
    src = cv::imread("C:/Users/Liujinfu/Desktop/opencv_bilibili/test1.jpg");
    if (src.empty()){
        printf("could not load image..\n");
        return -1;
    }
    cv::imshow("input", src);
    
    cv::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, 3, 3); // 使用拉普拉斯算子提取边缘
    cv::convertScaleAbs(edge, edge);
    
    cv::imshow("output", edge);
    cv::waitKey(0);

    return 0;
}

② 结果:

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值