【音频处理】离散傅里叶变换

前言

最近复现音乐驱动舞蹈的文章《Dancing-to-Music Character Animation》,用到了与傅里叶变换很相似的称为常Q变换的方法去分割音乐,所以对傅里叶变换做了一个小了解,本文不深入各种乱糟糟的理论,比如什么蝶形算法啥的,单纯讨论离散傅里叶变换(DFT),我们常见的是快速傅里叶变换(FFT),其实FFT是对DFT的一个计算优化,主要是剔除DFT里面有周期性之类的被冗余计算,但是FFT的算法有点小复杂,有兴趣深入理论的请移步如下几篇博文:

如何理解和掌握快速傅里叶变换的计算和概念?

如何通俗地解释什么是离散傅里叶变换?

傅里叶分析之掐死教程(完整版)更新于2014.06.06

傅里叶变换

快速傅里叶变换

第三章 离散傅里叶变换(DFT) 及其快速算法(FFT)

傅里叶级数和傅里叶变换是什么关系?

如何理解傅里叶变换公式?

An Introduction to the Fast Fourier Transform

FFT的详细解释,相信你看了就明白了

如果想仔细学习FFT,最好是看完上述推荐的博文并额外找资料,我是不想看了,因为我看着看着发现自己掉头发了o(╯□╰)o

#简介

傅里叶变换意思就是任何一个输入信号都可以使用多个余弦波叠加而成,简单的说就是把时序信号转换成频域信息。我们最终需要的就是找到这些余弦波的相关参数:幅值,相位。

复习一下三角函数的标准式:
y=Acos(wx+b)+ky=Acos(wx+b)+k y=Acos(wx+b)+k
AAA代表振幅,函数周期是2πw\frac{2\pi}{w}w2π,频率是周期的倒数w2π\frac{w}{2\pi}2πwbbb是函数初相位,kkk在信号处理中称为直流分量。

在很多工具的实现中,余弦波的个数就是信号长度,但是在理论公式中会出现一个参数N,是采样点,通常称为N点FFT。
X(k)=∑n=1Nx(n)∗exp⁡(−−1∗2π∗(k−1)∗(n−1)/N),1<=k<=N.X(k) = \sum _{n=1}^N x(n)*\exp(-\sqrt{-1}*2\pi*(k-1)*(n-1)/N), 1 <= k <= N. X(k)=n=1Nx(n)exp(12π(k1)(n1)/N),1<=k<=N.
上述公式就是DFT的求解方法了,不考虑它的优化方法FFT,我们直接在matlab中码公式,最后与FFT的结果做对比即可验证写的算法对不对。

#实例

假设我们的输入信号是函数是
S=0.2+0.7∗cos⁡(2π∗50t+20180π)+0.2∗cos⁡(2π∗100t+70180π)S=0.2+0.7*\cos(2\pi *50t+\frac{20}{180}\pi) + 0.2*\cos(2\pi*100t+\frac{70}{180}\pi) S=0.2+0.7cos(2π50t+18020π)+0.2cos(2π100t+18070π)
可以发现直流分量是0.2,以及两个余弦函数的叠加,余弦函数的幅值分别为0.7和0.2,频率分别为50和100,初相位分别为20度和70度。

画出信号图:

Fs = 1000;            % Sampling frequency
T = 1/Fs;             % Sampling period
L = 1000;             % Length of signal
t = (0:L-1)*T;        % Time vector
S = 0.2+0.7*cos(2*pi*50*t+20/180*pi) + 0.2*cos(2*pi*100*t+70/180*pi) ;
plot(1000*t(1:50),S(1:50))
title('Signal Corrupted with Zero-Mean Random Noise')
xlabel('t (milliseconds)')
ylabel('X(t)')

这里写图片描述

FFT变换

先使用matlab默认的快速傅里叶变换函数FFT求解叠加余弦的各参数

Y = fft(S);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);

首先直接对原始信号进行傅里叶变换得到YYY,它包含实部与虚部,然后求解归一化后YYY各项的模得到P2P2P2,由于matlab求解的结果包含对称的两个频谱,称为双侧频谱,我们只需要取一半得到P1P1P1,此时只需要将除第一个元素外的元素乘以2即可得到幅值信息,其实求解的根本就是来源于YYYYYY有多少项,就说明求解了多少个叠加的余弦波,接下来解释如何求解各参数:

  • 直流分量:就是Y的第一个值除以采样频率,一般来说是非复数
  • 频率:采样频率采样长度∗(第几项−1)\frac{采样频率}{采样长度}*(第几项-1)(1),本例中采样频率是1000,长度也是1000,那么YYY的第二项频率就是1,第三项频率是2,其实最终情况下,我们选取频率不接近0的值。
  • 幅值:2∗abs(Y各项采样长度)2*abs(\frac{Y各项}{采样长度})2abs(Y)
  • 初相位:atan2(Y的虚部Y的实部)atan2(\frac{Y的虚部}{Y的实部})atan2(YY)转角度制表示

这里写图片描述

P1P1P1的图中,我们很容易看出来幅值不接近0的位置分别是0,50,100附近,其实我们去看具体的位置发现是1,51,101,此三个位置的YYY值分别为:

>> Y(1)ans =200.0000>> Y(51)ans =3.2889e+02 + 1.1971e+02i>> Y(101)ans =34.2020 +93.9693i

那么按照描述,我们得到:

  • 直流分量:Y(1)Fs=0.2\frac{Y(1)}{Fs}=0.2FsY(1)=0.2

  • 频率:第51项和101项的频率分别为50和100

  • 幅值:2∗abs(Y(51)L)=0.72*abs\left(\frac{Y(51)}{L}\right)=0.72abs(LY(51))=0.7 同理2∗abs(Y(101)L)=0.22*abs\left(\frac{Y(101)}{L}\right)=0.22abs(LY(101))=0.2

  • 初相位:

    >> rad2deg(atan2(imag(Y(51)),real(Y(51))))ans =20.0000>> rad2deg(atan2(imag(Y(101)),real(Y(101))))ans =70.0000
    

【注1】: 在实际应用中,一般让余弦波的的数量与信号长度相同,如果不相同,那就是理论中常说的N点FFT

【注2】:幅值是通过模求解的,但是模一般都是正数,如果叠加信号的幅值是复数呢?不要忘记了−cos(x)=cos(x−π)-cos(x)=cos(x-\pi)cos(x)=cos(xπ),也就是说如果幅值是负数,那么只需要在正数幅值的基础上变化一初相位。比如把例子的函数换成:
S=0.2−0.7∗cos⁡(2π∗50t+20180π)+0.2∗cos⁡(2π∗100t+70180π)S=0.2-0.7*\cos(2\pi *50t+\frac{20}{180}\pi) + 0.2*\cos(2\pi*100t+\frac{70}{180}\pi) S=0.20.7cos(2π50t+18020π)+0.2cos(2π100t+18070π)
那么求解频率50对应余弦波的赋值为+0.7,初相位为-160

IFFT变换

顾名思义,IFFT就是逆傅里叶变换,用Y重构信号,其实我们通过Y都已经分析出了原始信号所需的余弦波的各参数,肯定能重构原始数据,这里还是做个实验吧,用IFFT函数:

figure
pred_X=ifft(Y);
plot(pred_X,'r-')
hold on
plot(S,'b*')

这里写图片描述

DFT变换

按照公式手撸DFT,看看计算得到YYYmatlab自带的FFT结果是否一致
X(k)=∑n=1Nx(n)∗exp⁡(−−1∗2π∗(k−1)∗(n−1)/N),1<=k<=N.X(k) = \sum _{n=1}^N x(n)*\exp(-\sqrt{-1}*2\pi*(k-1)*(n-1)/N), 1 <= k <= N. X(k)=n=1Nx(n)exp(12π(k1)(n1)/N),1<=k<=N.

%% DFT变换
%                    N
%      X(k) =       sum  x(n)*exp(-j*2*pi*(k-1)*(n-1)/N), 1 <= k <= N.
%                   n=1
DFT_X=zeros(1,L);
N=L;
for k=1:Lfor n=1:NDFT_X(k)=DFT_X(k)+S(n)*exp(-1j*2*pi*(k-1)*(n-1)/N);end
end
P2=abs(DFT_X/L);
P1 = 2*P2(1:L/2+1);
f = Fs*(0:(L/2))/L;
figure
plot(f,P1)
xlabel('频率')
ylabel('振幅')
title('DFT变换')

这里写图片描述
再计算与FFT求解的结果的误差

%% FFT变换
FFT_X=fft(S);
figure
plot(abs(FFT_X-DFT_X))
title('DFT和FFT误差')

这里写图片描述

IDFT

同样按照公式手撸逆离散傅里叶变换
x(n)=1N∑k=1NX(k)∗exp⁡(−1∗2π∗(k−1)∗(n−1)/N),1<=n<=N.x(n) = \frac{1}{N}\sum _{k=1}^N X(k)*\exp(\sqrt{-1}*2\pi*(k-1)*(n-1)/N), 1 <= n <= N. x(n)=N1k=1NX(k)exp(12π(k1)(n1)/N),1<=n<=N.

%% IDFT变换
%                    N
%      x(n) = (1/N) sum  X(k)*exp( j*2*pi*(k-1)*(n-1)/N), 1 <= n <= N.
%                   k=1
DFT_rec_x=zeros(1,k);
for n=1:Lfor k=1:LDFT_rec_x(n)=DFT_rec_x(n)+DFT_X(k)*exp( 1j*2*pi*(k-1)*(n-1)/N);end
end
DFT_rec_x=DFT_rec_x./N;
rec_err=real(DFT_rec_x)-S;
figure
plot(rec_err)
title('IFFT数据重构误差')

这里写图片描述
IFFT的结果对比一下:

%% IFFT变换
FFT_rec_x=ifft(FFT_X);
figure
plot(abs(DFT_rec_x-FFT_rec_x))
title('IDFT和IFFT误差')

这里写图片描述

后记

折腾了这么多,其实就是为了一个字:懒。为了避免复杂的FFT的理论理解,我直接按照DFT的公式码代码,取得了与FFT一样的结果,对于不喜欢复杂理论的同志,可以在代码实现FFT的时候直接采用DFT的代码作为替代品,虽然时间复杂度增大很多,但是理论理解起来简单很多倍不是。

贴一下文章代码,此外还有一个FFT的手动实现:链接:https://pan.baidu.com/s/1mUdclEQ3tgQUvZQ4XffN3g 密码:08tg

等我不掉头发了,再去纠结FFT的蝶形算法_

博客已同步更新到微信公众号中,有兴趣可关注一波,微信公众号与博客同步更新个人的学习笔记
在这里插入图片描述

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

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

相关文章

【音频处理】短时傅里叶变换

前言 上一篇博客讲了离散傅里叶变换&#xff0c;里面的实例是对整个信号进行计算&#xff0c;虽然理论上有N点傅里叶变换(本博客就不区分FFT和DFT了&#xff0c;因为它俩就是一个东东&#xff0c;只不过复杂度不同)&#xff0c;但是我个人理解是这个N点是信号前面连续的N个数值…

【theano-windows】学习笔记十九——循环神经网络

前言 前面已经介绍了RBM和CNN了&#xff0c;就剩最后一个RNN了&#xff0c;抽了一天时间简单看了一下原理&#xff0c;但是没细推RNN的参数更新算法BPTT&#xff0c;全名是Backpropagation Through Time。 【注】严谨来说RNN有两个称呼&#xff1a;①结构上递归的recursive n…

【theano-windows】学习笔记二十——LSTM理论及实现

前言 上一篇学习了RNN&#xff0c;也知道了在沿着时间线对上下文权重求梯度的时候&#xff0c;可能会导致梯度消失或者梯度爆炸&#xff0c;然后我们就得学习一波比较常见的优化方法之LSTM 国际惯例&#xff0c;参考网址&#xff1a; LSTM Networks for Sentiment Analysis …

刚体运动学——欧拉角、四元数、旋转矩阵

前言 刚体运动旋转一般用&#xff1a;欧拉角、四元数、轴角对等表示&#xff0c;在对某个坐标旋转的时候&#xff0c;只需将欧拉角或四元数转换为旋转矩阵&#xff0c;并与原始坐标相乘&#xff0c;便可得到旋转以后的坐标。这里主要看看欧拉角、四元数和旋转矩阵。 国际惯例…

刚体运动学-四元数插值

前言 之前对写了一篇关于刚体运动学相关知识博客&#xff1a;刚体运动学——欧拉角、四元数、旋转矩阵&#xff0c;本篇博客就举例来说明&#xff0c;如何在运动捕捉数据中进行四元数插值。 国际惯例&#xff0c;参考博客&#xff1a; 探讨&#xff1a;向量&#xff08;方向…

【TensorFlow-windows】学习笔记一——基础理解

前言 因为Theano已经停止更新了&#xff0c;所以在前面学完Theano搭建RBM,CNN,RNN相关结构以后&#xff0c;还是得选择一个主流框架的&#xff0c;由于我自身的学习最终是向强化学习靠近&#xff0c;可能用到的仿真环境是openai gym&#xff0c;所以选择了继续学习TensorFlow&…

【TensorFlow-windows】学习笔记二——低级API

前言 上一篇博客初步了解了tensorflow中建立机器学习模型的方法&#xff1a;可以使用eager execution和graph execution两种模式&#xff0c;可以使用高级API estimator中已经封装好的模型&#xff0c;也可以自己创建estimator&#xff0c;更重要的是我们也可以使用低级API自行…

【TensorFlow-windows】学习笔记五——自编码器

前言 上一篇博客介绍的是构建简单的CNN去识别手写数字&#xff0c;这一篇博客折腾一下自编码&#xff0c;理论很简单&#xff0c;就是实现对输入数据的重构&#xff0c;具体理论可以看我前面的【theano-windows】学习笔记十三——去噪自编码器 国际惯例&#xff0c;参考博客&…

【TensorFlow-windows】学习笔记六——变分自编码器

#前言 对理论没兴趣的直接看代码吧&#xff0c;理论一堆&#xff0c;而且还有点复杂&#xff0c;我自己的描述也不一定准确&#xff0c;但是代码就两三句话搞定了。 国际惯例&#xff0c;参考博文 论文&#xff1a;Tutorial on Variational Autoencoders 【干货】一文读懂…

【TensorFlow-windows】学习笔记七——生成对抗网络

前言 既然学习了变分自编码(VAE)&#xff0c;那也必须来一波生成对抗网络(GAN)。 国际惯例&#xff0c;参考网址&#xff1a; 论文: Generative Adversarial Nets PPT:Generative Adversarial Networks (GANs) Generative Adversarial Nets in TensorFlow GAN原理学习笔记…

Openpose——windows编译(炒鸡简单)

前言 最近准备看看rtpose的代码&#xff0c;发现已经由openpose这个项目维护着了&#xff0c;由于经常在windows下调试代码&#xff0c;所以尝试了一下如何在windows下编译openpose源码&#xff0c;整体来说非常简单的。 国际惯例&#xff0c;参考博客&#xff1a; [OpenPos…

强化学习——Qlearning

前言 在控制决策领域里面强化学习还是占很重比例的&#xff0c;最近出了几篇角色控制的论文需要研究&#xff0c;其中部分涉及到强化学习&#xff0c;都有开源&#xff0c;有兴趣可以点开看看&#xff1a; A Deep Learning Framework For Character Motion Synthesis and Edit…

【TensorFlow-windows】keras接口学习——线性回归与简单的分类

前言 之前有写过几篇TensorFlow相关文章&#xff0c;但是用的比较底层的写法&#xff0c;比如tf.nn和tf.layers&#xff0c;也写了部分基本模型如自编码和对抗网络等&#xff0c;感觉写起来不太舒服&#xff0c;最近看官方文档发现它的教程基本都使用的keras API&#xff0c;这…

【TensorFlow-windows】keras接口——卷积手写数字识别,模型保存和调用

前言 上一节学习了以TensorFlow为底端的keras接口最简单的使用&#xff0c;这里就继续学习怎么写卷积分类模型和各种保存方法(仅保存权重、权重和网络结构同时保存) 国际惯例&#xff0c;参考博客&#xff1a; 官方教程 【注】其实不用看博客&#xff0c;直接翻到文末看我的c…

【TensorFlow-windows】keras接口——BatchNorm和ResNet

前言 之前学习利用Keras简单地堆叠卷积网络去构建分类模型的方法&#xff0c;但是对于很深的网络结构很难保证梯度在各层能够正常传播&#xff0c;经常发生梯度消失、梯度爆炸或者其它奇奇怪怪的问题。为了解决这类问题&#xff0c;大佬们想了各种办法&#xff0c;比如最原始的…

【TensorFlow-windows】keras接口——卷积核可视化

前言 在机器之心上看到了关于卷积核可视化相关理论&#xff0c;但是作者的源代码是基于fastai写的&#xff0c;而fastai的底层是pytorch&#xff0c;本来准备自己用Keras复现一遍的&#xff0c;但是尴尬地发现Keras还没玩熟练&#xff0c;随后发现了一个keras-vis包可以用于做…

【TensorFlow-windows】投影变换

前言 没什么重要的&#xff0c;就是想测试一下tensorflow的投影变换函数tf.contrib.image.transform中每个参数的含义 国际惯例&#xff0c;参考文档 官方文档 描述 调用方法与默认参数&#xff1a; tf.contrib.image.transform(images,transforms,interpolationNEAREST,…

【TensorFlow-windows】扩展层之STN

前言 读TensorFlow相关代码看到了STN的应用&#xff0c;搜索以后发现可替代池化&#xff0c;增强网络对图像变换(旋转、缩放、偏移等)的抗干扰能力&#xff0c;简单说就是提高卷积神经网络的空间不变性。 国际惯例&#xff0c;参考博客&#xff1a; 理解Spatial Transformer…

【TensorFlow-windows】MobileNet理论概览与实现

前言 轻量级神经网络中&#xff0c;比较重要的有MobileNet和ShuffleNet&#xff0c;其实还有其它的&#xff0c;比如SqueezeNet、Xception等。 本博客为MobileNet的前两个版本的理论简介与Keras中封装好的模块的对应实现方案。 国际惯例&#xff0c;参考博客&#xff1a; 纵…

【TensorFlow-windows】keras接口——ImageDataGenerator裁剪

前言 Keras中有一个图像数据处理器ImageDataGenerator&#xff0c;能够很方便地进行数据增强&#xff0c;并且从文件中批量加载图片&#xff0c;避免数据集过大时&#xff0c;一下子加载进内存会崩掉。但是从官方文档发现&#xff0c;并没有一个比较重要的图像增强方式&#x…