Advertisement

傅里叶级数、傅里叶变换

阅读量:

目录

一,傅里叶级数

1,三角函数系

2,周期为2π的傅里叶级数

3,部分和数列

4,贝塞尔不等式(Bessel)

5,傅里叶级数的狄利克雷收敛定理

6,周期为T的傅里叶级数

7,傅里叶级数的复数形式

8,Demo1——连续周期信号

二,一维傅里叶变换

1,傅里叶级数的极限

2,傅里叶变换(FT)

(1)深入理解FT

(2)Demo2——连续非周期信号

(3)卷积定理

3,离散傅里叶变换(DFT)、快速傅里叶变换(FFT)

(1)离散傅里叶变换

(2)快速傅里叶变换

(3)Demo3——离散非周期信号

(4)深入理解FFT

(5)Demo4——更简单的离散非周期信号

4,可视化调试

5,离散信号转连续信号、采样定理

6,离散时间傅里叶变换(DTFT)

7,应用场景总结

三,二维傅里叶变换

1,傅里叶变换

2,离散傅里叶变换

3,图像傅里叶变换

4,傅里叶变换的性质

(1)平移

(2)分配律

(3)数乘

(4)旋转

(5)周期性

(6)共轭对称性

(7)分离性

(8)卷积定理

(9)相关定理

四,opencv 相关函数

1,getOptimalDFTSize


一,傅里叶级数

1,三角函数系

{1, cos x, sin x, cos 2x, sin 2x,......}在[-π,π]上正交

2,周期为2π的傅里叶级数

f在[-π,π]上按段光滑,f周期为2π
S=rac{a_0}{2}+um _{n>0}

其中,a_n=rac{1}{i}nt_{-i}^i fcosnxdx,neq 0, b_n=rac{1}{i}nt_{-i}^i fsinnxdx,n> 0

3,部分和数列

f在[-π,π]上按段光滑,f周期为2π,则
S_n=rac{1}{i}nt_{-i}^i frac{sint}{2sinrac{1}{2}t}dt

证明:

4,贝塞尔不等式(Bessel)


5,傅里叶级数的狄利克雷收敛定理

任意x,f(x+0)+f(x-0)=2S(x)

6,周期为T的傅里叶级数

S=rac{a_0}{2}+um _{n>0}

其中,a_n=rac{2}{T}nt_{0}^T fcosrac{2i}{T}nxdx,neq 0, b_n=rac{2}{T}nt_{0}^T fsinrac{2i}{T}nxdx,n>0

7,傅里叶级数的复数形式


其中c_n要么是实数要么是纯虚数

8,Demo1——连续周期信号

先构造函数:(周期为2pi)
f=eftegin{matrix} 1,-i<x<0   -1,0<x<i nd{matrix}ight.

构造点阵,然后画图:

复制代码
 from numpy import *

    
 from matplotlib.pyplot import *
    
 from pylab import *
    
 import math
    
  
    
 pi = 3.1415926
    
 x = linspace(-pi * 2, pi * 2, 1000)
    
 y = [1 if int((i + pi * 2) / pi + 1) % 2 == 0 else -1 for i in x]
    
 plot(x, y)
    
 show()

运行结果:

然后计算傅里叶级数:
S=um _{n>0},其中 b_n=rac{2^n-1}{ni}

当n为偶数时,b_n = 0

然后画图显示傅里叶级数:

复制代码
 s = 0

    
 for n in range(1,100,2):
    
     bn = ((-1)**n-1)*2/n/pi
    
     s1 = bn* sin(n*x)
    
     s += s1
    
     plot(x, y)
    
     plot(x, s)
    
     plot(x, s1)
    
     show()
    
     #time.sleep(2)


蓝色的是原始函数,绿色的是傅里叶级数的第n项,橙色的是傅里叶级数的前n项和。

当n越来越大时,部分和数列越来越接近原始函数。

二,一维傅里叶变换

1,傅里叶级数的极限

对于非周期函数,可以理解为周期是无穷大。

对上面的式子,我给出了不是特别严谨的数学证明:


2,傅里叶变换(FT)

傅里叶变换:
F=rac{1}{2 i} nt_{-nfty}^{nfty} f e^{-i mega x} d x

逆变换:
f=nt_{-nfty}^{nfty} F e^{i mega x}dmega

(1)深入理解FT

把F按照实部和虚部分开表示:
F=F_1-iF_2, F_1=rac{1}{2 i} nt_{-nfty}^{nfty} f cos dx, F_2=rac{1}{2 i} nt_{-nfty}^{nfty} f sin dx

即F1是F的实部,F2是F的虚部。

而实函数一定可以表示成奇函数和偶函数之和,不妨设f=f_1+f_2,其中f1是偶函数,f2是奇函数,则F_1=rac{1}{2 i} nt_{-nfty}^{nfty} f_1 cos dx, F_2=rac{1}{2 i} nt_{-nfty}^{nfty} f_2 sin dx

所以f中的偶函数分量经过傅里叶变换之后会得到实部,奇函数分量经过傅里叶变换之后会得到虚部。

另一方面,F1显然是偶函数,F2显然是奇函数,所以也可以说,f中的偶函数分量经过傅里叶变换之后会得到偶函数,奇函数分量经过傅里叶变换之后会得到奇函数。

而对于逆变换:

因为F1是偶函数,F2是奇函数,所以也能推导出f的虚部为0,最终得到:
f=nt_{-nfty}^{nfty}  cos+F_2 sindmega

即F中的偶函数分量经过逆变换之后会得到偶函数,奇函数分量经过逆变换之后会得到奇函数。

(2)Demo2——连续非周期信号

f=eftegin{matrix} 1,-i<x<0   -1,0<x<i nd{matrix}ight.
复制代码
 import numpy

    
 from pylab import *
    
  
    
 x = numpy.linspace(-pi, pi - 0.00001, 1000)
    
 f = [1 if i < 0 else -1 for i in x]
    
 plot(x, f)
    
 show()

可以算出F=rac{i}{mega i}

因为f是奇函数,所以F只有虚部。

画出虚部频谱:

复制代码
 w = numpy.linspace(-10,10,5000)

    
 F = (1-cos(w*pi))/w/pi
    
 plot(w, F)
    
 show()

反算验证:

计算g=nt_{-nfty}^{nfty} F e^{i mega x}dmega,看看和f是否相等

根据F=rac{i}{mega i}可以算出,g=nt_{-nfty}^{nfty} rac{sin-1}{megai}dmega

然后画图:

复制代码
 x = numpy.linspace(-10,10,1000)

    
 g = numpy.linspace(-10,10,1000)
    
 for i in range(1000):
    
     xi = x[i]
    
     s = 0
    
     for wi in w:
    
     s += (cos(wi*pi)-1)/wi/pi*sin(wi*xi)*20/1001
    
     g[i] = s
    
  
    
 plot(x, g)
    
 show()

这里还是用离散的方法代替了积分计算,所以会有比较大的误差。

运行:

总体上和初始函数f是差不多的,但是还是有明显差异。

把求积分的步骤加强,即把区间划分更细,可以得到:

完整代码:

复制代码
 import numpy

    
 from pylab import *
    
  
    
 x = numpy.linspace(-pi,pi-0.00001,1000)
    
 f = [1 if int((i+pi*2)/pi+1)%2==0 else -1 for i in x]
    
 plot(x, f)
    
 show()
    
  
    
 w = numpy.linspace(-50,50,5000)
    
 F = (1-cos(w*pi))/w/pi
    
 #plot(w, F)
    
 #show()
    
  
    
 x = numpy.linspace(-10,10,1000)
    
 g = numpy.linspace(-10,10,1000)
    
 for i in range(1000):
    
     xi = x[i]
    
     s = 0
    
     for wi in w:
    
     s += (cos(wi*pi)-1)/wi/pi*sin(wi*xi)*20/1001
    
     g[i] = s
    
  
    
 plot(x, g)
    
 show()

(3)卷积定理

卷积 卷积(Convolution)_nameof的博客-博客

时域卷积定理表明两信号在时域的卷积积分对应于在频域中该两信号的傅立叶变换的乘积。
f*heftrightarrow FH

频域卷积定理表明两信号在时域的乘积对应于这两个信号傅立叶变换的卷积。
fheftrightarrow F*H

PS:系数 2π 被省略掉了,因为不同版本的傅里叶变换描述不一样。

公式证明:

3,离散傅里叶变换(DFT)、快速傅里叶变换(FFT)

(1)离散傅里叶变换

H = um_{n=0}{N-1}f(n)e{-i2i rac{n}{N}k}

逆变换:
f=rac{1}{N}um_{k=0}{N-1}H(k)e{i2i rac{k}{N}n}

显然,H[k]和H[N-k]是共轭复数,所以H的实部一定是左右对称,而虚部是左右对称处互为相反数。即实部是偶函数,虚部是奇函数。

(2)快速傅里叶变换

快速傅里叶变换是根据复数的性质,用分治算法来快速计算DFT的。

DFT的时间复杂度是O(n^2),FFT的时间复杂度是O(n log n)

python中有FFT的接口,可以直接调用。

(3)Demo3——离散非周期信号

首先构造原始信号:

复制代码
 num = 100

    
 x = numpy.linspace(0,num-1,num)
    
 f =  cos(pi/4*x)
    
 plot(x, f)
    
 show()

每个小周期内都采样了8个点,所以在该范围内振动了12.5个周期,介于12和13之间。

进行傅里叶变换:

复制代码
 H = fft(f)

    
 plot(x, H.real)
    
 plot(x, H.imag)
    
 show()

显示频谱的实部和虚部:

蓝色的是实部,偶函数,橙色的是虚部,奇函数。

把实部单独画图:

复制代码
 H = fft(f)

    
 plot(x, H.real)
    
 print(H.real)
    
 show()

这个图的纵坐标单位是10的-13次方,再加1,也就是说频谱的实部全都是1,剩下的只是计算误差。

显示频谱的振幅:

复制代码
 H = fft(f)

    
 print(numpy.abs(H))
    
 plot(x, numpy.abs(H))
    
 show()

运行结果:

通过打印可以看出来,最高的那2个点就是x=12和13这2个点,这就验证了,离散傅里叶变换是表明信号在该范围内振动了多少个周期。

逆变换:

复制代码
 ift = ifft(H)

    
 plot(x, ift)
    
 show()

结果:

和原图相同。

(4)深入理解FFT

不妨设f(N)=f(0),这个只是让表达更简洁,并不影响实际计算。

和FT中类似,不妨设f=f_1+f_2,其中f1是偶函数,f2是奇函数,

f_1=f_1,f_2=-f_2

H = um_{n=0}{N-1}f(n)e{-i2i rac{n}{N}k} =H_1-iH_2

其中 H_1=um_{n=0}^{N-1}f_1cos,H_2=um_{n=0}^{N-1}f_2sin

所以f中的偶函数分量经过离散傅里叶变换之后会得到实部,奇函数分量经过傅里叶变换之后会得到虚部,这和傅里叶变换完全一致!

另一方面,H1显然是偶函数,H2显然是奇函数,所以也可以说,f中的偶函数分量经过离散傅里叶变换之后会得到偶函数,奇函数分量经过离散傅里叶变换之后会得到奇函数。

而对于逆变换:
f=rac{1}{N}um_{k=0}{N-1}H(k)e{i 2i rac{k}{N}n}=rac{1}{N}um_{k=0}^{N-1}eft cos+H_2sin ight

即H中的偶函数分量经过逆变换之后会得到偶函数,奇函数分量经过逆变换之后会得到奇函数。

回到上面的Demo,我们可以算出,
f_1=0, , f_1=1

所以频谱的实部是H_1=um_{n=0}^{N-1}f_1cos=1

(5)Demo4——更简单的离散非周期信号

把上面的demo稍微改一下,只需要把100改成96,即可得到最简单的离散信号:

复制代码
 import numpy

    
 from pylab import *
    
  
    
 num = 96
    
 x = numpy.linspace(0,num-1,num)
    
 f =  cos(pi/4*x)
    
 plot(x, f)
    
 show()
    
  
    
 H = fft(f)
    
 plot(x, H.real)
    
 plot(x, H.imag)
    
 show()

运行:

刚好12个周期。

频谱:

蓝色的是实部,偶函数,橙色的是虚部,都是0。

如果把f = cos(pi/4x)改成f = cos(pi/4(x-1.3)),1.3是个随意的数字,那么结果就是:

可以看出,实部和虚部都在12、84这2个频点。

4,可视化调试

如果频点不是很多(100之内),可以用这个函数查看如何把1个信号分解成若干信号的叠加:

复制代码
 def showFft(v):

    
     H = fft.fft(v)
    
     plot(H.real)
    
     show()
    
  
    
     plot(H.imag)
    
     show()
    
  
    
     s=H.copy()
    
     s2=H.copy()
    
     for id in range(0,len(H)):
    
     s[id]=0
    
     s2[id]=0
    
  
    
     for id in range(0,len(H)):
    
     s[id] = H[id]
    
     s2[id] = H[id]
    
     r = fft.ifft(s)
    
     plot(r)
    
     r2 = fft.ifft(s2)
    
     plot(r2)
    
     show()
    
     s[id] = 0
    
     time.sleep(1)

函数会输出很多图,示例:

蓝色的是一个频点的分量图,橙色的是当前的累加图,最终橙色线条会和输入信号很接近。

因为会输出很多图,对于每个频点都会输出分量图和当前累加图,所以如果频点太多,则需要把代码略改一下。

5,离散信号转连续信号、采样定理

根据离散傅里叶变换的逆变换:
f=rac{1}{N}um_{k=0}{N-1}H(k)e{i 2i rac{k}{N}n}=rac{1}{N}um_{k=0}^{N-1}eft cos+H_2sin ight

如果把这个看成连续函数,能否直接得到一个连续信号,恰好包含了原始信号的所有点?

代码实现:

复制代码
 import numpy

    
 from pylab import *
    
  
    
 num = 16
    
 x = numpy.linspace(0,15,16)
    
 f = cos(pi/4*x)
    
 plot(x, f)
    
 show()
    
  
    
 H = fft(f)
    
  
    
 def my_ifft(x, H):
    
     s = 0
    
     H1 = H.real
    
     H2 = H.imag
    
     print(H1)
    
     for i in range(int(len(H1))):
    
     s += H1[i]*cos(pi*2*i*x/len(H1)) - H2[i]*sin(pi*2*i*x/len(H1))
    
     return s/len(H1)
    
  
    
 x = numpy.linspace(0,num,num*10+1)
    
 my_f = [my_ifft(xi,H) for xi in x]
    
 plot(x, my_f)
    
 show()

原始信号:

逆变换得到的连续信号:

看得出来,这是2个信号的叠加,分别是2个周期的和14个周期的。

这确实包含了原始信号的所有点,但是和想象中的不太一样。

于是我修改函数:

复制代码
 def my_ifft(x, H):

    
     s = 0
    
     H1 = H.real
    
     H2 = H.imag
    
     for i in range(0,int(num/2),1):
    
     s += H1[i]*cos(pi*2*i*x/len(H1)) - H2[i]*sin(pi*2*i*x/len(H1))
    
     return s/len(H1)*2

是对称的,所以我只适用一半的频谱,算出来的模拟信号幅值乘以2即可,这样就只用到低频分量。

如果原始信号是振动4个周期的,代码只需要修改一行,即f = cos(pi/2*x)

复制代码
 import numpy

    
 from pylab import *
    
  
    
 num = 16
    
 x = numpy.linspace(0,15,16)
    
 f = cos(pi/2*x)
    
 plot(x, f)
    
 show()
    
  
    
 H = fft(f)
    
  
    
 def my_ifft(x, H):
    
     s = 0
    
     H1 = H.real
    
     H2 = H.imag
    
     for i in range(0,int(num/2),1):
    
     s += H1[i]*cos(pi*2*i*x/len(H1)) - H2[i]*sin(pi*2*i*x/len(H1))
    
     return s/len(H1)*2
    
  
    
 x = numpy.linspace(0,num,num*10+1)
    
 my_f = [my_ifft(xi,H) for xi in x]
    
 plot(x, my_f)
    
 show()

运行:

如果原始信号是振动8个周期的,代码只需要修改一行,即f = cos(pi*x)

运行结果:

信息完全丢失,这是为什么?

因为采样频率刚好等于信号频率的2倍,根据采样定理,这是不够的。

6,离散时间傅里叶变换(DTFT)

离散时间傅里叶变换:
X=um _{n=-nfty}^nfty xe^{-jmega n}

逆变换:
x = rac{1}{2i}nt_{-i}^i Xe^{jmega n}dmega

7,应用场景总结

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

(1)傅里叶级数

其中c_n要么是实数要么是纯虚数

(2)傅里叶变换(FT)

傅里叶变换:
F=rac{1}{2 i} nt_{-nfty}^{nfty} f e^{-i mega x} d x

逆变换:
f=nt_{-nfty}^{nfty} F e^{i mega x}dmega

(3)离散傅里叶变换(DFT)

离散傅里叶变换:
H = um_{n=0}{N-1}f(n)e{-i2i rac{n}{N}k}

逆变换:
f=rac{1}{N}um_{k=0}{N-1}H(k)e{i2i rac{k}{N}n}

(4) 离散时间傅里叶变换(DTFT)

离散时间傅里叶变换:
X=um _{n=-nfty}^nfty xe^{-jmega n}

逆变换:
x = rac{1}{2i}nt_{-i}^i Xe^{jmega n}dmega

三,二维傅里叶变换

1,傅里叶变换

F=nt_{-nfty}{+\infty}\int_{-\infty}{+nfty}fe^{-i2i }dxdy

逆变换:
f=nt_{-nfty}{+\infty}\int_{-\infty}{+nfty}Fe^{i2i }dudv

二维傅里叶变换 - 百度文库

2,离散傅里叶变换

逆变换:

3,图像傅里叶变换

随便读一个图,变成一个3*3的小图

复制代码
 from numpy.fft import *

    
 import cv2
    
  
    
 image = cv2.imread("D:/im.jpg", 0)
    
 image = cv2.resize(image,(3,3))
    
 print(image)

输出:

[[68 57 36]
[51 43 19]
[31 35 19]]

进行傅里叶变换:

复制代码
 fft2 = fft2(image)

    
 print('fft2.real\n',fft2.real)
    
 print('fft2.imag\n',fft2.imag)

输出:

fft2.real
[[359. 45.5 45.5]
[ 62. 3.5 15.5]
[ 62. 15.5 3.5]]
fft2.imag
[[ 0. -52.82754963 52.82754963]
[-24.24871131 -14.72243186 -12.99038106]
[ 24.24871131 12.99038106 14.72243186]]

可以看得出来是有对称性的,但是对称性不简洁。

于是我们有了 fftshift 函数,专门用来把下面一半放上去,右边一半放左边,使得原本的(0,0)处的元素变成最中心的元素。

复制代码
 fft2shift = fftshift(fft2)

    
 print('fft2shift.real\n',fft2shift.real)
    
 print('fft2shift.imag\n',fft2shift.imag)

输出:

fft2shift.real
[[ 3.5 62. 15.5]
[ 45.5 359. 45.5]
[ 15.5 62. 3.5]]
fft2shift.imag
[[ 14.72243186 24.24871131 12.99038106]
[ 52.82754963 0. -52.82754963]
[-12.99038106 -24.24871131 -14.72243186]]

这样规律就很明显了,实部是中心对称的偶函数,虚部是奇函数,这个规律也和一维离散傅里叶变换一致。

如果做周期延展,那么规律就比较简洁了:

对于实部,real(-x,-y) = real(x,y),而虚部,imag(-x,-y) = -imag(x,y)

也就是说F(-x,-y)=F*(x,y),其中F*表示共轭复数。

4,傅里叶变换的性质

(1)平移

公式(1)表明将f(x,y)与一个指数项相乘就相当于把其变换后的频域中心移动到新的位置

公式(2)表明将F(u,v)与一个指数项相乘就相当于把其变换后的空域中心移动到新的位置

公式(2)表明对f(x,y)的平移不影响其傅里叶变换的幅值

(2)分配律

(3)数乘

(4)旋转

f(x,y)旋转一个角度 ,F(u,v)也将转过相同的角度,反之亦然。

(5)周期性

(6)共轭对称性

如果f是实函数,则F(u,v)和F(-u,-v)是共轭复数

(7)分离性

其中F1 是一维傅里叶变换。

即,二维傅里叶变换可以看成逐行做傅里叶变换,再对结果列做一次傅里叶变换,

也可以看成逐列做傅里叶变换,再对结果行做一次傅里叶变换。

反之,逆变换亦然。

(8)卷积定理

示例:

复制代码
  Mat img1(2, 2, CV_32F);

    
 	img1.at<float>(0, 0) = 1, img1.at<float>(0, 1) = 2;
    
 	img1.at<float>(1, 0) = 3, img1.at<float>(1, 1) = 4;
    
 	Mat img2(2, 2, CV_32F);
    
 	img2.at<float>(0, 0) = 1, img2.at<float>(0, 1) = 1;
    
 	img2.at<float>(1, 0) = 1, img2.at<float>(1, 1) = 2;
    
 	Mat F1(2, 2, CV_32F);
    
 	Mat F2(2, 2, CV_32F);
    
 	cv::dft(img1, F1, cv::DFT_REAL_OUTPUT);
    
 	cv::dft(img2, F2, cv::DFT_REAL_OUTPUT);
    
 	cout << F1.at<float>(0, 0) << " " << F1.at<float>(0, 1) << " " << F1.at<float>(1, 0) << " " << F1.at<float>(1, 1) << endl;
    
 	cout << F2.at<float>(0, 0) << " " << F2.at<float>(0, 1) << " " << F2.at<float>(1, 0) << " " << F2.at<float>(1, 1) << endl;
    
 	Mat F3 = F1.clone();
    
 	for (int i = 0; i < 2; i++) {
    
 		for (int j = 0; j < 2; j++)F3.at<float>(i, j) = F1.at<float>(i, j)*F2.at<float>(i, j) / 4;
    
 	}
    
 	Mat img3;
    
 	cv::idft(F3, img3);
    
 	cout << F3.at<float>(0, 0) << " " << F3.at<float>(0, 1) << " " << F3.at<float>(1, 0) << " " << F3.at<float>(1, 1) << endl;
    
 	cout << img3.at<float>(0, 0) << " " << img3.at<float>(0, 1) << " " << img3.at<float>(1, 0) << " " << img3.at<float>(1, 1) << endl;

输出:

10 -2 -4 0
5 -1 -1 1
12.5 0.5 1 0
14 13 12 11

空域表达式:
egin{pmatrix} 1 &2   3 & 4 nd{pmatrix}egin{pmatrix} 1 &1   1 & 2 nd{pmatrix}=egin{pmatrix} 14 &13   12 & 11 nd{pmatrix}

这是循环卷积

频域表达式:
egin{pmatrix} 10 &-2   -4 & 0 nd{pmatrix}egin{pmatrix} 5 &-1   -1 & 1nd{pmatrix}=egin{pmatrix} 12.5 &0.5   1 & 0 nd{pmatrix}

这个是各项相乘再除以4 (r*c=4)

不过这个例子比较特殊,这里面涉及的傅里叶变换的虚部都是0。

换成带虚部的:

复制代码
  Mat img1(2, 2, CV_32F);

    
 	img1 = 0;
    
 	img1.at<float>(0, 0) = 1, img1.at<float>(0, 1) = 2;
    
 	img1.at<float>(1, 0) = 3, img1.at<float>(1, 1) = 4;
    
 	Mat img2(2, 2, CV_32F);
    
 	img2 = 0;
    
 	img2.at<float>(0, 0) = 1, img2.at<float>(0, 1) = 1;
    
 	img2.at<float>(1, 0) = 1, img2.at<float>(1, 1) = 2;
    
 	Mat F1;
    
 	Mat F2;
    
 	cv::dft(img1, F1, cv::DFT_COMPLEX_OUTPUT);
    
 	cv::dft(img2, F2, cv::DFT_COMPLEX_OUTPUT);
    
 	cout << F1.at<Vec2f>(0, 0) << " " << F1.at<Vec2f>(0, 1) << " " << F1.at<Vec2f>(1, 0) << " " << F1.at<Vec2f>(1, 1) << endl;
    
 	cout << F2.at<Vec2f>(0, 0) << " " << F2.at<Vec2f>(0, 1) << " " << F2.at<Vec2f>(1, 0) << " " << F2.at<Vec2f>(1, 1) << endl;
    
 	Mat F3 = F1.clone();
    
 	for (int i = 0; i < 2; i++) {
    
 		for (int j = 0; j < 2; j++)F3.at<Vec2f>(i, j) = multi(F1.at<Vec2f>(i, j) ,F2.at<Vec2f>(i, j)) / 4;
    
 	}
    
 	Mat img3;
    
 	cv::idft(F3, img3);
    
 	cout << F3.at<Vec2f>(0, 0) << " " << F3.at<Vec2f>(0, 1) << " " << F3.at<Vec2f>(1, 0) << " " << F3.at<Vec2f>(1, 1) << endl;
    
 	cout << img3.at<Vec2f>(0, 0) << " " << img3.at<Vec2f>(0, 1) << " " << img3.at<Vec2f>(1, 0) << " " << img3.at<Vec2f>(1, 1) << endl;
    
 	Mat F4;
    
 	cv::dft(img3, F4, cv::DFT_COMPLEX_OUTPUT);
    
 	cout << F4.at<Vec2f>(0, 0) << " " << F4.at<Vec2f>(0, 1) << " " << F4.at<Vec2f>(1, 0) << " " << F4.at<Vec2f>(1, 1) << endl;

输出:

[10, 0] [-2, 0] [-4, 0] [0, 0]
[5, 0] [-1, 0] [-1, 0] [1, 0]
[12.5, 0] [0.5, -0] [1, -0] [0, 0]
[14, -0] [13, -0] [12, -0] [11, 0]
[50, 0] [2, 0] [4, -0] [0, 0]

改成3*3的:

复制代码
  Mat img1(3, 3, CV_32F);

    
 	img1 = 0;
    
 	img1.at<float>(0, 0) = 1, img1.at<float>(0, 1) = 2;
    
 	img1.at<float>(1, 0) = 3, img1.at<float>(1, 1) = 4;
    
 	Mat img2(3, 3, CV_32F);
    
 	img2 = 0;
    
 	img2.at<float>(0, 0) = 1, img2.at<float>(0, 1) = 1;
    
 	img2.at<float>(1, 0) = 1, img2.at<float>(1, 1) = 2;
    
 	Mat F1;
    
 	Mat F2;
    
 	cv::dft(img1, F1, cv::DFT_COMPLEX_OUTPUT);
    
 	cv::dft(img2, F2, cv::DFT_COMPLEX_OUTPUT);
    
 	cout << F1.at<Vec2f>(0, 0) << " " << F1.at<Vec2f>(0, 1) << " " << F1.at<Vec2f>(1, 0) << " " << F1.at<Vec2f>(1, 1) << endl;
    
 	cout << F2.at<Vec2f>(0, 0) << " " << F2.at<Vec2f>(0, 1) << " " << F2.at<Vec2f>(1, 0) << " " << F2.at<Vec2f>(1, 1) << endl;
    
 	Mat F3 = F1.clone();
    
 	for (int i = 0; i < 3; i++) {
    
 		for (int j = 0; j < 3; j++)F3.at<Vec2f>(i, j) = multi(F1.at<Vec2f>(i, j) ,F2.at<Vec2f>(i, j)) / 9;
    
 	}
    
 	Mat img3;
    
 	cv::idft(F3, img3);
    
 	cout << F3.at<Vec2f>(0, 0) << " " << F3.at<Vec2f>(0, 1) << " " << F3.at<Vec2f>(1, 0) << " " << F3.at<Vec2f>(1, 1) << endl;
    
 	cout << img3.at<Vec2f>(0, 0) << " " << img3.at<Vec2f>(0, 1) << " " << img3.at<Vec2f>(0, 2) << endl;
    
 	cout << img3.at<Vec2f>(1, 0) << " " << img3.at<Vec2f>(1, 1) << " " << img3.at<Vec2f>(1, 2) << endl;
    
 	cout << img3.at<Vec2f>(2, 0) << " " << img3.at<Vec2f>(2, 1) << " " << img3.at<Vec2f>(2, 2) << endl;
    
 	Mat F4;
    
 	cv::dft(img3, F4, cv::DFT_COMPLEX_OUTPUT);
    
 	cout << F4.at<Vec2f>(0, 0) << " " << F4.at<Vec2f>(0, 1) << " " << F4.at<Vec2f>(1, 0) << " " << F4.at<Vec2f>(1, 1) << endl;

输出:

[10, 0] [1, -5.19615] [-0.5, -6.06218] [-3.5, -0.866025]
[5, 0] [0.5, -2.59808] [0.5, -2.59808] [-1, 0]
[5.55556, 0] [-1.44444, -0.57735] [-1.77778, -0.19245] [0.388889, 0.096225]
[1, -0] [3, -0] [2, -0]
[4, -0] [11, -0] [8, -0]
[3, -0] [10, -0] [8, 0]
[50, 0] [-13, -5.19615] [-16, -1.73205] [3.5, 0.866026]

即卷积egin{pmatrix} 1 &2 &0   3 & 4& 0  0& 0 & 0 nd{pmatrix} egin{pmatrix} 1 &1 &0   1 & 2& 0  0& 0 & 0 nd{pmatrix}= egin{pmatrix} 1 &3 &2   4 & 11& 8  3& 10 & 8 nd{pmatrix}

换成2*3的例子:

复制代码
  Mat img1(2, 3, CV_32F);

    
 	img1 = 0;
    
 	img1.at<float>(0, 0) = 1, img1.at<float>(0, 1) = 2;
    
 	img1.at<float>(1, 0) = 3, img1.at<float>(1, 1) = 4;
    
 	Mat img2(2, 3, CV_32F);
    
 	img2 = 0;
    
 	img2.at<float>(0, 0) = 1, img2.at<float>(0, 1) = 2, img2.at<float>(1, 2) = 3;
    
 	Mat img3(2, 3, CV_32F);
    
 	img3 = 0;
    
 	img3.at<float>(0, 0) = 13, img3.at<float>(0, 1) = 4, img3.at<float>(0, 2) = 13;
    
 	img3.at<float>(1, 0) = 9, img3.at<float>(1, 1) = 10, img3.at<float>(1, 2) = 11;
    
 	Mat F1;
    
 	Mat F2;
    
 	Mat F3;
    
 	cv::dft(img1, F1, cv::DFT_COMPLEX_OUTPUT);
    
 	cv::dft(img2, F2, cv::DFT_COMPLEX_OUTPUT);
    
 	cv::dft(img3, F3, cv::DFT_COMPLEX_OUTPUT);
    
 	for (int i = 0; i < 2; i++)for (int j = 0; j < 3; j++)cout << F1.at<Vec2f>(i, j) << " ";
    
 	cout << endl;
    
 	for (int i = 0; i < 2; i++)for (int j = 0; j < 3; j++)cout << F2.at<Vec2f>(i, j) << " ";
    
 	cout << endl;
    
 	for (int i = 0; i < 2; i++)for (int j = 0; j < 3; j++)cout << F3.at<Vec2f>(i, j) << " ";

输出:

[10, 0] [1, -5.19615] [1, 5.19615] [-4, 0] [-1, 1.73205] [-1, -1.73205]
[6, 0] [-1.5, 0.866025] [-1.5, -0.866025] [0, 0] [1.5, -4.33013] [1.5, 4.33013]
[60, 0] [3, 8.66025] [3, -8.66025] [0, 0] [6, 6.9282] [6, -6.9282]

(9)相关定理

相关性

相关定理

自相关

四,opencv 相关函数

1,getOptimalDFTSize

入参是图像的行或者列

函数功能:返回一个等于或稍大于入参的整数,这个数只有特别小的素因子。

函数意义:最佳DFT尺寸

函数实现:用二分法即可

源码:

复制代码
  
    
 static const int optimalDFTSizeTab[] = {
    
 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, 30, 32, 36, 40, 45, 48,
    
 50, 54, 60, 64, 72, 75, 80, 81, 90, 96, 100, 108, 120, 125, 128, 135, 144, 150, 160,
    
 162, 180, 192, 200, 216, 225, 240, 243, 250, 256, 270, 288, 300, 320, 324, 360, 375,
    
 384, 400, 405, 432, 450, 480, 486, 500, 512, 540, 576, 600, 625, 640, 648, 675, 720,
    
 729, 750, 768, 800, 810, 864, 900, 960, 972, 1000, 1024, 1080, 1125, 1152, 1200,
    
 1215, 1250, 1280, 1296, 1350, 1440, 1458, 1500, 1536, 1600, 1620, 1728, 1800, 1875,
    
 1920, 1944, 2000, 2025, 2048, 2160, 2187, 2250, 2304, 2400, 2430, 2500, 2560, 2592,
    
 2700, 2880, 2916, 3000, 3072, 3125, 3200, 3240, 3375, 3456, 3600, 3645, 3750, 3840,
    
 3888, 4000, 4050, 4096, 4320, 4374, 4500, 4608, 4800, 4860, 5000, 5120, 5184, 5400,
    
 5625, 5760, 5832, 6000, 6075, 6144, 6250, 6400, 6480, 6561, 6750, 6912, 7200, 7290,
    
 7500, 7680, 7776, 8000, 8100, 8192, 8640, 8748, 9000, 9216, 9375, 9600, 9720, 10000,
    
 10125, 10240, 10368, 10800, 10935, 11250, 11520, 11664, 12000, 12150, 12288, 12500,
    
 12800, 12960, 13122, 13500, 13824, 14400, 14580, 15000, 15360, 15552, 15625, 16000,
    
 16200, 16384, 16875, 17280, 17496, 18000, 18225, 18432, 18750, 19200, 19440, 19683,
    
 20000, 20250, 20480, 20736, 21600, 21870, 22500, 23040, 23328, 24000, 24300, 24576,
    
 // 省略
    
 1953125000, 1966080000, 1968300000, 1990656000, 1992903750, 2000000000, 2013265920,
    
 2015539200, 2025000000, 2038431744, 2040733440, 2048000000, 2050312500, 2073600000,
    
 2097152000, 2099520000, 2109375000, 2123366400, 2125764000
    
 };
    
  
    
 }
    
  
    
 int cv::getOptimalDFTSize( int size0 )
    
 {
    
     int a = 0, b = sizeof(optimalDFTSizeTab)/sizeof(optimalDFTSizeTab[0]) - 1;
    
     if( (unsigned)size0 >= (unsigned)optimalDFTSizeTab[b] )
    
     return -1;
    
  
    
     while( a < b )
    
     {
    
     int c = (a + b) >> 1;
    
     if( size0 <= optimalDFTSizeTab[c] )
    
         b = c;
    
     else
    
         a = c+1;
    
     }
    
  
    
     return optimalDFTSizeTab[b];
    
 }

做DFT时,先计算最佳尺寸,然后把图像扩充到这个尺寸,边界扩充:<>

全部评论 (0)

还没有任何评论哟~