傅里叶变换应用 (02/2):频域和相位

 

一、说明

        到目前为止,在我们的讨论中,我已经交替使用了“傅里叶变换”和“快速傅里叶变换(FFT)”。在这一点上,值得注意的是区别!FFT 是“离散”傅里叶变换 (DFT) 的有效算法实现。“离散”表示我们可以将变换应用于一系列点,而不是完整的连续信号。在数据科学应用中,我们通常有一组样本,而不是一个连续的输入函数,所以我们通常对DFT感兴趣!

二、傅里叶变换函数

2.1 np.fft.fft

FFT将时域或空间域中的实数或复数信号作为输入,并返回离散傅里叶变换。正如我们所讨论的,np.abs允许我们恢复频率分量的幅度,np.angle允许我们恢复相位。

我们之前已经见过这个功能(请参阅本系列前面的故事)!我们将回到这个笔记,但它足够重要,说两次!傅里叶变换可以应用于复杂的输入信号。对于复杂输入,傅里叶变换返回的负频率项对于完全重建信号是必要的。对于实际输入(如我们在本系列中到目前为止研究的输入),只需要正频率项。您仍然可以在实值信号上使用完整的FFT,只要知道您可以使用重复值较少的RFFT(请参阅下面的更多上下文)。

索引 0: 第一个值有时称为“DC”项(来自电气工程领域的“直流电”),并用作偏移项。它不会振荡,对应于 0Hz(因此是直流)。它只是信号的总和!

如果 N 是偶数,则索引 1 到 ((N/2) -1,否则为 1 到 ((N-1)/2): 正频率分量按越来越正的顺序排列。

如果 N 是偶数,则索引 (N/2) 到 (N-1),否则 ((N+1)/2) 到 (N-1): 负频率分量以递减的负(即越来越正)的顺序排列。

提示!如果您对哪些元素对应于哪些频率感到困惑,请查看np.fft.fftfreq!它会告诉你频率的顺序,例如 np.fft.fftfreq(5) 告诉我们频率箱中心 0.、0.2、0.4、-0.4 和 -0.2。

2.2 np.fft.ifft

IFFT将傅里叶变换作为输入,并返回时域或空间域中的真实或复杂的重建信号。

正如我们所讨论的,逆FFT允许我们从频域转换回时间/空间域。正如预期的那样,如果我们将IFFT应用于信号的FFT,我们最终会回到起点。

arr = np.random.normal(loc=0, scale=10, size=20)
np.allclose(np.fft.ifft(np.fft.fft(arr)), arr)
# >>> True

IFFT(FFT(x)) ≈ x,反性质成立!

2.3 np.fft.fftshift

FFT移位将傅里叶变换作为输入,并将值从“标准”顺序重新排序为“自然”顺序:最负到零再到最正。

如果将组件按自然顺序排序,则可视化和推理FFT的结果会容易得多。标准订单可能会非常混乱!

np.fft.fftfreq(5)
# >>> array([ 0. ,  0.2,  0.4, -0.4, -0.2])np.fft.fftshift(np.fft.fftfreq(5))
# >>> array([-0.4, -0.2,  0. ,  0.2,  0.4])

FFTSHIFT将频率中心从最负到最正排列。

2.4 np.fft.ifftshift

FFT移位将傅里叶变换作为输入,并将值从“自然”顺序重新排序为“标准”顺序:直流项,然后是正频率,然后是负频率。

np.fft.fftfreq(5)
# >>> array([ 0. ,  0.2,  0.4, -0.4, -0.2])np.fft.fftshift(np.fft.fftfreq(5))
# >>> array([-0.4, -0.2,  0. ,  0.2,  0.4])np.fft.ifftshift(np.fft.fftshift(np.fft.fftfreq(5)))
# >>> array([ 0. ,  0.2,  0.4, -0.4, -0.2])

IFFTSHIFT恢复了“标准”订单。

2.5 np.fft.rfft

RFFT将时域或空间域中的实数信号作为输入,并返回离散傅里叶变换。

真实信号的FFT具有本文前面提到的有趣特性:正负频率分量相互镜像。从形式上讲,这意味着真实信号的傅里叶变换是“埃尔米特变换”。其数学原因令人着迷,我希望在以后的文章中对其进行扩展。不过,就目前而言,我们只需要注意到这是真的。RFFT允许我们跳过这些多余的术语!

arr = np.random.normal(loc=0, scale=10, size=5)
np.fft.fftfreq(len(arr))
# >>> [ 0.   0.2  0.4 -0.4 -0.2 ]arr_fft = np.fft.fft(arr)  # rounded for display
# >>> [ -2. +0.j -11.+14.j   4.+14.j   4.-14.j -11.-14.j ]
# Notice that the neg. and pos. frequency components are mirrored!arr_rfft = np.fft.rfft(arr)  # rounded for display
# >>> [ -2. +0.j -11.+14.j   4.+14.j ]
# For a real signal, we can just look at the pos. frequency components

RFFT利用埃尔米特对称性来跳过重复的负频率分量。

2.6 np.fft.irfft

IRFFT 将实值函数的傅里叶变换作为输入,并返回时域或空间域中的真实重建信号。

arr = np.random.normal(loc=0, scale=10, size=20)
np.allclose(np.fft.irfft(np.fft.rfft(arr)), arr)
# >>> True

IRFFT(RFFT(x)) ≈ x,反性质成立!

2.7 np.fft.fft2

FFT2 将时域或空间域中的实数或复数 2D 信号作为输入,并返回离散傅里叶变换。正如我们所讨论的,np.abs允许我们恢复频率分量的幅度,np.angle允许我们恢复相位。

在之前的一篇文章中,我们证明了我们可以使用相同的逻辑来分解2D信号中频率分量的幅度和角度(如图像!np.fft.fft2 允许我们这样做:计算输入的二维快速傅里叶变换。

2.8 np.fft.ifft2

IFFT将傅里叶变换作为输入,并在时域或空间域中返回真实或复杂的2D重建信号。

正如预期的那样,我们有一个用于二维输入的类似逆变换!

arr2 = np.random.normal(loc=0, scale=10, size=(20, 20))
np.allclose(np.fft.ifft2(np.fft.fft2(arr2)), arr2)
# >>> True

IFFT2(FFT2(x)) ≈ x,二维反性质成立!

2.9 np.fft.fftn

我们可以将FFT扩展到三维,四维,五维输入!事实上,对于 n 维输入,有一个 n 维 FFT...

2.10 np.fft.ifftn

...相应地,n维逆变换。

arr3 = np.random.normal(loc=0, scale=10, size=(20, 20, 20))
np.allclose(np.fft.ifftn(np.fft.fftn(arr3)), arr3)

IFFTN(FFTN(x)) ≈ x,n-D 反性质成立!

感谢您加入,因为我们已经完成了这个系列!接下来我想写一些关于傅里叶变换实现的内容;如果您有兴趣或希望我解释其他概念,请在下面发表评论。

您可能还对我关于傅里叶直觉的其他一些文章感兴趣!

三、教授(并学习)几何傅里叶变换 

图片由作者提供。用傅里叶变换勾勒出我们最喜欢的侦探!

3.1 介绍

在其他文章中,我写过关于傅里叶变换在确定真实或复杂信号的频率分量方面的应用。

为了进一步建立我们的直觉,让我们学习用傅里叶变换绘画!

3.2 定义振荡器

我们已经在其他上下文中讨论了频率、相位和幅度,但值得快速复习一下!

  • https://towardsdatascience.com/the-fourier-transform-1-ca31adbfb9ef
  • https://towardsdatascience.com/the-fourier-transform-2-understanding-phase-angle-a85ad40a194e

频率是旋转速率,即给定振荡器的旋转速度。相位表示旋转的起始位置,即圆上开始旋转的点。幅度是信号中存在的给定频率的量,即振荡器的大小。

在上面的可视化中,每个旋转的圆圈都是一个振荡器。每个振荡器以固定的速度旋转:其频率。每个振荡器都从其旋转的给定点开始:其相位。每个振荡器都有一个特定的半径:其幅度。

我们可以从傅里叶变换中获取所有这些信息。傅里叶变换的绝对值(np.abs)给了我们量级。傅里叶变换的相位(np.angle)给了我们相位。这为我们提供了设置一些振荡器来绘制轮廓所需的所有信息!

  • https://towardsdatascience.com/the-fourier-transform-3-magnitude-and-phase-encoding-in-complex-data-8184e2ef75f0

四、 使用傅里叶变换绘制草图

4.1  将轮廓转换为复杂信号

        首先,我们需要从图像中获取轮廓。我决定使用Canny边缘检测从图像中提取边缘(在上面的示例中,是夏洛克·福尔摩斯的绘图)。

# see full context at https://github.com/peterbbryan/fourier-sketcher@staticmethod
def _from_jpeg(path_str: str) -> Image:""" Load image at path. """return Image.open(path_str)@property
def binary_mask(self):""" Binary mask from image. """im = np.array(self._image)  # pylint: disable=invalid-nameedges = cv.Canny(im, 100, 150)  # pylint:disable=no-memberreturn np.array(edges).astype(np.bool)

        这将为我们提供一个指示图像边缘的 2D 二进制数组。现在,我们需要将这些点表示为围绕原点的复杂坐标。x 轴表示实部,y 轴表示虚部。

  • https://towardsdatascience.com/mind-your-is-and-q-s-the-basics-of-i-q-data-d1f2b0dd81f4
# see full context at https://github.com/peterbbryan/fourier-sketcher@property
def complex_sequence(self) -> np.ndarray:""" Binary pixel coordinates as complex sequence. """# row/col to x/y will impart a directional flip on coordinateslocs = np.argwhere(np.flipud(self.binary_mask)).astype(np.complex128)n_rows, n_cols = self.binary_mask.shape# center about the originrows = locs[:, 0] - (n_rows // 2)columns = locs[:, 1] - (n_cols // 2)# represent y values with irows *= 1j# restructure to x, y orderreturn rows + columns

4.2. 重新排序以最小化连续点之间的距离

        现在,我们需要对点进行排序,以使进度尽可能顺利。在实践中,我发现排序以最小化从一个点到下一个点的差异是一个很好的方法!

# see full context at https://github.com/peterbbryan/fourier-sketcher@staticmethod
def _sort_points(complex_sequence: np.ndarray, stopping_threshold: float = 10
) -> np.ndarray:"""Arrange points so that neighbors are near each other in sequence.Args:complex_sequence: Points as complex array.Returns:Sorted points to minimize sequential differences."""reordered: List[np.complex128] = []current_point = complex_sequence[0]complex_sequence[0] = np.infwhile len(reordered) < complex_sequence.shape[0]:if np.min(np.abs(complex_sequence - current_point)) > stopping_threshold:breakmin_dist_ind = np.argmin(np.abs(complex_sequence - current_point))reordered.append(current_point)current_point = complex_sequence[min_dist_ind]complex_sequence[min_dist_ind] = np.infreturn np.array(reordered)

4.3. 调用傅里叶变换

现在,我们将应用傅里叶变换来提取与每个频率相关的幅度和相位信息。由于可视化所有振荡器的速度很慢,我们将过滤掉self._n_oscillators上面的所有频率分量。

# see full context at https://github.com/peterbbryan/fourier-sketcherdef _get_frequency_components(self) -> Tuple[np.ndarray, np.ndarray]:"""Get frequency components from outline.Returns:Tuple of frequency values and bin centers."""complex_sequence = self._sort_points(self._edger.complex_sequence)fft_values = np.fft.fft(complex_sequence)fft_bins = np.fft.fftfreq(len(fft_values), d=1 / (len(fft_values)))# make this less stupidfft_values = fft_values[np.abs(fft_bins) <= (self._n_oscillators / 2)]fft_bins = fft_bins[np.abs(fft_bins) <= (self._n_oscillators / 2)]fft_bins = fft_bins[np.argsort(-np.abs(fft_values))]fft_values = fft_values[np.argsort(-np.abs(fft_values))]return fft_values, fft_bins

4.5 素描 

        我们剩下要做的就是将碎片和情节结合起来......

# see full context at https://github.com/peterbbryan/fourier-sketcherdef _get_arr_from_fig(self, dpi: int = 90) -> np.ndarray:"""Get np.ndarray from a figure.Args:fig: Matplotlib Figure.dpi: Resolution.Returns:Numpy array of image contents."""buf = io.BytesIO()self._fig.savefig(buf, format="png", dpi=dpi)buf.seek(0)img_arr = np.frombuffer(buf.getvalue(), dtype=np.uint8)buf.close()img = cv2.imdecode(img_arr, 1)  # pylint: disable=no-memberimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # pylint: disable=no-memberreturn img@staticmethod
def _make_plot(points: List[Tuple[float, float]]) -> None:"""Plot outline so far.Args:points: x, y points to plot."""plt.scatter(*list(zip(*points)), linewidth=1)plt.draw()plt.axis("square")plt.axis("off")def make_gif(self, path_str: str) -> None:"""Make gif of outline.Args:path_str: Path to write gif to."""plt.ion()fft_values, fft_bins = self._get_frequency_components()plots, points = [], []for step in tqdm.tqdm(np.linspace(0, 2 * np.pi, self._n_steps)):edge = (0.0, 0.0)for i in range(self._n_oscillators):mag = np.abs(fft_values[i])ang = np.angle(fft_values[i])oscillator = self._get_oscillator(frequency=fft_bins[i], phase=ang)circle = self._get_circle(center=edge, radius=mag, angle=oscillator.angle(step))circle.draw()edge = circle.edge_pointif i == self._n_oscillators - 1:points.append(edge)self._make_plot(points)plot_img_np = self._get_arr_from_fig()plots.append(Image.fromarray(plot_img_np))plt.clf()self._write_gif(plots, path_str)

...创建 GIF!

# see full context at https://github.com/peterbbryan/fourier-sketcher@staticmethod
def _write_gif(plots: List[Image.Image],path_str: str,last_frame_repeats: int = 100,duration: int = 60,
) -> None:"""Write animated gif.Args:plots: Output plots to write.path_str: Path to output gif.last_frame_repeats: Number of repeats of last still.duration: Duration in seconds."""for _ in range(last_frame_repeats):plots.append(plots[-1])plots[0].save(path_str, save_all=True, append_images=plots[1:], duration=duration, loop=0,)

五、结论

        傅里叶变换是数据科学中的关键构建块,但学习起来并不容易!仅使用 FFT 将 2D 动画拼凑在一起是建立理解的好方法。

        下一步是什么?接下来,我想介绍一下使傅里叶变换成为可能的数学。如果您想解释其他主题,请在评论中回复!例如......彼得·巴雷特·布莱恩

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

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

相关文章

JavaScript-Ajax-axios-Xhr

JS的异步请求 主要有xhr xmlHttpRequest 以及axios 下面给出代码以及详细用法&#xff0c;都写在了注释里 直接拿去用即可 测试中默认的密码为123456 账号admin 其他一律返回登录失败 代码实例 <!DOCTYPE html> <html lang"en"> <head><…

科技抗老新突破,香港美容仪品牌内地重磅上市

近年来&#xff0c;新消费时代“颜值经济”的火热促使美容行业市场规模增长迅速&#xff0c;越来越多的人愿意为“美”买单&#xff0c;对美的需求也随之增长&#xff0c;美容行业已经成为成长最快的新锐产业。随着经济和科技的发展&#xff0c;“快捷”也成为了当今社会的时代…

想要精通算法和SQL的成长之路 - 最长回文子串

想要精通算法和SQL的成长之路 - 最长回文子串 前言一. 最长回文子串1.1 中心扩散法的运用 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 最长回文子串 原题链接 1.1 中心扩散法的运用 这类具有回文性质的题目&#xff0c;我们如果用常规的从左往右或者从右往左的遍历方…

中尺度混凝土二维有限元求解——运行弯曲、运行光盘、运行比较、运行半圆形(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

swift 约束布局

添加约束布局 背景图瀑全屏 如何三等分 外面view容器没有约束

【Spring Boot】Spring—加载监听器

这里写目录标题 前言加载监听器执行run方法加载配置文件封装Node调用构造器思考 前言 前几天的时候&#xff0c;项目里有一个需求&#xff0c;需要一个开关控制代码中是否执行一段逻辑&#xff0c;于是理所当然的在yml文件中配置了一个属性作为开关&#xff0c;再配合nacos就可…

笔记1.4 计算机网络性能

1. 速率 速率即数据率&#xff08;data rate&#xff09;或称数据传输速率或比特率 单位时间&#xff08;秒&#xff09;传输信息&#xff08;比特&#xff09;量 计算机网络中最重要的一个性能指标 单位&#xff1a;bps、kbps、Mbps k 10^3、M 10^6、G 10^9 速率往往…

网络安全深入学习第一课——热门框架漏洞(RCE-命令执行)

文章目录 一、RCE二、命令执行/注入-概述三、命令执行-常见函数四、PHP命令执行-常见函数1、exec&#xff1a;2、system3、passthru4、shell_exec5、反引号 backquote 五、PHP命令执行-常见函数总结六、命令执行漏洞成因七、命令执行漏洞利用条件八、命令执行漏洞分类1、代码层…

excel中的引用与查找函数篇2

如下所有案例中表头均不参与范围查找内&#xff1a; 1、LOOKUP(lookup_value,lookup_vector,[result_vector])&#xff1a;在一行或者一列中查找某个值并从另一行或者列中找到同位置的值 记住&#xff1a;中括号内的参数可以不赋值&#xff0c;若在中间用逗号隔开这个参数&…

思维模型 协议

1 模型故事 1.1 社会性质的协议 1 世界观的建立 1 2 3 4 5 6 7 8 9 0 这些阿拉伯数字 如此常见&#xff0c;那么我们是否想过 为什么 这些阿拉伯数字我们如此熟悉&#xff1f;为什么我们要学习这些玩意儿&#xff1f;这些东西为什么大家都要学习&#xff0c;都要使用&#x…

C++数据结构X篇_14_二叉树的递归遍历(先序遍历、中序遍历、后续遍历方法介绍;举例;代码实现)

我们知道数据的存储结构分为线性与非线性。线性就是1对1的结构&#xff0c;像栈与队列都属于线性结构。那什么是非线性的结构呢&#xff1f; 非线性即1对n的结构这更符合常规情况&#xff0c;线性结构本质上属于非线性结构中的一种特殊形式&#xff0c;像树就属于非线性结构。但…

neo4j下载安装配置步骤

目录 一、介绍 简介 Neo4j和JDK版本对应 二、下载 官网下载 直接获取 三、解压缩安装 四、配置环境变量 五、启动测试 一、介绍 简介 Neo4j是一款高性能的图数据库&#xff0c;专门用于存储和处理图形数据。它采用节点、关系和属性的图形结构&#xff0c;非常适用于…

6. 装饰器

UML 聚合(Aggregation)关系&#xff1a;大雁和雁群&#xff0c;上图中空心菱形箭头表示聚合关系组合(Composition)关系&#xff1a;大雁和翅膀 &#xff0c;实心菱形箭头表示组合(Composition)关系 测试代码 #include <iostream> #include <stdio.h> #include &l…

Spring复杂对象的3中创建方法

复杂对象是相对于简单对象可以直接 new 出的对象。这种对象在 Spring 中不可以通过简单对象的创建方式来创建。下面我们将通过实现 FactoryBean 接口、实例工厂、静态工厂三种方法来创建。 FactoryBean 接口 Spring 提供 FactoryBean 接口并且提供了 getObject 方法是为了支持…

“熊猫杯” | 赛宁网安获网络安全优秀创新成果大赛优胜奖

9月11日&#xff0c;四川省2023年国家网络安全宣传周正式启动。由四川省委网信办指导&#xff0c;中国网络安全产业联盟&#xff08;CCIA&#xff09;主办&#xff0c;成都信息工程大学、四川省网络空间安全协会承办的“2023年网络安全优秀创新成果大赛—成都分站赛(暨四川省‘…

Spring Boot - 用JUnit 5构建完美的Spring Boot测试套件

文章目录 PreJUnit 4 vs JUnit 5Junit5 常用注解栗子 Pre SpringBoot - 单元测试利器Mockito入门 SpringBoot - 应用程序测试方案 SpringBoot - SpringBootTest加速单元测试的小窍门 Spring Boot - Junit4 / Junit5 / Spring Boot / IDEA 关系梳理 package org.junit.jupit…

Excel VBA 变量,数据类型常量

几乎所有计算机程序中都使用变量&#xff0c;VBA 也不例外。 在过程开始时声明变量是一个好习惯。 这不是必需的&#xff0c;但有助于识别内容的性质&#xff08;文本&#xff0c;​​数据&#xff0c;数字等&#xff09; 在本教程中&#xff0c;您将学习- 一、VBA变量 变量是…

Unity中程序集dll

一&#xff1a;前言 一个程序集由一个或多个文件组成&#xff0c;通常为扩展名.exe和.dll的文件称为程序集&#xff0c;.exe是静态的程序集&#xff0c;可以在.net下直接运行加载&#xff0c;因为exe中有一个main函数(入口函数&#xff09;&#xff0c;.dll是动态链接库&#…

腾讯mini项目-【指标监控服务重构】2023-08-04

今日已办 关于 span-references 的调研 https://github.com/DataDog/dd-trace-js/issues/1761 https://github.com/open-telemetry/opentelemetry-specification/blob/874a451e7f6ac7fc54423ee3f03e5394197be35b/specification/compatibility/opentracing.md#span-references h…

基于springboot的OA人事办公管理系统

经典 oasys(OA自动化办公系统) 办公自动化&#xff08;OA&#xff09;是面向组织的日常运作和管理,员工及管理者使用频率最高的应用系统&#xff0c;极大提高公司的办公效率。 项目介绍 oasys是一个OA办公自动化系统&#xff0c;使用Maven进行项目管理。基于springboot框架开…