- 离散傅立叶变换:
对一张图像使用傅立叶变换就是将它分解成正弦和余弦两部分(任一函数都可以表示成无数个正弦和余弦函数的和的形式);
就是将图像从空间域(spatial domain)转换到频域(frequency domain) - 经过象限变化,如果中心(聚)亮,说明高频多,图像包含的细节/边/角信息较多;
而如果四角亮,说明低频多,图像模糊
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char ** argv)
{
Mat src;
src = imread("../path.jpg", IMREAD_GRAYSCALE);
if (src.empty())
{
cout << "could not load image..." << endl;
return -1;
}
//--------1.将图像延扩到最佳尺寸//当图像的尺寸是2,3,5的整数倍时,计算速度最快
//为了达到快速计算的目的,经常通过添凑新的边缘像素的方法获取最佳图像尺寸
Mat temp; //将输入图像延扩到最佳的尺寸
int row = getOptimalDFTSize(src.rows); //getOptimalDFTSize返回最佳尺寸
int col = getOptimalDFTSize(src.cols); //在边缘添加0
copyMakeBorder(src, temp, 0, row - src.rows, 0, col - src.cols, BORDER_CONSTANT, Scalar::all(0));//填充边缘像素//添加的像素初始化为0
//--------2.为傅里叶变换的结果(实部和虚部)分配存储空间
//傅立叶变换的结果是复数,这就是说对于每个原图像值,结果是两个图像值
//频域值范围远远超过空间值范围,因此至少要将频域储存在 float 格式中
Mat planes[] = { Mat_<float>(temp), Mat::zeros(temp.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI);//将多个单通道矩阵合并成一个多通道矩阵//为延扩后的图像增添一个初始化为0的通道
//--------3.进行离散傅立叶变换
dft(complexI, complexI);//变换结果保存在原始矩阵中
//--------4.将复数转换为幅度
// 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()计算二维矢量的幅值
第一个参数:InputArray类型的x,表示矢量的浮点型x坐标值,也就是实部
第二个参数:InputArray类型的y,表示矢量的浮点型y坐标值,也就是虚部
第三个参数:OutputArray类型的magnitude,输出的幅值,它和第一个参数x有着同样的尺寸和类型
*/
magnitude(planes[0], planes[1], planes[0]);//planes[0] = magnitude
Mat dst = planes[0];
//--------5.对数尺度(logarithmic scale)缩放
//傅立叶变换的幅度值范围大到不适合在屏幕上显示
//高值在屏幕上显示为白点,而低值为黑点,高低值的变化无法有效分辨
//为了在屏幕上凸显出高低变化的连续性,可以用对数尺度来替换线性尺度
dst += Scalar::all(1);//转换到对数尺度
log(dst, dst);
//--------6.剪切和重分布幅度图象限
dst = dst(Rect(0, 0, dst.cols & -2, dst.rows & -2));
//第五步得到的幅度图从中间划开得到四张1/4子图像,将每张子图像看成幅度图的一个象限,
//重新分布即将四个角点重叠到图片中心)
//这样的话原点(0,0)就位移到图像中心
//重新排列傅里叶图像的象限,使原点位于图像中心
int cx = dst.cols / 2;
int cy = dst.rows / 2;
Mat q0(dst, Rect(0, 0, cx, cy)); // Top-Left - 为每一个象限创建ROI
Mat q1(dst, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(dst, Rect(0, cy, cx, cy)); // Bottom-Left
Mat q3(dst, Rect(cx, cy, cx, cy)); // Bottom-Right
Mat tmp; // 交换象限 (Top-Left with Bottom-Right)
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp); // 交换象限 (Top-Right with Bottom-Left)
q2.copyTo(q1);
tmp.copyTo(q2);
//--------7.归一化
normalize(dst, dst, 0, 1, NORM_MINMAX); // 将float类型的矩阵转换到可显示图像范围
// (float [0, 1])
imshow("src", src);
imshow("dst", dst);
waitKey();
return 0;
}
输出结果: