快速傅里叶变换(FFT)的C#实现及详细注释

快速傅里叶变换(FFT)的C#实现及详细注释

-------------------------------------------------------------------------------------------------------------------

作者:随煜而安

时间:2015/7/21

注:本文为作者原创文章,所有参考内容均在参考文献中列出,转载请注明出处。

参考文献:

数字信号处理第三版(华中科技大学出版社)

C#数字图像处理算法典型实例(人民邮电出版社)

-------------------------------------------------------------------------------------------------------------------

在阅读下面的快速傅立叶变换实现代码前,需要先了解FFT的基本原理及过程。可以参考我的上一篇博客快速傅里叶变换原理解析

下面给出的代码实现的是一维频率抽取的基2FFT算法,上面链接的博客中已经说过,对于快速傅立叶变换FFT,按时间抽取和按频率抽取的快速算法的计算量是相同的,所以这里只给出了按频率抽取的代码。

首先,我们知道,傅里叶变换的序列和变换结果都应该是复数序列,所以我们要封装一个复数Complex类。详细代码我已经在我的博客C#复数类的封装中给出(我今天发现其中有个ToString的重载方法写的有点问题,如果要使用注意一下,但无伤大雅也不影响本博客的任何内容)。

封装好了复数Complex类,我们就可以开始实现FFT算法了。首先给出两个私有方法,也是真正实现FFT的核心方法。

一维频率抽取基2的FFT算法:

/// <summary>/// 一维频率抽取基2快速傅里叶变换/// 频率抽取:输入为自然顺序,输出为码位倒置顺序/// 基2:待变换的序列长度必须为2的整数次幂/// </summary>/// <param name="sourceData">待变换的序列(复数数组)</param>/// <param name="countN">序列长度,可以指定[0,sourceData.Length-1]区间内的任意数值</param>/// <returns>返回变换后的序列(复数数组)</returns>private Complex[] fft_frequency(Complex[] sourceData, int countN){//2的r次幂为N,求出r.r能代表fft算法的迭代次数int r = Convert.ToInt32(Math.Log(countN, 2));//分别存储蝶形运算过程中左右两列的结果Complex[] interVar1 = new Complex[countN];Complex[] interVar2 = new Complex[countN];interVar1 = (Complex[])sourceData.Clone();//w代表旋转因子Complex[] w = new Complex[countN / 2];//为旋转因子赋值。(在蝶形运算中使用的旋转因子是已经确定的,提前求出以便调用)//旋转因子公式 \  /\  /k __//              \/  \/N  --  exp(-j*2πk/N)//这里还用到了欧拉公式for (int i = 0; i < countN / 2; i++){double angle = -i * Math.PI * 2 / countN;w[i] = new Complex(Math.Cos(angle), Math.Sin(angle));}//蝶形运算for (int i = 0; i < r; i++){//i代表当前的迭代次数,r代表总共的迭代次数.//i记录着迭代的重要信息.通过i可以算出当前迭代共有几个分组,每个分组的长度//interval记录当前有几个组// <<是左移操作符,左移一位相当于*2//多使用位运算符可以人为提高算法速率^_^int interval = 1 << i;//halfN记录当前循环每个组的长度Nint halfN = 1 << (r - i);//循环,依次对每个组进行蝶形运算for (int j = 0; j < interval; j++){//j代表第j个组//gap=j*每组长度,代表着当前第j组的首元素的下标索引int gap = j * halfN;//进行蝶形运算for (int k = 0; k < halfN / 2; k++){interVar2[k + gap] = interVar1[k + gap] + interVar1[k + gap + halfN / 2];interVar2[k + halfN / 2 + gap] = (interVar1[k + gap] - interVar1[k + gap + halfN / 2]) * w[k * interval];}}//将结果拷贝到输入端,为下次迭代做好准备interVar1 = (Complex[])interVar2.Clone();}//将输出码位倒置for (uint j = 0; j < countN; j++){//j代表自然顺序的数组元素的下标索引//用rev记录j码位倒置后的结果uint rev = 0;//num作为中间变量uint num = j;//码位倒置(通过将j的最右端一位最先放入rev右端,然后左移,然后将j的次右端一位放入rev右端,然后左移...)//由于2的r次幂=N,所以任何j可由r位二进制数组表示,循环r次即可for (int i = 0; i < r; i++){rev <<= 1;rev |= num & 1;num >>= 1;}interVar2[rev] = interVar1[j];}return interVar2;}


  一维频率抽取基2的IFFT算法:


实现IFFT即逆变换的方法有多种,我采用的是能够重复调用写好的FFT方法的方式。这种方式需要在进行逆变换前对输入序列稍作处理取每个元素的共轭。然后调用FFT方法,最终同意对结果做除N处理即可。具体实现代码如下

/// <summary>/// 一维频率抽取基2快速傅里叶逆变换/// </summary>/// <param name="sourceData">待反变换的序列(复数数组)</param>/// <param name="countN">序列长度,可以指定[0,sourceData.Length-1]区间内的任意数值</param>/// <returns>返回逆变换后的序列(复数数组)</returns>private Complex[] ifft_frequency(Complex[] sourceData, int countN){//将待逆变换序列取共轭,再调用正变换得到结果,对结果统一再除以变换序列的长度Nfor (int i = 0; i < countN; i++){sourceData[i] = sourceData[i].Conjugate();}Complex[] interVar = new Complex[countN];interVar = fft_frequency(sourceData, countN);for (int i = 0; i < countN; i++){interVar[i] = new Complex(interVar[i].Real / countN, -interVar[i].Imaginary / countN);}return interVar;}

这样我们有了核心的两个方法,当然我们实现的是基2的FFT,对于其他情况,我打算在考完研后补充一个普通DFT的算法,一个针对N为合数的FFT算法。这样我们就可以封装一个供用户调用的公共方法,针对N的类型,智能的选择合适的算法。当然,目前就先实现到这里了,下面是我封装的公共方法,N为合数的FFT算法用注释代替了,虚位以待,考完研后补上!

/// <summary>/// 对给定的序列进行指定长度的离散傅里叶变换DFT/// 内部将使用快速傅里叶变换FFT/// </summary>/// <param name="sourceData">待变换的序列</param>/// <param name="countN">变换的长度N</param>/// <returns>返回变换后的结果(复数数组)</returns>public Complex[] DFT(Complex[] sourceData, int countN){if (countN > sourceData.Length || countN < 0)throw new Exception("指定的傅立叶变换长度越界!");//求出r,2的r次幂为Ndouble dr = Math.Log(countN, 2);int r = Convert.ToInt32(dr);//获取整数部分//初始化存储变换结果的数组Complex[] result = new Complex[countN];//判断选择合适的算法进行快速傅里叶变换FFTif ((dr - r) != 0){//待变换序列长度不是基2的}else{//待变换序列长度是基2的//使用一维频率抽取基2快速傅里叶变换result = fft_frequency(sourceData, countN);}return result;}



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499557.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

风机桨叶故障诊断(一) 样本的获取

风机桨叶故障诊断&#xff08;一&#xff09; 样本的获取今天团队接了个新项目&#xff0c;做一个风机桨叶故障诊断系统。虽然马上就是准备考研的关键期了&#xff0c;可是一想到这是我学习了机器学习后遇到的第一个实际项目&#xff0c;我觉得参与进来&#xff0c;也帮导师分担…

风机桨叶故障诊断(二) 获取图像几何主方向

风机桨叶故障诊断&#xff08;二&#xff09; 获取图像几何主方向 昨天&#xff0c;我将视频资源按帧抽取并筛选得到了可以用来提取样本的图像库。今天还是进行项目的准备工作。当我们拿到一张图片&#xff0c;我们的软件要做的大致可以分为三个步骤&#xff1a;从原图中识别桨…

风机桨叶故障诊断(三) 识别桨叶——初步构建BP神经网络

风机桨叶故障诊断&#xff08;三&#xff09; 识别桨叶——初步构建BP神经网络 新的一天&#xff0c;希望有好的运气。今天开始着手系统的第一个模块&#xff0c;从一幅图像中寻找到桨叶所在的位置。第一直觉我们的识别任务属于难度比较大&#xff0c;干扰因素多的了&#xff…

风机桨叶故障诊断(四) 正负样本准备——从图像中随机扣图

风机桨叶故障诊断&#xff08;四&#xff09; 正负样本准备——从图像中随机扣图 在之前的工作中&#xff0c;我们已经训练了一个400252的三层BP神经网络&#xff0c;通过这个基础的神经网络进行了误差分析&#xff0c;对我们的问题有了更深刻的认识。现在我们要开始不断完善我…

风机桨叶故障诊断(五) 修改隐含层神经元个数的尝试

风机桨叶故障诊断&#xff08;五&#xff09; 修改隐含层神经元个数的尝试 我们已经为训练一个更为稳健的神经网络做好了样本的准备工作&#xff0c;那么我们开始下一步的工作吧&#xff01; 我们已经有了样本集&#xff0c;目前我筛选出来了247个正样本&#xff0c;652个负样本…

风机桨叶故障诊断(六) 利用自编码器进行特征学习

风机桨叶故障诊断&#xff08;六&#xff09; 利用自编码器进行特征学习 在之前的工作中&#xff0c;我已经初步构建了三层的BP神经网络&#xff0c;并已经从样本集的选取&#xff0c;模型的选择&#xff08;隐含层神经元个数&#xff09;&#xff0c;和输出层神经元阈值选择这…

风机桨叶故障诊断(七) 滑动窗与非极大值抑制NMS

风机桨叶故障诊断&#xff08;七&#xff09;滑动窗与非极大值一直NMS 到目前为止&#xff0c;我已经利用自编码神经网络提取特征后训练得到了BP神经网络&#xff08;参见&#xff1a;点击打开链接&#xff09;&#xff0c;且在测试样本集上表现不错。下面我们就要应用到实际中…

Distinctive Image Features from Scale-Invariant Keypoints-SIFT算法译文

本文全篇转载自如下博客&#xff0c;感谢博主的无私分享 http://www.cnblogs.com/cuteshongshong/archive/2012/05/25/2506374.html ------------------------------------------------------------------------------------------------------ 从尺度不变的关键点选择可区分的…

将图像绘制成3维立体散点图

matlab源代码&#xff1a; Iimread(F:\绝缘子识别\绝缘子红外test图片\test (50).jpg); Irgb2gray(I); [wd,len]size(I); interval10; %设置绘制散点图的间隔&#xff0c;全部绘出会很卡 x[]; y[]; z[]; numfloor((len-1)/interval)1;%计算在当前间隔下图像的每一行…

二叉树的非递归遍历|前中后序遍历

二叉树的非递归遍历 文章目录 二叉树的非递归遍历前序遍历-栈层序遍历-队列中序遍历-栈后序遍历-栈 前序遍历-栈 首先我们应该创建一个Stack 用来存放节点&#xff0c;首先我们想要打印根节点的数据&#xff0c;此时Stack里面的内容为空&#xff0c;所以我们优先将头结点加入S…

C#灰度图转伪彩色图

/// <summary>/// 伪彩色图像构造器/// </summary>public class PseudoColorImageBuilder{/// <summary>/// 铁红色带映射表/// 每一行代表一个彩色分类&#xff0c;存放顺序是RGB/// </summary>public static byte[,] ironTable new byte[128, 3] {{…

砥志研思SVM(二) 拉格朗日乘子法与KKT条件

[1]最优化问题中的对偶性理论 [2]拉格朗日乘子法(上) [3]拉格朗日乘子法(下)

VS2015上配置opencv2.4.11

VS2015上配置opencv2.4.11版方法总结 最近给电脑重装了系统&#xff0c;需要的软件各种装。今天阅读了很多网上的博客&#xff0c;几经波折完成了opencv的配置。配置opencv与其他函数包或者软件相比算是麻烦的了&#xff0c;可能出现的问题也是五花八门&#xff0c;所以针对我的…

热传导方程的差分格式原理与matlab实现

function [ ] ParabolicEquation( h,k ) %求解抛物型方程中的一种&#xff1a;热传导方程 %h:x轴步长 %k:t轴步长rk/(h*h);%网格比 Mxfloor(1.0/h)1;%网格在x轴上的节点个数&#xff08;算上0&#xff09; Ntfloor(1.0/k)1;%网格在t轴上的节点个数&#xff08;算上0&#xff…