Tat*_*nho 7 python image fft dft
我在Python中使用FFT实现了一个问题.我有完全奇怪的结果.好吧,我想打开图像,获取RGB中每个像素的值,然后我需要在它上面使用fft,然后再次转换为图像.
我的步骤:
1)我正在使用Python中的PIL库打开图像
from PIL import Image
im = Image.open("test.png")
Run Code Online (Sandbox Code Playgroud)
2)我得到了像素
pixels = list(im.getdata())
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
4).我们假设我有一个像素(111,111,111).并在所有红色值上使用fft
red = np.fft.fft(red)
Run Code Online (Sandbox Code Playgroud)
然后:
print (red[0][0], green[0][0], blue[0][0])
Run Code Online (Sandbox Code Playgroud)
我的输出是:
(53866+0j) 111 111
Run Code Online (Sandbox Code Playgroud)
我认为这是完全错误的.我的图像是64x64,而来自gimp的FFT完全不同.实际上,我的FFT只给出了具有巨大值的数组,这就是为什么我的输出图像是黑色的.
你知道问题出在哪里吗?
[编辑]
我按照建议改变了
red= np.fft.fft2(red)
Run Code Online (Sandbox Code Playgroud)
然后我扩展它
scale = 1/(width*height)
red= abs(red* scale)
Run Code Online (Sandbox Code Playgroud)
而且,我只得到黑色图像.
[EDIT2]
假设我不想打开它并保存为灰度图像.所以我这样做.
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')
Run Code Online (Sandbox Code Playgroud)
在此之后,我得到了这张照片
,没关系.所以现在,我想在我将它保存到新图像之前对我的图像进行fft,所以我就是这样做的
scale = 1/(width*height)
greyscale = np.fft.fft2(greyscale)
greyscale = abs(greyscale * scale)
Run Code Online (Sandbox Code Playgroud)
加载后.保存到文件后,我有
.所以让我们现在尝试使用gimp打开test.png并使用FFT过滤器插件.我收到这张图片,这是正确的
我该怎么办呢?
Ahm*_*sih 10
好问题.我从来没有听说过它,但Gimp Fourier插件似乎很整洁:
一个简单的插件,可以对您的图像进行傅立叶变换.这个插件的主要优点是能够在GIMP中使用转换后的图像.您可以在傅立叶空间中绘制或应用滤波器,并使用逆FFT获得修改后的图像.
这个想法 - 对频域数据进行Gimp风格的操作并转换回图像 - 非常酷!尽管多年来使用FFT,但我从未考虑过这样做.不要乱用Gimp插件和C可执行文件和丑陋,让我们用Python做到这一点!
警告.我尝试了很多方法来做到这一点,试图从原始输入图像中获得接近输出Gimp Fourier图像(带有莫尔图案的灰色)的东西,但我根本不能.Gimp图像在图像中间看起来有些对称,但它不是垂直或水平翻转,也不是转置对称的.我希望插件能够使用真正的2D FFT将H×W图像转换为频域中的实数值数据的H×W阵列,在这种情况下,不存在对称性(它只是 - 对于像图像这样的实值输入,它是共轭对称的复数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')
Run Code Online (Sandbox Code Playgroud)
(旁白: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')
Run Code Online (Sandbox Code Playgroud)
这是左侧的低通滤镜掩码,右侧是结果单击以查看完整分辨率图像:
在掩模中,黑色= 0.0,白色= 1.0.所以最低频率保持在这里(白色),而高频率被阻挡(黑色).这通过衰减高频来模糊图像.低通滤波器遍布整个地方,包括在对图像进行抽取("下采样")时(尽管它们的形状比我在Gimp中绘制的要小得多).
这是一个带通滤波器,其中保留了最低频率(见左上角的白色位?)和高频,但中频频率被阻挡.相当奇怪!
这是一个高通滤波器,上面掩模中左上角的左上角被涂黑:
这就是边缘检测的工作原理.
后记.有人,使用这种技术制作一个webapp,让你绘制蒙版并将它们实时应用到图像中!
这里有几个问题。
1)手动转换为灰度效果不好。使用Image.open("test.png").convert('L')
2)很可能存在类型问题。在未确定 PIL 图像类型兼容的情况下,不应将其传递np.ndarray给PIL 图像。将返回一个类型的数组或类似的东西,而 PIL 图像将接收类似类型的数组的东西。fft2abs(np.fft.fft2(something))np.float32np.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')
Run Code Online (Sandbox Code Playgroud)
我必须承认我还没有获得与 GIMP FFT 插件相同的结果。据我所知它做了一些后处理。我的结果都是对比度非常低的混乱,GIMP 似乎通过调整对比度和缩小非信息通道来克服这个问题(在你的情况下,除了红色之外的所有通道都是空的)。参考图片: