本节学习的是卷积的应用,利用Sobel算子来进行图像边缘的提取
-
图像的边缘:
图像最基本的特征是边缘,边缘是图像性区域和另一个属性区域的交接处,是区域属性发生突变的地方,是图像中不确定性最大的地方,也是图像信息最集中的地方,图像的边缘包含着丰富的信息。边缘就是像素值发生跃迁的地方,我们从其像素值上可以看出,图像边缘就是图像像素变化最快的地方,也就是说是其导数最大的点。
-
Sobel算子与图像边缘提取的关系:
上面我们已经分析出来要想提取图像的边缘我们就要对图像进行一阶求导,找出其一阶导数最大的点。借助Sobel算子,进行卷积操作可以实现对图像像素进行一阶求导的效果。
Sobel算子概述:
Sobel算子是离散微分算子,可以用来计算图像灰度的近似梯度(因此在后面的代码中我们才需要先将图像转成灰度图像)
Sobel算子功能集合了高斯平滑与微分求导
Sobel算子又被成为一阶微分算子(Laplace算子是二阶求导算子),求导算子,在水平和垂直两个方向上求导,得到图像x方向与y方向的梯度图像
水平梯度:
垂直梯度:
上述两个矩阵是通过将这两个矩阵与原图像的矩阵进行运算,因为水平梯度与垂直梯度矩阵的权重不同,显而易见这就扩大了原图像的像素值之间的差异,达到了提取图像边缘的效果。
最终图像梯度:
-
用到的相关函数:
OpenCV中提供了两个函数来实现Sobel算子
函数①:Sobel函数CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize = 3, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
参数分析:
ddepth为输出图像的深度,在前面我们使用到类似的参数时我们通常都是使该值为-1,也就是说让输入图像的深度与输出图像的深度相同,但是在处理提取图像边缘的过程中,通过Sobel算子的运算,我们计算出来的图像可能其深度已经超过原图像的深度,因而我们通常要设置比输入图像深度更高的参数。
dx表示x方向的几阶导数
dy表示y方向的几阶导数
对x方向求梯度也就是dx=1,dy=0,反之对y则相反
ksize表示sobel算子的矩阵大小,必须为奇数
函数②:Scharr函数CV_EXPORTS_W void Scharr( InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
scharr函数的参数与sobel函数的参数基本相同
-
实验代码及内容:
#include<iostream> #include<opencv2\opencv.hpp> using namespace std; using namespace cv; int main() { Mat src, dst, x_dst, y_dst; src = imread("H:/opencv_images/test.jpg"); if (src.empty()) { cout << "could not load the images" << endl; return -1; } namedWindow("original_images", CV_WINDOW_AUTOSIZE); imshow("original_images", src); namedWindow("sobelx_change", CV_WINDOW_AUTOSIZE); namedWindow("sobely_change", CV_WINDOW_AUTOSIZE); namedWindow("sobelxy_change", CV_WINDOW_AUTOSIZE); GaussianBlur(src, dst, Size(3, 3), 0, 0); cvtColor(dst, dst, CV_BGR2GRAY); // Scharr(dst, x_dst, CV_16S, 1, 0); // Scharr(dst, y_dst, CV_16S, 0, 1); Sobel(dst, x_dst, CV_16S, 1, 0, 3); Sobel(dst, y_dst, CV_16S, 1, 0, 3); convertScaleAbs(x_dst, x_dst); convertScaleAbs(y_dst, y_dst); // addWeighted(x_dst, 0.5, y_dst, 0.5, 0, xy_dst); imshow("sobelx_change", x_dst); imshow("sobely_change", y_dst); Mat xy_dst = Mat(x_dst.size(), x_dst.type()); for (int row = 0; row < x_dst.rows; row++) { for (int col = 0; col < y_dst.cols; col++) { int x = x_dst.at<uchar>(row, col); int y = y_dst.at<uchar>(row, col); int xy = x + y; xy_dst.at<uchar>(row, col) = saturate_cast<uchar>(xy); } } imshow("sobelxy_change", xy_dst); waitKey(0); destroyAllWindows(); return 0; }