原理:
1.前面的程序已经实现了简单的卷积操作。一个最重要的卷积运算就是导数的计算(或者近似计算).
2.为什么对图像进行求导是重要的呢? 假设我们需要检测图像中的 边缘 ,如下图:
![示例程序030--Sobel导数 示例程序030--Sobel导数](https://i-blog.csdnimg.cn/blog_migrate/fa49cf165d76da9507bc1f3f3fd6264c.jpeg)
可以看到在 边缘 ,相素值显著的 改变 了。表示这一 改变 的一个方法是使用 导数 。 梯度值的大变预示着图像中内容的显著变化。
3.用更加形象的图像来解释,假设我们有一张一维图形。下图中灰度值的”跃升”表示边缘的存在:
![示例程序030--Sobel导数 示例程序030--Sobel导数](https://i-blog.csdnimg.cn/blog_migrate/6c995ae5a854123f78b89e5cf361cf1d.jpeg)
4. 使用一阶微分求导我们可以更加清晰的看到边缘”跃升”的存在(这里显示为高峰值)
![示例程序030--Sobel导数 示例程序030--Sobel导数](https://i-blog.csdnimg.cn/blog_migrate/92b97737326352fac8b3f5d7e391253a.jpeg)
5. 从上例中我们可以推论检测边缘可以通过定位梯度值大于邻域的相素的方法找到(或者推广到大于一个阀值).
Sobel算子:
1. Sobel 算子是一个离散微分算子 (discrete differentiation operator)。 它用来计算图像灰度函数的近似梯度。
2. Sobel 算子结合了高斯平滑和微分求导。
用到的函数:
1.void Sobel(InputArray src, OutputArray dst, int ddepth, int xorder, int yorder, int ksize=3, double scale=1, double delta=0, int borderType=BORDER_DEFAULT )
Calculates the first, second, third, or mixed image derivatives using an extended Sobel operator.
Parameters: |
- src – Source image.
- dst – Destination image of the same size and the same number of channels as src .
- ddepth – Destination image depth.
- xorder – Order of the derivative x.
- yorder – Order of the derivative y.
- ksize – Size of the extended Sobel kernel. It must be 1, 3, 5, or 7.
- scale – Optional scale factor for the computed derivative values. By default, no scaling is applied. See getDerivKernels() for details.
- delta – Optional delta value that is added to the results prior to storing them in dst .
- borderType – Pixel extrapolation method. See borderInterpolate() for details.
|
2.void convertScaleAbs(InputArray src, OutputArray dst, double alpha=1, double beta=0)¶
Scales, computes absolute values, and converts the result to 8-bit.
3.void Scharr(InputArray src, OutputArray dst, int ddepth, int xorder, int yorder, double scale=1, double delta=0, intborderType=BORDER_DEFAULT )
Calculates the first x- or y- image derivative using Scharr operator.
代码及主要注释:
// 037 Soble导数.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
int main( int argc, char** argv )
{
Mat src, src_gray;
Mat grad;
char* window_name = "Sobel Demo - Simple Edge Detector";
int scale = 1;
int delta = 0;
int ddepth = CV_16S;
src = imread( "Lena.jpg" );
if( !src.data )
{ return -1; }
//平滑处理,高斯降噪,3*3的内核
GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
/// 转换为灰度图
cvtColor( src, src_gray, CV_RGB2GRAY );
namedWindow( window_name, CV_WINDOW_AUTOSIZE );
/// 创建 grad_x 和 grad_y 矩阵
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y;
// Sobel函数参数介绍:
// rc_gray: 在本例中为输入图像,元素类型 CV_8U
// grad_x/grad_y: 输出图像.
// ddepth: 输出图像的深度,设定为 CV_16S 避免外溢。
// x_order: x 方向求导的阶数。
// y_order: y 方向求导的阶数。
// scale, delta 和 BORDER_DEFAULT: 使用默认值
/// 求 X方向梯度导数
// Scharr 函数,但该函数仅作用于大小为3的内核。该函数的运算与Sobel函数一样快,但结果却更加精确
//Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
//将中间结果转换到 CV_8U
convertScaleAbs( grad_x, abs_grad_x );
/// 求Y方向梯度导数
//Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
//将中间结果转换到 CV_8U
convertScaleAbs( grad_y, abs_grad_y );
/// 将两个方向的梯度相加来求取近似 梯度 (注意这里没有准确的计算,但是作为测试已经足够了)。
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
imshow( window_name, grad );
waitKey(0);
return 0;
}
运行结果:
Soble:![示例程序030--Sobel导数 示例程序030--Sobel导数](https://i-blog.csdnimg.cn/blog_migrate/f081f5495ceea3c0409b956a5d5d437f.jpeg)
Scharr:![示例程序030--Sobel导数 示例程序030--Sobel导数](http://s3.sinaimg.cn/small/a98e39a2nc786004557e2&690)