前面介绍的几种滤波器都属于平滑滤波器(低通滤波器),用来平滑图像和抑制噪声的;而锐化空间滤波器恰恰相反,主要用来增强图像的突变信息,图像的细节和边缘信息。
平滑滤波器主要是使用邻域的均值(或者中值、积分)来代替模板中心的像素,消弱和邻域间的差别,以达到平滑图像和抑制噪声的目的;模糊图像,称为低通滤波器
锐化滤波器则使用邻域的微分作为算子,增大邻域间像素的差值,使图像的突变部分变的更加明显。锐化的作用是加强图像的边沿和轮廓,通常也成为高通滤波器:
大学学的高数终于排上用场了,一阶微表示函数斜率的变化,二阶导表示函数斜率曲线的斜率变化;
求导后,另一阶导等于0,可求最值;二阶导等于0,判断极值。
例子:一个扫描线上的一阶导数和二阶导数
注意:零交叉点对于边缘定位是非常有用的。
数字图像中的边缘在灰度上常常类似于斜坡过渡,如上面的例子那样,这样会导致图像的一阶微分会产生较粗的边缘,因为沿着斜坡的积分非零。
另一方面,二阶微分产生零分开的一个像素的双边缘。
由此可以得出结论,二阶微分在增强细节方面要比一阶微分好得多,这是一个适合锐化图像的理想特性。
所以我们开始主要注意二阶微分。下面,我们来考虑二维函数二阶微分的实现以及在图像锐化处理中的应用
基本方法是,先定义一个二阶微分的离散公式,然后构造一个基于该公式的滤波器模板,然后再把该模板与原图片卷积,从而实现锐化。
我们最关注的是一种各向同性的滤波器,这种滤波器的响应与滤波器作用的图像的突变方向无关。也就是说,各向同性滤波器是旋转不变的,即将原图像旋转之后进行滤波处理,与先对图像滤波再旋转的结果应该是相同的。
可以证明,最简单的各向同性微分算子是拉普拉斯算子。
不同的算子对应了不同的求微分的方法。
因为任意阶微分都是线性操作,所以拉普拉斯变换也是一个线性算子。为了从离散形式描述这一公式,我们使用二阶微分的公式来进行推导:
x方向上有:
y方向上有:
所以遵循这三个公式,两个变量的离散拉普拉斯算子:
有下面模板实现
这个滤波器实现了拉普拉斯算子,以90°为增量进行旋转的一个各向同性结果。实现机理与线性平滑滤波器一样,只是系数不同。
但其实对角线方向也可以这样组成:在数字拉普拉斯变换的定义中,在左边加入两项,即两个对角线方向各加上一个。每个新添加项的形式与类似,只是其坐标轴的方向沿着对角线的方向。
由于每个对角线方向上的项还包含一个 -2f(x,y),所以一共应该减去8个f(x,y)
将产生如下的模板:
这个模板对45°增幅的结果是各向同性的。
由于拉普拉斯是一种微分算子,因此其应用强调的是图像中的灰度突变,并不强调灰度级缓慢变化的区域。
将原图像和拉普拉斯图像叠加在一起的简单方法,可以复原背景特性并保持拉普拉斯锐化处理的效果。
如果所使用的模板定义有负的中心系数,那么必须将原图像减去经拉普拉斯变换后的图像,而不是加上他,从而得到锐化后的结果。
由于拉普拉斯图像中即有正值又有负值,并且所有负值在显示时都被修剪为0,所以变换后的图像大部分都是黑色的。
一个典型的标定拉普拉斯图像的方法是对它的最小值加上一个新的替代0的最小值,然后将结果标定到整个灰度范围[0,L - 1]内。
所以我们使用拉普拉斯对图像增强的方法如下:
OpenCV中的Laplace函数:
void cv::Laplacian ( InputArray src,
OutputArray dst,
int ddepth,
int ksize = 1,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
)
参数:
src 源图片
dst 与src相同大小和相同通道数的目标图像。
ddepth 目标图像的所需深度。
ksize 用于计算二阶导数滤波器的孔径大小。有关详细信息,请参阅getDerivKernels。大小必须是正面和奇数。
scale 计算的拉普拉斯算子值的可选比例因子。默认情况下,不应用缩放。有关详细信息,请参阅getDerivKernels。
delta 在将结果存储在dst之前添加到结果中的可选增量值。
borderType 像素外推方法,请参阅cv :: BorderTypes
计算图像的拉普拉斯算子。
该函数通过将使用Sobel运算符计算的第二个x和y导数相加来计算源图像的拉普拉斯算子:
上式在ksize>1时。
如果ksize==1,拉普拉斯算子是
代码实现:
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include
using namespace cv;
int main( int argc, char** argv )
{
Mat src, dst;
int kernel_size = 1;
int scale = 1;
int delta = 0;
int ddepth = CV_16S;
const char* imageName = argc >=2 ? argv[1] : "123.tif";
src = imread( imageName, IMREAD_GRAYSCALE ); // Load an image
// Check if image is loaded fine
if(src.empty()){
printf(" Error opening image\n");
printf(" Program Arguments: [image_name -- default ../data/lena.jpg] \n");
return -1;
}
Mat imageEnhance;
Mat kernel = (Mat_
filter2D(src, imageEnhance, CV_8UC3, kernel);
imshow("filter2D", imageEnhance);
// GaussianBlur( src, src, Size(3, 3), 0, 0, BORDER_DEFAULT );
// cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to grayscale
Mat abs_dst;
Laplacian( src, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
// converting back to CV_8U
convertScaleAbs( dst, abs_dst );
imshow( "Laplace Demo", abs_dst );
Mat sub;
cv::subtract(imageEnhance, abs_dst, sub);
// 计算图像的最大最小值
double pixMin,pixMax;
cv::minMaxLoc(sub,&pixMin,&pixMax);
std::cout << "min_a=" << pixMin << " max_b=" << pixMax << std::endl;
waitKey(0);
return 0;
}
结果:
视觉上感觉有差异,但相减max_b=min_a,说明:
filter2D用算子(0,1,0, 1,-4,1, 0,1,0)和Laplacian函数ksize==1时效果一样;
当ksize==3时,
其实在利用此卷积函数是,根据核的不同可以产生各种的图像处理,比如: