2D FFT from MATLAB to Python - python

Most of my programming experience is in MATLAB and I recently started get familiar with Python.
I came across some great MATLAB code here that pertains to some things I'd like to work with, so I've tried to recreate it in Python:
import numpy as np
import math
import matplotlib.pyplot as plt
x = np.linspace(-2, 2, 100) # seconds
y = np.linspace(-3, 3, 200) # seconds
xFreq = 2; # Hz
yFreq = -3; # Hz
a = np.matrix(np.matrix(np.exp(2j * np.pi * y * yFreq)))
b = np.matrix(np.exp(2j * np.pi * np.matrix(x).T * xFreq))
c = np.dot(b,a).T
plt.imshow(c.real, cmap='gray', extent = [min(x), max(x), min(y), max(y)], aspect=2/3);
plt.colorbar()
plt.xlabel('x (Sec)')
plt.ylabel('y (Sec)')
plt.show()
nfftx = len(x);
fs = 1/np.diff(x)[0];
fx = np.linspace(-1,1,nfftx) * fs/2;
nffty = len(y);
fs = 1/np.diff(y)[0];
fy = np.linspace(-1,1,nffty) * fs/2;
imF = np.fft.fftshift(np.fft.fft2(c))/np.size(c)
plt.figure()
plt.title("FFT (real)")
plt.imshow(np.real(imF), cmap='gray', extent = [min(fx), max(fx), min(fy), max(fy)], aspect=2/3)
plt.xlabel('fx (Hz)')
plt.ylabel('fy (Hz)')
Any idea why the y frequency is shown at 3 Hz vice -3 Hz
I couldn't understand what the original commentator was doing in MATLAB with these two lines:
Nfft = 4 * 2 .^ nextpow2(size(im));
imF = fftshift(fft2(im, Nfft(1), Nfft(2))) / numel(im);
which is likely why my FFT signal is so intense relative to background. Thoughts on how I could adjust my FFT in Python?

I only have a partial answer.
If you look closely, the colors on the sinusoidal image generated with your Python code and the one generated with Matlab code you linked have inverted color (check the colors of the stripes closer to edges, and the colors on the color bar).
That explains why you have inverted colors on the FFT plot, and may be why you got 3 Hz, instead of -3 Hz. Unfortunately, I do not have access to a computer with Python right now and won't be able to verify this. I guess this may be a good thing to start troubleshooting with.
EDIT:
Yes, you are right. I completely missed the flipud in the Matlab script. I do no think your c calculation is wrong. The easiest way to check that is to save the Matlab data and import it to Python.
In Matlab:
save('data.mat', 'im')
Then import that to Python using scipy:
im_matlab = scipy.io.loadmat('data.mat')['im']
np.all(np.isclose(im_matlab, im))
If the last line gives you True, then that means the calculations are correct.
About the plot, imshow assumes that the origin (0-th index of the numpy array) is the top left corner, which is the norm for images. You can change this by using origin='lower' keyword in plt.imshow.
About fftshift, I think this answer to a different StackOverflow question is what you need.

Related

Having trouble with Amplitude Modulation and creating proper graphs to go with it

I am a college student working on a Amplitude Modulator that switches modulation constants every so often. The problem I am running into is when I plot my carrier signal graph I get this weird line plotted on the graph that I am unsure of where it comes from.
Can anyone help me understand where this is coming from?
The code is calling 5 seconds of audio I have loaded on my computer, loading the data and sampling rate, taking said data and making an array of nparrays that are 8 bits long. I then am modulating the signal with a carrier frequency to have a Modulated signal at the end. Or this is what I think im doing, because I'm a novice python coder.
Also any suggestions on how I building my code, I am still learning python and consider myself a novice at best, any helpful hints, recommendations, or tips help me greatly. Thank you for the time and help you offer me.
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
import librosa as lib
from math import trunc
#rewriting pi into a easy to use variable
pi = np.pi
myaudio, sr = lib.load("C:/Users/kaczm/Documents/Downloads/summer-bensound-royalty-free-music-
no-copyright-music_UpIcRKDG.mp3")
duration = len(myaudio)/sr
#print(trunc(len(myaudio) / 8))
time = np.arange(0,duration,1/sr)
plt.plot(time,myaudio)
plt.title('Signal original')
plt.xlabel('signal in time domain')
plt.ylabel('amplitude of signal')
plt.show()
#____________________________________________________________________________________________
arr1 = []
for i in range(0, (8 - (len(myaudio) % 8))):
arr1.append([0])
#print(len(arr1))
#adds zeros to the end of myaudio,and time so it is easisly dvisble by 8 no matter the size
myaudio = np.append(myaudio,arr1)
time = np.append(time,arr1)
#variable for how many frames needed to be modulated, and spliting the exact time frames with
it
frame_num = trunc(len(myaudio) / 8)
#an array of numpy arrays of each individual byte in order
Split_audio = np.split(myaudio,frame_num)
Split_time = np.split(time,frame_num)
#___________________________________________________________________________________________
#A = int()
carrier = []
modulator = []
envelope = []
Mod_sig = []
#needs 8 different variables to work, designed this way for ease of coding
k = [1,.5,2,1.5,.3,2,1,2]
for x , y in zip(Split_audio,Split_time):
for Val, T, con in zip(x,y,k):
modulator.append(float(Val * np.cos((2 * pi * T * 5 / sr))))
carrier.append(float(signal.square(2 * pi * T )))
envelope.append(float((1 + con * np.cos(2 * pi * T ))))
# A = A + 1
# print(con)
# print(A)
for x,y in zip(envelope,modulator):
Mod_sig.append(float(x * y))
#plt.plot(time,modulator)
#plt.title('Signal modulator')
#plt.xlabel('signal in time domain')
#plt.ylabel('amplitude of signal')
#plt.show()
plt.plot(time,carrier)
plt.title('Signal carrier wave')
plt.xlabel('signal in time domain')
plt.ylabel('amplitude of signal')
plt.show()
plt.plot(time,Mod_sig)
plt.title('Signal Modulated signal')
plt.xlabel('signal in time domain')
plt.ylabel('amplitude of signal')
plt.show()
All the commented out lines of code are ways for me to test and view what is happening with the code. It looks like everything is going fine when referencing the plotted graphs but the carrier graph has that strange line going through it. I have a feeling it could be, because I am implementing the math wrong, but I am unsure. Any help given is always greatly appreciated

matplotlib doesn't display the correct data

I am new to Python. For some reason when I look at the plot it displays all the data as if Y = 0 but the last one, which is weird since when I ask it to print Y it displays the right values. What am I doing wrong?
import math
import numpy as np
import matplotlib.pyplot as plt
y0=2 # [m]
g=9.81 # [m/s^2]
v=20 # initial speed [m/s]
y_target=1 # [m]
x=35 # [m]
n_iter=50
theta=np.linspace(0,0.5*math.pi,n_iter) # theta input [rad]
Y=np.zeros(n_iter) # y output [m]
for i in range(n_iter):
Y[i]=math.tan(theta[i])*x-g/(2*(v*math.cos(theta[i]))**2)*x**2+y0
plt.plot(theta,Y)
plt.ylabel('y [m]')
plt.xlabel('theta [rad]')
plt.ylim(top=max(Y),bottom=min(Y))
plt.show()
The problem is that the function blows up a bit as theta approaches π/2. Notice the little 1e33 at the top of the y-axis in the plot: the scale of that axis is huge, because the last value of y is essentially minus infinity (because of dividing by almost zero). If you change the limits of the y-axis, e.g. to (-1000, +1000), the plot looks correct.
But I can't resist helping you with something you didn't ask for help on... You are not using NumPy correctly. NumPy gives you two things: n-dimensional arrays as a data structure, and fast, optimized code for 'vectorized' computing with those arrays. In essence, you never need a loop in NumPy — you just compute with everything at once. Try doing 10 * np.array([1, 2, 3]) and you will get the idea.
So I would write your code like this:
import numpy as np
import matplotlib.pyplot as plt
# Problem parameters.
y0 = 2 # [m]
g = 9.81 # [m/s^2]
v = 20 # initial speed [m/s]
x = 35 # [m]
# Make theta [rad].
steps = 50
theta = np.linspace(0, 0.5*np.pi, steps)
# Compute y.
y = np.tan(theta) * x - g / (2 * (v * np.cos(theta))**2) * x**2 + y0
# Plot.
plt.plot(theta, y)
plt.ylabel('y [m]')
plt.xlabel('theta [rad]')
plt.ylim(-1000, 1000)
plt.show()
Notice that there's no loop — you just use the vector theta as if it were a scalar. And the math library (which can't handle NumPy's arrays, only scalars) is not needed at all when you're using NumPy.

Color gradient on one contour line

I'm very very new to Python, i usually do my animations with AfterEffects, but it requires a lot of computation time for quite simple things.
• So I would like to create this kind of animation (or at least image) :
AfterEffects graph (forget the shadows, i don't really need it at this point)
Those are circles merging together as they collide, one of them being highlighted (the orange one).
• For now i only managed to do the "merging thing" computing a "distance map" and ploting a contour line :
Python + Matplotlib graph with the following code :
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
part_size = 0.0002
nb_part = 200
mesh_res = 500 # resolution of grid
x = np.linspace(0, 1.9, mesh_res)
y = np.linspace(0, 1, mesh_res)
Xgrid, Ygrid = np.meshgrid(x, y)
centers = np.random.uniform(0,1,(nb_part,2)) # array filled with disks centers positions
sizes = part_size*np.ones(nb_part) # array filled whith disks sizes
#sizes = np.random.uniform(0,part_size,nb_part)
dist_map = np.zeros((mesh_res,mesh_res),float) # array to plot the contour of
for i in range(nb_part):
dist_map += sizes[i] / ((Xgrid - centers[i][0]) ** 2 + (Ygrid - centers[i][1]) ** 2) # function with (almost) value of 1 when on a cricle, so we want the contour of this array
fig, ax = plt.subplots()
contour_opts = {'levels': np.linspace(0.9, 1., 1), 'color':'red', 'linewidths': 4} # to plot only the one-ish values of contour
ax.contour(x, y, dist_map, **contour_opts)
def update(frame_number):
ax.collections = [] # reset the graph
centers[:] += 0.01*np.sin(2*np.pi*frame_number/100+np.stack((np.arange(nb_part),np.arange(nb_part)),axis=-1)) # just to move circles "randomly"
dist_map = np.zeros((mesh_res, mesh_res), float) # updating array of distances
for i in range(nb_part):
dist_map += sizes[i] / ((Xgrid - centers[i][0]) ** 2 + (Ygrid - centers[i][1]) ** 2)
ax.contour(x, y, dist_map, **contour_opts) # calculate the new contour
ani = FuncAnimation(fig, update, interval=20)
plt.show()
The result is not that bad but :
i can't figure how to highlight just one circle keeping the merging effect (ideally, the colors should merge as well, and i would like to keep the image transparency when exported)
it still requires some time to compute each frame (it is way faster than AfterEffects though), so i guess i'm still very far from using optimally python, numpy, and matplotlib. Maybe there are even libraries able to do that kind of things ? So if there is a better strategy to implement it, i'll take it.

Healpy: From Data to Healpix map

I have a data grid where the rows represent theta (0, pi) and the columns represent phi (0, 2*pi) and where f(theta,phi) is the density of dark matter at that location. I wanted to calculate the power spectrum for this and have decided to use healpy.
What I can not understand is how to format my data for healpy to use. If someone could provide code (in python for obvious reasons) or point me to a tutorial, that would be great! I have tried my hand at doing it with the following code:
#grid dimensions are Nrows*Ncols (subject to change)
theta = np.linspace(0, np.pi, num=grid.shape[0])[:, None]
phi = np.linspace(0, 2*np.pi, num=grid.shape[1])
nside = 512
print "Pixel area: %.2f square degrees" % hp.nside2pixarea(nside, degrees=True)
pix = hp.ang2pix(nside, theta, phi)
healpix_map = np.zeros(hp.nside2npix(nside), dtype=np.double)
healpix_map[pix] = grid
But, when I try to execute the code to do the power spectrum. Specifically, :
cl = hp.anafast(healpix_map[pix], lmax=1024)
I get this error:
TypeError: bad number of pixels
If anyone could point me to a good tutorial or help edit my code that would be great.
More specifications:
my data is in a 2d np array and I can change the numRows/numCols if I need to.
Edit:
I have solved this problem by first changing the args of anafast to healpix_map.
I also improved the spacing by making my Nrows*Ncols=12*nside*nside.
But, my power spectrum is still giving errors. If anyone has links to good documentation/tutorial on how to calculate the power spectrum (condition of theta/phi args), that would be incredibly helpful.
There you go, hope it's what you're looking for. Feel free to comment with questions :)
import healpy as hp
import numpy as np
import matplotlib.pyplot as plt
# Set the number of sources and the coordinates for the input
nsources = int(1.e4)
nside = 16
npix = hp.nside2npix(nside)
# Coordinates and the density field f
thetas = np.random.random(nsources) * np.pi
phis = np.random.random(nsources) * np.pi * 2.
fs = np.random.randn(nsources)
# Go from HEALPix coordinates to indices
indices = hp.ang2pix(nside, thetas, phis)
# Initate the map and fill it with the values
hpxmap = np.zeros(npix, dtype=np.float)
for i in range(nsources):
hpxmap[indices[i]] += fs[i]
# Inspect the map
hp.mollview(hpxmap)
Since the map above contains nothing but noise, the power spectrum should just contain shot noise, i.e. be flat.
# Get the power spectrum
Cl = hp.anafast(hpxmap)
plt.figure()
plt.plot(Cl)
There is a faster way to do the map initialization using numpy.add.at, following this answer.
This is several times faster on my machine as compared to the first section of Daniel's excellent answer:
import healpy as hp
import numpy as np
import matplotlib.pyplot as plt
# Set the number of sources and the coordinates for the input
nsources = int(1e7)
nside = 64
npix = hp.nside2npix(nside)
# Coordinates and the density field f
thetas = np.random.uniform(0, np.pi, nsources)
phis = np.random.uniform(0, 2*np.pi, nsources)
fs = np.random.randn(nsources)
# Go from HEALPix coordinates to indices
indices = hp.ang2pix(nside, thetas, phis)
# Baseline, from Daniel Lenz's answer:
# time: ~5 s
hpxmap1 = np.zeros(npix, dtype=np.float)
for i in range(nsources):
hpxmap1[indices[i]] += fs[i]
# Using numpy.add.at
# time: ~0.6 ms
hpxmap2 = np.zeros(npix, dtype=np.float)
np.add.at(hpxmap2, indices, fs)

Wrong freqs & amplitudes with numpy.fft, furthermore odd drawing of spectra

To understand the usage of ffts, I've just implemented a low-pass filter for a discrete signal in python.
The resulting filtered signal is pretty much what I wanted to get, but unfortunately, the spectra are not what I had expected. They seem OK on the first look, but as you can see, both the amplitudes and frequences are not correct.
The frequencys should be 0Hz, 220Hz, 660Hz and the amplitudes 3, 2, 1; but it comes out as shown in the plot below. Please note that the amplitudes in that plot are not correct due I wrote abs(F)/N instead of 2*abs(F)/N on the plot command. But when I do so, the DC value doubles to 6, which is wrong I think.
And also the drawing of the spectrum seems a little odd to me, please have a look at this:
I have no idea what I am doing wrong and would very much appreciate some help on this.
import numpy as np
import matplotlib.pyplot as plt
from math import pi
N = 2048
w0 = 2*pi*220
t = np.linspace(0, 0.1, N)
signal = lambda x: 3 + 2*np.sin(w0*x) + np.sin(3*w0*x)
f = np.array(signal(t))
F = np.fft.fft(f)
Fo = F.copy() # just for printing the unfiltered spectrum
freq = np.fft.fftfreq(len(f), 1/N)
# That's the filter, all parts over the frequency of fg should be damped.
fg = 50
for i in range(0, len(f)):
F[i] *= (1 if abs(freq[i]) < fg else 0)
ff = np.fft.ifft(F)
plt.subplot(3, 1, 1)
plt.plot(t, f, label='f original')
plt.plot(t, ff, label='f filtered')
plt.axis(xmin=0, xmax=16e-3)
plt.legend()
plt.subplot(3, 1, 2)
plt.plot(freq, abs(Fo)/N, label='spec original')
plt.axis(xmin=-200, xmax=200)
plt.legend()
plt.subplot(3, 1, 3)
plt.plot(freq, abs(F)/N, label='spec filtered')
plt.axis(xmin=-200, xmax=200)
plt.legend()
plt.show()
There are two questions here:
1) Your freq axis is off by a factor of 10 since fftfreq wants the sample spacing (eg, in seconds), which should be total_time/N (or, 0.1/N in your case), not 1/N as you're using.
2) The funny looking plot is because the returned values from the fft are not ordered by increasing values of the frequency, and it happens that the last frequency in the returned values are at the middle frequency in the plot, so that line just ends there. Try instead: just plotting points (rather than points connected by a line) and it will look reasonable; or use fftshift.

Categories

Resources