本节为opencv数字图像处理(8):频率域滤波的第五小节,使用频率域滤波器进行图像的平滑与锐化,主要包括:理想低通/高通滤波器,巴特沃斯低通/高通滤波器、高斯低通/高通滤波器、频率域拉普拉斯算子、高频强调滤波器以及同态滤波的介绍和C++实现。

1. 使用低通滤波器进行图像平滑

  考虑图像中的边缘与其他尖锐的灰度转变对其傅里叶变换的高频内容有贡献,因此在频率域平滑图像可通过高频分量的衰减来达到,即低通滤波器。比较典型的低通滤波器即理想低通滤波器、布特沃斯低通滤波器和高斯滤波器,对图像的平滑/模糊程度也是由高到底。其中布特沃斯滤波器有一个参数,成为滤波器的阶数,阶数值越高,越接近理想滤波器,反之更像高斯滤波器。

1.1. 理想低通滤波器

usm 锐化 opencv opencv锐化滤波器_低通滤波器为半径的圆内,无衰减地通过所有频率而在该圆外切断所有频率的二维低通滤波器,成为理想低通滤波器(ILPF),它由下面的函数来确定:

usm 锐化 opencv opencv锐化滤波器_opencv_02

usm 锐化 opencv opencv锐化滤波器_低通滤波器是一个正常数,usm 锐化 opencv opencv锐化滤波器_高通滤波_04是频率域中点usm 锐化 opencv opencv锐化滤波器_opencv_05与频率矩形中心的距离,即:

usm 锐化 opencv opencv锐化滤波器_opencv_06

  如下图所示,从左到右分别是一个理想低通滤波器变换函数的透视图、以图像形式显示的滤波器和滤波器径向横截面。

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_07

  使用一个理想低通滤波器进行图像平滑结果与C++代码如下:

usm 锐化 opencv opencv锐化滤波器_数字图像处理_08

  C++代码核心部分如下,将代码插入文章底部的框架代码中指定位置即可:

//1.理想低通滤波
	cv::Mat idealBlur(padded.size(), CV_32FC2);
	double D0 = 60;
	for (int i = 0; i < padded.rows; i++) {
		float* p = idealBlur.ptr<float>(i);
		for (int j = 0; j < padded.cols; j++) {
			double d = sqrt(pow((i - padded.rows / 2), 2) + pow((j - padded.cols / 2), 2));//分子,计算pow必须为float型
			if (d <= D0) {
				p[2 * j + 1] = 1;
				p[2 * j] = 1;
			}
			else {
				p[2 * j] = 0;
				p[2 * j + 1] = 0;
			}
		}
	}
	multiply(complexImg, idealBlur, idealBlur);
	cv::idft(idealBlur, idealBlur);
	cv::split(idealBlur, plane);
1.2. 巴特沃斯低通滤波器

usm 锐化 opencv opencv锐化滤波器_低通滤波器处的usm 锐化 opencv opencv锐化滤波器_opencv_10阶巴特沃斯低通滤波器BLPF的传递函数的定义为:

usm 锐化 opencv opencv锐化滤波器_低通滤波器_11

  下图显示了BLPF函数的透视图、图像显示和径向剖面图:

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_12

  BLPF传递函数并没有在通过频率和滤除频率之间给出明显截止的尖锐的不连续性,使用巴特沃斯低通滤波器的效果与C++代码如下:

usm 锐化 opencv opencv锐化滤波器_数字图像处理_13

  C++代码核心部分如下,将代码插入文章底部的框架代码中指定位置即可:

//2.巴特沃斯低通滤波
	cv::Mat butterworthBlur(padded.size(), CV_32FC2);
	double D0 = 60;
	int n = 1;
	for (int i = 0; i < padded.rows; i++) {
		float* p = butterworthBlur.ptr<float>(i);
		for (int j = 0; j < padded.cols; j++) {
			double d = sqrt(pow((i - padded.rows / 2), 2) + pow((j - padded.cols / 2), 2));//分子,计算pow必须为float型
			p[2*j] = 1.0 / (1 + pow(d / D0, 2 * n));
			p[2*j+1] = 1.0 / (1 + pow(d / D0, 2 * n));
		}
	}
	multiply(complexImg, butterworthBlur, butterworthBlur);
	cv::idft(butterworthBlur, butterworthBlur);
	cv::split(butterworthBlur, plane);

  BLPF用于平滑处理,如图像由于量化不足产生伪轮廓时,常可用低通滤波进行平滑以改进图像质量。通常,BLPF的平滑效果好于ILPF,当n的阶数较低时(n=1,2),没有振铃现象,当阶数较高时会产生明显的振铃现象。

1.3. 高斯低通滤波器

  高斯低通滤波器的传递函数如下所示:

usm 锐化 opencv opencv锐化滤波器_高通滤波_14

usm 锐化 opencv opencv锐化滤波器_低通滤波器是截止频率,当usm 锐化 opencv opencv锐化滤波器_高通滤波_16时,GLPF下降到其最大值0.607处。GLPF的傅里叶反变换也是高斯的,这意味这空间高斯滤波器将没有振铃。下图从左到右依次是GLPF函数的透视图、图像显示和径向剖面图。

  使用高斯低通滤波器平滑图像效果如下:

usm 锐化 opencv opencv锐化滤波器_高通滤波_17

  C++代码核心部分如下,将代码插入文章底部的框架代码中指定位置即可:

//3.高斯低通滤波
	cv::Mat gaussianBlur(padded.size(), CV_32FC2);
	float D0 = 2 * 1000 ;
	for (int i = 0; i < padded.rows; i++)
	{
		float* p = gaussianBlur.ptr<float>(i);
		for (int j = 0; j < padded.cols; j++)
		{
			float d = pow(i - padded.rows / 2, 2) + pow(j - padded.cols / 2, 2);
			p[2 * j] = expf(-d / D0);
			p[2 * j + 1] = expf(-d / D0);
		}
	}
	multiply(complexImg, gaussianBlur, gaussianBlur);
	cv::idft(gaussianBlur, gaussianBlur);
	cv::split(gaussianBlur, plane);

2. 使用高通滤波器进行图像锐化

  因为图像的边缘和其他灰度的急剧变化与高频分量有关,所以图像锐化可以通过高通滤波来实现,高通滤波会衰减傅立叶变换中的低频分量而不会扰乱高频信息,一个高通滤波器是从给定的低通滤波器用下式得到:

usm 锐化 opencv opencv锐化滤波器_低通滤波器_18

2.1. 理想高通滤波器

  一个二维理想高通滤波器IHPF定义为:

usm 锐化 opencv opencv锐化滤波器_高通滤波_19

  下图从左到右依次表示典型理想高通滤波器的透视图、图像表示和剖面图:

usm 锐化 opencv opencv锐化滤波器_数字图像处理_20

  下图显示的是典型理想高通滤波器的空间表示及通过滤波器中心的对应灰度剖面图:

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_21

  效果和代码如下:

usm 锐化 opencv opencv锐化滤波器_数字图像处理_22

  C++代码核心部分如下,将代码插入文章底部的框架代码中指定位置即可:

//4.理想高通滤波
	cv::Mat idealBlur(padded.size(), CV_32FC2);
	double D0 = 20;
	for (int i = 0; i < padded.rows; i++) {
		float* p = idealBlur.ptr<float>(i);
		for (int j = 0; j < padded.cols; j++) {
			double d = sqrt(pow((i - padded.rows / 2), 2) + pow((j - padded.cols / 2), 2));//分子,计算pow必须为float型
			if (d <= D0) {
				p[2 * j + 1] = 0;
				p[2 * j] = 0;
			}
			else {
				p[2 * j] = 1;
				p[2 * j + 1] = 1;
			}
		}
	}
	multiply(complexImg, idealBlur, idealBlur);
	cv::idft(idealBlur, idealBlur);
	cv::split(idealBlur, plane);
2.2. 巴特沃斯高通滤波器

usm 锐化 opencv opencv锐化滤波器_低通滤波器usm 锐化 opencv opencv锐化滤波器_opencv_10阶巴特沃斯高通滤波器BHPF定义为:

usm 锐化 opencv opencv锐化滤波器_高通滤波_25

  下图从左到右依次表示典型理想高通滤波器的透视图、图像表示和剖面图:

usm 锐化 opencv opencv锐化滤波器_opencv_26

  下图显示的是典型理想高通滤波器的空间表示及通过滤波器中心的对应灰度剖面图:

usm 锐化 opencv opencv锐化滤波器_低通滤波器_27

  效果和C++代码如下:

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_28

  C++代码核心部分如下,将代码插入文章底部的框架代码中指定位置即可:

//5.巴特沃斯高通滤波
	cv::Mat butterworthBlur(padded.size(), CV_32FC2);
	double D0 = 20;
	int n = 1;
	for (int i = 0; i < padded.rows; i++) {
		float* p = butterworthBlur.ptr<float>(i);
		for (int j = 0; j < padded.cols; j++) {
			double d = sqrt(pow((i - padded.rows / 2), 2) + pow((j - padded.cols / 2), 2));//分子,计算pow必须为float型
			p[2*j] = 1.0 / (1 + pow(D0 / d, 2 * n));
			p[2*j+1] = 1.0 / (1 + pow(D0 / d, 2 * n));
			
		}
	}
	multiply(complexImg, butterworthBlur, butterworthBlur);
	cv::idft(butterworthBlur, butterworthBlur);
	cv::split(butterworthBlur, plane);
2.3. 高斯高通滤波器

usm 锐化 opencv opencv锐化滤波器_低通滤波器的高斯高通滤波器GHPF的传递函数如下:

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_30

  下图从左到右依次表示典型理想高通滤波器的透视图、图像表示和剖面图:

usm 锐化 opencv opencv锐化滤波器_opencv_31

  下图显示的是典型理想高通滤波器的空间表示及通过滤波器中心的对应灰度剖面图:

usm 锐化 opencv opencv锐化滤波器_低通滤波器_32

  效果和代码如下所示:

usm 锐化 opencv opencv锐化滤波器_opencv_33

  C++代码核心部分如下,将代码插入文章底部的框架代码中指定位置即可:

//6.高斯高通滤波
	cv::Mat gaussianBlur(padded.size(), CV_32FC2);
	float D0 = 2 * 10 * 10;
	for (int i = 0; i < padded.rows; i++)
	{
		float* p = gaussianBlur.ptr<float>(i);
		for (int j = 0; j < padded.cols; j++)
		{
			float d = pow(i - padded.rows / 2, 2) + pow(j - padded.cols / 2, 2);
			p[2 * j] = 1 - expf(-d / D0);
			p[2 * j + 1] = 1 - expf(-d / D0);
		}
	}
	multiply(complexImg, gaussianBlur, gaussianBlur);
	cv::idft(gaussianBlur, gaussianBlur);
	cv::split(gaussianBlur, plane);
2.4. 频率域的拉普拉斯算子

  拉普拉斯算子使用如下滤波器在频率与实现:

usm 锐化 opencv opencv锐化滤波器_opencv_34

  或者说关于频率矩形的中心,使用如下滤波器:

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_35

  然后拉普拉斯图像由下式得到:

usm 锐化 opencv opencv锐化滤波器_高通滤波_36

  增强的话可用下式实现:

usm 锐化 opencv opencv锐化滤波器_低通滤波器_37

usm 锐化 opencv opencv锐化滤波器_高通滤波_38是负的,所以usm 锐化 opencv opencv锐化滤波器_opencv_39。频率域中上式可汇总为下面的式子:

usm 锐化 opencv opencv锐化滤波器_opencv_40

  效果图与C++实现如下:

usm 锐化 opencv opencv锐化滤波器_高通滤波_41

  C++代码核心部分如下,将代码插入文章底部的框架代码中指定位置即可:

//7.频率域拉普拉斯算子
	cv::Mat Laplace(padded.size(), CV_32FC2);
	for (int i = 0; i < padded.rows; i++)
	{
		float* p = Laplace.ptr<float>(i);
		for (int j = 0; j < padded.cols; j++)
		{
			float d = pow(i - padded.rows / 2, 2) + pow(j - padded.cols / 2, 2);
			p[2 * j] = 1 + 4 * pow(CV_PI, 2) * d;
			p[2 * j + 1] = 1 + 4 * pow(CV_PI, 2) * d;
		}
	}
	multiply(complexImg, Laplace, Laplace);
	cv::idft(Laplace, Laplace);
	cv::split(Laplace, plane);
2.5. 钝化模板、高提升滤波和高频强调滤波

  钝化模板技术在频率域:

usm 锐化 opencv opencv锐化滤波器_低通滤波器_42

  高提升滤波在频率域:

usm 锐化 opencv opencv锐化滤波器_高通滤波_43

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_44是一个低通滤波器,usm 锐化 opencv opencv锐化滤波器_数字图像处理_45usm 锐化 opencv opencv锐化滤波器_opencv_46的傅里叶变换,usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_47是平滑后的图像。然后,根据下式:

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_48

usm 锐化 opencv opencv锐化滤波器_数字图像处理_49时的钝化模板和usm 锐化 opencv opencv锐化滤波器_低通滤波器_50时的高提升滤波器。我们可以用涉及低通滤波器的频率域计算来表达上式:

usm 锐化 opencv opencv锐化滤波器_数字图像处理_51

  对应地,高通:

usm 锐化 opencv opencv锐化滤波器_高通滤波_52

  其中,方括号中的表达式称为高频强调滤波器。之前提到高通滤波器将直流项设为0,但是高频强调滤波不存在这一问题,更一般地,高频强调滤波的公式如下:

usm 锐化 opencv opencv锐化滤波器_高通滤波_53

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_54给出了控制距原点的偏移量,usm 锐化 opencv opencv锐化滤波器_高通滤波_55控制高频的贡献。

  书中给出了一个例子,从左上到右下,图中分别表示原图、高斯高通滤波器、基于高斯滤波的高频强调滤波器以及直方图均衡化后的结果。高频强调滤波

usm 锐化 opencv opencv锐化滤波器_opencv_56

usm 锐化 opencv opencv锐化滤波器_低通滤波器_57,有效的保留了灰度缓慢变化的区域。

2.6. 同态滤波

  照射-反射模型可用于开发一种频率域处理过程,该过程同时压缩灰度范围和增强对比度来改善一幅图像的表观,一幅图像可表示为其照射分量和反射分量的乘积:

usm 锐化 opencv opencv锐化滤波器_数字图像处理_58

  但是傅里叶变换的乘积并不等于乘积的傅立叶变换,即:

usm 锐化 opencv opencv锐化滤波器_opencv_59

  但是我们可以定义:

usm 锐化 opencv opencv锐化滤波器_低通滤波器_60

  则有:

usm 锐化 opencv opencv锐化滤波器_数字图像处理_61

  即:

usm 锐化 opencv opencv锐化滤波器_opencv_62

usm 锐化 opencv opencv锐化滤波器_数字图像处理_63进行滤波:

usm 锐化 opencv opencv锐化滤波器_低通滤波器_64

  空间域滤波后的图像:

usm 锐化 opencv opencv锐化滤波器_数字图像处理_65

  根据定义有:

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_66

  和

usm 锐化 opencv opencv锐化滤波器_opencv_67

  所以,滤波后空间域的图像可以写为:

usm 锐化 opencv opencv锐化滤波器_opencv_68

usm 锐化 opencv opencv锐化滤波器_数字图像处理_69是通过取输入图像的自然对数形成的,可通过滤波后取指数来形成输出图像:

usm 锐化 opencv opencv锐化滤波器_数字图像处理_70

  步骤总结如下:

usm 锐化 opencv opencv锐化滤波器_opencv_71

  该方法是以同态系统的一类系统的特殊情况为基础,方法的关键在于照射分量和反射分量的分离。图像的照射分量通常由慢的空间变化来表征,而反射分量往往引起突变,特别是在不同物体的连接部分。这样的特性导致图像取对数后的傅立叶变换的低频分量与照射相联系,而高频成分与反射相联系,虽然只是不强的联系。

  同态滤波函数如下:

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_72

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_73那么如下图所示的滤波器趋向于衰减低频/照射分量的贡献,而增强高频/反射分量的贡献,常数usm 锐化 opencv opencv锐化滤波器_高通滤波_74控制函数坡度的锐利度,在usm 锐化 opencv opencv锐化滤波器_高通滤波_75之间过渡,类似于高频强调滤波:

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_76

usm 锐化 opencv opencv锐化滤波器_高通滤波_77

usm 锐化 opencv opencv锐化滤波器_usm 锐化 opencv_78

  文中提到的代码框架:

#if 1
#include "opencv2/opencv.hpp"

int main()
{
	cv::Mat input = cv::imread("2.JPG", cv::IMREAD_GRAYSCALE);
	cv::imshow("step0_ori", input);
	int w = cv::getOptimalDFTSize(input.cols);
	int h = cv::getOptimalDFTSize(input.rows);
	cv::Mat padded;
	cv::copyMakeBorder(input, padded, 0, h - input.rows, 0, w - input.cols,
		cv::BORDER_CONSTANT, cv::Scalar::all(0));
	padded.convertTo(padded, CV_32FC1);
	cv::imshow("step1_padded", padded);
	for (int i = 0; i < padded.rows; i++)
	{
		float* ptr = padded.ptr<float>(i);
		for (int j = 0; j < padded.cols; j++)
			ptr[j] *= pow(-1, i + j);
	}
	cv::imshow("step2_center", padded);
	cv::Mat plane[] = { padded,cv::Mat::zeros(padded.size(),CV_32F) };
	cv::Mat complexImg;
	cv::merge(plane, 2, complexImg);
	cv::dft(complexImg, complexImg);
	cv::split(complexImg, plane);
	cv::magnitude(plane[0], plane[1], plane[0]);
	plane[0] += cv::Scalar::all(1);
	cv::log(plane[0], plane[0]);
	cv::normalize(plane[0], plane[0], 1, 0, cv::NORM_MINMAX);
	cv::imshow("dft", plane[0]);

	/***************************************************************************/
	//插入
	/***************************************************************************/
	cv::magnitude(plane[0], plane[1], plane[0]);
	cv::normalize(plane[0], plane[0], 1, 0, cv::NORM_MINMAX);
	cv::imshow("lpf", plane[0]);

	cv::waitKey();
	return 0;
}
#endif