2016-07-20 7 views
3

У меня проблема с реализацией FFT в Python. У меня совершенно странные результаты. Итак, я хочу открыть изображение, получить значение каждого пикселя в RGB, затем мне нужно использовать fft на нем и снова преобразовать в изображение.FFT на изображении с Python

Мои шаги:

1) Я открываю файл с библиотекой PIL в Python, как этого

from PIL import Image 
im = Image.open("test.png") 

2) Я получаю пикселей

pixels = list(im.getdata()) 

3) I» m разделить каждый пиксель на r, g, b значения

for x in range(width): 
for y in range(height): 
    r,g,b = pixels[x*width+y] 
    red[x][y] = r 
    green[x][y] = g 
    blue[x][y] = b 

4). Предположим, что у меня один пиксель (111,111,111). И использовать FFT на всех красных значений, как этот

red = np.fft.fft(red) 

А потом:

print (red[0][0],green[0][0],blue[0][0]) 

Мой вывод:

(53866+0j) 111 111 

Это совершенно неправильно, я думаю. Мое изображение 64x64, а FFT от gimp - совсем другое. На самом деле, мой FFT дает мне только массивы с огромными значениями, вот почему мое выходное изображение черное.

У вас есть вопросы, где проблема?

[EDIT]

Я изменился, как предложил

red= np.fft.fft2(red) 

И после этого я масштабировать его

scale = 1/(width*height) 
red= abs(red* scale) 

И еще, Im получать только черное изображение.

[EDIT2]

Итак, давайте одно изображение. test.png

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

def getGray(pixel): 
     r,g,b = pixel 
     return (r+g+b)/3  
    im = Image.open("test.png") 
    im.load() 
    pixels = list(im.getdata()) 
    width, height = im.size 
    for x in range(width): 
     for y in range(height): 
      greyscale[x][y] = getGray(pixels[x*width+y]) 
    data = [] 
    for x in range(width): 
     for y in range(height): 
      pix = greyscale[x][y] 
      data.append(pix) 
    img = Image.new("L", (width,height), "white") 
    img.putdata(data) 
    img.save('out.png') 

После этого я получаю это изображение greyscale, что хорошо. Так что теперь, я хочу сделать FFT на мое изображение, прежде чем я спасу его на новый, так что Im делает как этот

scale = 1/(width*height) 
greyscale = np.fft.fft2(greyscale) 
greyscale = abs(greyscale * scale) 

после загрузки. После сохранения его в файл у меня есть bad FFT. Поэтому давайте попробуем теперь открыть test.png с помощью gimp и использовать плагин фильтра FFT. Я получаю это изображение, которое является правильным good FFT

Как я могу справиться с этим?

+0

Если у вас есть изображение, я бы предложил вам использовать 'fft2' для 2d дискретного преобразования Фурье http://docs.scipy.org/doc/numpy/reference/generated/numpy.fft.fft2.html – giosans

+0

Я думаю, что вопрос имеет большую проблему [XY] (http://meta.stackexchange.com/a/66378/262011). Скажите, пожалуйста, что вы * действительно пытаетесь сделать *. Есть ли определенный алгоритм, который вы хотите реализовать? Кроме того, вы можете показать нам пример изображения и то, что производит FFT Gimp (что вы хотите попробовать и произвести на Python)? –

+0

Не могли бы вы также поделиться кодом, который сохраняет ваш FFT в качестве изображения? – Vovanrock2002

ответ

2

Большой вопрос. Я никогда не слышал об этом, но плагин Gimp Fourier кажется действительно опрятным:

Простой плагин для преобразования Фурье на изображение. Основным преимуществом этого плагина является возможность работать с преобразованным изображением внутри GIMP. Вы можете нарисовать или применить фильтры в пространстве Фурье и получить модифицированное изображение с обратным БПФ.

Эта идея - манипулирование в стиле Gimp в области частотной области и преобразование обратно в изображение - очень круто! Несмотря на годы работы с БПФ, я никогда не думал об этом. Вместо того, чтобы возиться с плагинами Gimp и исполняемыми файлами C и уродством, давайте сделаем это на Python!

Предостережение. Я экспериментировал с несколькими способами сделать это, пытаясь получить что-то близкое к выходному изображению Gimp Фурье (серое с рисунком муара) от исходного входного изображения, но я просто не мог. Изображение Gimp кажется несколько симметричным вокруг середины изображения, но оно не перевернуто вертикально или горизонтально, и оно не является транспонированным-симметричным. Я ожидаю, что плагин будет использовать реальный 2D FFT для преобразования изображения H × W в массив H × W вещественных данных в частотной области, и в этом случае симметрии не будет (это просто то, комплексный FFT, который сопряжен-симметричен для вещественных входов, таких как изображения). Поэтому я отказался от попытки перепрограммировать то, что делает плагин Gimp, и посмотрел, как я это сделаю с нуля.

Код. Очень просто: прочитайте изображение, примените scipy.fftpack.rfft в ведущих двух измерениях, чтобы получить «частотное изображение», перемасштабировать до 0-255 и сохранить.

Обратите внимание, как это отличается от других ответов! Отсутствие серого - 2D реальный БПФ реального времени происходит независимо от всех трех каналов. abs необходимо: изображение частотной области может иметь законные отрицательные значения, и если вы сделаете их положительными, вы не сможете восстановить исходное изображение.(Также приятная особенность:. никаких компромиссов по размеру изображений Размер массива остается неизменным до и после FFT, ширина/высота, является ли четным или нечетным.)

from PIL import Image 
import numpy as np 
import scipy.fftpack as fp 

## Functions to go from image to frequency-image and back 
im2freq = lambda data: fp.rfft(fp.rfft(data, axis=0), 
           axis=1) 
freq2im = lambda f: fp.irfft(fp.irfft(f, axis=1), 
          axis=0) 

## Read in data file and transform 
data = np.array(Image.open('test.png')) 

freq = im2freq(data) 
back = freq2im(freq) 
# Make sure the forward and backward transforms work! 
assert(np.allclose(data, back)) 

## Helper functions to rescale a frequency-image to [0, 255] and save 
remmax = lambda x: x/x.max() 
remmin = lambda x: x - np.amin(x, axis=(0,1), keepdims=True) 
touint8 = lambda x: (remmax(remmin(x))*(256-1e-4)).astype(int) 

def arr2im(data, fname): 
    out = Image.new('RGB', data.shape[1::-1]) 
    out.putdata(map(tuple, data.reshape(-1, 3))) 
    out.save(fname) 

arr2im(touint8(freq), 'freq.png') 

(Кроме: Замечание FFT-любовника. Подробнее см. Документацию для rfft, но я использовал модуль Scipy FFTPACK, потому что его rfft перемежает реальные и мнимые компоненты одного пикселя в виде двух смежных реальных значений, гарантируя, что вывод для 2D-изображения любого размера (даже vs odd, width vs height). Это контрастирует с Num44's numpy.fft.rfft2, который, поскольку он возвращает сложные данные размером width/2+1 от height/2+1, заставляет вас иметь дело с одним дополнительным столбцом/столбцом и иметь дело с обратным перемежающимся комплексом к реальности. Кому нужна эта хлопот для этой заявки.)

Результаты. Учитывая ввод имени test.png:

test input

этого фрагмент производит следующий вывод (глобальная мин/макс пересчитаны и квантуется 0-255):

test output, frequency domain

И широкие масштабы:

frequency, upscaled

На этом частотном изображении компонент частоты постоянного тока (0 Гц) находится в верхнем левом углу, а частоты движутся выше, когда вы идите вправо и вниз.

Теперь давайте посмотрим, что произойдет, когда вы манипулируете этим изображением несколькими способами. Вместо этого тестового изображения, давайте использовать cat photo.

original cat

Я сделал несколько масок изображений в Gimp, которые я затем загрузить в Python и умножить частотное изображение с, чтобы увидеть, какой эффект маска имеет на изображении.

Вот код:

# Make frequency-image of cat photo 
freq = im2freq(np.array(Image.open('cat.jpg'))) 

# Load three frequency-domain masks (DSP "filters") 
bpfMask = np.array(Image.open('cat-mask-bpfcorner.png')).astype(float)/255 
hpfMask = np.array(Image.open('cat-mask-hpfcorner.png')).astype(float)/255 
lpfMask = np.array(Image.open('cat-mask-corner.png')).astype(float)/255 

# Apply each filter and save the output 
arr2im(touint8(freq2im(freq * bpfMask)), 'cat-bpf.png') 
arr2im(touint8(freq2im(freq * hpfMask)), 'cat-hpf.png') 
arr2im(touint8(freq2im(freq * lpfMask)), 'cat-lpf.png') 

Вот фильтра низких частот маска слева, и справа, результат нажмите, чтобы увидеть полное разрешение изображения:

low-passed cat

В маске черный = 0,0, белый = 1,0. Таким образом, низкие частоты сохраняются здесь (белые), а высокие - заблокированы (черные). Это размывает изображение за счет ослабления высоких частот. Фильтры нижних частот используются повсюду, в том числе при уничтожении («понижающей дискретизации») изображения (хотя они будут формироваться гораздо более тщательно, чем я рисую в Gimp).

Это полосовой фильтр , в котором сохранены самые низкие частоты (см. Этот бит белого в верхнем левом углу?) И высокие частоты, но средние частоты блокируются. Довольно странно!

band-passed cat

Вот высоких частот фильтр, где верхний левый угол, который был оставлен белым в вышеприведенных масках затемнен:

high-passed filter

Это как edge- обнаружения.

Постскриптум. Кто-то, сделайте webapp, используя эту технику, которая позволяет нарисовать маски и применить их к изображению в режиме реального времени !!!

+0

Спасибо за отличное объяснение, но, похоже, это не совсем то, что я ищу. Я использовал fft-плагин от gimp на cat Image, и его, похоже, выглядит по-другому. И у меня много проблем с scipy-библиотекой на моей машине. – Tatarinho

+0

Ваша цель точно воспроизвести поведение плагина Gimp Фурье? Я могу взглянуть на этот исходный код и попытаться выяснить, что он делает - примеры, которые вы показали, не имеют смысла. Тем не менее, подход в моем коде очень общий, и примеры показывают, что он работает хорошо, поэтому, если вам просто нужна функциональность * аналогично * для плагина, код работает хорошо. –

+0

Какая у вас система, у вас проблемы с установкой scipy? –

1

Здесь есть несколько вопросов.

1) Ручное преобразование в оттенки серого не является хорошим. Использовать Image.open("test.png").convert('L')

2) Скорее всего, существует проблема с типами. Вы не должны передавать np.ndarray из fft2 на изображение PIL, не будучи уверенным, что их типы совместимы. abs(np.fft.fft2(something)) вернет вам массив типа np.float32 или что-то в этом роде, тогда как изображение PIL получит что-то вроде массива типа np.uint8.

3) Масштабирование, предложенное в комментариях, выглядит неправильно. Вам действительно нужны ваши значения, чтобы они соответствовали диапазону 0..255.

Вот мой код, который решает эти 3 пункта:

import numpy as np 
from PIL import Image 

def fft(channel): 
    fft = np.fft.fft2(channel) 
    fft *= 255.0/fft.max() # proper scaling into 0..255 range 
    return np.absolute(fft) 

input_image = Image.open("test.png") 
channels = input_image.split() # splits an image into R, G, B channels 
result_array = np.zeros_like(input_image) # make sure data types, 
# sizes and numbers of channels of input and output numpy arrays are the save 

if len(channels) > 1: # grayscale images have only one channel 
    for i, channel in enumerate(channels): 
     result_array[..., i] = fft(channel) 
else: 
    result_array[...] = fft(channels[0]) 

result_image = Image.fromarray(result_array) 
result_image.save('out.png') 

Я должен признать, что не удалось получить результаты, идентичные плагин GIMP FFT. Насколько я вижу, он выполняет некоторую пост-обработку. Мои результаты - это очень низкий уровень контрастности, и GIMP, похоже, преодолевает это, настраивая контраст и уменьшая неинформативные каналы (в вашем случае все chanels, кроме Red, просто пусты). Обратитесь к изображению:

enter image description here

 Смежные вопросы

  • Нет связанных вопросов^_^