.1. 索贝尔导数Sobel

一般,用来表示微分的最常用的算子是索贝尔(Sobel)导数算子。Sobel算子可以实现任意阶导数和混合偏导数。函数为cv::Sobel()。函数原型:

void cv::Sobel(
	cv::InputArray src,   // input image
	cv::OutputArray dst,  // result image
	int ddepth,  // pixel depth of output
	int xorder,  // order of corresponding derivative in x
	int yorder,  // order of corresponding derivative in y
	int ksize = 3,  // kernel size
	double scale = 1,  // scale (applied before assignment)
	double delta = 0,  // offset (applied before assignment)
	int borderType = cv::BORDER_DEFAULT  // border exterapolation
);
  • src和dst是目标图像;
  • 可以通过指明参数ddepth确定目标图像的深度或类型,假设src是一幅8位图像,那么dst需要至少CV_16S的深度保证不出现溢出;
  • xorder和yorder是求导顺序,取值范围是0、1和2,0表示在这个方向上不求导,这两个参数不能同时取0;
  • ksize是奇数,表示调用的滤波器的宽和高,目前最大支持到31,实际使用时,ksize≥3时才有意义,如果ksize=1,函数将自动使用3;
  • 阈值scale和偏移delta将在把结果存入dst前调用,有助于将求导结果可视化;

结果图像计算公式:

opencv求圆半径 opencv 求导_ci

Sobel算子有一个好处是可以将核定义为各种大小,并且可以快速、迭代式地构造这些核。
大的核可以更好地近似导数,可以消除噪声影响。但是如果导数在空间上变化剧烈,核太大结果会发生偏差。

 Sobel算子不是真正的导数,因为它定义在离散空间上。Sobel算子实际上表示的是以恶搞多项式,也就是说在x方向上进行二阶Sobel运算表示的并不是二阶导数,而是对抛物线函数的局部拟合,这也说明了为什么要使用一个更大的核,更大的核拟合了更多像素。

2. Scharr滤波器

离散空间上有很多方法来近似导数,Sobel算子的缺点是核比较小的时候准确度不高。对于大型的核,近似过程中使用了较多的点,因此精度问题不太显著。cv::Sobel()中使用的X、Y滤波器不会出现这种偏差,因为它们准确地与x、y轴对齐。当需要计算某一点的梯度时,就有问题了。

为了将图像内的信息联系起来,可能需要这样测量一幅图像,在处理过程中,通过在目标附近组织一幅梯度直方图来收集其形状信息,这些直方图是许多形状分类器训练和使用的基础,因此梯度叫的误差会降低分类器识别的效果。

对于3 x 3的Sobel滤波器,梯度叫距离水平或垂直方向越远,误差越明显。在OpenCV中,调用cv::Sobel()时设置ksize为cv::SCHARR,即可消除3 x 3这样小但是快的Sobel导数滤波器所带来的误差。Scharr滤波器和Sobel滤波器同样很快,但是前者精度更高。因此所选择的3 x 3的滤波器时,应当使用Scharr滤波器。Scharr滤波器的滤波器系数如下:

opencv求圆半径 opencv 求导_opencv_02

3. 拉普拉斯变换

OpenCv中的函数Laplacian实现了对拉普拉斯算子的离散近似。

opencv求圆半径 opencv 求导_ci_03

 Laplacian算子可以通过二阶导数定义,因此可以把它的离散实现与二阶Sobel导数联系起来,OpenCV在实现Laplacian算子时使用了Sobel算子:

void cv::Laplacian(
	cv::InputArray src,  // input image
	cv::OutputArray dst,  // result image
	int ddepth,  // depth of output image
	int ksize = 3,  // kernel size
	double scale = 1,  // scale applied before assignment to dst
	double delta = 0,  // offset applied before assignment to dst
	int borderType = cv::BORDER_DEFAULT  // border extrapolation to use
);

 除了求导顺序,cv::Laplacian()函数与cv::Sobel()函数的参数相同。孔径ksize与之前在Sobel导数中提到的一样,就是在二阶求导时像素采样区域的大小。实际情况中,只要ksize不为1,Laplacian算子的实现就是直接计算Sobel算子响应之和;当ksize等于1时,Laplacian算子计算图像的核的卷积如下:

opencv求圆半径 opencv 求导_opencv求圆半径_04

Laplacian算子可以用于各种场景处理,一种常见的应用就是匹配“半点”。Laplacian算子就是图像在x、y轴方向上导数之和,这意味着一个被较大值包围的点或小斑点处的值将会变得很大。相反,被较小值包围的点或小斑点处的值将在负方向上变得很大。

有了这些,Laplacian算子可以用于边缘检测,想象一下,一个函数的一阶导数在原函数变化大的地方,值会相应变大,图像中的边缘处也会有同样的变化,所以导数在这些地方将变得很大。可以在二阶导为0的地方搜寻这么一个极大值,源图像中的边缘通过Laplacian算子运算后会变成0。不幸的是真正的边和一些不是边的地方在Laplacian图像中都会变成0。这些问题能够解决,通过滤掉Sobel一阶导数中拥有较大值的点即可。