openCV中频率域增强的傅里叶变换已经比较成熟了,在他的官方tutorials文档里有一个完整的得到频谱图的例子,如下:
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
int main(int argc, char ** argv)
{
const char* filename = argc >=2 ? argv[1] : "lena.jpg";
Mat I = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
if( I.empty())
return -1;
Mat padded; //expand input image to optimal size
int m = getOptimalDFTSize( I.rows );
int n = getOptimalDFTSize( I.cols ); // on the border add zero values
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI;
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
dft(complexI, complexI); // this way the result may fit in the source matrix
// compute the magnitude and switch to logarithmic scale
// => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magI = planes[0];
magI += Scalar::all(1); // switch to logarithmic scale
log(magI, magI);
// crop the spectrum, if it has an odd number of rows or columns
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
// rearrange the quadrants of Fourier image so that the origin is at the image center
int cx = magI.cols/2;
int cy = magI.rows/2;
Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
q2.copyTo(q1);
tmp.copyTo(q2);
normalize(magI, magI, 0, 1, CV_MINMAX); // Transform the matrix with float values into a
// viewable image form (float between values 0 and 1).
imshow("Input Image" , I ); // Show the result
imshow("spectrum magnitude", magI);
waitKey();
return 0;
}
写的很好,但是不够完整。完整的频率域处理流程应该是家在图像-->傅里叶变换-->滤波-->傅里叶逆变换-->保存图像,其中滤波是在频谱图的基础上去处理的。他的例子中只完成了前两个步骤。由于某些原因,本人不便于贴出完整流程的代码,所以下面将只贴出后三步的核心代码。
1.滤波
频率域滤波器常用的有低通滤波器(理想低通、布特沃斯低通、高斯低通)、高通滤波器(理想高通、布特沃斯高通、高斯高通)、选择性滤波(带阻滤波器、带通滤波器、限波滤波器)等,各滤波器的原理可查阅相关的书籍,强烈推荐冈萨雷斯的《数字图像处理》。这里仅以高斯高通滤波器为例,代码如下:
void createGHPF( Mat *dst, float D0)
{
//center position
int cx = dst->cols/2;
int cy = dst->rows/2;
for( int i=0; i<dst->cols; i++)
{
for( int j=0; j<dst->rows; j++)
{
float X = i - cx + 1;
float Y = j - cy + 1;
float D = sqrt(X*X + Y*Y);
float param = exp(-pow(D,2)/(2*pow(D0,2)));
float parami = 1-param;
dst->at<float>(Point(i,j)) = parami;
}
}
return;
}
dst中存储的就是滤波器,由于傅里叶变换得到的频率是复数概念的,用Mat保存是用两个通道表示的,所以也要将此滤波器保存为表示复数的双通道形式。
shift(filter); //中心化处理,这个上面的tutorials代码里是有的,可以提出来单独作为一个函数
Mat f_planes[] = { Mat::zeros( complexI.size(), CV_32F), Mat::zeros( complexI.size(), CV_32F)};
f_planes[0] = filter;
f_planes[1] = filter;
Mat f_filter;
merge( f_planes, 2, f_filter);
mulSpectrums(complex, f_filter, complex, DFT_ROWS); //only DFT_ROWS accepted
最后一句是两个矩阵对应位置相乘的操作(区别于矩阵的乘法),就地转换后频谱图中存放的就是滤波后的频谱图了。
2.傅里叶逆变换
上面得到滤波后的频谱图,