OpenCV-C++ Sobel算子使用
目录
- Sobel算子
- 图像边缘提取
Sobel算子
Sobel算子主要用于边缘检测;
- 边缘:是像素值发生跃迁的地方,是图像的显著特征之一,在图像特征提取, 对象检测, 模式识别等方面都有重要的作用;
- 如何提取边缘,对图像求它的一阶导数;delta = f(x) - f(x-1), delta越大,说明在x方向变化越大;
sobel算子:
- 是离散微分算子,用来计算图像灰度的近似梯度
- sobel算子功能集合高斯平滑和微分求导;
- 又被称为一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到图像x方法与y方向梯度图像;
那么最终的图像梯度:
\[G = \sqrt{G_x^2+G_y^2} \]
可以近似为:
\[G = |G_x| + |G_y| \]
改进的版本Scharr算子:
当内核大小为3时,以上Sobel可能产生比较明显的误差(毕竟,Sobel算子只是求取了导数的近似值),为了解决这一问题,OpenCV提供了Scharr函数,但该函数仅作用域大小为3的内核;计算速度与Sobel函数一样快,但结果更加精确,不怕干扰,其内核为:
图像边缘提取
使用Sobel算子提取图像边缘,包含以下四个步骤:
- 对图像进行高斯模糊;
- 将图像转换到灰度空间;
- 利用Sobel算子或者Scharr算子计算图像梯度;
- 利用提取的梯度,调整图像灰度值,提取图像边缘;
Sobel算子API的使用方式:
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 );
- src表示输入的灰度图像
- dst表示输出的梯度;
- ddepth表示输出梯度的数据类型,必须大于输入的图像数据类型,关系如下图所示:
- dx=1, dy=0表示对x方向计算梯度;
- dx=0, dy=1表示对y方向计算梯度;
Scharr算子的使用方式:
void Scharr( InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
- 参数与Sobel算子基本一致;
- 但是不需要设置核的大小,因为Scharr默认就是3;
完成代码如下:
#include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; /** * 边缘处理 */ int main(){ // 读取图像 Mat src = imread("/home/chen/dataset/lena.jpg"); if (src.empty()){ cout << "could not load image." << endl; return -1; } namedWindow("src", WINDOW_AUTOSIZE); imshow("src", src); // 1. 高斯模糊 Mat srcBlur; GaussianBlur(src, srcBlur, Size(3, 3), 0, 0); // 2. 转灰度 Mat srcGray; cvtColor(srcBlur, srcGray, COLOR_BGR2GRAY); // 3. 求方向梯度 Mat gradX, gradY; Sobel(srcGray, gradX, CV_16S, 1, 0, 3); Sobel(srcGray, gradY, CV_16S, 0, 1, 3); // Scharr(srcGray, gradX, CV_16S, 1, 0); // Scharr(srcGray, gradY, CV_16S, 0, 1); convertScaleAbs(gradX, gradX); // calculates absolute values, and converts the result to 8-bit. convertScaleAbs(gradY, gradY); namedWindow("gradY", WINDOW_AUTOSIZE); imshow("gradX", gradX); namedWindow("gradY", WINDOW_AUTOSIZE); imshow("gradY", gradY); printf("type: %d, %d", gradX.type(), gradY.type()); // 4. 图像混合 Mat dst; addWeighted(gradX, 0.5, gradY, 0.5, 0, dst); namedWindow("dst", WINDOW_AUTOSIZE); imshow("dst", dst); // 4.1 Mat gradXY = Mat(gradX.size(), gradX.type()); for (int row = 0; row < gradX.rows; row++){ for (int col = 0; col < gradX.cols; col++){ int gX = gradX.at<uchar>(row, col); int gY = gradY.at<uchar>(row, col); gradXY.at<uchar>(row, col) = saturate_cast<uchar>(gX + gY); } } namedWindow("gradXY", WINDOW_AUTOSIZE); imshow("gradXY", gradXY); waitKey(0); return 0; }
使用Sobel的结果
使用Scharr算子的结果