I've just started to learn about images frecuency domain.
I have this function:
def fourier_transform(img):
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20*np.log(np.abs(fshift))
return magnitude_spectrum
And I want to implement this function:
def inverse_fourier_transform(magnitude_spectrum):
return img
But I don't know how.
My idea is to use magnitude_spectrum to get the original img.
How can I do it?
You are loosing phases here: np.abs(fshift).
np.abs takes only real part of your data. You could separate the amplitudes and phases by:
abs = fshift.real
ph = fshift.imag
In theory, you could work on abs and join them later together with phases and reverse FFT by np.fft.ifft2.
EDIT:
You could try this approach:
import numpy as np
import matplotlib.pyplot as plt
# single chanel image
img = np.random.random((100, 100))
img = plt.imread(r'path/to/color/img.jpg')[:,:,0]
# should be only width and height
print(img.shape)
# do the 2D fourier transform
fft_img = np.fft.fft2(img)
# shift FFT to the center
fft_img_shift = np.fft.fftshift(fft_img)
# extract real and phases
real = fft_img_shift.real
phases = fft_img_shift.imag
# modify real part, put your modification here
real_mod = real/3
# create an empty complex array with the shape of the input image
fft_img_shift_mod = np.empty(real.shape, dtype=complex)
# insert real and phases to the new file
fft_img_shift_mod.real = real_mod
fft_img_shift_mod.imag = phases
# reverse shift
fft_img_mod = np.fft.ifftshift(fft_img_shift_mod)
# reverse the 2D fourier transform
img_mod = np.fft.ifft2(fft_img_mod)
# using np.abs gives the scalar value of the complex number
# with img_mod.real gives only real part. Not sure which is proper
img_mod = np.abs(img_mod)
# show differences
plt.subplot(121)
plt.imshow(img, cmap='gray')
plt.subplot(122)
plt.imshow(img_mod, cmap='gray')
plt.show()
You cannot recover the exact original image without the phase information, so you cannot only use the magnitude of the fft2.
To use the fft2 to recover the image, you just need to call numpy.fft.ifft2. See the code below:
import numpy as np
from numpy.fft import fft2, ifft2, fftshift, ifftshift
#do the 2D fourier transform
fft_img = fftshift(fft2(img))
# reverse the 2D fourier transform
freq_filt_img = ifft2(ifftshift(fft_img))
freq_filt_img = np.abs(freq_filt_img)
freq_filt_img = freq_filt_img.astype(np.uint8)
Note that calling fftshift and ifftshift is not necessary if you just want to recover the original image directly, but I added them in case there is some plotting to be done in the middle or some other operation that requires the centering of the zero frequency.
The result of calling numpy.abs() or freq_filt_img.real (assuming positive values for each pixel) to recover the image should be the same because the imaginary part of the ifft2 should be really small. Of course, the complexity of numpy.abs() is O(n) while freq_filt_img.real is O(1)
Related
I'm trying to change the phase of an image in the Fourier domain pseudorandomly while keeping the magnitude same to get a noisy image. Here's the code for that:
import numpy as np
import matplotlib.pyplot as plt
import cv2
img_orig = cv2.imread("Lenna.png", 0)
plt.imshow(img_orig, cmap="gray");
Original Image
f = np.fft.fft2(img_orig)
mag_orig, ang_orig = np.abs(f), np.arctan2(f.imag, f.real)
np.random.seed(42)
ns = np.random.uniform(0, 6.28, size = f.shape)
ang_noise = ang_orig+ns
img_noise = np.abs(np.fft.ifft2(mag_orig*np.exp(ang_noise*1j)))
plt.imshow(img_noise, cmap="gray");
Noisy Image
But when I try to reconstruct the original image by removing the noise the way I added it, I get a noisy version of the original image. Here's the code:
f_noise = np.fft.fft2(img_noise)
mag_noise, ang_noise = np.abs(f_noise), np.arctan2(f_noise.imag, f_noise.real)
ang_recover = ang_noise-ns
img_recover = np.abs(np.fft.ifft2(mag_noise*np.exp(ang_recover*1j)))
plt.imshow(img_recover, cmap="gray");
Reconstructed Image
Any idea about why this is happening and how to remove it? I'll appreciate any help that I can get. Thank You
Add to yours code, after string
ns = np.random.uniform(0, 6.28, size = f.shape)
this make symmetric phase:
ns = np.fft.fft2(ns)
ns = np.arctan2(ns.imag, ns.real)
After adding noise in Fourier space, your image in real space will be complex (i.e will have both a magnitude and a phase). In your case you are taking the absolute value though, probably so that you can plot it, but in doing so you are removing this phase information and altering your image when you shouldn't.
In short, I think you need to remove the abs in this line:
img_noise = np.abs(np.fft.ifft2(mag_orig*np.exp(ang_noise*1j)))
I am new to image processing and am working with images like these:
In these pictures, there will be more than one curves that I need to straighten out for them to look like a straight line.
Here's a quick solution. It can be improved by doing a spline fit to the features rather than just fitting the parabola. The algorithm shifts each row in the image individually according to the fitted parabola:
from skimage import io, measure, morphology
from matplotlib import pyplot as plt
from scipy.optimize import curve_fit
image = io.imread('curves.png', as_gray=True)
# need a binary mask of features
mask = image == image.min()
# close holes in features
mask = morphology.binary_closing(mask, morphology.square(3))
plt.matshow(mask, cmap='gray')
# need to get the coordinates of each feature
rp = measure.regionprops(measure.label(mask))
# going to fit a parabola to the features
def parabola(x, x0, A, y0):
return A*(x-x0)**2 + y0
# get coords of one of the features
coords = rp[0].coords
# do parabola fit
pop, pcov = curve_fit(parabola, coords[:,0], coords[:,1])
# generate fit
fit = parabola(np.arange(mask.shape[0]), *pop)
# plot fit
plt.plot(fit, np.arange(mask.shape[0])) # invert axes
# generate new image to shift
out = np.empty_like(image)
# shift each row individually and add to out array
for i, row in enumerate(image):
out[i] = np.roll(row, -int(round(fit[i] - pop[-1])))
plt.matshow(out, cmap='gray')
Original mask and fitted parabola:
Result:
I have 4D( 2D + slices along z axis + time frames) gray-scale image for the heart beating on different moments.
I do like to take Fourier Transform along time axis(for each slice separately), and analyze the fundamental Harmonic (also called H1 component, where H stands for Hilbert Space) so I can determine pixel regions corresponding to ROI which show strongest response to cardiac frequency.
I'm using python for this purpose, and I tried to do that with the following code, but I'm not sure that this is the correct way to do it, because I don't know how to determine the cut-frequency to keep only the fundamental Harmonic.
This link to the image which I'm dealing with
import nibabel as nib
import numpy as np
import matplotlib.pyplot as plt
img = nib.load('patient057_4d.nii.gz')
f = np.fft.fft2(img)
# Move the DC component of the FFT output to the center of the spectrum
fshift = np.fft.fftshift(f)
fshift_orig = fshift.copy()
# logarithmic transformation
magnitude_spectrum = 20*np.log(np.abs(fshift))
# Create mask
rows, cols = img.shape
crow, ccol = int(rows/2), int(cols/2)
# Use mask to remove low frequency components
dist1 = 20
dist2 = 10
fshift[crow-dist1:crow+dist1, ccol-dist1:ccol+dist1] = 0
#fshift[crow-dist2:crow+dist2, ccol-dist2:ccol+dist2] = fshift_orig[crow-dist2:crow+dist2, ccol-dist2:ccol+dist2]
# logarithmic transformation
magnitude_spectrum1 = 20*np.log(np.abs(fshift))
f_ishift = np.fft.ifftshift(fshift)
# inverse Fourier transform
img_back = np.fft.ifft2(f_ishift)
# get rid of imaginary part by abs
img_back = np.abs(img_back)
plt.figure(num = 'Im_Back')
plt.imshow(abs(fshift[:,:,2,2]).astype('uint8'),cmap='gray')
plt.show()
The solution was to take Fourier transform 3D for each slice seperately, then to chose only the 2nd component of the Transform to transform it back to the spatial space, and that's it.
The benefit of this is to detect if something is moving along the third axis(time in my case).
for sl in range(img.shape[2]):
#-----Fourier--H1-----------------------------------------
# ff1[:, :, 1] H1 compnent 1, if 0 then DC
ff1 = FFT.fftn(img[:,:,sl,:])
fh = np.absolute(FFT.ifftn(ff1[:, :, 1]))
#-----Fourier--H1-----------------------------------------
a research professor asked me to generate 2d-spatial spectrum density plots for a couple of videos. I have two problems:
How can I plot the PSD vs. x,y axis?
I know how to generate PSD for images, but uncertain how to do the same on videos. I thought about getting PSDs for every frame in the video and take the average, but I am having difficulties implementing it in python.
Below is the code I have
curr_dir = os.getcwd()
img = cv2.imread(curr_dir+'/test.jpg',0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
mag = 20*np.log(np.abs(fshift))
plt.subplot(121), plt.imshow(img,cmap='gray')
plt.subplot(122), plt.imshow(mag,cmap='gray')
plt.show()
This generates something like this:
I would like to get something like this:
Any help/advice is greatly appreciated!
Since you show two 1d spectra, it would seem that you are looking for something like the following.
We read in the image, Fourier transform along one axis, and then sum the power in each bin, along the other axis. Since the input is real valued, we use rfft() so what we do not have to shift the spectrum, and we use rfftreq() to calculate the frequency for each bin. We graph the result omitting the sometimes large signal in the 0 frequency bin (which corresponds to baseline) so that the useful part of the spectrum appears on a convenient scale.
#!/usr/bin/python3
import cv2
import os
import math
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
curr_dir = os.getcwd()
img = cv2.imread(curr_dir+'/temp.png',0)
print( img.shape )
# Fourier Transform along the first axis
# Round up the size along this axis to an even number
n = int( math.ceil(img.shape[0] / 2.) * 2 )
# We use rfft since we are processing real values
a = np.fft.rfft(img,n, axis=0)
# Sum power along the second axis
a = a.real*a.real + a.imag*a.imag
a = a.sum(axis=1)/a.shape[1]
# Generate a list of frequencies
f = np.fft.rfftfreq(n)
# Graph it
plt.plot(f[1:],a[1:], label = 'sum of amplitudes over y vs f_x')
# Fourier Transform along the second axis
# Same steps as above
n = int( math.ceil(img.shape[1] / 2.) * 2 )
a = np.fft.rfft(img,n,axis=1)
a = a.real*a.real + a.imag*a.imag
a = a.sum(axis=0)/a.shape[0]
f = np.fft.rfftfreq(n)
plt.plot(f[1:],a[1:], label ='sum of amplitudes over x vs f_y')
plt.ylabel( 'amplitude' )
plt.xlabel( 'frequency' )
plt.yscale( 'log' )
plt.legend()
plt.savefig( 'test_rfft.png' )
#plt.show()
Applying this to the photograph posted in your question, produces the following result,
So I have an array (it's large - 2048x2048), and I would like to do some element wise operations dependent on where they are. I'm very confused how to do this (I was told not to use for loops, and when I tried that my IDE froze and it was going really slow).
Onto the question:
h = aperatureimage
h[:,:] = 0
indices = np.where(aperatureimage>1)
for True in h:
h[index] = np.exp(1j*k*z)*np.exp(1j*k*(x**2+y**2)/(2*z))/(1j*wave*z)
So I have an index, which is (I'm assuming here) essentially a 'cropped' version of my larger aperatureimage array. *Note: Aperature image is a grayscale image converted to an array, it has a shape or text on it, and I would like to find all the 'white' regions of the aperature and perform my operation.
How can I access the individual x/y values of index which will allow me to perform my exponential operation? When I try index[:,None], leads to the program spitting out 'ValueError: broadcast dimensions too large'. I also get array is not broadcastable to correct shape. Any help would be appreciated!
One more clarification: x and y are the only values I would like to change (essentially the points in my array where there is white, z, k, and whatever else are defined previously).
EDIT:
I'm not sure the code I posted above is correct, it returns two empty arrays. When I do this though
index = (aperatureimage==1)
print len(index)
Actually, nothing I've done so far works correctly. I have a 2048x2048 image with a 128x128 white square in the middle of it. I would like to convert this image to an array, look through all the values and determine the index values (x,y) where the array is not black (I only have white/black, bilevel image didn't work for me). I would then like to take all the values (x,y) where the array is not 0, and multiply them by the h[index] value listed above.
I can post more information if necessary. If you can't tell, I'm stuck.
EDIT2: Here's some code that might help - I think I have the problem above solved (I can now access members of the array and perform operations on them). But - for some reason the Fx values in my for loop never increase, it loops Fy forever....
import sys, os
from scipy.signal import *
import numpy as np
import Image, ImageDraw, ImageFont, ImageOps, ImageEnhance, ImageColor
def createImage(aperature, type):
imsize = aperature*8
middle = imsize/2
im = Image.new("L", (imsize,imsize))
draw = ImageDraw.Draw(im)
box = ((middle-aperature/2, middle-aperature/2), (middle+aperature/2, middle+aperature/2))
import sys, os
from scipy.signal import *
import numpy as np
import Image, ImageDraw, ImageFont, ImageOps, ImageEnhance, ImageColor
def createImage(aperature, type):
imsize = aperature*8 #Add 0 padding to make it nice
middle = imsize/2 # The middle (physical 0) of our image will be the imagesize/2
im = Image.new("L", (imsize,imsize)) #Make a grayscale image with imsize*imsize pixels
draw = ImageDraw.Draw(im) #Create a new draw method
box = ((middle-aperature/2, middle-aperature/2), (middle+aperature/2, middle+aperature/2)) #Bounding box for aperature
if type == 'Rectangle':
draw.rectangle(box, fill = 'white') #Draw rectangle in the box and color it white
del draw
return im, middle
def Diffraction(aperaturediameter = 1, type = 'Rectangle', z = 2000000, wave = .001):
# Constants
deltaF = 1/8 # Image will be 8mm wide
z = 1/3.
wave = 0.001
k = 2*pi/wave
# Now let's get to work
aperature = aperaturediameter * 128 # Aperaturediameter (in mm) to some pixels
im, middle = createImage(aperature, type) #Create an image depending on type of aperature
aperaturearray = np.array(im) # Turn image into numpy array
# Fourier Transform of Aperature
Ta = np.fft.fftshift(np.fft.fft2(aperaturearray))/(len(aperaturearray))
# Transforming and calculating of Transfer Function Method
H = aperaturearray.copy() # Copy image so H (transfer function) has the same dimensions as aperaturearray
H[:,:] = 0 # Set H to 0
U = aperaturearray.copy()
U[:,:] = 0
index = np.nonzero(aperaturearray) # Find nonzero elements of aperaturearray
H[index[0],index[1]] = np.exp(1j*k*z)*np.exp(-1j*k*wave*z*((index[0]-middle)**2+(index[1]-middle)**2)) # Free space transfer for ap array
Utfm = abs(np.fft.fftshift(np.fft.ifft2(Ta*H))) # Compute intensity at distance z
# Fourier Integral Method
apindex = np.nonzero(aperaturearray)
U[index[0],index[1]] = aperaturearray[index[0],index[1]] * np.exp(1j*k*((index[0]-middle)**2+(index[1]-middle)**2)/(2*z))
Ufim = abs(np.fft.fftshift(np.fft.fft2(U))/len(U))
# Save image
fim = Image.fromarray(np.uint8(Ufim))
fim.save("PATH\Fim.jpg")
ftfm = Image.fromarray(np.uint8(Utfm))
ftfm.save("PATH\FTFM.jpg")
print "that may have worked..."
return
if __name__ == '__main__':
Diffraction()
You'll need numpy, scipy, and PIL to work with this code.
When I run this, it goes through the code, but there is no data in them (everything is black). Now I have a real problem here as I don't entirely understand the math I'm doing (this is for HW), and I don't have a firm grasp on Python.
U[index[0],index[1]] = aperaturearray[index[0],index[1]] * np.exp(1j*k*((index[0]-middle)**2+(index[1]-middle)**2)/(2*z))
Should that line work for performing elementwise calculations on my array?
Could you perhaps post a minimal, yet complete, example? One that we can copy/paste and run ourselves?
In the meantime, in the first two lines of your current example:
h = aperatureimage
h[:,:] = 0
you set both 'aperatureimage' and 'h' to 0. That's probably not what you intended. You might want to consider:
h = aperatureimage.copy()
This generates a copy of aperatureimage while your code simply points h to the same array as aperatureimage. So changing one changes the other.
Be aware, copying very large arrays might cost you more memory then you would prefer.
What I think you are trying to do is this:
import numpy as np
N = 2048
M = 64
a = np.zeros((N, N))
a[N/2-M:N/2+M,N/2-M:N/2+M]=1
x,y = np.meshgrid(np.linspace(0, 1, N), np.linspace(0, 1, N))
b = a.copy()
indices = np.where(a>0)
b[indices] = np.exp(x[indices]**2+y[indices]**2)
Or something similar. This, in any case, sets some values in 'b' based on the x/y coordinates where 'a' is bigger than 0. Try visualizing it with imshow. Good luck!
Concerning the edit
You should normalize your output so it fits in the 8 bit integer. Currently, one of your arrays has a maximum value much larger than 255 and one has a maximum much smaller. Try this instead:
fim = Image.fromarray(np.uint8(255*Ufim/np.amax(Ufim)))
fim.save("PATH\Fim.jpg")
ftfm = Image.fromarray(np.uint8(255*Utfm/np.amax(Utfm)))
ftfm.save("PATH\FTFM.jpg")
Also consider np.zeros_like() instead of copying and clearing H and U.
Finally, I personally very much like working with ipython when developing something like this. If you put the code in your Diffraction function in the top level of your script (in place of 'if __ name __ &c.'), then you can access the variables directly from ipython. A quick command like np.amax(Utfm) would show you that there are indeed values!=0. imshow() is always nice to look at matrices.