首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >比较枕叶、火炬和傅里叶周期卷积时的不一致性

比较枕叶、火炬和傅里叶周期卷积时的不一致性
EN

Stack Overflow用户
提问于 2019-03-16 16:55:27
回答 1查看 569关注 0票数 2

我正在用三种不同的方式在合成图像上实现2d周期卷积:使用scipy、使用torch和使用Fourier变换(也是在torch框架下)。

然而,我有不同的结果。通过手工运算,我可以看到scipy的卷积得到了正确的结果,而torch的空间版本则产生了预期的结果倒置。最后,Fourier版本返回了一些意想不到的结果。

守则如下:

代码语言:javascript
复制
import torch
import numpy as np
import scipy.signal as sig
import torch.nn.functional as F
import matplotlib.pyplot as plt


def numpy_periodic_conv(f, k):
    H, W = f.shape
    periodic_f = np.hstack([f, f])
    periodic_f = np.vstack([periodic_f, periodic_f])
    conv = sig.convolve2d(periodic_f, k, mode='same')
    conv = conv[H // 2:-H // 2, W // 2:-W // 2]
    return periodic_f, conv

def torch_periodic_conv(f, k):
    H, W = f.shape[-2:]
    periodic_f = f.repeat(1, 1, 2, 2)
    conv = F.conv2d(periodic_f, k, padding=1)
    conv = conv[:, :, H // 2:-H // 2, W // 2:-W // 2]
    return periodic_f.squeeze().numpy(), conv.squeeze().numpy()

def torch_fourier_conv(f, k):
    pad_x = f.shape[-2] - k.shape[-2]
    pad_y = f.shape[-1] - k.shape[-1]
    expanded_kernel = F.pad(k, [0, pad_x, 0, pad_y])
    fft_x = torch.rfft(f, 2, onesided=False)
    fft_kernel = torch.rfft(expanded_kernel, 2, onesided=False)
    real = fft_x[:, :, :, :, 0] * fft_kernel[:, :, :, :, 0] - \
           fft_x[:, :, :, :, 1] * fft_kernel[:, :, :, :, 1]
    im = fft_x[:, :, :, :, 0] * fft_kernel[:, :, :, :, 1] + \
         fft_x[:, :, :, :, 1] * fft_kernel[:, :, :, :, 0]
    fft_conv = torch.stack([real, im], -1) # (a+bj)*(c+dj) = (ac-bd)+(ad+bc)j
    ifft_conv = torch.irfft(fft_conv, 2, onesided=False)
    return expanded_kernel.squeeze().numpy(), ifft_conv.squeeze().numpy()

if __name__ == '__main__':
    f = np.concatenate([np.ones((10, 5)), np.zeros((10, 5))], 1)
    k = np.array([[1, 0, -1], [2, 0, -2], [1, 0, -1]])

    f_tensor = torch.from_numpy(f).unsqueeze(0).unsqueeze(0).float()
    k_tensor = torch.from_numpy(k).unsqueeze(0).unsqueeze(0).float()

    np_periodic_f, np_periodic_conv = numpy_periodic_conv(f, k)
    tc_periodic_f, tc_periodic_conv = torch_periodic_conv(f_tensor, k_tensor)
    tc_fourier_k, tc_fourier_conv = torch_fourier_conv(f_tensor, k_tensor)

    print('Spatial numpy conv shape= ', np_periodic_conv.shape)
    print('Spatial torch conv shape= ', tc_periodic_conv.shape)
    print('Fourier torch conv shape= ', tc_fourier_conv.shape)

    r_np = dict(name='numpy', im=np_periodic_f, k=k, conv=np_periodic_conv)
    r_torch = dict(name='torch', im=tc_periodic_f, k=k, conv=tc_periodic_conv)
    r_fourier = dict(name='fourier', im=f, k=tc_fourier_k, conv=tc_fourier_conv)
    titles = ['{} im', '{} kernel', '{} conv']
    results = [r_np, r_torch, r_fourier]
    fig, axs = plt.subplots(3, 3)
    for i, r_dict in enumerate(results):
        axs[i, 0].imshow(r_dict['im'], cmap='gray')
        axs[i, 0].set_title(titles[0].format(r_dict['name']))
        axs[i, 1].imshow(r_dict['k'], cmap='gray')
        axs[i, 1].set_title(titles[1].format(r_dict['name']))
        axs[i, 2].imshow(r_dict['conv'], cmap='gray')
        axs[i, 2].set_title(titles[2].format(r_dict['name']))
    plt.show()

我所取得的结果:

注意:两个numpy__and torch版本的图像都显示周期图像,这是执行周期卷积所必需的。Fourier版本的内核显示原始内核零填充到图像大小,这是计算频域元素方向乘法所必需的。

-Edit1:在Fourier版本的乘法中,我在做(ac-bd)+(ad-bc)j而不是(ac-bd)+(ad+bc)j时出错了。但是现在,我得到了一个列的卷积。

-Edit2:torch的空间卷积结果是倒置的,因为运算实际上是互相关的。这在pytorch的官方论坛这里中得到了证实。此外,在将内核填充作为Cris Luengo的答案之后,频率方法得到了与关联相同的结果。这对我来说相当奇怪,因为据我所知,频率特性是卷积的,而不是相关性的。

新的-修复内核后的结果:

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-16 18:22:32

  1. FFT结果是错误的,因为填充是错误的。填充时,需要将原点(内核的中心)放在图像的左上角。详情请参见另一个答案
  2. 另外两种方法的区别是卷积和相关的区别。看上去“不起眼”的结果是一种卷积,而“火炬”则是一种相关性。
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55199256

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档