簡體   English   中英

使用Python在圖像上進行FFT

[英]FFT on image with Python

我在Python中使用FFT實現了一個問題。 我有完全奇怪的結果。 好吧,我想打開圖像,獲取RGB中每個像素的值,然后我需要在它上面使用fft,然后再次轉換為圖像。

我的步驟:

1)我正在使用Python中的PIL庫打開圖像

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

2)我得到了像素

pixels = list(im.getdata())

3)我將每個像素分成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,而來自gimp的FFT完全不同。 實際上,我的FFT只給出了具有巨大值的數組,這就是為什么我的輸出圖像是黑色的。

你知道問題出在哪里嗎?

[編輯]

我按照建議改變了

red= np.fft.fft2(red)

然后我擴展它

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

而且,我只得到黑色圖像。

[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')

在此之后,我得到了這張照片 灰階 ,沒關系。 所以現在,我想在我將它保存到新圖像之前對我的圖像進行fft,所以我就是這樣做的

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

加載后。 保存到文件后,我有 壞FFT 所以讓我們現在嘗試使用gimp打開test.png並使用FFT過濾器插件。 我收到這張圖片,這是正確的 好的FFT

我該怎么辦呢?

好問題。 我從來沒有聽說過它,但Gimp Fourier插件似乎很整潔:

一個簡單的插件,可以對您的圖像進行傅立葉變換。 這個插件的主要優點是能夠在GIMP中使用轉換后的圖像。 您可以在傅立葉空間中繪制或應用濾波器,並使用逆FFT獲得修改后的圖像。

這個想法 - 對頻域數據進行Gimp風格的操作並轉換回圖像 - 非常酷! 盡管多年來使用FFT,但我從未考慮過這樣做。 不要亂用Gimp插件和C可執行文件和丑陋,讓我們用Python做到這一點!

警告。 我嘗試了很多方法來做到這一點,試圖從原始輸入圖像中獲得接近輸出Gimp Fourier圖像(帶有莫爾圖案的灰色)的東西,但我根本不能。 Gimp圖像在圖像中間看起來有些對稱,但它不是垂直或水平翻轉,也不是轉置對稱的。 我希望插件能夠使用真正的2D FFT將H×W圖像轉換為頻域中的實數值數據的H×W陣列,在這種情況下,不存在對稱性(它只是 - 對於像圖像這樣的實值輸入,它是共軛對稱的復數FFT。 所以我放棄了嘗試逆向工程Gimp插件正在做什么,看看我是如何從頭做的。

編碼。 非常簡單:讀取圖像, scipy.fftpack.rfft兩個維度中應用scipy.fftpack.rfft以獲得“頻率圖像”,重新縮放到0-255,然后保存。

請注意這與其他答案有何不同! 沒有灰度 - 2D實際到FFT在所有三個通道上獨立發生。 不需要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')

rfft :FFT-lover geek note。有關詳細信息,請查看rfft的文檔,但我使用了Scipy的FFTPACK模塊,因為它的rfft將單個像素的實部和虛部交錯為兩個相鄰的實數值,保證了任意大小的輸出將保留2D圖像(偶數與奇數,寬度與高度)。這與Numpy的numpy.fft.rfft2形成對比,因為它返回大小為width/2+1 height/2+1復雜數據,迫使您處理一個額外的行/列並處理自己復雜到真實的解交織。誰需要為這個應用程序麻煩。)

結果。 給定名為test.png輸入:

測試輸入

此代碼段產生以下輸出(全局最小值/最大值已重新調整並量化為0-255):

測試輸出,頻域

並升級:

頻率,升級

在此頻率圖像中,DC(0 Hz頻率)分量位於左上角,頻率隨着向右和向下移動而變高。

現在,讓我們看看當您以幾種方式操作此圖像時會發生什么。 而不是這個測試圖像,讓我們使用貓照片

原始的貓

我在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')

這是左側的低通濾鏡掩碼,右側是結果單擊以查看完整分辨率圖像:

低過的貓

在掩模中,黑色= 0.0,白色= 1.0。 所以最低頻率保持在這里(白色),而高頻率被阻擋(黑色)。 這通過衰減高頻來模糊圖像。 低通濾波器遍布整個地方,包括對圖像進行抽取(“下采樣”)時(雖然它們的形狀比我在Gimp中繪制的要小得多)。

這是一個帶通濾波器 ,其中保留了最低頻率(見左上角的白色位?)和高頻,但中頻頻率被阻擋。 相當奇怪!

帶通貓

這是一個高通濾波器 ,上面掩模中左上角的左上角被塗黑:

高通濾波器

這就是邊緣檢測的工作原理。

后記。 有人,使用這種技術制作一個webapp,讓你繪制蒙版並將它們實時應用到圖像中!

這里有幾個問題。

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插件相同的結果。 據我所知,它做了一些后期處理。 我的結果都是非常低對比度的混亂,GIMP似乎通過調整對比度和縮小非信息渠道來克服這個問題(在你的情況下,除Red之外的所有chanel都是空的)。 參考圖片:

在此輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM