Свертывание изображения с ядром в области Фурье - PullRequest
0 голосов
/ 26 февраля 2019

Я использую заполнение нулями вокруг моего изображения и ядра свертки, преобразовываю их в область Фурье и инвертирую их обратно, чтобы получить свернутое изображение, см. Код ниже.Результат, однако, неверен.Я ожидал размытое изображение, но на выходе получается четыре сдвинутых четверти.Почему вывод неправильный, и как я могу исправить код?

Входное изображение:

input image

Результат свертки:

output image

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()

1 Ответ

0 голосов
/ 04 марта 2019

Дискретное преобразование Фурье (ДПФ) и, соответственно, БПФ (которое вычисляет ДПФ) имеют начало в первом элементе (для изображения, верхний левый пиксель) для оба вход и выход.По этой причине мы часто используем функцию fftshift на выходе, чтобы переместить начало координат в более знакомое нам место (середина изображения).

Это означает, что нам нужно преобразоватьравномерно взвешенное размытое ядро ​​3x3, чтобы оно выглядело так перед передачей его функции FFT:

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].Это означает, что нам нужно заполнить различное количество слева и справа (и снизу и сверху).Мы должны быть осторожны при вычислении этого отступа:

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, не нужно дополнять (хотя вы можете сделать это, если хотите установить размер, для которогоБПФ дешевле).Также нет необходимости применять fftshift к результату БПФ перед умножением, а затем изменять это смещение сразу после того, как эти сдвиги являются избыточными.Вам следует использовать fftshift, только если вы хотите отобразить изображение домена Фурье.Наконец, неверно применять логарифмическое масштабирование к отфильтрованному изображению.

В результате получается код (я использую pyplot для отображения, вообще не использую PIL):

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()

Обратите внимание, что яЯ беру реальную часть обратного БПФ.Мнимая часть должна содержать только значения, очень близкие к нулю, которые являются результатом ошибок округления в вычислениях.Взятие абсолютного значения, хотя и обычное, неверно.Например, вы можете применить фильтр к изображению, содержащему отрицательные значения, или применить фильтр, который создает отрицательные значения.Принятие здесь абсолютного значения создаст артефакты.Если выходные данные обратного БПФ содержат мнимые значения, значительно отличающиеся от нуля, то возникает ошибка в способе заполнения ядра фильтрации.

Также обратите внимание, что ядро ​​здесь крошечное, и, следовательно, эффект размытиятоже крошечный.Чтобы лучше увидеть эффект размытия, сделайте ядро ​​большего размера, например np.ones((7,7)) / 49.

...