Use function eval( )+ function find() - matlab

%%%%%%%%%%%%% 2 - Old %%%%%%%%%%%%%%%%%%%%
I modified the code as suggested by achieve greater efficiency. I have new errors in the code, not being an expert of access to the cell array
for i = first:N_Files
mat{i} = load(files(i).name);
A{i} = mat{1,i};
x{i} = A{:,i};
ind{i} = find(x{i}>= -0.5 & x{i}<=0.5);
% New error
B{i} = A{ind{i,:},:}; **Index exceeds matrix dimensions.**
xx{i} = B(:,1);
end
%%%%%%%%%%%%% 1 - Old %%%%%%%%%%%%%%%%%%%%
I wrote a routine to access files placed in different folders.
Then build a cell array where are stored file data with the routine:
for i = first:N_Files
mat{i} = load(files(i).name); % 1x3 cell
eval(['A' num2str(i) '= mat{1,i} ;']) % A1,A2,A3 dim : 114336x6 double
end
A = {A1, A2, A3}; cell array 1x3
I have a problem of access to some vectors created on the evaluation of a matrix cell.
for i = first:N_Files
eval(['x' num2str(i) '= A{:,i} ;']) % create x1, x2, x3
% incorrect code
eval(['ind' num2str(i) '= find('x' num2str(i) >= -0.5 & 'x' num2str(i)
<=0.5) ;'])
end
% need this indexing solution
ind1 = find(x1>= -0.5 & x1<=0.5);
ind2 = find(x2>= -0.5 & x2<=0.5);
ind3 = find(x3>= -0.5 & x3<=0.5);
I necessity to have the eval function with the find function,
it's possible?
have a useful solution?
thanks
%%%%%%%%%%%%% Complete Code %%%%%%
This is the code that I had to get.
for i = first:N_Files
A{i} = load(files(i).name); % 1x3 cell
x = A{:,1};
ind{i} = find(x(:,1)>= -0.5 & x(:,1)<=0.5); % This looks cleaner
B{i} = A{i}(ind{i},:); % Correct,first access element i in A then filter.
xx{i} = B{i}(:,1); %1x3 cell **what i really wanted**
yy{i} = B{i}(:,2);
zz{i} = B{i}(:,3);
u{i} = B{i}(:,4); % vettore velocità
v{i} = B{i}(:,5);
w{i} = B{i}(:,6);
c1{i} = [-0.5:0.01:0.5];
c2{i} = [0:0.01:2] ;
[X{i},Z{i}] = meshgrid(c1{i},c2{i});
U{i} = griddata(xx{i},zz{i},u{i},X{i},Z{i});
U{i}(isnan(U{i}))=0; % interpolazione ai bordi
W{i} = griddata(xx{i},zz{i},w{i},X{i},Z{i});
W{i}(isnan(W{i}))=0;
figure
pcolor(X{i},Z{i},(U{i}.^2+W{i}.^2).^0.5);
shading(gca,'interp')
title('Velocità')
colorbar;
axis square
hh = streamslice(X{i},Z{i},U{i},W{i});
set(hh,'color','k');
end

Ok I will try to help you as good as I can. First, if you have a cell that have the dimension 1xN, you do only need one index: A{i} = mat{i}. However, you then see that A==mat, which means that both are not needed. On the next row, you do x{i} = A{:,i} which seems to be the same as you do on the former row => x==mat. This means that we can remove two rows. Then I guess that your goal is to find the indices which have an absolute value smaller than 0.5 for each file and store each in a cell, right? Then to the error: By doing A{ind{i,:},:} you are actually sub-referencing A itself and not each element of A. The size of A is 1*nFiles. What you are trying to do is indeed this: B{i} = A{i}(ind{i}).
So if this is not what you want please comment. Otherwise, remove redundant variables. And make sure that you do not mix up cell with matrix. You use a cell as a container for arrays: a{n} refers to the array in cell element n and a{n}(m) refers to matrix element m in the array located in cell element n. Good luck!
for i = first:N_Files
mat{i} = load(files(i).name);
%A{i} = mat{1,i}; % not needed mat==x
%x{i} = A{:,i}; % not needed x==A
x = mat; % Fix this later, I do not want to change any variable names.
% ind{i} = find(x{i}>= -0.5 & x{i}<=0.5);
ind{i} = find(abs(x{i})<=0.5); % This looks cleaner
%B{i} = A{ind{i,:},:}; **Index exceeds matrix dimensions.**
B{i} = A{i}(ind{i}); % Correct,first access element i in A then filter.
xx{i} = B(:,1); %I do not know what you tries to do but probably
%xx{i}=B{i} => x==B so one variable is redundant.
end

Related

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

using Matlab to reshape a 4d matrix into a cell array of vectors

I have a 4D matrix (dims - x,y,z,t). I want to reshape it to a 1D cell array of length x*y*z where each element is a long vector of size t which captures all elements at each volume location (x,y,z). After that I need to reshape it back.
I thought of looping over the array to do it since I can't really find a built in function to do it.
Any insights will be super helpful! Thanks!
See if this is that you want:
x = randn(2,3,4,5); % example data
x = reshape(x, [], size(x,4)); % collapse first three dimensions
x = mat2cell(x, ones(1,size(x,1)), size(x,2)); % split first dimension into cells
Luis's answer is great for being semi-vectorized (mat2cell uses a loop). If what you desire is a cell array of size x*y*z where each element is t long, it's possible to use a loop over each volume location and extract the t elements that "temporally" occupy this spot in 4D. Make sure you squeeze out any singleton dimensions to get the resulting vector. This is something to consider if you wanted to go with the loop approach. Assuming your matrix is called A, try the following:
B = cell(size(A,1)*size(A,2)*size(A,3), 1);
count = 1;
for ii = 1 : size(A,1)
for jj = 1 : size(A,2)
for kk = 1 : size(A,3)
B{count} = squeeze(A(ii,jj,kk,:));
count = count + 1;
end
end
end
To get this back into a 4D matrix form, you'd just apply the same logic but in reverse:
Ar = zeros(size(A));
count = 1;
for ii = 1 : size(A,1)
for jj = 1 : size(A,2)
for kk = 1 : size(A,3)
Ar(ii,jj,kk,:) = B{count};
count = count + 1;
end
end
end
Like Luis' solution, but simpler and more complete:
% Transform to cell
x = randn(2,3,4,5); % example data
y = reshape(x, [], size(x,4));
z = num2cell(y,2);
% transform back
x = reshape(cat(1,z{:}), size(x));

Assignment error "the number of elements in B and I must be the same." while trying to calculate centroid in Matlab

Error: In an assignment A(I) = B, the number of elements in B and I must be the same.
Error in ==> test at 22 Centroid(i)=k(i).Centroid;
test.m
I=imread('1_1.jpg');
I=rgb2gray(I);
I2 = Thresholding(I);
cc = bwconncomp(I2,8);
n = cc.NumObjects;
Area = zeros(n,1);
Centroid = zeros(n,1);
Perimeter = zeros(n,1);
MajorAxis = zeros(n,1);
MinorAxis = zeros(n,1);
k = regionprops(cc, 'Area','Centroid','Perimeter','MajorAxisLength', 'MinorAxisLength');
for i=1:n
Area(i) = k(i).Area;
Centroid(i)=k(i).Centroid;
Perimeter(i) = k(i).Perimeter;
MajorAxis(i) = k(i).MajorAxisLength(i);
MinorAxis(i) = k(i).MinorAxisLength(i);
end
handdata(1,1) = mean(Area);
handdata(2,1) = mean(Centroid);
handdata(3,1) = mean(Perimeter);
handdata(4,1) = mean(MajorAxis);
handdata(5,1) = mean(MinorAxis);
Thresholding.m
function im = Thresholding(I);
[r,c] = size(I);
im = zeros(r,c);
for i=1:r
for j=1:c
if I(i,j)>105
im(i,j)=1;
end
end
end
im = bwareaopen(im,5);
im = imfill(im, 'holes');
end
Solution
The Centroid field is a vector of size 1x2 (for holding the y and x coordinates).
Therefore, you'll need to modify your code accordingly:
Outside the for loop:
Centroid = zeros(n,2); %The centroids array should be nx2, to contain both x and y positions
Inside the for loop:
Centroid(i,:)=k(i).Centroid; %fill the corresponded i'th row
MajorAxis(i) = k(i).MajorAxisLength; %remove the coordinate from MajorAxisLength and MinorAxisLength fields
MinorAxis(i) = k(i).MinorAxisLength;
From that reason, you'll also need to modify the following line of code, since mean(Centroid) is a vector of size 1x2. handdata will be a vector of 4x1, and the centroid mean will be placed in a different variable, called centroidData.
handdata(1,1) = mean(Area);
handdata(2,1) = mean(Perimeter);
handdata(3,1) = mean(MajorAxis);
handdata(4,1) = mean(MinorAxis);
centroidData = mean(Centroid);
Two more suggestions
1.In the Thresholding function, instead of using double for loop you can simply write
im = I > 105;
2.in the main for loop (located in the main script) use ii instead of i as a variable name for your counter.

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.