Canonical Way to Aggregate Structures into a Vector - matlab

I feel dumb even having to ask this, it really should be dead simple, but being new to MatLab I'd like to know how a more experienced person would do it.
Simple problem; I need to find some regions in multiple images, correlate them by position, save those regions of interest, and use them later. One way to do that would be to store the regions in a vector.
%% pseudo code
regions = [];
for i = some_vector_of_images
% segment, get mask
% find regions
cc = bwconncomp(mask);
stats = regionprops(cc, 'all');
% correlate against known x/y
% save for later
regions[index++] = stats;
end
% use 'regions'
However, the array declaration is problematic. Its default type is double, so that won't work (can't assign a struct to an element). I've tried struct.empty, but the array is not resizable. I've tried a cell array, but I get a similar error (Conversion to cell from struct is not possible.)
Really, I just need a way to have some collection declared prior to the loop that will hold instances of these structures. Again, pretty noobish question and slightly embarrassed here... please take pity.

See if using struct2cell helps you with this. Give this pseudo-code a try -
regions = cell(num_of_images,1) %// This will be before the loop starts
...
regions[index++] = {struct2cell(stats)} %// Inside the loop
Please not that this a pseudo-code, so square brackets and ++ won't work.
Thus, the complete version of pseudo-code would be -
%%// --- pseudo code
%// Without this pre-allocation you would get the error -
%%// "Conversion to cell from struct is not possible"
regions = cell(numel(some_vector_of_images),1)
for i = some_vector_of_images
% segment, get mask
% find regions
cc = bwconncomp(mask);
stats = regionprops(cc, 'all');
% correlate against known x/y
% save for later
regions(i) = {struct2cell(stats)}
end

You can cast your empty array to a structure array by appending a structure. Replace regions[index++] = stats; with
regions = [regions, stats];
This line will continue to build the array within the loop. This idiom is generally frowned on in MATLAB because a new array needs to be created each loop.
Another method is to preallocate the array with a template structure, using repmat.
stats = some_operations_on(some_vector_of_images(1));
regions = repmat(stats, numel(some_vector_of_images), 1);
and within the loop, assign with
regions(i) = stats;

In this scenario, typically I just don't preallocate at all, or use a cell-cat pattern.
Not initializing
This one doesn't initialize the struct array, but works fine. Make sure i is an index of each element in this case.
for i = 1:numel(some_vector_of_images)
% mask = outcome of some_vector_of_images(i)
cc = bwconncomp(mask);
regions(i) = regionprops(cc, 'all');
end
cell-cat pattern
This one catches results in a cell array, and concatenates all elements at the end.
regions = cell(numel(some_vector_of_images), 1);
index = 1;
for i = some_vector_of_images
% mask = outcome of i
cc = bwconncomp(mask);
regions{index} = regionprops(cc, 'all');
index = index + 1;
end
regions = cat(1, regions{:});

Related

How can I store multiple point objects in an array or struct?

I am trying to use matlab's drawpoint to capture some points of interest in an image interactively.
The output of the argument is of images.roi.Point object type.
How can I store the selected points in an array or struct, so I can iterate over many points instead of defining a new variable for each point?
This is my code at the moment, it's functional, however I want to be able to loop over a certain number of points instead of defining different variables manually.
img = imread('test.jpg');
imshow(img)
p1 = drawpoint;
p2 = drawpoint;
p3 = drawpoint;
p4 = drawpoint;
disp('Press a key when selection is finalized!')
pause;
p = [p1.Position; p2.Position; p3.Position; p4.Position];
The reason I'm using drawpoint is that I want to select the points, adjust their position without loosing zooming capability and store all points once finalized.
How can I modify the code to enable iteration over a certain number of points?
Any help would be much appreciated
I don’t know if it is possible to create an array of these objects. I suspect it is possible, but I don’t know exactly what the syntax should look like. You can also use a cell array, as follows:
N = 4; % number of points
pts = cell(N,1);
for ii = 1:N
pts{ii} = drawpoint;
end
pause;
coords = zeros(N,2);
for ii = 1:N
coords(ii,:) = pts{ii}.Position;
end

save vectors of different sizes in matrix

I would like to divide a vector in many vectors and put all of them in a matrix. I got this error "Subscripted assignment dimension mismatch."
STEP = zeros(50,1);
STEPS = zeros(50,length(locate));
for i = 1:(length(locate)-1)
STEP = filtered(locate(i):locate(i+1));
STEPS(:,i) = STEP;
end
I take the value of "filtered" from (1:50) at the first time for example and I would like to stock it in the first row of a matrix, then for iterations 2, I take value of "filtered from(50:70) for example and I stock it in row 2 in the matrix, and this until the end of the loop..
If someone has an idea, I don't get it! Thank you!
As mentioned in the comments, to make it work you can edit the loopy code at the end with -
STEPS(1:numel(STEP),i) = STEP;
Also, output array STEPS doesn't seem to use the last column. So, the initialization could use one less column, like so -
STEPS = zeros(50,length(locate)-1);
All is good with the loopy code, but in the long run with a high level language like MATLAB, you might want to look for faster codes and one way to achieve that would be vectorized codes. So, let me suggest a vectorized solution using bsxfun's masking capability to process such ragged-arrays. The implementation to cover generic elements in locate would look something like this -
% Get differentiation, which represent the interval lengths for each col
diffs = diff(locate)+1;
% Initialize output array
out = zeros(max(diffs),length(locate)-1);
% Get elements from filtered array for setting into o/p array
vals = filtered(sort([locate(1):locate(end) locate(2:end-1)]));
% Use bsxfun to create a mask that are to be set in o/p array and set thereafter
out(bsxfun(#ge,diffs,(1:max(diffs)).')) = vals;
Sample run for verification -
>> % Inputs
locate = [6,50,70,82];
filtered = randi(9,1,120);
% Get extent of output array for number of rows
N = max(diff(locate))+1;
>> % Original code with corrections
STEP = zeros(N,1);
STEPS = zeros(N,length(locate)-1);
for i = 1:(length(locate)-1)
STEP = filtered(locate(i):locate(i+1));
STEPS(1:numel(STEP),i) = STEP;
end
>> % Proposed code
diffs = diff(locate)+1;
out = zeros(max(diffs),length(locate)-1);
vals = filtered(sort([locate(1):locate(end) locate(2:end-1)]));
out(bsxfun(#ge,diffs,(1:max(diffs)).')) = vals;
>> max_error = max(abs(out(:)-STEPS(:)))
max_error =
0

Converting mixed empty/non-empty cells into a numeric matrix

I am working on a code to extract my AR(1)-GARCH(1) parameter, which I estimated using an AR(1)-GJR(1,1) model to individual matrices so that I can use them as variables in my calculations. As I have 16 time series variables, I combine the code with a loop in the following way:
for i=1:nIndices
AA_ARCH(:,i) = cell2mat(fit{i}.Variance.ARCH)';
end;
My problem is that for some variables is are no for AA_ARCH(:,i) the dimension is lower than nIndices. Naturally, when I try to export the estimates in the loop which specified the dimension of (:,i) and nIndices matlab reports a dimension mismatch. I would like to tell Matlab to replace the NaN with 0 instead of leaving the spot empty so that it is able to produce a (1,nIndices) matrix from AA_ARCH.
I thought of something like the this:
fit{i}.Variance.Leverage(isnan(fit{i}.Variance.Leverage))=0
but I wasn't able to combine this part with the previous code.
I would be very happy about any hints!
Best, Carolin
UPDATE:
Here is a fully a runnable version of my code which produces my problem. Notice that the code produces a dimension mismatch error because there is no ARCH and GARCH estimate in the fit.gjr(1,1) for time series 1. For these missing values I would like to have 0 as a placeholder in the extracted matrix.
returns = randn(2,750)';
T = size(returns,1);
nIndices = 2;
model = arima('AR', NaN, 'Variance', gjr(1,1));
residuals = NaN(T, nIndices);
variances = NaN(T, nIndices);
fit = cell(nIndices,1);
options = optimset('fmincon');
options = optimset(options, 'Display' , 'off', 'Diagnostics', 'off', ...
'Algorithm', 'sqp', 'TolCon' , 1e-7);
for i = 1:nIndices
fit{i} = estimate(model, returns(:,i), 'print', false, 'options', options);
[residuals(:,i), variances(:,i)] = infer(fit{i}, returns(:,i));
end
for i=1:nIndices
AA_beta(:,i) = cell2mat(fit{i}.AR)';
AA_GARCH(:,i) = cell2mat(fit{i}.Variance.GARCH)';
AA_ARCH(:,i) = cell2mat(fit{i}.Variance.ARCH)';
AA_Leverage(:,i) = cell2mat(fit{i}.Variance.Leverage)';
end;
I have some general things to say about the code, but first a solution to your problem:
You can put a simple if/else structure in your loop to handle the case of an empty array:
for ind1=1:nIndices
AA_beta(:,ind1) = cell2mat(fit{ind1}.AR)'; %//'
%// GARCH
if isempty(cell2mat(fit{ind1}.Variance.GARCH)') %//'
AA_GARCH(1,ind1) = 0;
else
AA_GARCH(:,ind1) = cell2mat(fit{ind1}.Variance.GARCH)'; %//'
end
%// ARCH (same exact code, should probably be exported to a function)
if isempty(cell2mat(fit{ind1}.Variance.ARCH)') %//'
AA_ARCH(1,ind1) = 0;
else
AA_ARCH(:,ind1) = cell2mat(fit{ind1}.Variance.ARCH)'; %//'
end
AA_Leverage(:,ind1) = cell2mat(fit{ind1}.Variance.Leverage)'; %//'
end;
Side note: I initially tried something like this: soz = #(A)isempty(A)*0+~isempty(A)*A; as an inline replacement for the if/else, but it turns out that MATLAB doesn't handle [] + 0 the way I wanted (it results in [] instead of 0; unlike other languages like JS).
As for the other things I have to say:
I am a firm supporter of the notion that one shouldn't use i,j as loop indices, as this may cause compatibility problems in some cases where complex numbers are involved (e.g. if you loop index is i then 1*i now refers to the loop index instead of to the square root of -1).
Part of your problem was that the arrays you were writing into weren't preallocated - which also means the correct datatype was unknown to MATLAB at the time of their creation. Besides the obvious performance hit this entails, it could also result in errors like the one you encountered here. If, for example, you used cells for AA_beta etc. then they could contain empty values, which you could later replace with whichever placeholder your heart desired using a combination of cellfun and isempty. Bottom line: lint (aka the colorful square on the top right of the editor window) is your friend - don't ignore it :)

Create a matrix combining many variables by using their names and a for loop

Suppose I have n .mat files and each are named as follows: a1, a2, ..., an
And within each of these mat files there is a variable called: var (nxn matrix)
I would like to create a matrix: A = [a1.var a2.var, ..., an.var] without writing it all out because there are many .mat files
A for-loop comes to mind, something like this:
A = []
for i = 1:n
[B] = ['a',num2str(i),'.mat',var];
A = [A B]
end
but this doesn't seem to work or even for the most simple case where I have variables that aren't stored as a(i) but rather 'a1', 'a2' etc.
Thank you very much!
load and concatenate 'var' from each of 'a(#).mat':
n = 10;
for i = n:-1:1 % 1
file_i = sprintf('a%d.mat', i); % 2
t = load(file_i, 'var');
varsCell{i} = t.var; % 3
end
A = [varsCell{:}]; % concatenate each 'var' in one step.
Here are some comment on the above code. All the memory-related stuff isn't very important here, but it's good to keep in mind during larger projects.
1)
In MATLAB, it is rarely a good idea or necessary to grow variables during a for loop. Each time an element is added, MATLAB must find and allocate a new block of RAM. This can really slow things down, especially for long loops or large variables. When possible, pre-allocate your variables (A = zeros(n,n*n)). Alternatively, it sometimes works to count backwards in the loop. MATLAB pre-allocates the whole array, since you're effectively telling it the final size.
2)
Equivalent to file_i = ['a',num2str(i),'.mat'] in this case, sprintf can be clearer and more powerful.
3)
Store each 'var' in a cell array. This is a balance between allocating all the needed memory and the complication of indexing into the correct places of a preallocated array. Internally, the cell array is a list of pointers to the location of each loaded 'var' matrix.
to create a test set...
generate 'n' matrices of n*n random doubles
save each as 'a(#).mat' in current directory
for i = 1:n
var = rand(n);
save(sprintf('a%d.mat',i), 'var');
end
Code
%%// The final result, A would have size nX(nXn)
A = zeros(n,n*n); %%// Pre-allocation for better performance
for k =1:n
load(strcat('a',num2str(k),'.mat'))
A(1:n,(k-1)*n+1:(k-1)*n+n) = var;
end

Foreach loop problems in MATLAB

I have the following piece of code:
for query = queryFiles
queryImage = imread(strcat('Queries/', query));
queryImage = im2single(rgb2gray(queryImage));
[qf,qd] = vl_covdet(queryImage, opts{:}) ;
for databaseEntry = databaseFiles
entryImage = imread(databaseEntry.name);
entryImage = im2single(rgb2gray(entryImage));
[df,dd] = vl_covdet(entryImage, opts{:}) ;
[matches, H] = matchFeatures(qf,qf,df,dd) ;
result = [result; query, databaseEntry, length(matches)];
end
end
It is my understanding that it should work as a Java/C++ for(query:queryFiles), however the query appears to be a copy of the queryFiles. How do I iterate through this vector normally?
I managed to sort the problem out. It was mainly to my MATLAB ignorance. I wasn't aware of cell arrays and that's the reason I had this problem. That and the required transposition.
From your code it appears that queryFiles is a numeric vector. Maybe it's a column vector? In that case you should convert it into a row:
for query = queryFiles.'
This is because the for loop in Matlab picks a column at each iteration. If your vector is a single column, it picks the whole vector in just one iteration.
In MATLAB, the for construct expects a row vector as input:
for ii = 1:5
will work (loops 5 times with ii = 1, 2, ...)
x = 1:5;
for ii = x
works the same way
However, when you have something other than a row vector, you would simply get a copy (or a column of data at a time).
To help you better, you need to tell us what the data type of queryFiles is. I am guessing it might be a cell array of strings since you are concatenating with a file path (look at fullfile function for the "right" way to do this). If so, then a "safe" approach is:
for ii = 1:numel(queryFiles)
query = queryFiles{ii}; % or queryFiles(ii)
It is often helpful to know what loop number you are in, and in this case ii provides that count for you. This approach is robust even when you don't know ahead of time what the shape of queryFiles is.
Here is how you can loop over all elements in queryFiles, this works for scalars, row vectors, column vectors and even high dimensional matrices:
for query = queryFiles(:)'
% Do stuff
end
Is queryFiles a cell array? The safest way to do this is to use an index:
for i = 1:numel(queryFiles)
query = queryFiles{i};
...
end