以下是我学习傅里叶变换过程中参考的一些书籍、网站等,记录下来,方便以后查阅:
DFT:Discrete Fourier Transform 离散傅里叶变换
傅里叶分析之掐死教程(生动形象,入门必备)
数字图像处理(第二版)-冈萨雷斯(有大量数学公式,严谨,但有点深奥难懂)
(精心整理)图像的傅里叶变换(有例题解释、图像实例介绍和matlab代码)
二维DFT具体实现算法
官方文档:离散傅立叶变换(有opencv实现代码)
OpenCV实现基于傅里叶变换的旋转文本校正(傅里叶变换应用)
我参考(4. DFT实现算法)修改了(5. 官方文档)的代码,主要删除了5中“剪切和重分布幅度图象限”这一步。替换的方法是在进行傅里叶变换之前用(-1)^(x+y)乘以输入的图像函数,就可以将傅里叶变换的原点F(0,0)移到(M/2,N/2)上。效果与官方代码类似。
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main()
{
//读入图像灰度图
Mat I = imread("Spectrum.tif", IMREAD_GRAYSCALE);
//判断图像是否加载成功
if (I.empty())
{
cout << "图像加载失败!" << endl;
return -1;
}
else
cout << "图像加载成功!" << endl << endl;
//(-1)^(x+y)乘以输入的图像函数,将傅里叶变换的原点F(0,0)移到(M/2,N/2)上。
Mat padded(I.rows,I.cols,CV_32F);
for (int i = 0;i < I.rows;i++)
{
for (int j = 0;j < I.cols;j++)
{
float temp= I.at(i, j);
padded.at<float>(i, j) = temp*pow(-1, i + j);
}
}
//将planes融合合并成一个多通道数组complexI , 初始时实部就是图像
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(),CV_32F) };
Mat complexI;
merge(planes, 2, complexI);
//进行傅里叶变换
dft(complexI, complexI);
//计算幅值,planes[0] = Re(DFT(I),planes[1] = Im(DFT(I)) , 即planes[0]为实部,planes[1]为虚部
split(complexI, planes);
magnitude(planes[0], planes[1], planes[0]);
Mat magI = planes[0];
//转换到对数尺度 => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
magI += Scalar::all(1);
log(magI, magI);
//归一化处理,用0-1之间的浮点数将矩阵变换为可视的图像格式
normalize(magI, magI, 0, 1, CV_MINMAX);
imshow("输入图像", I);
imshow("频谱图", magI);
waitKey(0);
return 0;
}
在此基础上,我实现了从离散傅里叶变换到经典低通高通滤波、傅里叶逆变换这一整套流程,代码如下:
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
#define Order 2 //巴特沃斯阶数
#define D0 80 //截止频率
#define PI 3.14159
//理想低通滤波器
void ILPF(Mat src, int d0)
{
int M = src.rows;
int N = src.cols;
for (int i = 0;i < src.rows;i++)
{
//src为双通道,所以乘2,j=j+2,pow(j / 2 - N / 2, 2)
for (int j = 0;j < 2*src.cols;j=j+2)
{
int Dx = sqrt(pow(i - M / 2, 2) + pow(j / 2 - N / 2, 2));
if (Dx > D0)
{
src.at<float>(i, j) = 0;
src.at<float>(i, j+1) = 0;
}
}
}
}
//理想高通滤波器
void IHPF(Mat src, int d0)
{
int M = src.rows;
int N = src.cols;
for (int i = 0;i < src.rows;i++)
{
for (int j = 0;j < 2 * src.cols;j = j + 2)
{
int Dx = sqrt(pow(i - M / 2, 2) + pow(j / 2 - N / 2, 2));
if (Dx <= D0)
{
src.at<float>(i, j) = 0;
src.at<float>(i, j + 1) = 0;
}
}
}
}
//巴特沃斯低通滤波器Butterworth
void BLPF(Mat src,int n,int d0)
{
int M = src.rows;
int N = src.cols;
for (int i = 0;i < src.rows;i++)
{
for (int j = 0;j < 2 * src.cols;j = j + 2)
{
int Dx = sqrt(pow(i - M / 2, 2) + pow(j / 2 - N / 2, 2));
float Hx = 1 / (1 + pow((Dx / d0), 2 * n));
//处理实部虚部
src.at<float>(i, j) = src.at<float>(i, j)*Hx;
src.at<float>(i, j + 1) = src.at<float>(i, j + 1)*Hx;
}
}
}
//巴特沃斯高通滤波器Butterworth
void BHPF(Mat src, int n, int d0)
{
int M = src.rows;
int N = src.cols;
for (int i = 0;i < src.rows;i++)
{
for (int j = 0;j < 2 * src.cols;j = j + 2)
{
int Dx = sqrt(pow(i - M / 2, 2) + pow(j / 2 - N / 2, 2));
float Hx;
//排除Dx为0情况,防止d0/Dx无效
if (Dx == 0)
Hx = 0;
else
Hx = 1 / (1 + pow((d0 / Dx), 2 * n));
//处理实部虚部
src.at<float>(i, j) = src.at<float>(i, j)*Hx;
src.at<float>(i, j + 1) = src.at<float>(i, j + 1)*Hx;
}
}
}
//高斯低通滤波器
void GLPF(Mat src,int sigma)
{
int M = src.rows;
int N = src.cols;
for (int i = 0;i < src.rows;i++)
{
for (int j = 0;j < 2 * src.cols;j = j + 2)
{
int Dx = sqrt(pow(i - M / 2, 2) + pow(j / 2 - N / 2, 2));
float Hx = exp(-Dx*Dx / (2 * sigma*sigma));
//处理实部虚部
src.at<float>(i, j) = src.at<float>(i, j)*Hx;
src.at<float>(i, j + 1) = src.at<float>(i, j + 1)*Hx;
}
}
}
//高斯高通滤波器
void GHPF(Mat src, int sigma)
{
int M = src.rows;
int N = src.cols;
for (int i = 0;i < src.rows;i++)
{
for (int j = 0;j < 2 * src.cols;j = j + 2)
{
int Dx = sqrt(pow(i - M / 2, 2) + pow(j / 2 - N / 2, 2));
float Hx = 1 - exp(-Dx*Dx / (2 * sigma*sigma));
//处理实部虚部
src.at<float>(i, j) = src.at<float>(i, j)*Hx;
src.at<float>(i, j + 1) = src.at<float>(i, j + 1)*Hx;
}
}
}
//频域拉普拉斯算子
void Laplacian(Mat src)
{
int M = src.rows;
int N = src.cols;
for (int i = 0;i < src.rows;i++)
{
for (int j = 0;j < 2 * src.cols;j = j + 2)
{
//float Hx = -4*PI*PI*(i*i+j*j);
float Hx = -(pow(i - M / 2, 2) + pow(j - N / 2, 2));
//处理实部虚部
src.at<float>(i, j) = src.at<float>(i, j)*Hx;
src.at<float>(i, j + 1) = src.at<float>(i, j + 1)*Hx;
}
}
}
int main()
{
//读入图像灰度图
Mat I = imread("Moon2.tif", IMREAD_GRAYSCALE);
//判断图像是否加载成功
if (I.empty())
{
cout << "图像加载失败!" << endl;
return -1;
}
else
cout << "图像加载成功!" << endl << endl;
//(-1)^(x+y)乘以输入的图像函数,将傅里叶变换的原点F(0,0)移到(M/2,N/2)上。
Mat padded(I.rows,I.cols,CV_32F);
for (int i = 0;i < I.rows;i++)
{
for (int j = 0;j < I.cols;j++)
{
float temp= I.at(i, j);
padded.at<float>(i, j) = temp*pow(-1, i + j);
}
}
//将planes融合合并成一个多通道数组complexI , 初始时实部就是图像
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(),CV_32F) };
Mat complexI;
merge(planes, 2, complexI);
//进行傅里叶变换及低通高通滤波
dft(complexI, complexI);
//ILPF(complexI, D0);
//BLPF(complexI, Order, D0);
//GLPF(complexI, D0);
//IHPF(complexI, D0);
//BHPF(complexI, Order, D0);
//GHPF(complexI, D0);
Laplacian(complexI);
idft(complexI, complexI);
//计算对应的幅值=(R^2+I^2)^1/2,planes[0] = Re(DFT(I),planes[1] = Im(DFT(I)) , 即planes[0]为实部,planes[1]为虚部
split(complexI, planes);
magnitude(planes[0], planes[1], planes[0]);
Mat magI = planes[0];
//归一化处理,用0-1之间的浮点数将矩阵变换为可视的图像格式
normalize(magI, magI, 0, 1, CV_MINMAX);
imshow("输入图像", I);
imshow("结果图", magI);
waitKey(0);
return 0;
}