【OpenCV C++】离散傅里叶变换

离散傅里叶变换

傅里叶变换是什么?一看就懂,写的超级棒!https://blog.csdn.net/m0_51233386/article/details/114764782

离散傅里叶变换(Discrete Fourier Transform,DFT)傅里叶分析方法是信号分析的最基本方法,傅里叶变换是傅里叶分析的核心,通过它把信号从时间域变换到频率域,进而研究信号的频谱结构和变化规律。

离散傅里叶变换(DFT),**是傅里叶变换在时域和频域上都呈现离散的形式,将时域信号的采样变换为在离散时间傅里叶变换(DTFT)频域的采样。**在形式上,变换两端(时域和频域上)的序列是有限长的,而实际上这两组序列都应当被认为是离散周期信号的主值序列。即使对有限长的离散信号作DFT,也应当将其看作经过周期延拓成为周期信号再作变换。在实际应用中通常采用快速傅里叶变换以高效计算DFT。

程序说明

// 程序描述:OpenCV C++离散傅里叶变换
// 参 考:毛星云《OpenCV3编程入门》
// 操作系统: Windows 10 64bit
// 开发语言: C++
// IDE 版 本:Visual Studio 2019
// OpenCV版本:4.20

代码

#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;

//--------------------------------------【main( )函数】-----------------------------------------
//          描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-------------------------------------------------------------------------------------------------
int main()
{
	//【1】以灰度模式读取原始图像并显示
	Mat srcImage = imread("23.jpg",0);
	if (!srcImage.data) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; }
	imshow("原始灰度图", srcImage);

	//【2】将输入图像延扩到最佳的尺寸,边界用0补充
	int m = getOptimalDFTSize(srcImage.rows);
	int n = getOptimalDFTSize(srcImage.cols);
	//将添加的像素初始化为0.
	Mat padded;
	copyMakeBorder(srcImage, padded, 0, m - srcImage.rows, 0, n - srcImage.cols, BORDER_CONSTANT, Scalar::all(0));

	//【3】为傅立叶变换的结果(实部和虚部)分配存储空间。
	//将planes数组组合合并成一个多通道的数组complexI
	Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
	Mat complexI;
	merge(planes, 2, complexI);

	//【4】进行就地离散傅里叶变换
	dft(complexI, complexI);

	//【5】将复数转换为幅值,即=> log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
	split(complexI, planes); // 将多通道数组complexI分离成几个单通道数组,planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
	magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude  
	Mat magnitudeImage = planes[0];

	//【6】进行对数尺度(logarithmic scale)缩放
	magnitudeImage += Scalar::all(1);
	log(magnitudeImage, magnitudeImage);//求自然对数

	//【7】剪切和重分布幅度图象限
	//若有奇数行或奇数列,进行频谱裁剪      
	magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols & -2, magnitudeImage.rows & -2));
	//重新排列傅立叶图像中的象限,使得原点位于图像中心  
	int cx = magnitudeImage.cols / 2;
	int cy = magnitudeImage.rows / 2;
	Mat q0(magnitudeImage, Rect(0, 0, cx, cy));   // ROI区域的左上
	Mat q1(magnitudeImage, Rect(cx, 0, cx, cy));  // ROI区域的右上
	Mat q2(magnitudeImage, Rect(0, cy, cx, cy));  // ROI区域的左下
	Mat q3(magnitudeImage, Rect(cx, cy, cx, cy)); // ROI区域的右下
	//交换象限(左上与右下进行交换)
	Mat tmp;
	q0.copyTo(tmp);
	q3.copyTo(q0);
	tmp.copyTo(q3);
	//交换象限(右上与左下进行交换)
	q1.copyTo(tmp);
	q2.copyTo(q1);
	tmp.copyTo(q2);

	//【8】归一化,用0到1之间的浮点值将矩阵变换为可视的图像格式
	//此句代码的OpenCV2版为:
	//normalize(magnitudeImage, magnitudeImage, 0, 1, CV_MINMAX); 
	//此句代码的OpenCV3版为:
	normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);

	//【9】显示效果图
	imshow("频谱幅值", magnitudeImage);
	waitKey();

	return 0;
}


运行效果

示例(1)
在这里插入图片描述

在这里插入图片描述
示例(2)
为了便于理解变换过程,我将中间过程的图片也输出了,其实我们只需要得到最后一幅频谱图。
在这里插入图片描述在这里插入图片描述
离散傅里叶变换的运行速度与图片尺寸有很大关系,当图像的尺寸的2,3, 5的整数倍时,计算速度最快。因此,为了达到快速计算的目的,经常通过添加新的边缘像素的方法获取最佳图像尺寸。

将输入图像延扩到最佳的尺寸,边界用0补充,可以看到图片下方多出了宽度大概0.5厘米的黑条。
在这里插入图片描述在这里插入图片描述
示例(3)
在这里插入图片描述
在这里插入图片描述
离散傅里叶变换的运行速度与图片尺寸有很大关系,当图像的尺寸的2,3, 5的整数倍时,计算速度最快。因此,为了达到快速计算的目的,经常通过添加新的边缘像素的方法获取最佳图像尺寸。

将输入图像延扩到最佳的尺寸,边界用0补充,可以看到图片下方多出了宽度大概0.9厘米的黑条。
在这里插入图片描述
示例(4)
在这里插入图片描述在这里插入图片描述
此图尺寸刚好合适,边缘就没有明显的新增像素。
在这里插入图片描述在这里插入图片描述

傅里叶变换的频谱图的含义

详情参考:https://blog.csdn.net/m0_51233386/article/details/115134101

二维频谱中的每一个点都是一个与之一一对应的二维正弦/余弦波。

频谱图除了周期性之外,还表示高频低频的分布。在经过频谱居中后的频谱中,中间最亮的点是最低频率,属于直流分量(DC分量)。越往边外走,频率越高。所以,频谱图中的四个角和X,Y轴的尽头都是高频。

猜你喜欢

转载自blog.csdn.net/m0_51233386/article/details/115112896
今日推荐