How to reduce for loops in a moving window based operation? - matlab

How to reduce for loops in a moving window based operation? I'm using a 15x15 window across two images and performing multiplication to get average value per pixel.
[ma,na]=size(g);
z= (win1 -1)/2;%centre of window
ini=z+1;
for i= ini :(ma-z)
for j= ini:(na-z)
for a= (i-z):(i+z)
for b=(j-z):(j+z)
W(pp,qq)= g(a, b);%window on image
Es(pp,qq)=edg(a,b);%window on edge
qq=qq+1;
end
qq=1;
pp=pp+1;
end
pp=1;
E(i,j)=sum(sum(W.*Es))/sum(sum(Es));
end
end

I might have gotten lost in your loops and i can't exactly read the formula (it's a bit fuzzy) but i think this is what you want:
g = rand(5); %sample img1
edg = rand(5); %sample img2
windowsize = 3; %set this to 15 for real images
A = g.*edg; % multiply each element beforehand, corresponds to mu*sigma in formula
B = movsum(movsum(A,windowsize,2),windowsize,1); % get moving window sum of A, corresponds to numerator in formula
C = movsum(movsum(edg,windowsize,2),windowsize,1); % get moving window sum of edg, corresponds to denominator in formula
E = B./C; %hopefully what you wanted
Ps: You need 2016a or newer to run this

Related

moving(sliding) window average to create base line in Matlab

I need to compute the moving window average with a window size of 20 samples.That window should move throughout the data. Example: For the first 20 samples it will calculate the average and the window moves from 0 to and 20 to 21 and calculate the average from 1 to 21 samples and further it should move on.
I have written a code which 'x' variable is loaded with .dat file and a for loop has been written to calculate mean. The code as follows
clear all;
close all;
x= load ('cpp1500.dat');
for i=1:length(x)
s(i)=sum(x(1:i));
r(i)=s(i)./i;
end
plot(x,'R')
hold on;
plot(r)
Please suggest me a method to calculate the moving window average.
Graphs of samples v/s sensor data
In your code you seem to have mixed up some of the indexing. To answer the question (this is not the recommended version, for that refer to the version further below)
clear all;
close all;
x= load ('cpp1500.dat');
wndSize = 20;
for i=1:length(x) - wndSize
s(i)=sum(x(i:i + wndSize));
end
r = s ./ wndSize
plot(x,'R')
hold on;
plot(r)
Matlab usually punishes looping over arrays with high runtimes. You want to perform a 1d-filtering with a box filter of size 20 where Matlab happens to have efficiently implemented functions. This should do the trick:
windowSize = 20;
b = (1/windowSize)*ones(1,windowSize);
a = 1;
r = filter( b, a, x )
Beware that the implementation of filter is slightly different regarding the handling of elements from x that are close than windowSize to the ends of the array. However, it yields a usable sliding average.
See also: https://de.mathworks.com/help/matlab/ref/filter.html?requestedDomain=www.mathworks.com
That's very easy to do. You would just use the index of your for loop to help you compute that for you. Instead of going from 1 to i, you would go from i to i + 19. You will need to make sure that your for loop doesn't go beyond the bounds of the signal though, so you will need to iterate up to length(x) - 19. You also need to divide your signal by 20 before you go to the next window:
clear all;
close all;
x = load ('cpp1500.dat');
for i = 1 : length(x) - 19 % Change
s(i) = sum(x(i : i + 19)); % Change
r(i) = s(i) / 20; % Change
end
plot(x,'R');
hold on;
plot(r)
However, if I can recommend something, don't use a loop here. Use filter and specify the right side coefficients as all 1s while the left coefficient is 20. You also have to be cognizant that there will be a delay because it starts immediately filtering the signal before you have collected 20 samples to get a moving average. Therefore you will need to remove the first 19 samples from the output after you're done:
clear all;
close all;
x = load ('cpp1500.dat');
r = filter(ones(20, 1), 20, x(:));
r = r(20 : end);
plot(x, 'R');
hold on;
plot(r);
Note that I have no idea what structure your loaded vector is, so I've ensured that it's a column vector.
If you R2016a or later, you can use the movmean function to do this.
r = movmean(x, [0 19]);
will take a 20-point moving average starting at the current point and using 19 points to the left. There are other options for the function, depending on how you want to align the window, handle endpoints, tec.

Computing a moving average

I need to compute a moving average over a data series, within a for loop. I have to get the moving average over N=9 days. The array I'm computing in is 4 series of 365 values (M), which itself are mean values of another set of data. I want to plot the mean values of my data with the moving average in one plot.
I googled a bit about moving averages and the "conv" command and found something which i tried implementing in my code.:
hold on
for ii=1:4;
M=mean(C{ii},2)
wts = [1/24;repmat(1/12,11,1);1/24];
Ms=conv(M,wts,'valid')
plot(M)
plot(Ms,'r')
end
hold off
So basically, I compute my mean and plot it with a (wrong) moving average. I picked the "wts" value right off the mathworks site, so that is incorrect. (source: http://www.mathworks.nl/help/econ/moving-average-trend-estimation.html) My problem though, is that I do not understand what this "wts" is. Could anyone explain? If it has something to do with the weights of the values: that is invalid in this case. All values are weighted the same.
And if I am doing this entirely wrong, could I get some help with it?
My sincerest thanks.
There are two more alternatives:
1) filter
From the doc:
You can use filter to find a running average without using a for loop.
This example finds the running average of a 16-element vector, using a
window size of 5.
data = [1:0.2:4]'; %'
windowSize = 5;
filter(ones(1,windowSize)/windowSize,1,data)
2) smooth as part of the Curve Fitting Toolbox (which is available in most cases)
From the doc:
yy = smooth(y) smooths the data in the column vector y using a moving
average filter. Results are returned in the column vector yy. The
default span for the moving average is 5.
%// Create noisy data with outliers:
x = 15*rand(150,1);
y = sin(x) + 0.5*(rand(size(x))-0.5);
y(ceil(length(x)*rand(2,1))) = 3;
%// Smooth the data using the loess and rloess methods with a span of 10%:
yy1 = smooth(x,y,0.1,'loess');
yy2 = smooth(x,y,0.1,'rloess');
In 2016 MATLAB added the movmean function that calculates a moving average:
N = 9;
M_moving_average = movmean(M,N)
Using conv is an excellent way to implement a moving average. In the code you are using, wts is how much you are weighing each value (as you guessed). the sum of that vector should always be equal to one. If you wish to weight each value evenly and do a size N moving filter then you would want to do
N = 7;
wts = ones(N,1)/N;
sum(wts) % result = 1
Using the 'valid' argument in conv will result in having fewer values in Ms than you have in M. Use 'same' if you don't mind the effects of zero padding. If you have the signal processing toolbox you can use cconv if you want to try a circular moving average. Something like
N = 7;
wts = ones(N,1)/N;
cconv(x,wts,N);
should work.
You should read the conv and cconv documentation for more information if you haven't already.
I would use this:
% does moving average on signal x, window size is w
function y = movingAverage(x, w)
k = ones(1, w) / w
y = conv(x, k, 'same');
end
ripped straight from here.
To comment on your current implementation. wts is the weighting vector, which from the Mathworks, is a 13 point average, with special attention on the first and last point of weightings half of the rest.

Multiply an arbitrary number of matrices an arbitrary number of times

I have found several questions/answers for vectorizing and speeding up routines for multiplying a matrix and a vector in a single loop, but I am trying to do something a little more general, namely multiplying an arbitrary number of matrices together, and then performing that operation an arbitrary number of times.
I am writing a general routine for calculating thin-film reflection from an arbitrary number of layers vs optical frequency. For each optical frequency W each layer has an index of refraction N and an associated 2x2 transfer matrix L and 2x2 interface matrix I which depends on the index of refraction and the thickness of the layer. If n is the number of layers, and m is the number of frequencies, then I can vectorize the index into an n x m matrix, but then in order to calculate the reflection at each frequency, I have to do nested loops. Since I am ultimately using this as part of a fitting routine, anything I can do to speed it up would be greatly appreciated.
This should provide a minimum working example:
W = 1260:0.1:1400; %frequency in cm^-1
N = rand(4,numel(W))+1i*rand(4,numel(W)); %dummy complex index of refraction
D = [0 0.1 0.2 0]/1e4; %thicknesses in cm
[n,m] = size(N);
r = zeros(size(W));
for x = 1:m %loop over frequencies
C = eye(2); % first medium is air
for y = 2:n %loop over layers
na = N(y-1,x);
nb = N(y,x);
%I = InterfaceMatrix(na,nb); % calculate the 2x2 interface matrix
I = [1 na*nb;na*nb 1]; % dummy matrix
%L = TransferMatrix(nb) % calculate the 2x2 transfer matrix
L = [exp(-1i*nb*W(x)*D(y)) 0; 0 exp(+1i*nb*W(x)*D(y))]; % dummy matrix
C = C*I*L;
end
a = C(1,1);
c = C(2,1);
r(x) = c/a; % reflectivity, the answer I want.
end
Running this twice for two different polarizations for a three layer (air/stuff/substrate) problem with 2562 frequencies takes 0.952 seconds while solving the exact same problem with the explicit formula (vectorized) for a three layer system takes 0.0265 seconds. The problem is that beyond 3 layers, the explicit formula rapidly becomes intractable and I would have to have a different subroutine for each number of layers while the above is completely general.
Is there hope for vectorizing this code or otherwise speeding it up?
(edited to add that I've left several things out of the code to shorten it, so please don't try to use this to actually calculate reflectivity)
Edit: In order to clarify, I and L are different for each layer and for each frequency, so they change in each loop. Simply taking the exponent will not work. For a real world example, take the simplest case of a soap bubble in air. There are three layers (air/soap/air) and two interfaces. For a given frequency, the full transfer matrix C is:
C = L_air * I_air2soap * L_soap * I_soap2air * L_air;
and I_air2soap ~= I_soap2air. Thus, I start with L_air = eye(2) and then go down successive layers, computing I_(y-1,y) and L_y, multiplying them with the result from the previous loop, and going on until I get to the bottom of the stack. Then I grab the first and third values, take the ratio, and that is the reflectivity at that frequency. Then I move on to the next frequency and do it all again.
I suspect that the answer is going to somehow involve a block-diagonal matrix for each layer as mentioned below.
Not next to a matlab, so that's only a starter,
Instead of the double loop you can write na*nb as Nab=N(1:end-1,:).*N(2:end,:);
The term in the exponent nb*W(x)*D(y) can be written as e=N(2:end,:)*W'*D;
The result of I*L is a 2x2 block matrix that has this form:
M = [1, Nab; Nab, 1]*[e-, 0;0, e+] = [e- , Nab*e+ ; Nab*e- , e+]
with e- as exp(-1i*e), and e+ as exp(1i*e)'
see kron on how to get the block matrix form, to vectorize the propagation C=C*I*L just take M^n
#Lama put me on the right path by suggesting block matrices, but the ultimate answer ended up being more complicated, and so I put it here for posterity. Since the transfer and interface matrix is different for each layer, I leave in the loop over the layers, but construct a large sparse block matrix where each block represents a frequency.
W = 1260:0.1:1400; %frequency in cm^-1
N = rand(4,numel(W))+1i*rand(4,numel(W)); %dummy complex index of refraction
D = [0 0.1 0.2 0]/1e4; %thicknesses in cm
[n,m] = size(N);
r = zeros(size(W));
C = speye(2*m); % first medium is air
even = 2:2:2*m;
odd = 1:2:2*m-1;
for y = 2:n %loop over layers
na = N(y-1,:);
nb = N(y,:);
% get the reflection and transmission coefficients from subroutines as a vector
% of length m, one value for each frequency
%t = Tab(na, nb);
%r = Rab(na, nb);
t = rand(size(W)); % dummy vector for MWE
r = rand(size(W)); % dummy vector for MWE
% create diagonal and off-diagonal elements. each block is [1 r;r 1]/t
Id(even) = 1./t;
Id(odd) = Id(even);
Io(even) = 0;
Io(odd) = r./t;
It = [Io;Id/2].';
I = spdiags(It,[-1 0],2*m,2*m);
I = I + I.';
b = 1i.*(2*pi*D(n).*nb).*W;
B(even) = -b;
B(odd) = b;
L = spdiags(exp(B).',0,2*m,2*m);
C = C*I*L;
end
a = spdiags(C,0);
a = a(odd).';
c = spdiags(C,-1);
c = c(odd).';
r = c./a; % reflectivity, the answer I want.
With the 3 layer system mentioned above, it isn't quite as fast as the explicit formula, but it's close and probably can get a little faster after some profiling. The full version of the original code clocks at 0.97 seconds, the formula at 0.012 seconds and the sparse diagonal version here at 0.065 seconds.

How to create sliding window over signal on matlab

I have a sequence of data. So I want to plot that data inside the sliding windows due to windows length.
Help me please.
Actually data is from mean and variance of frames. So I want to plot that mean and variance inside the sliding windows. Also I can't create sliding windows on Matlab.
My approach would be,
a = randi(100,[1,50]); % My sequence
win_width = 10; %Sliding window width
slide_incr = 1; %Slide for each iteration
numstps = (length(a)-win_width)/slide_incr; %Number of windows
for i = 1:numstps
mean_win(i) = mean(a(i:i+win_width)); %Calculation for each window
end
plot(mean_win)
there may be better ways of doing it..
This is how I've always done it (adapted from code for 2 sliding windows). You can calculate the mean and variance however you'd like.
T = 25; % Window Size
K = size(data,1) - T; % Number of repetitions
for i = 1:K
window = data(i:i+T-1,:);
% Mean and Variance Calculations here
% Plotting here
% call 'drawnow' for incremental plotting (animation)
end
So if I understand you correctly you want to change the x-axis limits of the plot. Use xlim for that, for example:
a=1:10;
plot(a)
xmin = 5;
xmax = 7.6;
xlim([xmin xmax])
or if you want a window of a constant size you can xlim([xmin xmin+window]) etc...

Matlab - moving window, avoiding nested loops

I'm trying to write a "weighted moving window" without nested loops for speed improvement.
I already tried using arrayfun without getting exciting results, but maybe I did it in a wrong way.
The window has a different weight in each position (stored in B) and should be superimposed on a matrix A returning the values of the matrix A that lie inside the window, times the weight of the window in that position (read from B).
Also, the windows can overlap one on the other and in this case the maximum value should be kept.
Finally window's dimension and shift should be parameters of the function.
It looks more difficult that it actually is, so I show you the code that I would like to improve:
A = reshape([1:35],7,5)'; % values matrix
B = [1:3;4:6]; % window s weight matrix
% matrices size
[m n] = size(A);
[a b] = size(B);
% window s parameters
shift = 2; % window s movement at each iteration
zone = 3; % window s size (zone x zone)
% preallocation
C = ones(m,n); % to store the right weight to be applied in each position
% loop through positions and find the best weight when they overlap
for i=1:m
for j=1:n
C(i,j) = max(max(B( max(round((i-zone)/shift)+1,1) : min(ceil(i/shift),a) , max(round((j-zone)/shift)+1,1) : min(ceil(j/shift),b))));
end
end
% find the output of the windows
result = C.*A;
I hope that I made myself clear, but if you need more details please ask.
Thank you in advance for your help!
If you have access to the Image Processing Toolbox, you'll want to check out how to perform sliding neighborhood operations. In particular, I think the function NLFILTER can be used to achieve the result you want:
A = reshape([1:35],7,5)'; %'# Matrix to be filtered
B = [1:3;4:6]; %# Window weights
result = nlfilter(A,[2 3],#(M) max(M(:).*B(:)));
I would use im2col. Assuming your image is j x k, and your window is m x n, you'll get a matrix that is mn x (j-m+1)*(k-n+1). Then, you can just take every other column.
Example Code:
%A = your_image
B = im2col(A, [m n],'sliding');
C = B(:,1:2:end);
And there's your sliding window with "shift 2".
Try filter.
For example, to do a windowed average, over 5 elements:
outdata = filter([ 0.2 0.2 0.2 0.2 0.2 ], 1, indata);