【问题标题】:FFT on image with Python使用 Python 对图像进行 FFT
【发布时间】:2016-11-23 09:37:19
【问题描述】:

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

而且,我得到的只是黑色图像。

[编辑2]

好的,让我们拍一张。

假设我不想打开它并保存为灰度图像。所以我就是这样做的。

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)

加载后。将其保存到文件后,我有。现在让我们尝试使用 gimp 打开 test.png 并使用 FFT 过滤器插件。我得到这张图片,这是正确的

我该如何处理?

【问题讨论】:

  • 如果你有图片,我建议你使用fft2进行二维离散傅里叶变换docs.scipy.org/doc/numpy/reference/generated/…
  • 我认为这个问题有一个很大的XY problem。请告诉我们您真正想要做什么。是否有您想要实现的特定算法?另外,您能否向我们展示一个示例图像以及 Gimp 的 FFT 生成的内容(您想在 Python 中尝试和生成的内容)?
  • 能否请您分享将生成的 FFT 保存为图像的代码?

标签: python image fft dft


【解决方案1】:

很好的问题。我从没听说过,但Gimp Fourier 插件看起来很整洁:

一个简单的插件来对你的图像进行傅里叶变换。这个插件的主要优点是能够在 GIMP 中处理转换后的图像。您可以在傅立叶空间中绘制或应用过滤器,并通过逆 FFT 获得修改后的图像。

这个想法——对频域数据进行 Gimp 风格的操作并转换回图像——非常酷!尽管与 FFT 合作多年,但我从未想过这样做。与其搞乱 Gimp 插件和 C 可执行文件和丑陋的东西,不如用 Python 来做吧!

警告。我尝试了多种方法来做到这一点,试图从原始输入图像中获得接近输出 Gimp Fourier 图像(带有莫尔图案的灰色)的东西,但我根本做不到不。 Gimp 图像在图像中间看起来有点对称,但它没有垂直或水平翻转,也不是转置对称的。我希望插件使用真正的 2D FFT 将 H×W 图像转换为频域中实值数据的 H×W 数组,在这种情况下将没有对称性(这只是 to-复数 FFT,对于像图像这样的实值输入是共轭对称的)。所以我放弃了尝试对 Gimp 插件的功能进行逆向工程,而是从头开始研究如何做到这一点。

代码。 很简单:读取一张图片,在前两个维度应用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')

(Aside: FFT-lover geek note. 查看rfft 的文档了解详细信息,但我使用了 Scipy 的 FFTPACK 模块,因为它的 rfft 将单个像素的实部和虚部交错作为两个相邻的实数值,保证将保留任意大小的 2D 图像(偶数与奇数,宽度与高度)的输出。这与 Numpy 的 numpy.fft.rfft2 形成对比,因为它返回大小为 width/2+1 的复杂数据height/2+1,强制您处理一个额外的行/列,并自己处理复杂到真实的去交错。谁需要这个应用程序的麻烦。)

结果。给定名为test.png的输入:

这个 sn-p 产生以下输出(全局最小值/最大值已重新缩放并量化为 0-255):

并升级:

在此频率图像中,直流(0 Hz 频率)分量位于左上角,随着您向右和向下移动,频率会更高。

现在,让我们看看当您以几种方式操作此图像时会发生什么。让我们使用cat photo,而不是这个测试图像。

我在 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 中绘制的要仔细得多?)。

这是一个带通滤波器,其中保留了最低频率(看到左上角的那一点白色吗?)和高频,但中频被阻挡。好诡异!

这是一个高通滤波器,上面蒙版中的左上角被涂黑了:

这就是边缘检测的工作原理。

后记。 有人,使用这种技术制作一个网络应用程序,让您可以绘制蒙版并将它们实时应用于图像!!!

【讨论】:

  • 感谢您的精彩解释,但似乎这不是我想要的。我在 cat Image 上使用了 gimp 的 fft 插件,它看起来似乎有所不同。而且我的机器上的 scipy 库有很多问题。
  • 您的目标是完全复制 Gimp Fourier 插件的行为吗?我可以看看那个源代码并试图弄清楚它在做什么——你展示的例子没有意义。尽管如此,我的代码中的方法非常通用,并且示例表明它运行良好,因此如果您只需要与插件相似的功能,则代码运行良好。
  • 你有什么系统安装 scipy 时遇到问题?
  • 此网站可让您自定义蒙版并将其应用于图像。 bigwww.epfl.ch/demo/ip/demos/03-FFT-filtering
  • @CristianArteaga 现在结束于bigwww.epfl.ch/demo/ip/demos/FFT-filtering - 但非常酷,感谢分享链接!
【解决方案2】:

这里有几个问题。

1) 手动转换为灰度不好。使用Image.open("test.png").convert('L')

2) 很可能存在类型问题。您不应该将 np.ndarrayfft2 传递到 PIL 图像,而不能确定它们的类型是否兼容。 abs(np.fft.fft2(something)) 将返回一个 np.float32 类型的数组或类似的东西,而 PIL 图像将接收类似 np.uint8 类型的数组。

3) cmets 中建议的缩放比例看起来错误。您实际上需要您的值适合 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 似乎通过调整对比度和缩小非信息通道来克服这个问题(在你的情况下,除了红色之外的所有通道都是空的)。参考图片:

【讨论】:

    猜你喜欢
    • 2012-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-11
    • 2013-10-19
    • 2014-12-31
    • 1970-01-01
    相关资源
    最近更新 更多