首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >傅里叶域核卷积图像的研究

傅里叶域核卷积图像的研究
EN

Stack Overflow用户
提问于 2019-02-26 03:11:36
回答 1查看 6.4K关注 0票数 4

我在我的图像和卷积内核周围使用零填充,将它们转换到Fourier域,并将它们反转回来以获得卷积图像,参见下面的代码。然而,结果是错误的。我本来以为会有模糊的图像,但是输出是四个移位的四分之一。为什么输出是错误的,以及如何修复代码?

输入图像:

卷积结果:

代码语言:javascript
复制
from PIL import Image,ImageDraw,ImageOps,ImageFilter
import numpy as np 
from scipy import fftpack
from copy import deepcopy
import imageio
## STEP 1 ##
im1=Image.open("pika.jpeg")
im1=ImageOps.grayscale(im1)
im1.show()
print("s",im1.size)
## working on this image array
im_W=np.array(im1).T
print("before",im_W.shape)
if(im_W.shape[0]%2==0):
im_W=np.pad(im_W, ((1,0),(0,0)), 'constant')
if(im_W.shape[1]%2==0):
im_W=np.pad(im_W, ((0,0),(1,0)), 'constant')
print("after",im_W.shape)
Boxblur=np.array([[1/9,1/9,1/9],[1/9,1/9,1/9],[1/9,1/9,1/9]])
dim=Boxblur.shape[0]

##padding before frequency domain multipication
pad_size=(Boxblur.shape[0]-1)/2
pad_size=int(pad_size)
##padded the image(starts here)

p_im=np.pad(im_W, ((pad_size,pad_size),(pad_size,pad_size)), 'constant')
t_b=(p_im.shape[0]-dim)/2
l_r=(p_im.shape[1]-dim)/2
t_b=int(t_b)
l_r=int(l_r)

##padded the image(ends here)

## padded the kernel(starts here)
k_im=np.pad(Boxblur, ((t_b,t_b),(l_r,l_r)), 'constant')
print("hjhj",k_im)
print("kernel",k_im.shape)

##fourier transforms image and kernel
fft_im = fftpack.fftshift(fftpack.fft2(p_im))
fft_k  = fftpack.fftshift(fftpack.fft2(k_im))
con_in_f=fft_im*fft_k
ifft2 = abs(fftpack.ifft2(fftpack.ifftshift(con_in_f)))
convolved=(np.log(abs(ifft2))* 255 / np.amax(np.log(abs(ifft2)))).astype(np.uint8)
final=Image.fromarray(convolved.T)
final.show()
u=im1.filter(ImageFilter.Kernel((3,3), [1/9,1/9,1/9,1/9,1/9,1/9,1/9,1/9,1/9], scale=None, offset=0))
u.show()
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-04 06:13:28

离散傅里叶变换(DFT)和扩展的FFT (计算DFT)具有输入和输出的第一元素(对于图像,左上像素)的原点。这就是我们经常在输出上使用fftshift函数的原因,以便将原点转移到我们更熟悉的位置(图像的中间)。

这意味着,在将3x3统一加权模糊内核传递给FFT函数之前,需要将其转换为如下所示:

代码语言:javascript
复制
1/9  1/9  0  0  ... 0  1/9
1/9  1/9  0  0  ... 0  1/9
  0    0  0  0  ... 0    0
...  ...               ...
  0    0  0  0  ... 0    0
1/9  1/9  0  0  ... 0  1/9

也就是说,内核的中间位于图像的左上角,中间的像素围绕在中间的上方和左边,并出现在图像的右端和底部。

我们可以使用ifftshift函数来完成这个任务,在填充之后应用到内核中。在填充内核时,我们需要注意的是,起源(内核的中部)位于k_im.shape // 2 (整数除法)的位置,在内核映像k_im中。最初的起源是在[3,3]//2 == [1,1]。通常,我们所匹配的图像的大小是均匀的,例如[256,256]。那里的原产地将在[256,256]//2 == [128,128]。这意味着我们需要在左边和右边(以及底部和顶部)放置一个不同的值。我们需要小心计算这个填充:

代码语言:javascript
复制
sz = img.shape  # the sizes we're matching
kernel = np.ones((3,3)) / 9
sz = (sz[0] - kernel.shape[0], sz[1] - kernel.shape[1])  # total amount of padding
kernel = np.pad(kernel, (((sz[0]+1)//2, sz[0]//2), ((sz[1]+1)//2, sz[1]//2)), 'constant')
kernel = fftpack.ifftshift(kernel)

请注意,输入图像img不需要填充(但是如果您想要强制执行FFT更便宜的大小,则可以这样做)。在乘法之前,也不需要将fftshift应用到快速傅立叶变换的结果中,然后在乘法后立即逆转这一移位,这些移位是多余的。只有当要显示傅里叶域图像时,才应该使用fftshift。最后,对滤波后的图像进行对数缩放是错误的。

生成的代码是(我使用pyplot来显示,根本不使用PIL ):

代码语言:javascript
复制
import numpy as np
from scipy import misc
from scipy import fftpack
import matplotlib.pyplot as plt

img = misc.face()[:,:,0]

kernel = np.ones((3,3)) / 9
sz = (img.shape[0] - kernel.shape[0], img.shape[1] - kernel.shape[1])  # total amount of padding
kernel = np.pad(kernel, (((sz[0]+1)//2, sz[0]//2), ((sz[1]+1)//2, sz[1]//2)), 'constant')
kernel = fftpack.ifftshift(kernel)

filtered = np.real(fftpack.ifft2(fftpack.fft2(img) * fftpack.fft2(kernel)))
plt.imshow(filtered, vmin=0, vmax=255)
plt.show()

请注意,我取的是逆FFT的实部。虚部应该只包含非常接近于零的值,这是计算中舍入误差的结果。取绝对值,虽然很常见,但是不正确的。例如,您可能希望对包含负值的图像应用筛选器,或者应用生成负值的筛选器。把这里的绝对价值取下来,就会创造出艺术品。如果反FFT的输出包含与零显著不同的虚值,那么滤波核填充的方式就会出现错误。

还请注意,这里的内核很小,因此模糊效应也很小。要更好地了解模糊的效果,请创建一个更大的内核,例如np.ones((7,7)) / 49

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54877892

复制
相关文章

相似问题

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