2016-07-20 19 views
3

PythonでのFFT実装に問題があります。私は完全に奇妙な結果があります。 さて、画像を開き、RGBのすべてのピクセルの値を取得したい場合は、fftを使用して画像に再度変換する必要があります。Pythonでの画像のFFT

私の手順は:

1)私はこの

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

2)私は取得していますピクセル

pixels = list(im.getdata()) 
)私は」のようなPythonでPILライブラリでイメージを開いています各画素を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)。 1つのピクセル(111,111,111)があるとします。そして、この

red = np.fft.fft(red) 

など、すべての赤の値に対してFFTを使用します。

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

私の出力は次のようになります。

(53866+0j) 111 111 

それは私が考える完全に間違っているのです。私の画像は64x64で、gimpからのFFTは全く違っています。実際には、私の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を得ています。これは問題ありません。だから今、私はイムは、それをロードした後、この

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

のようにやって、私は、新しいものに保存します前に、私のイメージにFFTを作りたいです。ファイルに保存した後、私はbad FFTを持っています。ですから、gimpでtest.pngを開き、FFTフィルタプラグインを使用してみましょう。私はそれを処理することができますどのようにgood FFT

正しいか、このイメージを、取得していますか?

+0

イメージをお持ちの場合は、2d離散フーリエ変換で 'fft2'を使用することをお勧めします。http://docs.scipy.org/doc/numpy/reference/generated/numpy.fft.fft2.html – giosans

+0

私は質問に大きなXY問題があると思う(http://meta.stackexchange.com/a/66378/262011)。あなたが本当にやろうとしていることを教えてください*。実装する特定のアルゴリズムはありますか?また、サンプル画像と、GimpのFFTが生成するもの(Pythonで試してみたいもの)を見せてもらえますか? –

+0

FFTを画像として保存するコードを共有してください。 – Vovanrock2002

答えて

2

グレート質問です。私はそれについて聞いたことがありませんが、プラグインは本当にすっきりしているようです:

イメージをフーリエ変換する簡単なプラグインです。このプラグインの主な利点は、GIMP内で変換されたイメージを扱うことができることです。フーリエ空間でフィルタを描くか、または適用し、逆FFTで修正した画像を得ることができます。

周波数領域のデータに対してGimpスタイルの操作を行い、画像に戻すこのアイデアは非常にクールです!何年ものFFTの作業にもかかわらず、私はこれを行うことについて決して考えなかった。 GimpのプラグインとCの実行可能ファイルと醜さを混乱させるのではなく、これをPythonでやってみましょう!

注意してください。私は、元の入力画像から出力されたGimpフーリエ画像(モアレ模様の灰色)の近くに何かを得ようと試みていましたが、それはできませんでした。 Gimp画像は、画像の中央付近で幾分対称的に見えますが、縦または横に反転することはなく、転置対称です。私はプラグインが実際の2D FFTを使ってH×Wイメージを周波数領域の実数データのH×W配列に変換することを期待しています。この場合、対称性はありません(それは、画像のような実数値入力に対して共役対称である複素FFT)。だから私は、Gimpプラグインが何をしているのかをリバースエンジニアリングしようとするのをやめ、これを最初からやる方法を見ました。

コードです。非常に単純です:イメージを読み込み、先頭の2つの次元にscipy.fftpack.rfftを適用して「周波数イメージ」を取得し、0-255に再スケーリングして保存します。

これは他の回答とどのように異なる点に注意してください。 グレースケールなし - 2D実数から実数へのFFTは、3つのチャネルすべてで独立して行われます。 いいえ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のドキュメントで見えるが、そのrfft任意のサイズの2D画像のために、その出力を保証し、二つの隣接する実数値のような単一のピクセルの実数及び虚数成分をインターリーブするので、私はscipyのダウンロードのFFTPACKモジュールを使用(偶数奇数の対、高さ対幅)は、サイズの複雑なデータを返すために保存される。これは、numpyののnumpy.fft.rfft2いるとは対照的ですheight/2+1によって、あなたは1つの余分の行/列に対処し、自分を複素数から実数デインターリーブに対処するために強制します。誰がこのアプリのためにその手間を必要とします。)

結果を。 test.pngという名前与えられた入力:

test input

このスニペットは、次の出力(グローバル最小/最大は0〜255に再スケーリングし、量子化されています)生産:

test output, frequency domain

とスケールアップ:

frequency, upscaled

この周波数画像では、DC(0Hz周波数)成分は左上にあり、周波数は右下がりに高く移動します。

さて、あなたはこの画像はいくつかの方法で操作するときに何が起こるか見てみましょう。このテスト画像の代わりに、cat photoを使用しましょう。

original cat

私はその後のPythonにロード及びマスク画像に対して有するどのような影響を参照すると、周波数画像を乗算Gimpのに少数のマスク画像を作りました。

ここでは、コードです:

# 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で描画するよりもはるかに注意深く整形されます)。

バンドパスフィルタここで、最も低い周波数(左上隅の白いビットを参照)と高い周波数が維持されますが、中間周波数はブロックされます。かなり奇妙な!edge-

high-passed filter

これはどのようである:ここでは

band-passed cat

は、上記マスクに白のままにして左上隅がブラックアウトされハイパスフィルタ、です検出が機能する。

ポストスクリプト。誰かがこの技術を使ってwebappを作って、マスクを描いてイメージにリアルタイムで適用することができます!

+0

すばらしい説明をありがとうございますが、それは私が探しているものではないようです。私はcatの画像にgimpのfftプラグインを使っていて、それは違って見えます。私はマシン上のscipyライブラリに多くの問題があります。 – Tatarinho

+0

Gimp Fourierプラグインの動作を正確に再現するという目標はありますか?そのソースコードを見て、それが何をしているのか理解しようと思います。あなたが示した例は理にかなっていません。それにもかかわらず、私のコードのアプローチは非常に一般的であり、その例はうまくいくことを示しているので、プラグインと同様の*機能が必要な場合は、コードがうまく機能します。 –

+0

scipyのインストールに問題があるシステムはありますか? –

1

ここにいくつかの問題があります。

1)グレースケールへの手動変換は良くありません。使用Image.open("test.png").convert('L')

2)おそらくタイプに問題があります。タイプが互換性があることを確認せずにnp.ndarrayfft2からPILイメージに渡すべきではありません。 abs(np.fft.fft2(something))はタイプnp.float32などの配列を返しますが、PILイメージはタイプnp.uint8の配列のように表示されます。

3)コメントに示唆されているスケーリングが間違っています。実際には、0..255の範囲に収まるように値が必要です。

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プラグインと同じ結果を得るために管理していない:

は、ここでこれらの3点に対処する私のコードです。私が見る限り、いくつかの後処理をしています。私の結果はすべて非常にコントラストが低く、GIMPはコントラストを調整し、非有益なチャンネルを縮小することでこれを克服しているようです(Red以外のすべてのチャンネルは空です)。画像を参照してください:

enter image description here