Optics : convert MTF to PSF - python

I'm working with a Modulation Transfert Function (MTF) that describes the contrast of an optical system with regards to the spatial frequency, expressed in cycles/mm:
The MTF is stored in a numpy array :
mtf = [1 0.99 0.98 0.97 ... 2e-2 1e-2]
freq = [0 1 2 3 ... 199 200]
I would like to compute the associated Point Spread Function (PSF), which is defined as the inverse Fourier Transform of the MTF, in order to compare the size of the PSF to the pixel pitch.
I've tried psf = np.real(np.fft.fftshift(np.fft.ifft(mtf)))
Here is the result :
Am I doing this right ? What are the units of the x and y axis of the PSF graph here ?
How can I convert the x-axis in µm in order to compare the FWHM of the PSF to the pixel size ?

Related

Shifting a signal to a higher frequency range

I want to shift an audio file from an audible frequency band to one that is higher than 20kHz using python.
I have been searching online on how to do this and the closest i have got is using fft.fftshift.
The code is as follows:
samplerate, data = wavfile.read('./aud/vader.wav')
fft_out = fft(data)
shifted = np.fft.fftshift(fft_out)
I want 'shifted' to be the shifted version of fft_out. It should be shifted by a magnitude of 20kHz
fftshift doesn't shift the original 20kHz frequency band to a higher band.
As you can read in the manual:
numpy.fft.fftshift¶ ... Shift the zero-frequency component to the
center of the spectrum. This function swaps half-spaces for all axes
listed
If you want to shift the band 0Hz-20kHz for example to the band 100kHz - 120kHz, you can multiply the audio signal with a cosinus function with frequency 100kHz.
This produces the two bands 80kHz-100kHz and 100kHz-120 kHz.
From the resulting spectrum you have to delete the lower band 80kHz-100kHz. You have to do this for the positive frequencies as well as for the negative frequencies, which are mirrored on the zero axis.
Why does this work?
cos(x) * cos(y) = 0.5 * ( cos(x+y) + cos(x-y) )
if x are the frequencies of your audio band and y is the shifting frequency of 100kHz you get:
cos(2pi*audiofrequency) * cos(2pi*100kHz) = 0.5 * (cos(2pi*(100kHz + audiofrequency)) + (cos(2pi*(100kHz - audiofrequency)) )

Detecting Alpha waves in EEG data through SNR? python

I am writing a function that detect Alpha waves in real time. My function receives 256 sample values of single channel as an argument.
After that its fft has to be found and then classified in alpha , beta and gamma ranges. Then I have to find SNR to check whether at alpha waves are present or not i.e is there any peak that exists at 10 hz frequency. So i need to find square of amplitude of values at 10hz divided by sum of square of all values in b/w range of 8-12hz divided by N values.
SNR = Square of Amp Value at 10hz/ (Square of rest values in 8-12hz / No. of these values)
Then 20 log SNR and check threshold.
So basically how to get square of Amp of values that are at 10 hz and then exclude this value and divided by rest of values.
I have written kick starter code below can someone guide or help to complete the code to get the desired job done.
Many thanks.
def classification(flag, data=[]):
fs = 200 # Sampling rate (512 Hz)
# Get real amplitudes of FFT (only in postive frequencies)
fft_vals = np.absolute(np.fft.rfft(data)) #these are my fft values rfft returns only the part of the result that corresponds to nonpositive frequences. (Avoids complex conjugaes) faster and for plotting
# Get frequencies for amplitudes in Hz
fft_freq = np.fft.rfftfreq(len(data), 1.0 / fs) # that might be fixed (window length n , and sample spacing) inverse of the sampling rate returns sample freq of length n .
# Define EEG bands
eeg_bands = {'Delta': (0, 4),
'Theta': (4, 8),
'Alpha': (8, 12),
'Beta': (12, 30),
'Gamma': (30, 45)}
# Take the mean of the fft amplitude for each EEG band
eeg_band_fft = dict()
for band in eeg_bands:
freq_ix = np.where((fft_freq >= eeg_bands[band][0]) & #np.where is like asking "tell me where in this array, entries satisfy a given condition".
(fft_freq <= eeg_bands[band][1]))[0] #for fft_frreq at all point where it satisfies it returns the index (in array)
#if fftfreq[np.where bla bla] will give values array
eeg_band_fft[band] = np.mean(fft_vals[freq_ix])
This code is already extracting the alpha frequencies using band-pass filter. Now to find SNR at specific frequency you simply sum square the values at that frequency divided by rest of frequncies and take 20 log of the division.

How to get frequency axis from an fft function?

So, I am probably missing something obvious, but I have searched through lots of tutorials and documentation and can't seem to find a straight answer. How do you find the frequency axis of a function that you performed an fft on in Python(specifically the fft in the scipy library)?
I am trying to get a raw EMG signal, perform a bandpass filter on it, and then perform an fft to see the remaining frequency components. However, I am not sure how to find an accurate x component list. The specific signal I am working on currently was sampled at 1000 Hz and has 5378 samples.
Is it just creating a linear x starting from 0 and going to the length of the fft'd data? I see a lot of people creating a linspace from 0 to sample points times the sample spacing. But what would be my sample spacing in this case? Would it just be samples/sampling rate? Or is it something else completely?
Here is an example.
First create a sine wave with sampling interval pre-determined. we will combine two sine waves with frequencies 20 and 40. Remember high frequencies might be aliased if the time interval is large.
#Import the necessary packages
from scipy import fftpack
import matplotlib.pyplot as plt
import numpy as np
# sampling freq in herts 20Hz, and 40Hz
freq_sampling1 = 10
freq_sampling2 = 20
amplitude1 = 2 # amplitude of first sine wave
amplitude2 = 4 # amplitude of second sine wave
time = np.linspace(0, 6, 500, endpoint=True) # time range with total samples of 500 from 0 to 6 with time interval equals 6/500
y = amplitude1*np.sin(2*np.pi*freq_sampling1*time) + amplitude2*np.sin(2*np.pi*freq_sampling2*time)
plt.figure(figsize=(10, 4))
plt.plot(time,y, 'k', lw=0.8)
plt.xlim(0,6)
plt.show()
Notice in the figure that two sine waves are superimposed. One with freq. 10 and amplitude 2 and the other with freq. 20 and amplitude 4.
# apply fft function
yf = fftpack.fft(y, time.size)
amp = np.abs(yf) # get amplitude spectrum
freq = np.linspace(0.0, 1.0/(2.0*(6/500)), time.size//2) # get freq axis
# plot the amp spectrum
plt.figure(figsize=(10,6))
plt.plot(freq, (2/amp.size)*amp[0:amp.size//2])
plt.show()
Notice in the amplitude spectrum the two frequencies are recovered while amplitude is zero at other frequencies. the Amplitude values are also 2 and 4 respectively.
you can use instead fftpack.fftfreq to obtain frequency axis as suggested by tom10
Therefore, the code changes to
yf = fftpack.fft(y, time.size)
amp = np.abs(yf) # get amplitude spectrum
freq = fftpack.fftfreq(time.size, 6/500)
plt.figure(figsize=(10,6))
plt.plot(freq[0:freq.size//2], (2/amp.size)*amp[0:amp.size//2])
plt.show()
We are only plotting the positive part of the amplitude spectrum [0:amp.size//2]
Once you feed your window of samples into the FFT call it will return an array of imaginary points ... the freqency separation between each element of returned array is determined by
freq_resolution = sampling_freq / number_of_samples
the 0th element is your DC offset which will be zero if your input curve is balanced straddling the zero crossing point ... so in your case
freq_resolution = 1000 / 5378
In general, for efficiency, you will want to feed an even power of 2 number of samples into your FFT call, important if you are say sliding your window of samples forward in time and repeatedly calling FFT on each window
To calculate the magnitude of a frequency in a given freq_bin (an element of the returned imaginary array)
X = A + jB
A on real axis
B on imag axis
for above formula its
mag = 2.0 * math.Sqrt(A*A+B*B) / number_of_samples
phase == arctan( B / A )
you iterate across each element up to the Nyquist limit which is why you double above magnitude
So yes its a linear increment with same frequency spacing between each freq_bin

Generating & Plotting Histograms of Gradient Magnitude & Orientation

Using matplotlib how can I generate & display a histogram of the image gradient magnitude and orientation?
One thing I don't know is the x axis limits; for magnitude should it be 0 to 1? Ie, the strength of an edge is between 0 and 1? For orientation I assume the x axis limits would be 0 to 360 (degrees) or should I do radians?
Below is how I generate my gradient, magnitude and orientation. How can I now show each as a histogram?
def get_gradient(src):
sobelx = cv2.Sobel(src,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(src,cv2.CV_64F,0,1,ksize=5)
grad = sobelx + sobely
mag = cv2.magnitude(sobelx, sobely) # so my Mat element values could be anything between 0 and 1???
ori = cv2.phase(sobelx, sobely, True) # so my Mat element values could be anything between 0 and 360 degrees???
return [grad, mag, ori]
grad_res = get_gradient(src)
# number of bins is 100 from 0 to 1. Ie, 0.001, 0.002, ... 1.000
# am I correct?
mag_hist = cv2.calcHist([grad_res[1]],[0],None,[100],[0,1])
ori_hist = cv2.calcHist([grad_res[2]],[0],None,[360],[0,360])
plt.plot(mag_hist)
plt.xlim([0,1])
plt.plot(ori_hist)
plt.xlim([0,360])
plt.show()
Edit: Current error from the above code:
mag_hist = cv2.calcHist([gradient[1]],[0],None,[10],[0,100])
error: C:\projects\opencv-python\opencv\modules\imgproc\src\histogram.cpp:1422: error: (-210) in function cv::calcHist
You're returning 64-bit floats, which calcHist does not support. You need to cast them to 32-bit floats. Either change the type in your cv2.Sobel() call to cv2.CV_32F or use [grad_res[1].astype('float32')] as the argument in cv2.calcHist().
From OpenCV docs:
images – Source arrays. They all should have the same depth, CV_8U or CV_32F , and the same size. Each of them can have an arbitrary number of channels.

How to calculate OpenCV camera projectionMatrix in python

I am trying to calculate projection_matrix using OpenCV 2.4 in Python 2.7 for my camera (I am using ps eye). I need it for cv2.triangulatePoints(). I already did the calibration using cv2.calibrateCamera() (using calibrate.py from OpenCV examples) so I have rms, camera_matrix, dist_coefs, rvecs and tvecs.
But I have a problem actually calculating projection_matrix from these parameters (I did not find any Python examples online).
PS: Do I have to calibrate every ps eye camera ? I have 3 and I would like to track object in 3D space.
If you have only one camera, projection matrix should be equal to camera_matrix. There is only one complication.
The cv2.triangulatePoints is defined to work with 2 views from 2 different cameras.
Documentation also states that
The function reconstructs 3-dimensional points (in homogeneous
coordinates) by using their observations with a stereo camera.
Projections matrices can be obtained from stereoRectify().
So yes, you have to calibrate each camera and calibrate every pair of cameras in order to retrieve each camera matrix and the rotation matrix and the translation vector from one camera to the "main camera".
For a given couple of cameras, with K1 and K2 the camera matrices, it is true that
The projection matrix of the main camera (the camera is the world reference system) is
P1 = K1*[I | z]
where I is the identy matrix and z is a 0,0,0 vector in the fourth column.
You could think something like
1 0 0 0
0 1 0 0
0 0 1 0
If R is the rotation matrix between the 2 cameras and t the distance between the two cameras, the second projection matrix is
P2 = K2*[R | t]
In python, if you can not obtain the matrices from stereoRectify, one method to do it manually is
import numpy as np
P = np.concatenate((np.dot(K,R),np.dot(K,t)), axis = 1)

Categories

Resources