Python pyFFTW中的卷积与scipy不同

Python pyFFTW中的卷积与scipy不同,python,scipy,convolution,fftw,pyfftw,Python,Scipy,Convolution,Fftw,Pyfftw,我正在尝试实现一个FFT卷积,该卷积模拟使用pyfftw作为性能和图片输入的scipy.fftconvolve: import numpy as np import pyfftw a = np.ones((6000, 4000), dtype='float32') b = np.kaiser(25, 8) b = np.outer(b, b).astype('float32') class fftconvolve: def __init__(self, A, B, domain, th

我正在尝试实现一个FFT卷积,该卷积模拟使用pyfftw作为性能和图片输入的
scipy.fftconvolve

import numpy as np
import pyfftw
a = np.ones((6000, 4000), dtype='float32')
b = np.kaiser(25, 8)
b = np.outer(b, b).astype('float32')

class fftconvolve:
    def __init__(self, A, B, domain, threads=8):
        MK =  B.shape[0]
        NK = B.shape[1]
        M = A.shape[0]
        N = A.shape[1]

        if domain =="same":
            Y = M
            X = N
        elif domain == "valid":
            Y = M - MK + 1
            X = N - NK + 1
        elif domain == "full":
            Y = M + MK - 1
            X = N + NK - 1

        self.fft_A_obj = pyfftw.builders.rfft2(A, s=(M + MK -1, N + NK -1), threads=threads)
        self.fft_B_obj = pyfftw.builders.rfft2(B, s=(M + MK -1, N + NK -1), threads=threads)
        self.ifft_obj = pyfftw.builders.irfft2(self.fft_A_obj.output_array, s=(Y, X), threads=threads)

    def __call__(self, A, B):
        return self.ifft_obj(np.fft.ifftshift(
            np.fft.fftshift(self.fft_A_obj(A)) * np.fft.fftshift(self.fft_B_obj(B))
        ))
称之为:

plan = fftconvolve(a, b, "full", threads=8)
c_1 = plan(a, b)
c_1
输出:

array([[  3.89971137e-06,   3.51986018e-05,   1.24518745e-04, ...,
          1.25271297e-04,   3.56316777e-05,   4.04627326e-06],
       [  4.91737483e-05,   2.60021159e-04,   8.61040782e-04, ...,
          8.63055116e-04,   2.61142646e-04,   4.95371969e-05],
       [  1.26523402e-04,   8.49825097e-04,   2.90915114e-03, ...,
          2.90881563e-03,   8.49568460e-04,   1.26304061e-04],
       ..., 
       [  1.28503540e-04,   8.52331228e-04,   2.91197700e-03, ...,
          2.91016186e-03,   8.51134886e-04,   1.28111642e-04],
       [  2.14206957e-05,   2.32703838e-04,   8.34190170e-04, ...,
          8.34319100e-04,   2.32750244e-04,   2.14206957e-05],
       [ -8.42595455e-06,   2.29651105e-05,   1.12404508e-04, ...,
          1.12760317e-04,   2.31778213e-05,  -8.35505125e-06]], dtype=float32)
调用scipy等效项:

c_2 = scipy.signal.fftconvolve(a, b, "full").astype(np.float32)
c_2
输出:

array([[  5.47012860e-06,   3.68362089e-05,   1.26135841e-04, ...,
          1.26135841e-04,   3.68362089e-05,   5.47012769e-06],
       [  3.68362089e-05,   2.48057506e-04,   8.49407224e-04, ...,
          8.49407224e-04,   2.48057506e-04,   3.68362089e-05],
       [  1.26135841e-04,   8.49407224e-04,   2.90856976e-03, ...,
          2.90856976e-03,   8.49407224e-04,   1.26135841e-04],
       ..., 
       [  1.26135841e-04,   8.49407224e-04,   2.90856976e-03, ...,
          2.90856976e-03,   8.49407224e-04,   1.26135841e-04],
       [  3.68362089e-05,   2.48057506e-04,   8.49407224e-04, ...,
          8.49407224e-04,   2.48057506e-04,   3.68362089e-05],
       [  5.47012814e-06,   3.68362089e-05,   1.26135841e-04, ...,
          1.26135841e-04,   3.68362089e-05,   5.47012814e-06]], dtype=float32)
检查输出:

c_1 == c_2
给出:

array([[False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False],
       ..., 
       [False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False]], dtype=bool)
以及:

给出:

False
所以输出是不正确的。卸下
fftshift
不会改变任何事情

在我的版本中,scipy版本提供了正确的图像,带有我的实现的pyfftw提供了模糊的输出

编辑

我也在
double
类型(
np.float64
)中进行了测试,虽然卷积的原始结果足够接近(实际上,scipy以double进行卷积),但图片仍然不好:

用scipy解卷积:

在这里使用自定义卷积进行解卷积:它不仅模糊,而且边缘有条纹:

现在:

返回:

True

什么会产生这样的结果?

这与预期一样有效:

class fftconvolve:
    def __init__(self, A, B, domain, threads=8):
        MK =  B.shape[0]
        NK = B.shape[1]
        M = A.shape[0]
        N = A.shape[1]

        if domain =="same":
            self.Y = M
            self.X = N
        elif domain == "valid":
            self.Y = M - MK + 1
            self.X = N - NK + 1
        elif domain == "full":
            self.Y = M + MK - 1
            self.X = N + NK - 1

        self.M = M + MK - 1
        self.N = N + NK - 1

        a = np.pad(A, ((0, MK - 1), (0, NK - 1)), mode='constant')
        b = np.pad(B, ((0, M - 1), (0, N - 1)), mode='constant')

        self.fft_A_obj = pyfftw.builders.rfft2(a, s=(self.M, self.N), threads=threads)
        self.fft_B_obj = pyfftw.builders.rfft2(b, s=(self.M, self.N), threads=threads)
        self.ifft_obj = pyfftw.builders.irfft2(self.fft_A_obj.output_array, s=(self.M, self.N), threads=threads)


        self.offset_Y = int(np.floor((self.M - self.Y)/2))
        self.offset_X = int(np.floor((self.N - self.X)/2))

    def __call__(self, A, B):
        MK =  B.shape[0]
        NK = B.shape[1]
        M = A.shape[0]
        N = A.shape[1]

        a = np.pad(A, ((0, MK - 1), (0, NK - 1)), mode='constant')
        b = np.pad(B, ((0, M - 1), (0, N - 1)), mode='constant')

        return self.ifft_obj(self.fft_A_obj(a) * self.fft_B_obj(b))[self.offset_Y:self.offset_Y + self.Y, self.offset_X:self.offset_X + self.X]

这与预期的效果一样:

class fftconvolve:
    def __init__(self, A, B, domain, threads=8):
        MK =  B.shape[0]
        NK = B.shape[1]
        M = A.shape[0]
        N = A.shape[1]

        if domain =="same":
            self.Y = M
            self.X = N
        elif domain == "valid":
            self.Y = M - MK + 1
            self.X = N - NK + 1
        elif domain == "full":
            self.Y = M + MK - 1
            self.X = N + NK - 1

        self.M = M + MK - 1
        self.N = N + NK - 1

        a = np.pad(A, ((0, MK - 1), (0, NK - 1)), mode='constant')
        b = np.pad(B, ((0, M - 1), (0, N - 1)), mode='constant')

        self.fft_A_obj = pyfftw.builders.rfft2(a, s=(self.M, self.N), threads=threads)
        self.fft_B_obj = pyfftw.builders.rfft2(b, s=(self.M, self.N), threads=threads)
        self.ifft_obj = pyfftw.builders.irfft2(self.fft_A_obj.output_array, s=(self.M, self.N), threads=threads)


        self.offset_Y = int(np.floor((self.M - self.Y)/2))
        self.offset_X = int(np.floor((self.N - self.X)/2))

    def __call__(self, A, B):
        MK =  B.shape[0]
        NK = B.shape[1]
        M = A.shape[0]
        N = A.shape[1]

        a = np.pad(A, ((0, MK - 1), (0, NK - 1)), mode='constant')
        b = np.pad(B, ((0, M - 1), (0, N - 1)), mode='constant')

        return self.ifft_obj(self.fft_A_obj(a) * self.fft_B_obj(b))[self.offset_Y:self.offset_Y + self.Y, self.offset_X:self.offset_X + self.X]

顺便说一句,这是一种比较浮动数组的错误方法。请参见Thank@IgnacioVergaraKausel,我不知道由于许可证问题,此oneSciPy不使用
fftw
,而是使用
fftpack
。因此,可能存在实现差异,最有可能的是在转换之前有一个标量
1/sqrt(n)
,等等。顺便说一下,您不需要一个类来完成这项工作。这只是一个函数。看起来FFTW会自动规范化FFT,使ifft(FFT(a))=a。此外,pyfftw在转换之前进行规划的方式使得类成为必要,fft构建器不是ufunc而是内存规划器。如果问题仍然与该部分相关,则应使用allclose函数更新问题。在我看来,通过pyfftw,您观察到一个工件,很可能是包装中的问题。测试使用正弦或余弦等更简单的函数会发生什么。顺便说一句,这是一种比较浮点数组的错误方法。谢谢@IgnacioVergaraKausel,我不知道由于许可证问题,此oneSciPy不使用
fftw
,而是使用
fftpack
。因此,可能存在实现差异,最有可能的是在转换之前有一个标量
1/sqrt(n)
,等等。顺便说一下,您不需要一个类来完成这项工作。这只是一个函数。看起来FFTW会自动规范化FFT,使ifft(FFT(a))=a。此外,pyfftw在转换之前进行规划的方式使得类成为必要,fft构建器不是ufunc而是内存规划器。如果问题仍然与该部分相关,则应使用allclose函数更新问题。在我看来,通过pyfftw,您观察到一个工件,很可能是包装中的问题。用简单的正弦或余弦来测试会发生什么。出于好奇,我发现自己正在寻找更快的
fftconvolve
实现,我尝试了各种示例和“相同”卷积,发现这需要两倍的时间
scipy.signal.fftconvolve
。你认为这是什么问题?只是出于好奇,而且由于我发现自己正在寻找更快的
fftconvolve
实现,我尝试了各种示例和“相同”卷积,发现这需要两倍的时间
scipy.signal.fftconvolve
。你认为是什么问题?
True
class fftconvolve:
    def __init__(self, A, B, domain, threads=8):
        MK =  B.shape[0]
        NK = B.shape[1]
        M = A.shape[0]
        N = A.shape[1]

        if domain =="same":
            self.Y = M
            self.X = N
        elif domain == "valid":
            self.Y = M - MK + 1
            self.X = N - NK + 1
        elif domain == "full":
            self.Y = M + MK - 1
            self.X = N + NK - 1

        self.M = M + MK - 1
        self.N = N + NK - 1

        a = np.pad(A, ((0, MK - 1), (0, NK - 1)), mode='constant')
        b = np.pad(B, ((0, M - 1), (0, N - 1)), mode='constant')

        self.fft_A_obj = pyfftw.builders.rfft2(a, s=(self.M, self.N), threads=threads)
        self.fft_B_obj = pyfftw.builders.rfft2(b, s=(self.M, self.N), threads=threads)
        self.ifft_obj = pyfftw.builders.irfft2(self.fft_A_obj.output_array, s=(self.M, self.N), threads=threads)


        self.offset_Y = int(np.floor((self.M - self.Y)/2))
        self.offset_X = int(np.floor((self.N - self.X)/2))

    def __call__(self, A, B):
        MK =  B.shape[0]
        NK = B.shape[1]
        M = A.shape[0]
        N = A.shape[1]

        a = np.pad(A, ((0, MK - 1), (0, NK - 1)), mode='constant')
        b = np.pad(B, ((0, M - 1), (0, N - 1)), mode='constant')

        return self.ifft_obj(self.fft_A_obj(a) * self.fft_B_obj(b))[self.offset_Y:self.offset_Y + self.Y, self.offset_X:self.offset_X + self.X]