Sobel算子

原理 

       Soble边缘检测算法比较简,实际应用中效率比canny边缘检测效率要高,但是边缘不如Canny检测的准确,但是很多实际应用的场合,sobel边缘却是首选,尤其是对效率要求较高,而对细纹理不太关心的时候。Soble边缘检测通常带有方向性,可以只检测竖直边缘或垂直边缘或都检测。
        算子使用两个3*3的矩阵(图1)算子使用两个3*3的矩阵(图1)去和原始图片作卷积,分别得到横向G(x)和纵向G(y)的梯度值,如果梯度值大于某一个阈值,则认为该点为边缘点。

Sobel卷积因子

sobel算子的可分离性 

我们先来看一个3阶的Sobel边缘检测算子,如下所示:

                                       

 

      显然,3*3的Sobel算子是可分离的,它是Sobel算子的标准形式,可以利用二项式展开式的系数构建窗口更大的Sobel算子,如5*5、7*7等,但是有一点必须要注意,窗口大小要为奇数。

 

Sobel算子_第1张图片

        该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以A代表原始图像,Gx及Gy分别代表经横向及纵向边缘检测的图像灰度值,其公式如下: 

Sobel算子_第2张图片

图像的每一个像素的横向及纵向梯度近似值可用以下的公式结合,来计算梯度的大小。

可用以下公式计算梯度方向。

在以上例子中,如果以上的角度Θ等于零,即代表图像该处拥有纵向边缘,左方较右方暗。

       除此之外:由于基础核具有关于0,0,0所在的中轴正负对称,所以通过对基础核的旋转,和图像做卷积,可以获得灰度图的边缘图,同时消去旋转角方向+180°上的边缘,迭代多个方向即可消去多个方向的边缘,但是为消去的边缘会加倍。

基础核 0°         (顺时针为正)旋转 45°          90°                   135°                      180°                        225°                       270° 

 Sobel算子_第3张图片 Sobel算子_第4张图片  Sobel算子_第5张图片   Sobel算子_第6张图片     Sobel算子_第7张图片Sobel算子_第8张图片    Sobel算子_第9张图片

       当内核大小为 3 时, 以上Sobel内核可能产生比较明显的误差(毕竟,Sobel算子只是求取了导数的近似值)。 为解决这一问题,OpenCV提供了 Scharr 函数,但该函数仅作用于大小为3的内核。该函数的运算与Sobel函数一样快,但结果却更加精确,其内核为:

这里写图片描述

 C++ 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 );

@param src 为输入图像,填Mat类型即可。
@param dst 即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
@param ddepth 输出图像的深度。
若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F 
若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F 
若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F 
若src.depth() = CV_64F, 取ddepth = -1/CV_64F
@param dx x 方向上的差分阶数。
@param dy y方向上的差分阶数。
@param ksize 有默认值3,表示Sobel核的大小;必须取1,3,5或7。
@param scale 计算导数值时可选的缩放因子,默认值是1,表示默认情况下是没有应用缩放的。我们可以在文档中查阅getDerivKernels的相关介绍,来得到这个参数的更多信息。
@param delta 表示在结果存入目标图(第二个参数dst)之前可选的delta值,有默认值0。
@param borderType 边界模式,默认值为BORDER_DEFAULT。这个参数可以在官方文档中borderInterpolate处得到更详细的信息。

梯度计算 

    Sobel算子_第10张图片               Sobel算子_第11张图片

写了一段代码验证一下,首先创建一个5*7的图像,并且创建一个滤波器,使用filter2D进行卷积,dst为输出图像。

	Mat A = (Mat_(5, 8) <<
		20, 30, 15, 3, 16, 9, 56, 8,
		21, 33, 66, 15, 3, 9, 75, 12,
		11, 0, 3, 2, 6, 12, 35, 54,
		15, 26, 35, 15, 12, 3, 33, 87,
		21, 14, 12, 95, 13, 2, 15, 46);
	Mat kernel = (Mat_(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);
	Mat dst;
	//copyMakeBorder(A, B, 1, 1, 1, 1, BORDER_CONSTANT,0);
	filter2D(A, dst, CV_16S, kernel);
	convertScaleAbs(dst, dst);
	dst = dst(Rect(1, 1, dst.cols - 2, dst.rows - 2));

	waitKey();

 红色方框中对应右边的输出结果。Sobel算子_第12张图片

Sobel算子_第13张图片

测试代码 

Mat src = imread("test.png", IMREAD_GRAYSCALE);
imshow("src", src);

//使用高斯滤波消除噪声 
GaussianBlur(src, src, Size(3, 3), 0.1);

Mat sobelX;
Sobel(src, sobelX, CV_16S, 1, 0);	//求X方向梯度  
convertScaleAbs(q_matsobelX, q_matsobelX);
imshow("效果图窗口X", sobelX);

Mat sobelY;
Sobel(src, sobelY, CV_16S, 0, 1);//求Y方向梯度
convertScaleAbs(sobelY, sobelY);
imshow("效果图窗口Y", sobelY);
//合并梯度
Mat dst;
addWeighted(sobelX, 0.5, sobelY, 0.5, 0, dst);
imshow("合并效果图窗口", dst);

waitKey();

Sobel算子_第14张图片

 

Sobel算子_第15张图片

 

Sobel算子_第16张图片

 参考:

https://blog.csdn.net/yanmy2012/article/details/8110316

https://blog.csdn.net/weixin_41695564/article/details/79965676

 

你可能感兴趣的:(#,【OpenCV_边/线检测】)