MATLAB: Converting from for loop, to no loop - matlab

I have a function provided by the instructor, which is not available for us to see the inner workings.
This function basically integrates whatever you input it.
Here is our loop:
for i = 1:length(time)-1
intdefValues(i) = intdef(t, r, time(i), time(i+1));
end
We are trying to do this:
intdefValues = intdef(t, r, time(1:end-1), time(2:end));
Are we wrong? Is there anyway that the function doesn't support vectors?
Syntax for the function in case you are curious;
intdef(t, x, a, b)
Where t = time vector, x = function, and a,b are the start / end parameters.

tic(); % ------------
% TEST ALSO
% PERFORMANCE:
% prepare another "vectorOfTimeENDs",
% aligned with your loop-ing strategy
% for a fully vectorised call
startsVector = time(1:end-1); % just a syntax-sugar, may omit for speed == time(1:end-1)
endsVector = time(2:end); % this one is important
intdefValues = intdef( tVector, rFunction, startsVector, endsVector );
toc()
% ---------------------------------------------------------------------------
tic(); % COMPARE WITH THE ORIGINAL FOR/LOOP:
for i = 1:length(time)-1
intdefValues(i) = intdef( tVector, rFunction, time(i), time(i+1) );
end
toc()

Related

MATLAB: Entering multiple input values for the x-axis in a graph

The MATLAB program below is a function that references specific input values for S, E, r, sigma, and tau.
function [C, Cdelta, P, Pdelta] = ch08(S,E,r,sigma,tau)
% Input arguments: S = asset price at time t
% E = Exercise price
% r = interest rate
% sigma = volatility
% tau = time to expiry (T-t)
%
% Output arguments: C = call value, Cdelta = delta value of call
% P = Put value, Pdelta = delta value of put
%
% function [C, Cdelta, P, Pdelta] = ch08(S,E,r,sigma,tau)
if tau > 0
d1 = (log(S/E) + (r + 0.5*sigma^2)*(tau))/(sigma*sqrt(tau));
d2 = d1 - sigma*sqrt(tau);
N1 = 0.5*(1+erf(d1/sqrt(2)));
N2 = 0.5*(1+erf(d2/sqrt(2)));
C = S*N1-E*exp(-r*(tau))*N2;
Cdelta = N1;
P = C + E*exp(-r*tau) - S;
Pdelta = Cdelta - 1;
title('Graph of Call Value vs. Time to Expiry')
xlabel('Time to expiry')
ylabel('Call Value')
plot (tau,C)
else
C = max(S-E,0);
Cdelta = 0.5*(sign(S-E) + 1);
P = max(E-S,0);
Pdelta = Cdelta - 1;
title('Graph of Call Value vs. Time to Expiry')
xlabel('Time to expiry')
ylabel('Call Value')
plot (tau,C)
end
After running
ch08(3,2.5,0.03,0.25,1)
The following output is produced
After running the function again,
ch08(3,2.5,0.03,0.25,1)
hold on
ch08(3,2.5,0.03,0.25,0.9)
Two data points are produced
After manually typing decreasing tau values,
ch08(3,2.5,0.03,0.25,1)
hold on
ch08(3,2.5,0.03,0.25,0.9)
hold on
ch08(3,2.5,0.03,0.25,0.8)
hold on
ch08(3,2.5,0.03,0.25,0.7)
hold on
ch08(3,2.5,0.03,0.25,0.6)
hold on
ch08(3,2.5,0.03,0.25,0.5)
hold on
ch08(3,2.5,0.03,0.25,0.4)
hold on
ch08(3,2.5,0.03,0.25,0.3)
hold on
ch08(3,2.5,0.03,0.25,0.2)
hold on
ch08(3,2.5,0.03,0.25,0.1)
The graph will produce a bunch of data points,
Is there a way to automate the tau values entered in ch08(S,E,r,sigma,tau) so that the user doesn't have to type all of the input in?
As I suggested in comments, you need to use a for loop. You can create an array with values of tau that you want to use, and call your function with a different element from that array in each loop iteration:
figure, hold on
tau = 10.^(0:-1:-6);
for ii = 1:length(tau)
ch08(3,2.5,0.03,0.25,tau(ii))
end
However, a better solution would be to not plot within your ch08 function, and return the value C as you did in your first version of your question. Then you can do this:
tau = 10.^(0:-1:-6);
C = zeros(size(tau));
for ii = 1:length(tau)
C(ii) = ch08(3,2.5,0.03,0.25,tau(ii));
end
plot(tau,C,'.');
This would allow you to change your plot as you please, for example drawing a line through your points.
PS: Note that you only need to give hold on once. It sets a flag in the figure window that is not cleared until you do hold off or clf.

How can I vectorize the loops of this function in Octave?

I want to be able to vectorize the for-loops of this function to then be able to parallelize it in octave. Can these for-loops be vectorized? Thank you very much in advance!
I attach the code of the function commenting on the start and end of each for-loop and if-else.
function [par]=pem_v(tsm,pr)
% tsm and pr are arrays of N by n. % par is an array of N by 8
tss=[27:0.5:32];
tc=[20:0.01:29];
N=size(tsm,1);
% main-loop
for ii=1:N
% I extract the rows in each loop because each one represents a sample
sst=tsm(ii,:); sst=sst'; %then I convert each sample to column vectors
pre=pr(ii,:); pre=pre';
% main-condition
if isnan(nanmean(sst))==1;
par(ii,1:8)=NaN;
else
% first sub-loop
for k=1:length(tss);
idxx=find(sst>=tss(k)-0.25 & sst<=tss(k)+0.25);
out(k)=prctile(pre(idxx),90);
end
% end first sub-loop
tp90=tss(find(max(out)==out));
% second sub-loop
for j=1:length(tc)
cond1=find(sst>=tc(j) & sst<=tp90);
cond2=find(sst>=tp90);
pem=zeros(length(sst),1);
A=[sst(cond1),ones(length(cond1),1)];
B=regress(pre(cond1),A);
pt90=B(1)*(tp90-tc(j));
AA=[(sst(cond2)-tp90)];
BB=regress(pre(cond2)-pt90,AA);
pem(cond1)=max(0,B(1)*(sst(cond1)-tc(j)));
pem(cond2)=max(0,(BB(1)*(sst(cond2)-tp90))+pt90);
clear A B AA BB;
E(j)=sqrt(nansum((pem-pre).^2)/length(pre));
clear pem;
end
% end second sub-loop
tcc=tc(find(E==min(E)));
% sub-condition
if(isempty(tcc)==1);
par(ii,1:9)=NaN;
else
cond1=find(sst>=tcc & sst<=tp90);
cond2=find(sst>=tp90);
pem=zeros(length(sst),1);
A=[sst(cond1),ones(length(cond1),1)];
B=regress(pre(cond1),A);
pt90=B(1)*(tp90-tcc);
AA=[sst(cond2)-tp90];
BB=regress(pre(cond2)-pt90,AA);
pem(cond1)=max(0,B(1)*(sst(cond1)-tcc));
pem(cond2)=max(0,(BB(1)*(sst(cond2)-tp90))+pt90);
RMSE=sqrt(nansum((pem-pre).^2)/length(pre));
% outputs
par(ii,1)=tcc;
par(ii,2)=tp90;
par(ii,3)=B(1);
par(ii,4)=BB(1);
par(ii,5)=RMSE;
par(ii,6)=nanmean(sst);
par(ii,7)=nanmean(pre);
par(ii,8)=nanmean(pem);
end
% end sub-condition
clear pem pre sst RMSE BB B tp90 tcc
end
% end main-condition
end
% end main-loop
You haven't given any example inputs, so I've created some like so:
N = 5; n = 800;
tsm = rand(N,n)*5+27; pr = rand(N,n);
Then, before you even consider vectorising your code, you should keep 4 things in mind...
Avoid calulating the same thing (like the size of a vector) every loop, instead do it before looping
Pre-allocate arrays where possible (declare them as zeros/NaNs etc)
Don't use find to convert logical indices into linear indices, there is no need and it will slow down your code
Don't repeatedly use clear, especially many times within loops. It is slow! Instead, use pre-allocation to ensure the variables are as you expect each loop.
Using the above random inputs, and taking account of these 4 things, the below code is ~65% quicker than your code. Note: this is without even doing any vectorising!
function [par]=pem_v(tsm,pr)
% tsm and pr are arrays of N by n.
% par is an array of N by 8
tss=[27:0.5:32];
tc=[20:0.01:29];
N=size(tsm,1);
% Transpose once here instead of every loop
tsm = tsm';
pr = pr';
% Pre-allocate memory for output 'par'
par = NaN(N, 8);
% Don't compute these every loop, do it before the loop.
% numel simpler than length for vectors, and size is clearer still
ntss = numel(tss);
nsst = size(tsm,1);
ntc = numel(tc);
npr = size(pr, 1);
for ii=1:N
% Extract the columns in each loop because each one represents a sample
sst=tsm(:,ii);
pre=pr(:,ii);
% main-condition. Previously isnan(nanmean(sst))==1, but that's only true if all(isnan(sst))
% We don't need to assign par(ii,1:8)=NaN since we initialised par to a matrix of NaNs
if ~all(isnan(sst));
% first sub-loop, initialise 'out' first
out = zeros(1, ntss);
for k=1:ntss;
% Don't use FIND on an indexing vector. Use the logical index raw, it's quicker
idxx = (sst>=tss(k)-0.25 & sst<=tss(k)+0.25);
% We need a check that some values of idxx are true, otherwise prctile will error.
if nnz(idxx) > 0
out(k) = prctile(pre(idxx), 90);
end
end
% Again, no need for FIND, just reduces speed. This is a theme...
tp90=tss(max(out)==out);
for jj=1:ntc
cond1 = (sst>=tc(jj) & sst<=tp90);
cond2 = (sst>=tp90);
% Use nnz (numer of non-zero) instead of length, since cond1 is now a logical vector of all elements
A = [sst(cond1),ones(nnz(cond1),1)];
B = regress(pre(cond1), A);
pt90 = B(1)*(tp90-tc(jj));
AA = [(sst(cond2)-tp90)];
BB = regress(pre(cond2)-pt90,AA);
pem=zeros(nsst,1);
pem(cond1) = max(0, B(1)*(sst(cond1)-tc(jj)));
pem(cond2) = max(0, (BB(1)*(sst(cond2)-tp90))+pt90);
E(jj) = sqrt(nansum((pem-pre).^2)/npr);
end
tcc = tc(E==min(E));
if ~isempty(tcc);
cond1 = (sst>=tcc & sst<=tp90);
cond2 = (sst>=tp90);
A = [sst(cond1),ones(nnz(cond1),1)];
B = regress(pre(cond1),A);
pt90 = B(1)*(tp90-tcc);
AA = [sst(cond2)-tp90];
BB = regress(pre(cond2)-pt90,AA);
pem = zeros(length(sst),1);
pem(cond1) = max(0, B(1)*(sst(cond1)-tcc));
pem(cond2) = max(0, (BB(1)*(sst(cond2)-tp90))+pt90);
RMSE = sqrt(nansum((pem-pre).^2)/npr);
% Outputs, which we might as well assign all at once!
par(ii,:)=[tcc, tp90, B(1), BB(1), RMSE, ...
nanmean(sst), nanmean(pre), nanmean(pem)];
end
end
end

parfor doesn't consider information about vectors which are used in it

This is a part of my code in Matlab. I tried to make it parallel but there is an error:
The variable gax in a parfor cannot be classified.
I know why the error occurs. because I should tell Matlab that v is an incresing vector which doesn't contain repeated elements. Could anyone help me to use this information to parallelize the code?
v=[1,3,6,8];
ggx=5.*ones(15,14);
gax=ones(15,14);
for m=v
if m > 1
parfor j=1:m-1
gax(j,m-1) = ggx(j,m-1);
end
end
if m<nn
parfor jo=m+1:15
gax(jo,m) = ggx(jo,m);
end
end
end
Optimizing a code should be closely related to its purpose, especially when you use parfor. The code you wrote in the question can be written in a much more efficient way, and definitely, do not need to be parallelized.
However, I understand that you tried to simplify the problem, just to get the idea of how to slice your variables, so here is a fixed version the can run with parfor. But this is surely not the way to write this code:
v = [1,3,6,8];
ggx = 5.*ones(15,14);
gax = ones(15,14);
nn = 5;
for m = v
if m > 1
temp_end = m-1;
temp = ggx(:,temp_end);
parfor ja = 1:temp_end
gax(ja,temp_end) = temp(ja);
end
end
if m < nn
temp = ggx(:,m);
parfor jo = m+1:15
gax(jo,m) = temp(jo);
end
end
end
A vectorized implementation will look like this:
v = [1,3,6,8];
ggx = 5.*ones(15,14);
gax = ones(15,14);
nn = 5;
m1 = v>1; % first condition with logical indexing
temp = v(m1)-1; % get the values from v
r = ones(1,sum(temp)); % generate a vector of indicies
r(cumsum(temp)) = -temp+1; % place the reseting locations
r = cumsum(r); % calculate the indecies
r(cumsum(temp)) = temp; % place the ending points
c = repelem(temp,temp); % create an indecies vector for the columns
inds1 = sub2ind(size(gax),r,c); % convert the indecies to linear
mnn = v<nn; % second condition with logical indexing
temp = v(mnn)+1; % get the values from v
r_max = size(gax,1); % get the height of gax
r_count = r_max-temp+1; % calculate no. of rows per value in v
r = ones(1,sum(r_count)); % generate a vector of indicies
r([1 r_count(1:end-1)+1]) = temp; % set the t indicies
r(cumsum(r_count)+1) = -(r_count-temp)+1; % place the reseting locations
r = cumsum(r(1:end-1)); % calculate the indecies
c = repelem(temp-1,r_count); % create an indecies vector for the columns
inds2 = sub2ind(size(gax),r,c); % convert the indecies to linear
gax([inds1 inds2]) = ggx([inds1 inds2]); % assgin the relevant values
This is indeed quite complicated, and not always necessary. A good thing to remember, though, is that nested for loop are much slower than a single loop, so in some cases (depend on the size of the output), this will may be the fastest solution:
for m = v
if m > 1
gax(1:m-1,m-1) = ggx(1:m-1,m-1);
end
if m<nn
gax(m+1:15,m) = ggx(m+1:15,m);
end
end

AABB Intersections with Space Partitions, A Sample Code Performance and Reliability

I have originally written the following Matlab code to find intersection between a set of Axes Aligned Bounding Boxes (AABB) and space partitions (here 8 partitions). I believe it is readable by itself, moreover, I have added some comments for even more clarity.
function [A,B] = AABBPart(bbx,it) % bbx: aabb, it: iteration
global F
IT = it+1;
n = size(bbx,1);
F = cell(n,it);
A = Part([min(bbx(:,1:3)),max(bbx(:,4:6))],it,0); % recursive partitioning
B = F; % matlab does not allow
function s = Part(bx,it,J) % output to be global
s = {};
if it < 1; return; end
s = cell(8,1);
p = bx(1:3);
q = bx(4:6);
h = 0.5*(p+q);
prt = [p,h;... % 8 sub-parts (octa)
h(1),p(2:3),q(1),h(2:3);...
p(1),h(2),p(3),h(1),q(2),h(3);...
h(1:2),p(3),q(1:2),h(3);...
p(1:2),h(1),h(1:2),q(3);...
h(1),p(2),h(3),q(1),h(2),q(3);...
p(1),h(2:3),h(1),q(2:3);...
h,q];
for j=1:8 % check for each sub-part
k = 0;
t = zeros(0,1);
for i=1:n
if all(bbx(i,1:3) <= prt(j,4:6)) && ... % interscetion test for
all(prt(j,1:3) <= bbx(i,4:6)) % every aabb and sub-parts
k = k+1;
t(k) = i;
end
end
if ~isempty(t)
s{j,1} = [t; Part(prt(j,:),it-1,j)]; % recursive call
for i=1:numel(t) % collecting the results
if isempty(F{t(i),IT-it})
F{t(i),IT-it} = [-J,j];
else
F{t(i),IT-it} = [F{t(i),IT-it}; [-J,j]];
end
end
end
end
end
end
Concerns:
In my tests, it seems that probably few intersections are missing, say, 10 or so for 1000 or more setup. So I would be glad if you could help to find out any problematic parts in the code.
I am also concerned about using global F. I prefer to get rid of it.
Any other better solution in terms of speed, will be loved.
Note that the code is complete. And you can easily try it by some following setup.
n = 10000; % in the original application, n would be millions
bbx = rand(n,6);
it = 3;
[A,B] = AABBPart(bbx,it);

change filter(B,A, X) in matlab and Out of memoy Error

this post is related to my previous question : image processing in matlab
as I have uploaded my algorithme there.
the think is that I am trying to change the filtering part of the code.
in matlab filter.m function can accpet filter(B, A, my pixels evolutions in Time) and it return me the filtered values.
but at the moment I have to pass the the whole time series together.
but the problem is that now I want to change the code in a way instead of passing the whole timeseries into the filter, I want to pass one value at a time, but I want filter function treat the value like the nth value not the first valu.
I have created a sudo code, as I am injecting one picture into the code, but I dont know how can change the filtering part., any body has any idea??
clear all
j=1;
for i=0:3000
str = num2str(i);
str1 = strcat(str,'.mat');
load(str1);
D{j}=A(20:200,130:230);
j=j+1;
end
N=5;
w = [0.00000002 0.05;0.05 0.1;0.1 0.15;0.15 0.20;
0.20 0.25;0.25 0.30;0.30 0.35;0.35 0.40;
0.40 0.45;0.45 0.50;0.50 0.55;0.55 0.60;
0.60 0.65;0.65 0.70;0.70 0.75;0.75 0.80;
0.80 0.85;0.85 0.90;0.90 0.95;0.95 0.99999999];
for ind=1:20
wn = w(ind,:);
[b,a] = butter(N,wn);
bCoeff(ind,:)=b;
aCoeff(ind,:)=a;
end
ts=[];
sumLastImages=[];
for k=1:10 %number of images
for bands=1:20 %number of bands
for i=1:10 %image h
for j=1:10 %image w
pixelValue = D{k}(i,j);
% reflectivity elimination
% for the current pixel, have the summation of the same position from before
% images and create a mean value base on the temporal values
sumLastImages(i,j)=pixelValue+sumLastImages(i,j);
meanValue = sumLastImages(i,j)/k;
if(meanValue==0)
filteredimages{bands}(i,j)=0;
continue;
else
pixel_calculated_meanvalue = pixelValue/meanValue;
end
% filter part that need to be changed, and because we are adding
% one value then the reutrn of the filter is one too
ts_f = filter(bCoeff(bands,:), aCoeff(bands,:), ...
pixel_calculated_meanvalue);
filteredimages{bands}(i,j)=ts_f;
end
end
finalImagesSummation{bands}(:,:) = ...
(filteredimages{bands}(:,:)^2)+finalImagesSummation{bands}(:,:);
finalImages{bands}(:,:)=finalImagesSummation/k;
end
end
EDIT
I managed to change the code like this, which now I load the fist 200 images, and after that I am able to inject the images one by one into the filter, but now the problem is that I am getting "Out of memory. Type HELP MEMORY for your options." error for big
images.
here is my code any idea to efficent the code :
%%
cd('D:\MatlabTest\06-06-Lentils');
clear all
%%
N=5;
W = [0.0 0.10;0.10 0.20;0.20 0.30;0.30 0.40;
0.40 0.50;0.50 0.60 ;0.60 0.70;0.70 0.80 ;
0.80 0.90;0.90 1.0];
[bCoeff{1},aCoeff{1}] = butter(N,0.1,'Low');
for ind=2:9
wn = W(ind,:);
[b,a] = butter(N,wn);
bCoeff{ind}=b;
aCoeff{ind}=a;
end
[bCoeff{10},aCoeff{10}] = butter(N,0.9,'high');
%%
j=1;
D = zeros(200,380,320);
T = 200;
K = 0:399;
l = T+1;
Yout = cell(1,10);
figure;
for i = 1:length(K)-200
disp(i)
if i == 1
for j = 1:T
str = int2str(K(1)+j-1);
str1 = strcat(str,'.mat');
load(str1);
D(j,1:380,1:320) = A;
end
else
str = int2str(l);
str1 = strcat(str,'.mat');
load(str1);
temp = D(2:200,1:380,1:320) ;
temp(200,1:380,1:320) = A;
D = temp;
clear temp
l = l +1;
end
for p = 1:380
for q = 1:320
x = D(:,p,q) - mean(D(:,p,q));
for k = 1:10
temp = filter(bCoeff{k},aCoeff{k},x);
if i == 1
Yout{k}(p,q) = mean(temp);
else
Yout{k}(p,q) = (Yout{k}(p,q) + mean(temp))/2;
end
end
end
end
for k = 1:10
subplot(5,2,k)
subimage(Yout{k}*1000,[0 255]);
color bar
colormap jet
end
pause(0.01);
end
disp('Done Loading...')
No need to rewrite the filter function, there is a simple solution!
If you want to feed filter with one sample at a time, you need to pass the state parameters as well so that each input sample is processed depending on its predecessor. After filtering, the new state is actually returned as a second parameter, so that most of the work is already done by MATLAB for you. This is good news!
For the sake of readability, allow me to temporarily replace your variable names with simple letters:
a = aCoeff(bands, :);
b = bCoeff(bands, :);
x = pixel_calculated_meanvalue;
ts_f is represented by y.
And so, this:
y = filter(b, a, x);
is actually equivalent to this:
N = numel(x);
y = zeros(N, 1); %# Preallocate memory for output
z = zeros(max(length(a), length(b)) - 1, 1); %# This is the initial filter state
for i = 1:N
[y(i), z] = filter(b, a, x(i), z);
end
You can check for yourself that the result is the same!
For your example, the code would be:
N = numel(pixel_calculated_meanvalue);
ts_f = zeros(N, 1);
z = zeros(max(length(aCoeff(bands, :)), length(bCoeff(bands, :))) - 1, 1);
for i = 1:N
[ts_f(i), z] = filter(bCoeff(bands, :), aCoeff(bands, :), ...
pixel_calculated_meanvalue(i), z);
end
With this method you can process one input sample at a time, just make sure you store the last filter state after every filter call. If you plan on using multiple filters, you'll have to store a state vector per filter!
Overview
If all you want to have is an IIR filter, which you can feed incrementally, i.e. where you do not have to supply the full vector at once, you can simply implement your own function and use either persistent variables.
Simple approach
The code snippet below defines a function myFilter, which makes use of persistent
variables, which you can control with the following commands:
init: set up the IIR coefficients
getA: returns the A coefficients, i.e. the outputweights
getB: returns the B coefficients, i.e. the input weights
getX: returns the stored input data x[0], x[1], ... x[M]
getY: returns the output data y[0], y[1], ... y[N]
getCurrentY: returns the last output data y[N]
Here is the function:
function result = myFilter(varargin)
% myFilter A simple IIR filter which can be fed incrementally.
%
% The filter is controlled with the following commands:
% myFilter('init', B, A)
% Initializes the coefficients B and A. B are the weights for the
% input and A for the output.
% myFilter('reset')
% Resets the input and output buffers to zero.
% A = myFilter('getA')
% B = myFilter('getB')
% Returns the filter coefficients A and B, respectively.
% x = myFilter('getX')
% y = myFilter('getY')
% Returns the buffered input and output vectors.
% y = myFilter('getCurrentY')
% Returns the current output value.
% myFilter(x)
% Adds the new value x as input to the filter and updates the
% output.
persistent Bcoeff
persistent Acoeff
persistent x
persistent y
if ischar(varargin{1})
% This is a command.
switch varargin{1}
case 'init'
Bcoeff = varargin{2};
Acoeff = varargin{3};
Bcoeff = Bcoeff / Acoeff(1);
Acoeff = Acoeff / Acoeff(1);
x = zeros(size(Bcoeff));
y = zeros(1, length(Acoeff) - 1);
return
case 'reset'
x = zeros(size(Bcoeff));
y = zeros(1, length(Acoeff) - 1);
return
case 'getA'
result = Acoeff;
return
case 'getB'
result = Bcoeff;
return
case 'getX'
result = x;
return
case 'getY'
result = y;
return
case 'getCurrentY'
result = y(1);
return
otherwise
error('Unknown command');
end
end
% A new value has to be filtered.
xnew = varargin{1};
x = [xnew, x(1:end-1)];
ynew = sum(x .* Bcoeff) - sum(y .* Acoeff(2:end));
y = [ynew, y(1:end-1)];
end
And a usage example:
% Define the filter coefficients. Bcoeff acts on the input, Acoeff on
% the output.
Bcoeff = [4, 5];
Acoeff = [1, 2, 3];
% Initialize the filter.
myFilter('init', Bcoeff, Acoeff);
% Add a value to be filtered.
myFilter(10)
myFilter('getCurrentY')
ans =
40
% Add another one.
myFilter(20)
myFilter('getCurrentY')
ans =
50
% And a third one.
myFilter(30)
myFilter('getCurrentY')
ans =
0
% Compare with the Matlab filter function.
filter(Bcoeff, Acoeff, [10 20 30])
ans =
40 50 0
The drawback of this approach is that it is only possible to have one active filter
simultaneously. This is problematic e.g. in your question, where you have different
filters which are updated in an alternating fashion.
Advanced approach
In order to operate multiple filters simultatenously, you need some way to identify
the filter. The solution I present here works with handles. A handle is simple an
integer. To be more precise, it is actually an index into a persistent array, which
itself holds the filter state, i.e. the filter coefficients and the buffers for the
input and the output.
The calling syntax is a bit more complicated, because you have to pass a handle. However,
it is possible that multiple filters are active simultaneously.
function result = myFilterH(varargin)
% myFilterH A simple IIR filter which can be fed incrementally.
% Operates on a filter handle.
%
% The filter is controlled with the following commands:
% handle = myFilterH('create')
% Creates a new filter handle.
% myFilterH(handle, 'init', B, A)
% Initializes the coefficients B and A. B are the weights for the
% input and A for the output. handle identifies the filter.
% myFilterH(handle, 'reset')
% Resets the input and output buffers to zero.
% A = myFilterH(handle, 'getA')
% B = myFilterH(handle 'getB')
% Returns the filter coefficients A and B, respectively.
% x = myFilterH(handle, 'getX')
% y = myFilterH(handle, 'getY')
% Returns the buffered input and output vectors.
% y = myFilterH(handle, 'getCurrentY')
% Returns the current output value.
% myFilterH(handle, x)
% Adds the new value x as input to the filter and updates the
% output.
persistent handles
if ischar(varargin{1})
if strcmp(varargin{1}, 'create')
if isempty(handles)
handles = struct('x', [], 'y', [], 'A', [], 'B', []);
result = 1;
else
result = length(handles) + 1;
handles(result).x = [];
end
return
else
error('First argument must be a filter handle or ''create''');
end
end
% The first input should be the handles(hIdx).
hIdx = varargin{1};
if hIdx < 0 || hIdx > length(handles)
error('Invalid filter handle')
end
if ischar(varargin{2})
% This is a command.
switch varargin{2}
case 'init'
handles(hIdx).B = varargin{3};
handles(hIdx).A = varargin{4};
handles(hIdx).B = handles(hIdx).B / handles(hIdx).A(1);
handles(hIdx).A = handles(hIdx).A / handles(hIdx).A(1);
handles(hIdx).x = zeros(size(handles(hIdx).B));
handles(hIdx).y = zeros(1, length(handles(hIdx).A) - 1);
return
case 'reset'
handles(hIdx).x = zeros(size(handles(hIdx).B));
handles(hIdx).y = zeros(1, length(handles(hIdx).A) - 1);
return
case 'getA'
result = handles(hIdx).A;
return
case 'getB'
result = handles(hIdx).B;
return
case 'getX'
result = handles(hIdx).x;
return
case 'getY'
result = handles(hIdx).y;
return
case 'getCurrentY'
result = handles(hIdx).y(1);
return
otherwise
error('Unknown command');
end
end
% A new value has to be filtered.
xnew = varargin{2};
handles(hIdx).x = [xnew, handles(hIdx).x(1:end-1)];
ynew = sum(handles(hIdx).x .* handles(hIdx).B) ...
- sum(handles(hIdx).y .* handles(hIdx).A(2:end));
handles(hIdx).y = [ynew, handles(hIdx).y(1:end-1)];
end
And the example:
% Define the filter coefficients.
Bcoeff = [4, 5];
Acoeff = [1, 2, 3];
% Create new filter handles.
fh1 = myFilterH('create');
fh2 = myFilterH('create');
% Initialize the filter handle. Note that reversing Acoeff and Bcoeff creates
% two totally different filters.
myFilterH(fh1, 'init', Bcoeff, Acoeff);
myFilterH(fh2, 'init', Acoeff, Bcoeff);
% Add a value to be filtered.
myFilterH(fh1, 10);
myFilterH(fh2, 10);
[myFilterH(fh1, 'getCurrentY'), myFilterH(fh2, 'getCurrentY')]
ans =
40.0000 2.5000
% Add another one.
myFilterH(fh1, 20);
myFilterH(fh2, 20);
[myFilterH(fh1, 'getCurrentY'), myFilterH(fh2, 'getCurrentY')]
ans =
50.0000 6.8750
% And a third one.
myFilterH(fh1, 30);
myFilterH(fh2, 30);
[myFilterH(fh1, 'getCurrentY'), myFilterH(fh2, 'getCurrentY')]
ans =
0 16.4063
% Compare with the Matlab filter function.
filter(Bcoeff, Acoeff, [10 20 30])
ans =
40 50 0
filter(Acoeff, Bcoeff, [10 20 30])
ans =
2.5000 6.8750 16.4063
Using it for your example
To use the advanced approach in your example, you could first create an array of filters:
fh = [];
for ind = 1:20
wn = w(ind,:);
[b,a] = butter(N,wn);
fh(ind) = myFilterH('create');
myFilterH(fh(ind), 'init', b, a);
end
Later on in the filter part simply add your value to all of the filters. However, for this
you also need to reverse the loops because right now you would need one filter per
band per pixel. If you loop over pixels first and then over bands, you can reuse the filters:
for height = 1:10
for width = 1:10
for bands = 1:20
myFilterH(fh(bands), 'reset');
for k = 1:10
[...]
ts_f = myFilterH(fh(bands), ...
pixel_calculated_meanvalue);
filteredimages{bands}(i,j) = myFilterH(fh(bands), 'getCurrentY');
end
end
end
end
Hope that helps!
If I understand the question correctly, the crux is "How do I return multiple things from a Matlab function?"
You can return multiple things like this:
function [a, b, np, x, y] = filter(ord, a, b, np, x, y)
%code of function here
%make some changes to a, b, np, x, and y
end
If you want to call the filter from another function and catch its return values, you can do this:
function [] = main()
%do some stuff, presumably generate initial values for ord, a, b, np, x, y
[new_a, new_b, new_np, new_x, new_y] = filter(ord, a, b, np, x, y)
end
In short, if you do function [x, y] = myfunc(a, b), then myfunc returns both x and y.