1. 函数定义

1.1 声明

	//索贝尔滤波器
	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
		);
		
	//夏尔滤波器
	void Scharr(
		InputArray src,
		OutputArray dst, 
		int ddepth,
		int dx, 
		int dy, 
		double scale = 1,
		double delta = 0,
		int borderType = BORDER_DEFAULT
		);
  • 以下是《学习OpenCV3》中对Sobel的讲解:
    • Sobel算子有一个好处就是可以将核定义为各种大小,并且可以快速、迭代式地构造这些核。
    • 大的核可以更好的近似导数,因为可以消除噪声影响。不过。假如导数在空间上变化剧烈,核太大会使结果发生偏差。
    • Sobel不是真正的导数,因为它定义在离散空间上。它实际上是一个多项式,在x方向上进行二阶Sobel运算表示的并不是二阶导数,而是对抛物线函数的局部拟合。这也说明了为什么要使用一个更大的核,更大的核拟合了更多的像素。
    • Sobel算子的缺点是核比较小的时候准确度不高。对于大型的核,近似过程使用了较多的点,因此精度问题不太显著。对于3×3的Sobel滤波器,梯度角距离水平或垂直方向越远,误差越明显。
    • Scahrr可消除3×3这样小但是快的Sobel导数滤波器所带来的误差。Scharr同样很快,但是精度更高。

1.2 重要参数解析

  • Sobel函数支持就地调用。
  • dx: x方向的求导顺序,0表示不求导。
  • dy: y方向的求导顺序,0表示不求导。
  • ksize:Sobel滤波器的尺寸,正奇数,OpenCV给出的取值范围是 1, 3, 5, or 7,实际上最大可以支持到31。
  • 一般情况下,都是用ksize x ksize内核来计算导数的。然而,有一种特殊情况——当ksize为1时,往往会使用3 x 1或者1 x 3的内核。且这种情况下,并没有进行高斯平滑操作。
  • scale:用来计算导数的可选择的缩放比例因子,缺省值是不使用缩放。
  • delta:可选择的偏移值。
  • 缩放因子和偏移值都会在把结果存入dst之前调用,这有助于将求导结果可视化。
  • ksize有一个特殊的值“-1”,也就是FILTER_SCHARR,这意味着即将执行的是Scharr算法。使用的是 3×3 Scharr滤波器:
  • 因此 Scharr(src, dst, ddepth, dx, dy, scale, delta, borderType) 和 Sobel(src, dst, ddepth, dx, dy, FILTER_SCHARR, scale, delta, borderType) 是一样的,你完全可以只使用Sobel算子,想使用Scharr算子时设置一下ksize就好了。
  • OpenCV官方文档中关于 Sobel 的讲解
  • OpenCV官方文档中关于 Scharr 的讲解

2. 例程

2.1 处理效果

原图

在这里插入图片描述

在x,y方向分别求导

  • 可以看出在x方向求导检测的是纵向的边缘,而在y方向求导检测的是横向的边缘。
    在这里插入图片描述

如果两个方向一起求导,结果会如何呢?

  • 结果并不好,边缘根本无法被检测出来。
    在这里插入图片描述

dx的值为1和2有什么区别呢?

  • 不难看出当dx为2相对于1来说,轮廓线条更多更细了。
    在这里插入图片描述

参数scale对结果的影响

  • 可以看出scale变大之后,边缘的亮度明显提升了
    在这里插入图片描述

参数delta对结果的影响

  • delta影响了除边缘以外的背景色灰度值
    在这里插入图片描述

SCHARR和Scharr

  • 这两者之间完全等同
    在这里插入图片描述

2.2 代码

#include "stdafx.h"
#include <opencv.hpp>
using namespace cv;

int main()
{
	Mat m_SrcImg = imread("./1.bmp", IMREAD_GRAYSCALE);
	imshow("原图", m_SrcImg);

	Mat m_DstImg;
	//这里需要注意的是:dx,dy的取值是0,1,2
	//0代表在这个方向不求导,
	Sobel(m_SrcImg, m_DstImg, m_SrcImg.depth(), 1, 0, 3);
	imshow("Sobel x", m_DstImg);

	Sobel(m_SrcImg, m_DstImg, m_SrcImg.depth(), 0, 1, 3);
	imshow("Sobel y", m_DstImg);

	//观察dx为1和2时的区别
	Sobel(m_SrcImg, m_DstImg, m_SrcImg.depth(), 2, 0, 3);
	imshow("Sobel x2", m_DstImg);

	//两个方向一起求导,边缘根本无法被检测出来
	Sobel(m_SrcImg, m_DstImg, m_SrcImg.depth(), 1, 1, 3);
	imshow("Sobel x1 y1", m_DstImg);

	Sobel(m_SrcImg, m_DstImg, m_SrcImg.depth(), 1, 2, 3);
	imshow("Sobel x1 y2", m_DstImg);

	//观察参数scale和delta对结果的影响
	Sobel(m_SrcImg, m_DstImg, m_SrcImg.depth(), 1, 0, 3, 1, 0);
	imshow("Sobel no scale no delta", m_DstImg);

	Sobel(m_SrcImg, m_DstImg, m_SrcImg.depth(), 1, 0, 3, 3, 0);
	imshow("Sobel scale", m_DstImg);

	Sobel(m_SrcImg, m_DstImg, m_SrcImg.depth(), 1, 0, 3, 1, 100);
	imshow("Sobel delta", m_DstImg);

	//SCHARR和Scharr等价
	Sobel(m_SrcImg, m_DstImg, m_SrcImg.depth(), 0, 1, -1);
	imshow("Sobel SCHARR", m_DstImg);

	Scharr(m_SrcImg, m_DstImg, m_SrcImg.depth(), 0, 1);
	imshow("Scharr", m_DstImg);

	waitKey(0);
    return 0;
}

 转载自:https://liuhui.blog.csdn.net/article/details/121740032