目录
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); // 使用Gxcv::Sobel(src, dst2, 3, 0, 1, 3); // 使用Gycv::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); // 使用Gxcv::Scharr(src, dst2, 3, 0, 1, 3); // 使用Gycv::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); // 使用Gxcv::Sobel(src, dst2, 3, 0, 1, 3); // 使用Gycv::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;
则梯度可以通过以下公式进行计算:;
上面的公式一般可以简化为:;
注:上面的代码都没有将图像先转化为灰度图,导致处理出现问题,需要添加以下代码:
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); // 使用Gxcv::Sobel(src, dst2, 3, 0, 1, 3); // 使用Gycv::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--原理
拉普拉斯算子模板函数:
理论公式:
上面的 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;}
② 结果: