作用
离散傅里叶变换主要是将连续的信号转换为离散的信号。如在时域上连续的有时在频域上是离散的。然而我们知道,任何的一个函数都可以由无数个正弦函数和余弦函数相结合的形式来表示。即:
如果将一个图像进行离散傅里叶变换,就是将图像从空间域转换到频域上。其中f是空间域的值,F是频域的值。转换后的频域值是复数。所以,图像可由幅度图像加相位图像或者是实数图像加虚数图像表示。在实际处理图像的过程中,幅度图像就已经包含了原图像几乎的所有信息,所以一般我们只使用幅度图像。但是若想通过修改幅度图像或相位图像来间接修改原图像,要用逆傅里叶变换得到修改后的空间图像,这样就必须同时保留幅度图像和相位图像。
实例
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
char* filename = "C://Users/yangping/Desktop/maomao.jpg";
Mat I = imread(filename, CV_LOAD_IMAGE_GRAYSCALE);
//将输入图像展开为最佳尺寸expand input image to optimal size
Mat padded;
int m = getOptimalDFTSize(I.rows);
int n = getOptimalDFTSize(I.cols);
//分配缓冲区并置0 填充边缘像素
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);
//
dft(complexI, complexI);
//通道分离 存入planes中
split(complexI, planes);
//计算幅度
magnitude(planes[0], planes[1], planes[0]);
Mat magI = planes[0];
magI += Scalar::all(1);
log(magI, magI);
//将图像扩充的部分删除
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
//重新排列象限,使原(0,0)点位于图像中心点
int cx = magI.cols / 2;
int cy = magI.rows / 2;
Mat q0(magI, Rect(0, 0, cx, cy)); //top left
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;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
//交换象限 右上角和左下角
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
//矩阵归一化 把数据经过处理后限制在一定范围内
normalize(magI, magI, 0, 1, CV_MINMAX); //将矩阵数据值控制在0,1之间
imshow("input image ", I);
imshow("spectrum magnitude", magI);
waitKey();
return 0;
}
函数解析
1.int getOptimalDFTSize(int vecsize)
将图像展开为最优尺寸,即需要扩充的数量。如2,3,5的倍数可以更高效的进行傅里叶变换,速度更快。
vecsize:向量尺寸,图片的rows,cols
2.
void copyMakeBorder(InputArray src, OutputArray dst, int top, int bottom,int left, int right, int borderType, const Scalar& value=Scalar() )
扩充图像到最佳尺寸,扩充的地方用0像素添加。
src:输入图像
dst:输出图像
top bottom left right:在原图像的上下左右方向分别添加的像素的宽度
borderType:边界类型
value:边界值
3
void merge(const Mat*mv, size_t count, OutputArray dst)
合并通道。
因为傅里叶变换的结果是复数,这就是说对于每个原图像值,结果是两个图像值。 此外,频域值范围远远超过空间值范围, 因此至少要将频域储存在 float 格式中。 结果我们将输入图像转换成浮点类型,并多加一个额外通道来储存复数部分。所以将输入图像转换成浮点型,并多加了一个初始化为0的通道用来存储复数部分。
mv:被合并的图像指针,图像必须是相同的size和depth
count:被合并的图像的数目
dst:输出图像,图像的size和depht合mv[0]相同,通道数为被合并图像通道数的总和
4.
void dft(InputArraysrc, OutputArray dst, int flags=0, int nonzeroRows=0)
进行傅里叶变换,支持图像原地计算,结果保存在原始图像中
5.
void magnitude(InputArray x, InputArray y, OutputArray magnitude)
计算幅度,将复数转换为幅度
x:实部
y:虚部
magnitude:输出图像,和x有同样的size和type
6.
void log(InputArraysrc, OutputArray dst)
对数组中的元素取对数。傅里叶变换的幅度范围太大不适合在屏幕上显示。高值显示为白点,低值显示为黑点,高低值得变化无法有效分辨。为了凸显出高低变化的连续性,用对数尺度替换线性尺度
src:输入图像
dst:输出图像
7.
void normalize(InputArray src, OutputArraydst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1,InputArray mask=noArray() )
矩阵归一化。重新分布的幅度图的幅度值超过可显示范围[0,1],normalize函数将幅度归一化到可显示的范围。
src:输入图像
dst:输出图像
alpha:归一化后的最大值
beta:归一化后的最小值
norm_type:归一化类型。有NORM_INF, NORM_L1, NORM_L2和NORM_MINMAX