飞道的博客

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

314人阅读  评论(0)

目录

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--代码实例


  
  1. #include <opencv2/opencv.hpp>
  2. # include<cstdio>
  3. int main(int argc, char** argv){
  4. cv::Mat src;
  5. src = cv:: imread( "C:/Users/Liujinfu/Desktop/opencv_bilibili/test1.jpg");
  6. if (src. empty()){
  7. printf( "could not load image..\n");
  8. return -1;
  9. }
  10. cv:: imshow( "input", src);
  11. cv::Mat dst1, dst2;
  12. cv:: Sobel(src, dst1, 3, 1, 0, 3); // 使用Gx
  13. cv:: Sobel(src, dst2, 3, 0, 1, 3); // 使用Gy
  14. cv:: convertScaleAbs(dst1, dst1);
  15. cv:: convertScaleAbs(dst2, dst2);
  16. cv:: imshow( "dst1", dst1);
  17. cv:: imshow( "dst2", dst2);
  18. cv:: waitKey( 0);
  19. return 0;
  20. }

         由上图结果可知,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 算子如下图所示:

         代码测试:


  
  1. #include <opencv2/opencv.hpp>
  2. # include<cstdio>
  3. int main(int argc, char** argv){
  4. cv::Mat src;
  5. src = cv:: imread( "C:/Users/Liujinfu/Desktop/opencv_bilibili/test1.jpg");
  6. if (src. empty()){
  7. printf( "could not load image..\n");
  8. return -1;
  9. }
  10. cv:: imshow( "input", src);
  11. cv::Mat dst1, dst2;
  12. cv:: Scharr(src, dst1, 3, 1, 0, 3); // 使用Gx
  13. cv:: Scharr(src, dst2, 3, 0, 1, 3); // 使用Gy
  14. cv:: convertScaleAbs(dst1, dst1);
  15. cv:: convertScaleAbs(dst2, dst2);
  16. cv:: imshow( "dst1", dst1);
  17. cv:: imshow( "dst2", dst2);
  18. cv:: waitKey( 0);
  19. return 0;
  20. }

1-5--扩展操作

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


  
  1. #include <opencv2/opencv.hpp>
  2. # include<cstdio>
  3. int main(int argc, char** argv){
  4. cv::Mat src;
  5. src = cv:: imread( "C:/Users/Liujinfu/Desktop/opencv_bilibili/test1.jpg");
  6. if (src. empty()){
  7. printf( "could not load image..\n");
  8. return -1;
  9. }
  10. cv:: imshow( "input", src);
  11. cv::Mat dst1, dst2;
  12. cv:: Sobel(src, dst1, 3, 1, 0, 3); // 使用Gx
  13. cv:: Sobel(src, dst2, 3, 0, 1, 3); // 使用Gy
  14. cv:: convertScaleAbs(dst1, dst1);
  15. cv:: convertScaleAbs(dst2, dst2);
  16. cv:: imshow( "dst1", dst1);
  17. cv:: imshow( "dst2", dst2);
  18. cv::Mat dst3 = cv:: Mat(dst1. size(), dst1. type()); // 初始化图像
  19. // // 遍历像素值,融合两幅图像
  20. // for(int row = 0; row < dst1.rows; row++){
  21. // for(int col = 0; col < dst1.cols; col++){
  22. // int x = dst1.at<uchar>(row, col);
  23. // int y = dst2.at<uchar>(row, col);
  24. // int xy = x + y;
  25. // //printf("%d \n", xy);
  26. // dst3.at<uchar>(row, col) = cv::saturate_cast<uchar>(xy);
  27. // }
  28. // }
  29. cv:: addWeighted(dst1, 0.5, dst2, 0.5, 0, dst3);
  30. cv:: imshow( "dst3", dst3);
  31. cv:: waitKey( 0);
  32. return 0;
  33. }

② 计算梯度

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

        则梯度可以通过以下公式进行计算:

        上面的公式一般可以简化为:

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

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

修正代码:


  
  1. # include <opencv2/opencv.hpp>
  2. # include <cstdio>
  3. # include <iostream>
  4. int main(int argc, char** argv){
  5. cv::Mat src;
  6. src = cv:: imread( "C:/Users/Liujinfu/Desktop/opencv_bilibili/test1.jpg");
  7. if (src. empty()){
  8. printf( "could not load image..\n");
  9. return -1;
  10. }
  11. cv:: imshow( "input", src);
  12. cv:: cvtColor(src, src, cv::COLOR_BGR2GRAY);
  13. cv::Mat dst1, dst2;
  14. cv:: Sobel(src, dst1, 3, 1, 0, 3); // 使用Gx
  15. cv:: Sobel(src, dst2, 3, 0, 1, 3); // 使用Gy
  16. cv:: convertScaleAbs(dst1, dst1);
  17. cv:: convertScaleAbs(dst2, dst2);
  18. cv:: imshow( "dst1", dst1);
  19. cv:: imshow( "dst2", dst2);
  20. std::cout << "type:" << dst1. type() << std::endl;
  21. cv::Mat dst3 = cv:: Mat(dst1. size(), dst1. type()); // 初始化图像
  22. // 遍历像素值,融合两幅图像
  23. for( int row = 0; row < dst1.rows; row++){
  24. for( int col = 0; col < dst1.cols; col++){
  25. int x = dst1. at<uchar>(row, col);
  26. int y = dst2. at<uchar>(row, col);
  27. int xy = x + y;
  28. dst3. at<uchar>(row, col) = cv:: saturate_cast<uchar>(xy);
  29. }
  30. }
  31. //cv::addWeighted(dst1, 0.5, dst2, 0.5, 0, dst3);
  32. cv:: imshow( "dst3", dst3);
  33. cv:: waitKey( 0);
  34. return 0;
  35. }

2--laplacian算子

2-1--原理

        拉普拉斯算子模板函数:

         理论公式:

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

2-2--Opencv API

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

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

① 代码:


  
  1. #include <opencv2/opencv.hpp>
  2. # include<cstdio>
  3. int main(int argc, char** argv){
  4. cv::Mat src;
  5. src = cv:: imread( "C:/Users/Liujinfu/Desktop/opencv_bilibili/test1.jpg");
  6. if (src. empty()){
  7. printf( "could not load image..\n");
  8. return -1;
  9. }
  10. cv:: imshow( "input", src);
  11. cv::Mat dst, gray, edge;
  12. cv:: GaussianBlur(src, dst, cv:: Size( 3, 3), 0 , 0); // 高斯模糊 去除噪声
  13. cv:: cvtColor(dst, gray, cv::COLOR_BGR2GRAY); // 灰度化
  14. cv:: Laplacian(gray, edge, 3, 3); // 使用拉普拉斯算子提取边缘
  15. cv:: convertScaleAbs(edge, edge);
  16. cv:: imshow( "output", edge);
  17. cv:: waitKey( 0);
  18. return 0;
  19. }

② 结果:


转载:https://blog.csdn.net/weixin_43863869/article/details/128377755
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场