目录
补充:
第 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;
则梯度可以通过以下公式进行计算:;
上面的公式一般可以简化为:;
注:上面的代码都没有将图像先转化为灰度图,导致处理出现问题,需要添加以下代码:
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--原理
拉普拉斯算子模板函数:
理论公式:
上面的 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;
-
}
② 结果:
转载:https://blog.csdn.net/weixin_43863869/article/details/128377755