卷积和傅立叶变换
来源:互联网 发布:详解进化论知乎 编辑:程序博客网 时间:2024/05/17 02:27
卷积和傅立叶变换
本文版权属于重庆大学计算机学院刘骥,禁止转载
- 卷积和傅立叶变换
- 什么是卷积
- 卷积的数学定义
- 卷积的计算
- 卷积的作用
- 傅立叶变换
- 傅立叶变换的用途
- 时域频域
- 卷积与傅立叶变换
- 后记
- 什么是卷积
什么是卷积
卷积的数学定义
若
卷积的计算
先用离散卷积公式举个例子,假设
用离散卷积计算如下:
在计算中,如果下标超过数列的范围(例如出现
这就是
卷积不难计算,但为什么书上或者其他资料上看起来那么难呢?因为大多数教材和资料,都是用连续卷积来举例。例如
import numpy as npf=np.array([1,1,1])g=np.array([2,3,2,6])fg=np.convolve(f,g)print(fg)
输出结果如下:
与手工计算没有差别。所以卷积的计算不会让人困惑,真正让人困惑的是卷积的作用。
卷积的作用
初学者都希望知道卷积的物理意义,知乎上有不错的主题可以参考。但你真的需要了解的那么清楚吗?事实上,你只需要了解卷积对于程序员的价值:
在程序中,卷积往往作为加权求和的替代物,因为程序员懒得再写一个加权求和函数。
在程序中,卷积往往作为加权求和的替代物,因为程序员懒得再写一个加权求和函数。
在程序中,卷积往往作为加权求和的替代物,因为程序员懒得再写一个加权求和函数。
重要的的事情说三遍。姑且不论卷积有什么重大的物理意义,在编程过程中,大多数程序员用卷积函数,只是为了替代加权和。考虑这样一个应用场景,有一个数列
具体来说这个程序如下:
# coding=utf-8import numpy as npg=np.array([1,2,3,4])h=np.zeros(f.shape)for i in range(0,f.shape[-1]): h[i]=2*g[i-1]+3*g[i]+4*g[i+1] #注意这里有错误print(h)
上述程序没有考虑边界条件,因此运行的时候会出现索引超出范围的异常:
当然可以修改这个程序,加入边界条件的判断(或者修改迭代范围):
# coding=utf-8import numpy as npg=np.array([1,2,3,4])h=np.zeros(f.shape)for i in range(0,f.shape[-1]): if i-1<0: a=0 else: a=g[i-1] if i+1>f.shape[-1]-1: b=0 else: b=g[i+1] h[i]=2*a+3*g[i]+4*b print(h)
输出结果为:
这么写也太麻烦了吧?有没有办法简化一下?有的,方法如下:
# coding=utf-8import numpy as npf=np.array([4,3,2]) #注意f中数值的顺序g=np.array([1,2,3,4])h=np.convolve(f,g) #用卷积替代print(h)
结果如下:
好像多了两个值。很正常,因为卷积默认计算了两个边界。所以只要去掉边界就是想要的结果,代码需要改为:
h=np.convolve(f,g,mode='same')
现在留意一下这段代码:
f=np.array([4,3,2])
请注意,之前定义的权重顺序为
换言之,当把
再来做一次练习,如果要做以下操作:
那么对应的权重相当于
# coding=utf-8import numpy as npf=np.array([0.5,0,-0.5]) #注意f中数值的顺序g=np.array([1,2,3,4])h=np.convolve(f,g,mode='same') #用卷积替代print(h)
结果就是:
最后一项由于超出边界,因此是
至此,我们可以得到如下结论:
当程序员需要加权和时,他们会用卷积替代。计算机中所谓卷积,有两种含义:真正意义上的卷积,或者加权和的另一种说法。
当程序员需要加权和时,他们会用卷积替代。计算机中所谓卷积,有两种含义:真正意义上的卷积,或者加权和的另一种说法。
当程序员需要加权和时,他们会用卷积替代。计算机中所谓卷积,有两种含义:真正意义上的卷积,或者加权和的另一种说法。
重要的事情说三遍。最后我们来谈谈图像处理中的这样卷积,那样卷积。比如高斯卷积。现在你知道了,程序员的意思是高斯函数为权重系数的加权和
。很多matlab代码,真的是用加权和而不是卷积公式来实现高斯卷积的!虽然这么写没错,但每当看到这些代码,很怀疑作者是否真的理解卷积。
好吧,大多数情况下,你不用理解卷积,你只需要记住,将卷积核反过来就是权重,卷积就变成了加权和。
傅立叶变换
傅立叶变换的用途
傅立叶变换是图像处理中最复杂的一个概念。它之所以复杂是因为它是信号处理的知识。就像软件架构,架构是一个建筑学术语。另一个学科的常识,在其他学科眼中就变成了高深的学问。例如时间复杂度O(n)是计算机学科的常识,试试给学历史的朋友解释一下吧。那么要搞懂傅立叶需要学信号处理吗?Yes!不学所以搞不懂,没有其他原因。那读完这篇文章可以搞懂傅立叶变换吗?做梦!这篇文章只是一个科普,目的仅仅在于科普傅立叶变换的应用,而具体的原理肯定需要系统的学习。
首先我们来看一个问题,如图:
图上蓝色的线是一条直线
能够做到这一点吗?当然能够做到这一点。如果站在上帝的视角,可以知道:
因此函数
问题在于,首先没有上帝视角,其次
下面再举一个例子:
问如何将
那么
问题在于,现实中,如果只能观察到
再来看一个例子,图像是这样的:
怎么知道她其实是这样的?
这些问题都是信号处理的典型问题。而结论是:傅立叶变换可以解决。
时域、频域
理解傅立叶变换所碰到的第一个问题就是何谓时域、频域,而傅立叶变换的实质就是将时域的函数变换为频域的函数
。好吧,无论怎么说,反正就是不懂。因此我准备给你一点感性认识。假设有这样两个数列:
如果把它们的图像画出来,就是这样的:
现在对它们做傅里叶变换,python代码如下:
# coding=utf-8import numpy as npg1=np.array([1,1,1,1,1,1,1,1]) #时域值g2=np.array([1,2,1,2,1,2,1,2]) #时域值fftg1=np.fft.rfft(g1) #频域值fftg2=np.fft.rfft(g2) #频域值print(fftg1)print(fftg2)
得到结果如下:
是复数啊?!当然是复数,请参考傅立叶变换的公式。这些值就是时域对应的频域值。它们是原始数据的另一种表现形式,好比把C语言程序翻译成了汇编码
。没错你看到的就是汇编码。那么它们有什么用呢?如果修改汇编码,是不是可以改变程序的运行呢?傅立叶变换的用途也在于此。应用傅里叶变换,重点不在傅立叶变换本身,而在于变换之后的处理。
变换之后的处理,则是一门专门的学问,不是一两篇文章可以说得清楚的。
因此为什么看不懂傅立叶变换呢?其原因在于:
傅立叶变换是加减乘除,懂加减乘除并没有卵用。
傅立叶变换是加减乘除,懂加减乘除并没有卵用。
傅立叶变换是加减乘除,懂加减乘除并没有卵用。
重要的事情说三遍。下面我们来看看傅立叶变换之后的一种用途(实际的用途有成百上千种,这是一门专门的学问)。下面来绘制频谱。什么是频谱呢?通俗的讲是每个复数的模值,不通俗的讲是信号中每个频率的占比(我也不是很懂)。代码如下:
# coding=utf-8import numpy as npfrom pylab import *g1=np.array([1,1,1,1,1,1,1,1]) #时域值g2=np.array([1,2,1,2,1,2,1,2]) #时域值fftg1=np.fft.rfft(g1) #频域值fftg2=np.fft.rfft(g2) #频域值figure()subplot(121)plot(abs(fftg1)) #一直没搞懂求模值为什么是abs而不是normsubplot(122)plot(abs(fftg2))show()
显示结果如下:
不一样对吧?肯定不一样啦!数据不一样,肯定不一样啦!简直是废话!我们看看下面的程序呢?
# coding=utf-8import numpy as npfrom pylab import *g1=np.array([1,1,1,1,1,1,1,1]) #时域值g2=np.array([30,30,30,30,30,30,30,30]) #时域值fftg1=np.fft.rfft(g1) #频域值fftg2=np.fft.rfft(g2) #频域值figure()subplot(121)plot(abs(fftg1)) subplot(122)plot(abs(fftg2))show()
可以发现,尽管数据不同,曲线的形式是相同的。再来看一个更一般的程序:
# coding=utf-8import numpy as npfrom pylab import *x=np.linspace(0,2*pi,1000)g1=sin(x) #时域值g2=10*sin(x) #时域值fftg1=np.fft.rfft(g1) #频域值fftg2=np.fft.rfft(g2) #频域值figure()subplot(121)plot(abs(fftg1))subplot(122)plot(abs(fftg2))show()
结果如下:
再试试这个:
# coding=utf-8import numpy as npfrom pylab import *x=np.linspace(0,2*pi,1000)g1=sin(x) #时域值g2=sin(131*x) #时域值,频率不同fftg1=np.fft.rfft(g1) #频域值fftg2=np.fft.rfft(g2) #频域值figure()subplot(121)plot(abs(fftg1)) subplot(122)plot(abs(fftg2))show()
结果如下:
是不是隐约有点感觉呢?不同数列的频率分布是不同的,那么它们对应的频谱就不同。那是否可以通过调整频率分布来对信号进行处理呢?例如:
# coding=utf-8import numpy as npfrom pylab import *x=np.linspace(0,2*pi,1000)g1=sin(x) #时域值fftg1=np.fft.rfft(g1) #频域值s=abs(fftg1) #频谱fftg1=fftg1/s rs=roll(s,100) #频谱向右移动100figure()subplot(121)plot(s)subplot(122)plot(rs)fftg1=fftg1*rs #恢复信号figure()subplot(121)plot(x,g1) subplot(122)plot(x,np.fft.irfft(fftg1)) #逆傅立叶变换show()
结果如下:
程序利用roll函数将频谱右移,相当于把频率变高。将信号还原,就可以看到变换后的结果:
最后来看一个更复杂的操作:
# coding=utf-8import numpy as npfrom pylab import *x=np.linspace(0,2*pi,1000)g1=2*x+1+sin(10*x) #时域值fftg1=np.fft.rfft(g1) #频域值s=abs(fftg1)mask=np.ones(s.shape,dtype=np.float32)fftg1=fftg1/smask[:20]=0rs=s*mask #用mask处理频域figure()subplot(121)plot(s)subplot(122)plot(rs)fftg1=fftg1*rsfigure()subplot(121)plot(x,g1) subplot(122)plot(x,np.fft.irfft(fftg1))show()
频域的变换结果为:
转换后的信号变为:
从上面的例子可以看出:
傅立叶变换的研究重点不是如何进行变换,而是如何处理变换后的结果。
傅立叶变换的研究重点不是如何进行变换,而是如何处理变换后的结果。
傅立叶变换的研究重点不是如何进行变换,而是如何处理变换后的结果。
这个问题太复杂了,可以研究一辈子。数字图像处理书籍中通常只会介绍几种比较简单的、常用的处理。而什么时候需要把时域函数转换为频域函数呢?当然是频域处理起来更方便的时候!
卷积与傅立叶变换
现在讨论本文的最后一个问题。卷积的公式忘记了吗?重新贴一下:
有没有想过为什么公式这么奇葩,这么不好理解呢?有一个原因:
时域上的卷积对应着频域上的乘积(傅立叶变换)
什么意思呢?看下面这段代码:
# coding=utf-8import numpy as npf=np.array([1,1,1])g=np.array([2,3,2,6])fg=np.convolve(f,g) #直接卷积n = len(f)+len(g)-1N = 2**(int(np.log2(n))+1)a=np.fft.rfft(f,N) #因为f和g不等长,所以扩展为相同的N位b=np.fft.rfft(g,N)c=a*b #f和g傅立叶变换后的乘积fft_fg=np.fft.irfft(c)[:n] #逆傅立叶变换,前n位有效print(fg) #直接卷积print(fft_fg) #傅立叶卷积print(fg-fft_fg) #直接卷积和傅立叶卷积的差异
执行结果就是:
如果写成数学公式,那就是:
这条性质意味着什么?
加权和
加权和
简化之后就是:
加权和
这有什么用吗?至少有以下两个用途:
1. 加速算法运行。因为加权和,又要加又要乘,计算量是比较大的,而频域的乘法是按位相乘,计算量要小得多。不过实际应用时要权衡傅立叶变换和逆变的时间。
2. 某些处理在频域很容易(比如屏蔽特定频率)。常规的做法是先做傅立叶变换,处理之后再逆变。如果能够找到与之对应的加权和(卷积),那么就可以直接用加权和(卷积)来计算。例如高斯滤波的实质是过滤高频、低频,保留中频。实际应用中用卷积来实现高斯滤波,因为高斯函数是一种非常特殊的函数,它的傅里叶变换是高斯函数,逆变仍然是高斯函数。
后记
本文所述内容只能作为科普,若想真正掌握,还需要阅读专业书籍,做大量练习,期待某一天突然开窍。如果你觉得本文通俗易懂,那么说明我是搞懂了,但你未必。推荐一本书:
本书对傅立叶变换的描述是非常清楚的,只需要很少的数学知识也能读懂。当然某些部分需要较长时间的思考,有价值的知识,都是有门槛的
。
- 卷积和傅立叶变换
- 傅立叶变换的意义和卷积(ZZ)
- 卷积与傅立叶变换
- 五、卷积与傅立叶变换
- 数字图像处理线性系统、卷积、傅立叶变换
- 数字图像处理 线性系统、卷积、傅立叶变换
- FFT NTT FWT: 傅立叶变换, 求卷积
- 傅立叶变换的意义和卷积-----(从别处转来的哈)
- 傅立叶级数和傅立叶变换是什么关系?
- 二维傅立叶变换和二维傅立叶反变换
- 傅立叶变换和小波变换分析
- 数字图像处理:第九章 线性系统、卷积、傅立叶变换
- 傅立叶变换
- 傅立叶变换
- 傅立叶变换
- 傅立叶变换
- 傅立叶变换
- 傅立叶变换
- 【java】 Dubbo访问有时候访问到,有时候访问不到问题
- Test Mission: Definition, Components and Application
- 修改项目的JDK和WEB版本
- Leetcode 389 Find the Difference
- java练习题2
- 卷积和傅立叶变换
- Leetcode 412 Fizz Buzz
- 正则表达式基本用法
- Redis
- 解密
- PPM图像格式说明
- 矩形面积并
- Java编程思想读书笔记——控制执行流程
- Leetcode 476 Number Complement