I was trying to plot STFT using plot3 in MATLAB but failed. Can somebody guide me how to do that? My MWE is given below:
%% STFT Computataion
clear; clc; clf;
%% Get input and calculate frame size and overlap/shift
[Y,Fs]=wavread('D_NEHU_F0001_MN_10001');
frame_size=round(20*Fs/1000); % calculate frame size for 20ms
frame_shift=round(10*Fs/1000); % calculate frame shift for 10ms
%% Plot the input signal in time domain
t=1/Fs:1/Fs:(length(Y)/Fs);
subplot(2,1,1)
plot(t,Y);
title('Speech signal in time domain');
ylabel('Magnitude of samples');
xlabel('time in seconds');
%% Calculation of STFT
%NoOfFrames=floor((length(Y)/frame_shift)-1);
NoOfFrames=length(Y)-frame_size;
j=1;
%for i=1:frame_shift:(length(Y)-frame_size)
for i=1:frame_shift:((length(Y)-frame_size))%+frame_shift)
sp_frame=Y(i:(i+frame_size)).*hamming(frame_size+1);
sp_frame_dft=abs(fft(sp_frame)); % Compute STFT
sp_frame_array(:,j)=sp_frame_dft;
j=j+1;
end
%% Plot the STFT in 3D
[rows,cols]=size(sp_frame_array);
F=linspace(1/Fs,Fs/2000,cols);
T=1/Fs:(frame_shift*Fs/1000):(cols*(frame_shift*Fs/1000));
Z=1:frame_size+1;
subplot(2,1,2)
%mesh(sp_frame_array);
%surf(sp_frame_array,'EdgeColor','none');
plot3(T,F,sp_frame_array);
I am not sure what your question exactly is about, but I guess the problem is, with the provided code, that you do not get a plot similar to the one you'd get, say, with surf.
Furthermore, I am also not quite sure why you would want to use plot3, maybe to get the labels on the time and frequency right ? you could do that all the same with surf:
surf(T, F, sp_frame_array,'EdgeColor','none');
As a matter of fact, the reason why your plot3 does not give the same figure is because the arguments of plot3 must be three matrices of the same size (check it on help plot3). Your code should actually be broken on Matlab, which it's not, according to my test. Well, once again Matlab allowing people to mess around without warnings (go Python! :D)... Anyway, try to set the matrices more like the following:
F=linspace(1/Fs,Fs/2000, rows); % note: has to be rows, not cols here!
Fmat = F(:) * ones(1,cols); % or use repmat
T=1/Fs:(frame_shift*Fs/1000):(cols*(frame_shift*Fs/1000));
Tmat = ones(rows,1) * T(:)';
plot3(Tmat,Fmat,sp_frame_array);
While this will normally produce something more in line with what I would expect in drawing a spectrogram, I'd still make some remarks:
your F vector should go up to Fs, because of the way you filled sp_frame_dft in. More specifically, it should go from 0Hz to Fs - Fs/rows:
F = linspace(0,Fs*(1-1/rows)/1000,rows); % in kHz
you would probably like to draw the amplitudes in dBs:
plot3(Tmat,Fmat,db(sp_frame_array));
plot3 draws one line per column of the provided matrices. That means potentially lots of lines to draw! As #atul-ingle asked, are you sure this is what you want? Maybe waterfall would provide a better rendering at a lower cost?
waterfall(T,F,db(sp_frame_array));
Well, you'll get the lines for the rows, instead of the columns, so you might need to transpose if the latter is what you want.
You might also prefer to visualise only the first half of the matrix (because the frequencies higher than Fs/2 are only mirrors of the other half of the matrix).
Hope that helps!
Related
This is the kind of plot i imagined myself.
https://de.mathworks.com/help/matlab/ref/waterfall.html
Ok, i don't want to explain too much, how my code works. It would take too much time. Just try the second code yourself. Take any small wav-file you can find. When you compile the code, you can see three frequency bands and see that many spectrums are plotted every 30ms. If you have a specifically question concerning my code, how it works, ask me in the comments.
I want every spectrum, at least from one frequency band, to plot it in a 3-dimensional plot. In short, what are the coordinates of the first spectrum and the 2nd, the 3rd, the 4th and so on.
My time segment on which is a fft applied, is 30 ms long. The first point on the x-axis is 30 ms, the next one 60ms and the next one 90ms and so on. What is the y-coordinate from the 30ms? This would be on the frequency axis or the y-axis. The z-axis would be the magnitude out of a frequency component at some point in time (or at a given sliding window frame). How can i do that? How do i write that? I am having big trouble with this matter. And since every explanation is in another language, it makes it much more harder for me.
As you may know, i have an audiofile (music) on which i compute a STFT. I want to visualise it. See the following explanation in my code. Read the comments!
My first idea to do this way, was using the function "mesh" or something similar.
Here is my mesh-code:
X=1:10;
Y=1:15;
Z = [];
% Here i would define the number of time segments
% See the next following code, to understand, what i mean.
for i = 1:length(X)
% Here in this line, i want to compute my short fft
%
% number of frequencies
for j = 1: length(Y)
Z(j,i) = 1.0/(i*j);
end
end
mesh(X,Y,Z)
This code plots me a mesh, i just wanted to know for myself, how this works. Anyway please be aware, that i am quite sure that i do not know, how the function "mesh" works to the fullest, but i think, i understood most of it.
Another thing i need to mention is, that i am defining frequency bands in my next following code. I did this, because i noticed, i have very high amplitudes in a range from 1 - 1000Hz, which is why, i defined 3 frequency bands. It is not necessary to plot all of them, but i want to visualise at least one. Not visualising the whole frequency range from the audio signal, but only the specificially chosen band.
%% MATLAB
%_________________________________________
[y,fs]=audioread('dontstopmenow.wav');
% audioread = Read WAV-file
% y = Vector, which contains audio signal
% fs = Sample Rate
% 'dontstopmenow' = WAV-file
%_________________________________________
%PARAMETER FOR STFT
%_________________________________________
t_seg=0.03; % Length of segment in ms
fftlen = 4096; %FFT-Points
%Defining the length of my frequency bands
f_LOW= 1:200; % contain lower frequencies
f_MEDIUM= 201:600; % contain medium frequencies
f_HIGH= 601:1000; % contain higher frequencies
%_______________________________________________________
segl =floor(t_seg*fs);
% Length of segment, on which we use the fft
% "floor" rounds off the result
windowshift=segl/2;
% size of window which goes to the next segment
window=hann(segl);
%hann function
window=window.';
% From a row vector to a column vector
si=1;
%Start index
ei=segl;
%End index
N= length(y)/windowshift - 1;
% Number of time segements in audio signal
f1=figure;
% New window
f=0:1:fftlen-1;
f=f/(fftlen-1)*fs;
% frequency vector
Ya=zeros(1,fftlen);
%Plotting time segments!
for m= 1:1:N
y_a = y(si:ei);
y_a= y_a.*window;
Ya=fft(y_a, fftlen);
Ya=abs(Ya(1:end/2));
%One-sided-spectrum
drawnow; %Updates graphical objects
figure(f1);
plot(f(1:end/2), 20*log10(Ya));
%STFT __ plots the whole audio signal after a stft, every 30ms
%% L,M,H - Bands
subplot(3,1,1)
y_low = Ya(f_LOW);
plot(f_LOW,y_low);
ylim([-20 60]);
title('Spektrum (LOW)');
xlabel('f(Hz)');
ylabel('dB');
grid on
subplot(3,1,2)
y_medium = Ya(f_MEDIUM);
plot(f_MEDIUM,y_medium);
ylim([-20 30]);
title('Spektrum (MEDIUM)');
xlabel('f(Hz)');
ylabel('dB');
grid on
subplot(3,1,3)
y_high = Ya(f_HIGH);
plot(f_HIGH,y_high);
ylim([-20 30]);
title('Spektrum (HIGH)');
xlabel('f(Hz)');
ylabel('dB');
grid on;
si=si+windowshift;
% start index updated
ei=ei+windowshift;
% end index updated
end
Here's the list of statements you could add to your code to generate the waterfall plot:
Let's store all STFT outputs in a matrix named Yb. Firstly, allocate the memory by adding this line before the for-loop.
Yb = NaN(N, fftlen/2);
Next, in the for-loop, save the fft result for each segment. This can be done by adding the following line after you have finished the computation of Ya (just before drawnow).
Yb(m,:) = Ya;
Now you're ready to generate the waterfall plot. This can be done by adding the following code after the end of the for-loop.
figure;
waterfall(f(f_LOW), (1:N)*windowshift/fs, Yb(:,f_LOW));
xlabel('Frequency (Hz)');
ylabel('Time (s)');
Hope this achieves what you want.
Following is not related to the main question: I also have the following suggestions to improve some other aspects of your code:
(1) The computation of the frequency grid f has a small scaling error. It should be:
f=f/fftlen*fs;
(2) Depending on the WAV file you use, your code may get fractional values in windowshift and N, but both of them need to be integers. So, use appropriate rounding methods while computing them. I'd suggest the following:
windowshift = round(segl/2);
N = floor(length(y)/windowshift);
(3) In the for-loop, you plot the whole fft only to overwrite that with the subplots immediately. That line should be removed.
I have a really weird question.
Let's assume i have an oscillogram like the one shown below.
(source: engineeringvista.com)
I need to -somehow- capture the points that compose the signal and afterwards try to make a Fourier analysis and plot the harmonics of the signal.
Do you think such an operation could be done in matlab or any other software?
This forum is not a Give Me the Code service!!!...
Having said that, here is precisely what you requested, with the code profusely commented and plotting the image processing steps :).....
%% Processing
% Reading Image
X0=imread('Pentode 10kHz.gif');
X1=512*(X0([21:219],[15:263]));
imshow(512*(1-X1));
X2=X1;
% Removing Division Dotted Lines and Axis
X2(5:5:end,:)=[];
X2(:,5:5:end)=[];
imshow(512*(1-X2));
%% Evaluating
% Obtain Maximums per Points
[~,i0]=max(X2);
% Setting the Proper Scaling
[lx,lt]=size(X2);
xmax=8*2; %2V per division, 8 divisions
tmax=10*20e-6; % 20us per division, 10 divisions
% Applying Time Scaling
t0=(1:lt)'/lt*tmax; % 200us total
% Remove Spurious Points
t0=t0(i0>1);
% Applying Volt Scaling
x0=xmax/2-i0(i0>1)'/lx*xmax;
%% Signal
plot(t0,x0)
xlabel('Time [s]');
ylabel('Volts [V]')
%% Fourier Transform
ln=length(x0);
h=fftshift(fft(x0))/1000; % f in kHz
h0=abs(h);p0=phase(h);
fmax=1/tmax*ln/2/1000; % F in kHz
f1=(0:(ln/2+1))'/ln*fmax;
h1=[h0(ln/2);2*h0(ln/2:end)];
plot(f1,h1)
axis([0 100 0 1])
xlabel('Frequency [kHz]')
ylabel('Spectral Magnitude [x/kHz]')
Check finally the Fourier Transform. Note the proper scales, and the single point around 10kHz, which is the correct value. Note that, because having only two periods of the signal, the FT is not the best method for confirming the frequency and yes, the period in this case is.
Greetings.............
I have a code to find the pdf's approximation of a vector based on the formula for kernel estimation:
I implemented this formula in the code below (see previous question). However, that code takes long time to run (two loops are used). Could you see the below code and help me to make it faster?
This is the code:
function pdf_est=KDE()
close all;
%%Random values of 20 pixels, range=[1 256]
data=randi([1 256],1,20)-1; %// changed: "-1"
%% Estimate histogram%%%%%
pdf_est=zeros(1,256);
z=256;
for i=0:z-1 %// changed_ subtracted 1
for j=1:length(data)
pdf_est(i+1)=pdf_est(i+1)+Gaussian(i-data(j)); %// changed: "+1" (twice)
end
end
%% Plot real histogram 1 to 256; binsize=1;
hold on
plot(0:255, imhist(uint8(data))./length(data),'r'); %// changed: explicit x axis
%% Plot histogram estimation
plot(0:255, pdf_est./length(data),'b'); %// changed: explicit x axis
hold off
function K=Gaussian(x)
sigma=1; %// change? Set as desired
K=1./(sqrt(2*pi)*sigma)*exp(-x^2./(2*sigma^2));
You can get rid of both of those nasty nested loops and then use the hardcoded sigma to have a mega-reduced vectorized solution -
pdf_est = sum(1./(sqrt(2*pi))*exp(-bsxfun(#minus,[0:z-1]',data).^2/2),2)
Or if you would like to have the flexibility to have sigma around, use this -
sum(1./(sqrt(2*pi)*sigma)*exp(-bsxfun(#minus,[0:z-1]',data).^2/(2*sigma^2)),2)
That's all really there is!
Quick tests put this to speedup the original code by 10x!
Hello i am having some problems understading basic plotting in matlab.
I can understand why you would use a for loop when plotting data?
Can anybody explain this to me?
I am making a simple linear plot. Is there any reason this should be inside a loop
If you are making a simple plot there is virtually no reason to use a loop.
If you check doc plot you will find that plot can take some vectors as input, or even matrices for more interesting situations.
Example:
x=0:0.01:1;
y=sin(x);
plot(x,y)
No there is no need in Matlab to use a for loop for plotting. If you are looking for a simple linear plot your code could look like this:
x=1:100;
y=3*x+4;
plot(x,y)
As you see there is no for loop needed. Same goes for nearly all plots and visualization.
A possible reason to use a for loop to plot thing may be having several data to plot in a single matrix. Say you have two matrix Ax (MxN) and Ay (MxN) where N the length of each data and M is the amount of different data wanted to plot. For example like in this case N is 201 and M is 3:
% Create Ax and Ay
Ax=meshgrid(0:0.1:20,1:3);
Ay=zeros(size(Ax));
% Sinusoidals with different frequencies
for k=1:3
Ay(k,:)=sin(k.*Ax(k,:));
end
% create colours
colorVec = hsv(3);
% Plot
hold on
for k=1:3
plot(Ax(k,:),Ay(k,:),'Color',colorVec(k,:))
end
hold off
You get:
I'm using pwelch to plot a power spectral density. I want to use the format
pwelch=(x,window,noverlap,nfft,fs,'onesided')
but with a log scale on the x axis.
I've also tried
[P,F]=(x,window,noverlap,nfft,fs);
plot(F,P)
but it doesn't give the same resulting plot as above. Therefore,
semilogx(F,P)
isn't a good solution.
OK, so to start, I've never heard of this function or this method. However, I was able to generate the same plot that the function produced using output arguments instead. I ran the example from the help text.
EXAMPLE:
Fs = 1000; t = 0:1/Fs:.296;
x = cos(2*pi*t*200)+randn(size(t)); % A cosine of 200Hz plus noise
pwelch(x,[],[],[],Fs,'twosided'); % Uses default window, overlap & NFFT.
That produces this plot:
I then did: plot(bar,10*log10(foo)); grid on; to produce the linear version (same exact plot, minus labels):
or
semilogx(bar,10*log10(foo)); grid on; for the log scale on the x-axis.
I don't like that the x-scale is sampled linearly but displayed logarithmically (that's a word right?), but it seems to look ok.
Good enough?