知识点概述
1.空间滤波和空间滤波器的定义
使用空间模板进行的图像处理,被称为空间滤波。模板本身被称为空间滤波器
2. 锐化滤波器
锐化滤波器的分类:
a. 二阶微分滤波器-拉普拉斯算子
b. 一阶微分滤波器-梯度算子
3. 梯度算子
微分滤波器模板系数设计
a, Roberts交叉梯度算子
b, Prewitt梯度算子
c , Sobel梯度算子
导数计算是一个重要的卷积
Sobel导数
用来表达微分的最常用的操作是Sobel微分算子。Sobel算子包含任意阶的微分及融合偏导。
Sobel导数可以定义任意大小的核,而且这些核可以用快速且迭代的方式构造,大核对导数有更好的逼近,因为小核对噪声更敏感
再者,Sobel导数并不是真正的导数,因为Sobel算子定义在一个离散的空间上。Sobel算子真正表示的是多项式拟合。
Sobel
使用扩展 Sobel 算子计算一阶、二阶、三阶或混合图像差分
void cvSobel( const CvArr* src, CvArr* dst, int xorder, int yorder, int aperture_size=3 );
-
src
- 输入图像. dst
- 输出图像. xorder
- x 方向上的差分阶数 yorder
- y 方向上的差分阶数 aperture_size
- 扩展 Sobel 核的大小,必须是 1, 3, 5 或 7。 除了尺寸为 1, 其它情况下, aperture_size ×aperture_size 可分离内核将用来计算差分。对 aperture_size=1的情况, 使用 3x1 或 1x3 内核 (不进行高斯平滑操作)。这里有一个特殊变量 CV_SCHARR (=-1),对应 3x3 Scharr 滤波器,可以给出比 3x3 Sobel 滤波更精确的结果。Scharr 滤波器系数是:
- 对 x-方向 或矩阵转置后对 y-方向。
函数 cvSobel 通过对图像用相应的内核进行卷积操作来计算图像差分:
由于Sobel 算子结合了 Gaussian 平滑和微分,所以,其结果或多或少对噪声有一定的鲁棒性。通常情况,函数调用采用如下参数 (xorder=1, yorder=0, aperture_size=3) 或 (xorder=0, yorder=1, aperture_size=3) 来计算一阶 x- 或 y- 方向的图像差分。第一种情况对应:
核。
第二种对应:
或者
核的选则依赖于图像原点的定义 (origin 来自 IplImage 结构的定义)。由于该函数不进行图像尺度变换,所以和输入图像(数组)相比,输出图像(数组)的元素通常具有更大的绝对数值(译者注:即像素的位深)。为防止溢出,当输入图像是 8 位的,要求输出图像是 16 位的。当然可以用函数 cvConvertScale 或 cvConvertScaleAbs 把运算结果(dst)转换为 8 位的。除了8-位图像,函数也接受 32-位 浮点数图像。所有输入和输出图像都必须是单通道的,并且具有相同的图像尺寸或者ROI尺寸。
拉普拉斯变换
拉普拉斯算子可以用二次导数的形式定义,可假设其离散实现类似于二阶Sobel导数
Laplace
计算图像的 Laplacian 变换
void cvLaplace( const CvArr* src, CvArr* dst, int aperture_size=3 );
-
src
- 输入图像. dst
- 输出图像. aperture_size
- 核大小 (与 cvSobel 中定义一样).
函数 cvLaplace 计算输入图像的 Laplacian变换,方法是先用 sobel 算子计算二阶 x- 和 y- 差分,再求和:
对 aperture_size=1 则给出最快计算结果,相当于对图像采用如下内核做卷积:
类似于 cvSobel 函数,该函数也不作图像的尺度变换,所支持的输入、输出图像类型的组合和cvSobel一致。
实现代码:
/*
* Sobel导数,拉普拉斯变换,Canny
*/
#include "highgui.h"
#include "cv.h"
void doSobel(IplImage* img)
{
IplImage* dst = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3);
//Sobel导数
//cvSobel(img,dst,0,1,3);
//拉普拉斯变换
cvLaplace(img, dst, 3);
cvNamedWindow("dst");
cvShowImage("dst", dst);
cvReleaseImage(&dst);
cvWaitKey(0);
cvDestroyWindow("dst");
}
结果: 第一幅图为Sobel的结果,第二幅图为拉普拉斯变换的结果
Canny算子
与拉普拉斯算法不同,Canny算法中,首先在x和y方向求一阶导数, 然后组合为四个方向的导数。这些方向导数达到局部最大值的点就是组成边缘的候选点。
Canny算法最重要的特点是其试图将独立边的候选像素拼装成轮廓。
轮廓的形成是对这些像素运用滞后性阈值。这意味着有两个阈值,上限和下限。
如果一个像素的梯度大于上限阈值,则认为是边缘像素,如果低于下限阈值,则抛弃,在二者之间的,只有其余高于上限阈值的像素连接时才会被接受。
Canny
采用 Canny 算法做边缘检测
void cvCanny( const CvArr* image, CvArr* edges, double threshold1, double threshold2, int aperture_size=3 );
-
image
- 单通道输入图像. edges
- 单通道存储边缘的输出图像 threshold1
- 第一个阈值 threshold2
- 第二个阈值 aperture_size
- Sobel 算子内核大小 (见 cvSobel).
函数 cvCanny 采用 CANNY 算法发现输入图像的边缘而且在输出图像中标识这些边缘。threshold1和threshold2 当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割。
- 注意事项:cvCanny只接受单通道图像作为输入。
- 外部链接:经典的canny自调整阈值算法的一个opencv的实现见在OpenCV中自适应确定canny算法的分割门限
- 注意事项:cvCanny只接受单通道图像作为输入。
实现代码:
/*
* Sobel导数,拉普拉斯变换,Canny
*/
#include "highgui.h"
#include "cv.h"
void doSobel(IplImage* img)
{
//IplImage* dst = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3);
//Sobel导数
//cvSobel(img,dst,0,1,3);
//拉普拉斯变换
//cvLaplace(img, dst, 3);
IplImage* dst = cvCreateImage(
cvGetSize(img),IPL_DEPTH_8U,
1); // Canny只接受单通道图像
cvCanny(img, dst, 50, 150, 3);
cvNamedWindow("dst");
cvShowImage("dst", dst);
cvReleaseImage(&dst);
cvWaitKey(0);
cvDestroyWindow("dst");
}
结果:
参考:《学习OpenCV》及