[英]Why ifft2 is not working but fft2 is fine?
我想使用 DFT 矩阵实现 ifft2。 以下代码适用于 fft2。
import numpy as np
def DFT_matrix(N):
i, j = np.meshgrid(np.arange(N), np.arange(N))
omega = np.exp( - 2 * np.pi * 1j / N )
W = np.power( omega, i * j ) # Normalization by sqrt(N) Not included
return W
sizeM=40
sizeN=20
np.random.seed(0)
rA=np.random.rand(sizeM,sizeN)
rAfft=np.fft.fft2(rA)
dftMtxM=DFT_matrix(sizeM)
dftMtxN=DFT_matrix(sizeN)
# Matrix multiply the 3 matrices together
mA = dftMtxM @ rA @ dftMtxN
print(np.allclose(np.abs(mA), np.abs(rAfft)))
print(np.allclose(np.angle(mA), np.angle(rAfft)))
要到达 ifft2,我假设我只需要将 dft 矩阵更改为它的转置,所以期望以下工作,但我对最后两个打印错误有任何建议吗?
import numpy as np
def DFT_matrix(N):
i, j = np.meshgrid(np.arange(N), np.arange(N))
omega = np.exp( - 2 * np.pi * 1j / N )
W = np.power( omega, i * j ) # Normalization by sqrt(N) Not included
return W
sizeM=40
sizeN=20
np.random.seed(0)
rA=np.random.rand(sizeM,sizeN)
rAfft=np.fft.ifft2(rA)
dftMtxM=np.conj(DFT_matrix(sizeM))
dftMtxN=np.conj(DFT_matrix(sizeN))
# Matrix multiply the 3 matrices together
mA = dftMtxM @ rA @ dftMtxN
print(np.allclose(np.abs(mA), np.abs(rAfft)))
print(np.allclose(np.angle(mA), np.angle(rAfft)))
我将根据我对您上一个问题的回答来构建一些东西。 请注意,我将尝试区分离散傅里叶变换( DFT
)和快速傅里叶变换( FFT
)这两个术语。 请记住, DFT
是一种变换,而FFT
只是一种执行它的有效算法。 包括我自己在内的人们通常将DFT
称为FFT
,因为它实际上是用于计算DFT
的唯一算法
这里的问题再次是数据的规范化。 有趣的是,这是任何DFT
操作中如此基本且令人困惑的部分,但我在互联网上找不到很好的解释。 我将尝试在最后提供一个关于DFT
规范化的总结,但我认为理解这一点的最好方法是自己研究一些例子。
需要注意的是,尽管两个allclose
测试看似都失败了,但它们实际上并不是比较两个复数 arrays 的好方法。
特别是,问题在于比较角度。 如果你只取-pi
和pi
边界上的两个近角的差,你可以得到一个大约2*pi
的值。 allclose
只取值之间的差异并检查它们是否低于某个阈值。 因此,在我们的案例中,它可以报告假阴性。
比较角度的更好方法是沿此 function 的路线:
def angle_difference(a, b):
diff = a - b
diff[diff < -np.pi] += 2*np.pi
diff[diff > np.pi] -= 2*np.pi
return diff
然后,您可以获取最大绝对值并检查它是否低于某个阈值:
np.max(np.abs(angle_difference(np.angle(mA), np.angle(rAfft)))) < threshold
在您的示例中,最大差异为3.072209153742733e-12
。
所以角度实际上是正确的!
当我们查看矩阵iDFT
和库iFFT
之间的幅度比时,我们可以了解这个问题。
print(np.abs(mA)/np.abs(rAfft))
我们发现mA
中的所有值都是800
,这意味着我们的绝对值比库计算的值大800
倍。 可疑的是, 800 = 40 * 20
,我们数据的维度。 我想你可以看到我要去哪里。
DFT
归一化当我们查看取自Numpy FFT文档的FFT
公式时,我们发现了一些迹象表明为什么会出现这种情况:
您会注意到,虽然前向变换没有通过任何方式进行归一化。 逆变换将 output 除以1/N
。 这些是 1D FFTs
,但同样适用于 2D 情况,逆变换将所有内容乘以1/(N*M)
所以在我们的例子中,如果我们更新这条线,我们将得到一致的幅度:
mA = dftMtxM @ rA/(sizeM * sizeN) @ dftMtxN
关于比较输出的旁注,比较复数的另一种方法是比较实部和虚部:
print(np.allclose(mA.real, rAfft.real))
print(np.allclose(mA.imag, rAfft.imag))
我们发现现在这两种方法确实是一致的。
DFT 变换必须满足的基本性质是iDFT(DFT(x)) = x
。 当您进行数学运算时,您会发现总和之前的两个系数的乘积必须是1/N
。
还有一个叫做Parseval 定理的东西。 简而言之,它指出信号中的能量只是时域和频域中的平方绝对值之和。 对于FFT
,这归结为这种关系:
这是用于计算信号能量的 function:
def energy(x):
return np.sum(np.abs(x)**2)
您基本上面临关于1/N
因子的选择:
您可以将1/N
放在DFT
sum 之前。 这是有道理的,因为k=0
DC 分量将等于时域值的平均值。 但是,您必须将频域中的能量乘以N
才能将其与时域频率匹配。
N = len(x) X = np.fft.fft(x)/N # Compute the FFT scaled by `1/N` # Energy related by `N` np.allclose(energy(x), energy(X) * N) == True # Perform some processing... Y = X * H y = np.fft.ifft(Y*N) # Compute the iFFT, remember to cancel out the built in `1/N` of ifft
您将1/N
放在iDFT
之前。 这是,有点违反直觉,大多数实现,包括 Numpy 所做的。 这背后的原因我无法找到明确的共识,但我认为这与实施效率有关。 (如果有人对此有更好的解释,请在评论中留下)如前面的等式所示,频域中的能量必须除以N
以匹配时域能量。
N = len(x) X = np.fft.fft(x) # Compute the FFT without scaling # Energy, related by 1/N np.allclose(energy(x), energy(X) / N) == True # Perform some processing... Y = X * H y = np.fft.ifft(Y) # Compute the iFFT with the build in `1/N`
您可以通过在每个变换之前放置1/sqrt(N)
来拆分1/N
,从而使它们完全对称。 在Numpy中,您可以为fft
函数提供参数norm="ortho"
,这将使它们使用1/sqrt(N)
归一化: np.fft.fft(x, norm="ortho")
这里的好属性是能量现在在两个域中匹配。
X = np.fft.fft(x, norm='orth') # Compute the FFT scaled by `1/sqrt(N)` # Perform some processing... # Energy are equal: np.allclose(energy(x), energy(X)) == True Y = X * H y = np.fft.ifft(Y, norm='orth') # Compute the iFFT, with scaling by `1/sqrt(N)`
最后归结为你需要什么。 大多数时候,你的DFT
的绝对大小实际上并不那么重要。 您最感兴趣的是各种分量的比率,或者您想在频域中执行一些操作,然后转换回时域,或者您对相位(角度)感兴趣。 在所有这些情况下,只要您保持一致,规范化并不会真正发挥重要作用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.