regionprops returning single result - matlab

I do not understand the function 'regionprops' properly. For example if I create a binary matrix with three different areas, it only gives me a single centerpoint as output:
a = zeros(100,100);
a(1:49,1:49) = 1;
a(1:25,75:100) = 1;
a(51:100,51:100)= 1;
spy(a)
regionprops(a,'Centroid')
But if I add the line
a=bwmorph(a,'erode',0);
which does absolutely nothing, I get three different center points as output, one for each area. Why do they give different outputs and is it really necesarry to add a useless line of code?

The input to regionprops should be a logical array. If it's not, then it's assumed that the input is a labels matrix, as such it's processed as if all of the 1 values are part of the same object.
You can fix this by explicitly converting it to a logical matrix
regionprops(logical(a), 'Centroid') % or regionprops(a == 1, 'Centroid')
The better option may be to make a a logical to begin with by using false rather than zeros to construct a.
a = false(100, 100);
a(1:49,1:49) = 1;
a(1:25,75:100) = 1;
a(51:100,51:100)= 1;
The reason why the no-op erode causes it to work, is that the output of bwmorph is a logical matrix.

Related

MATLAB find on multiple columns to multiple column result

Setup
I have an array of captured data. The data may be captured on just 1 device or up to a dozen devices, with each device being a column in the array. I have a prior statement which I execute on the array to then turn it into a logical array to find particular points of interest in the data. Due to the nature of the data, there are many 0's and only a few 1's. I need to return an array with the indices of the 1's so I can go back and capture the data between those points (see update below).
find is an obvious choice for a function - however, the result I need, needs to have 1 column for each device. Normally find will do a linear index regardless of the dimensions of the array.
The devices follow a pattern - but aren't exactly the same. So, complicating this is the fact that the number of 1's in each column is close to, but not guaranteed to be exactly the same depending on the exact timing the data capture is stopped (they are most often different from each other by 1 element, but could be different by more).
MATLAB CODE ATTEMPTS
Because of that difference, I can't use the following simple code:
for p = 1:np
indices( :, p ) = find( device.data.cross( :, p ) );
end
Notes:
np is the number of columns in the data = number of devices captured.
devices is a class representing the collection of devices
data is a TimeTable containing captured data on all the devices
cross is a column in the data TimeTable which contains the logical array
Even this simple code is inefficient and generates the Code Analyzer warning:
The variable 'indices' appears to change size on every loop
iteration (within a script). Consider preallocating for speed.
As expected, it doesn't work as I get an error similar to the following:
Unable to perform assignment because the size of the left side is
448-by-1 and the size of the right side is 449-by-1.
I know why I get this error - each column in an array in MATLAB has to have the same number of rows, so I can't make the assignment if the row size doesn't match. I need to pad the "short" columns somehow. In this case, repeating the last index will work for my later operations without causing an error.
I can't figure out a "good" way to do this. I can't pre-populate the array rows because I don't know how many rows there will be until I've done the find operation.
I can change the code as follows:
indices = [];
for p = 1:np
tempindices = find( devices.data.cross(:, p) );
sizediff = size( tempindices, 1 ) - size( indices, 1 );
if p > 1
if sizediff > 0
padding = repmat(indices(end, 1:(p - 1)), sizediff, 1);
indices = [indices; padding];
elseif sizediff < 0
padding = repmat(tempindices(end), abs(sizediff), 1);
tempindices = [tempindices; padding];
end
end
indices(:,p) = tempindices;
end
Note: padarray would have been useful here, but I don't have the Image Processing Toolbox so I cannot use it.
This code works, but it is very inefficient, it creates multiple otherwise unneeded variables in the workspace and generates multiple "appears to change size on every loop iteration" warnings in Code Analyzer. Is there a more efficient way to do this?
Update / Additional Information:
Some more context is needed for my issue. Given that devices.data.cross is a logical array, to just "pick" the data I want from other columns in my table (as I originally described my problem) I could select a column from devices.data.cross and pass that logical column as a subscript to get that data. I do that where it works. However, for some of the columns I need to select "chunks" of the data between the indices and that's where (I think) I need the indices. Or, at least I don't know of another way to do it.
Here is example code of how I use the indices:
for p = 1:np
for i = 2:num_indices
these_indices = indices(i-1, p):( indices(i, p) - 1 );
rmsvoltage = sqrt( mean( devices.data.voltage(these_indices).^2 ) );
end
end
This is just one routine I do on the "chunks" of data. I also have a couple of functions where these chunks of data are passed for processing.
When I understood your problem correctly, the code below should work. I'm using the approach that Cris Luengo suggested in a comment under your question.
Key element is [rowIdcs, colIdcs] = find( cros ); which gives you the subscripts of positions in cros having a value of one. Please find further comments inline.
% Create some data for testing
volt = randn(10,10);
cros = randi(10,10,10) > 9;
% Get rowIdcs and colIdcs, which have both a size of Nx1,
% with N denoting the number of ones in the mask.
% rowIdcs and colIdcs are the subscripts of the ones in the mask.
[rowIdcs, colIdcs] = find( cros );
% The number of chunks is equal to number N of ones found in the mask;
numChunks = numel( rowIdcs );
% Initilize a vector for the rms
rms = zeros( numChunks, 1 );
% Loop over the chunks
for k = 1 : numChunks
curRow = rowIdcs(k);
curCol = colIdcs(k);
% Get indices of range over neighbouring rows
chunkRowIdcs = curRow + [-1 0 1]; %i.e. these_indices in your example
% Remove indices that are out of range
chunkRowIdcs( chunkRowIdcs < 1 | chunkRowIdcs > size(volt,1) ) = [];
% Get voltages covered by chunk
chunkVoltages = volt( chunkRowIdcs, curCol );
% Get RMS over voltages
rms(k) = sqrt( mean( chunkVoltages(:).^2 ));
end

insert value in a matrix in a for loop

I wrote this matlab code in order to concatenate the results of the integration of all the columns of a matrix extracted form a multi matrix array.
"datimf" is a matrix composed by 100 matrices, each of 224*640, vertically concatenated.
In the first loop i select every single matrix.
In the second loop i integrate every single column of the selected matrix
obtaining a row of 640 elements.
The third loop must concatenate vertically all the lines previously calculated.
Anyway i got always a problem with the third loop. Where is the error?
singleframe = zeros(224,640);
int_frame_all = zeros(1,640);
conc = zeros(100,640);
for i=0:224:(22400-224)
for j = 1:640
for k = 1:100
singleframe(:,:) = datimf([i+1:(i+223)+1],:);
int_frame_all(:,j) = trapz(singleframe(:,j));
conc(:,k) = vertcat(int_frame_all);
end
end
end
An alternate way to do this without using any explicit loops (edited in response to rayryeng's comment below. It's also worth noting that using cellfun may not be more efficient than explicitly looping.):
nmats = 100;
nrows = 224;
ncols = 640;
datimf = rand(nmats*nrows, ncols);
% convert to an nmats x 1 cell array containing each matrix
cellOfMats = mat2cell(datimf, ones(1, nmats)*nrows, ncols);
% Apply trapz to the contents of each cell
cellOfIntegrals = cellfun(#trapz, cellOfMats, 'UniformOutput', false);
% concatenate the results
conc = cat(1, cellOfIntegrals{:});
Taking inspiration from user2305193's answer, here's an even better "loop-free" solution, based on reshaping the matrix and applying trapz along the appropriate dimension:
datReshaped = reshape(datimf, nrows, nmats, ncols);
solution = squeeze(trapz(datReshaped, 1));
% verify solutions are equivalent:
all(solution(:) == conc(:)) % ans = true
I think I understand what you want. The third loop is unnecessary as both the inner and outer loops are 100 elements long. Also the way you have it you are assigning singleframe lots more times than necessary since it does not depend on the inner loops j or k. You were also trying to add int_frame_all to conc before int_frame_all was finished being populated.
On top of that the j loop isn't required either since trapz can operate on the entire matrix at once anyway.
I think this is closer to what you intended:
datimf = rand(224*100,640);
singleframe = zeros(224,640);
int_frame_all = zeros(1,640);
conc = zeros(100,640);
for i=1:100
idx = (i-1)*224+1;
singleframe(:,:) = datimf(idx:idx+223,:);
% for j = 1:640
% int_frame_all(:,j) = trapz(singleframe(:,j));
% end
% The loop is uncessary as trapz can operate on the entire matrix at once.
int_frame_all = trapz(singleframe,1);
%I think this is what you really want...
conc(i,:) = int_frame_all;
end
It looks like you're processing frames in a video.
The most efficent approach in my experience would be to reshape datimf to be 3-dimensional. This can easily be achieved with the reshape command.
something along the line of vid=reshape(datimf,224,640,[]); should get you far in this regard, where the 3rd dimension is time. vid(:,:,1) then would display the first frame of the video.

In an assignment A(I) = B, the number of elements in B and I must be the same

This is my code in Matlab: How could I get all values of all 5 images saved? This code only returns the last image! I tried using IM(l) but it gives me an error: In an assignment A(I) = B, the number of elements in B and I must be the same.
Amount_measurements = 5;
IM=zeros(2097152,1);
l=1;
for l=(1:Amount_measurements)
if l < 9
%index = double(0)+double(0)+double(l+1);
index = strcat(num2str(double(0)),num2str(double(0)),num2str(double(l+1)));
elseif l < 99
index = double(0)+double(l+1);
else
index = double(l+1);
end
file_name1='trial.nii.gz';
%disp(file_name1);
jesu=load_nii(file_name1);
[x,y,z] = meshgrid(1:256,1:256,1:256);
[lx,ly,lz] = meshgrid(1:2:256,1:2:256,1:2:256);
newImage = interp3(x,y,z,jesu.img,lx,ly,lz);
IM= newImage(:);
end
I want the values newImage(:) to be stored as IM1=newImage(:) IM2=newImage(:) IM3=newImage(:) IM4=newImage(:) so on... How could I go about with it?
Since you mentioned wanting a variable-length version of IM1=newImage(:) IM2=newImage(:) IM3=newImage(:) IM4=newImage(:), you're looking for a cell array. Try
IM{l} = newImage;
instead of
IM(l) = newImage(:);
The important difference is the use of braces rather than parentheses. Use a right-hand side ofnewImage(:) if you want to reshape into a vector, just newImage if you want to preserve it as a matrix.
By using IM(l) you're trying to add an entire column vector (newImage(:)) to a single element (the l-th element) in the array IM, that's why Matlab throws the error.
You should consider concatenation: since newImage(:) is a column-vector, replace
IM= newImage(:);
with
IM=[IM newImage(:)];
but at the top of the script you should also initialize IM as
IM=[];
At the end of the loop, the resulting IM will have Amount_measurements columns where 1 column = 1 newImage(:).
Note #1: this will only work if newImage(:) always has the same length.
Note #2: if you know a priori how long the vector newImage(:) is and, again, by assuming that its length never changes, you should consider preallocating the IM matrix by replacing IM=[]; with IM=zeros(X,Amount_measurements); where X is the number of elements in newImage(:). Finally, regarding the concatenation stage, you should replace IM=[IM newImage(:)]; with IM(:,l)=newImage(:).
Note #3: as instead, if the size of newImage(:) can change you cannot rely on preallocation and matrices, but you must use cell arrays: the last instruction in your loop should be IM{l}=newImage(:);.

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 :)

Get the iteration number inside a MATLAB for loop

Say I have a for loop in MATLAB:
scales = 5:5:95;
for scale = scales
do stuff
end
How can I get the iteration number inside a MATLAB for loop as concisely as possible?
In Python for example I would use:
for idx, item in enumerate(scales):
where idx is the iteration number.
I know that in MATLAB (like in any other language) I could create a count variable:
scales = 5:5:95;
scale_count = 0;
for scale = scales
scale_count = scale_count + 1;
do stuff
end
I could otherwise use find:
scales = 5:5:95;
for scale = scales
scale_count = find(scales == scale);
do stuff
end
But I'm curious to know whether there exists a more concise way to do it, e.g. like in the Python example.
Maybe you can use the following:
scales = 5:5:95;
for iter = 1:length(scales)
scale=scales(iter); % "iter" is the iteration number.
do stuff
end
Since for iterates over the columns of whatever you give it, another way of approximating multiple loop variables would be to use an appropriately constructed matrix:
for scale=[5:5:95; 1:19]
% do stuff with scale(1) or scale(2) as appropriate
end
(my personal preference is to loop over the indices as per Parag's answer and just refer to data(index) directly within the loop, without an intermediate. Matlab's syntax isn't very concise at the best of times - you just get used to it)
The MATLAB way is probably doing it with vectors.
For example suppose you want to find in a vector if there is a value that is equal to its position. You would generally do this:
a = [10 20 1 3 5];
found = 0;
for index = 1:length(a)
if a(index) == index
found = 1;
break;
end
end
Instead you can do:
found = any(a == 1:length(a));
In general
for i=1:length(a)
dostuff(a(i), i);
end
can be replaced with:
dostuff(a(i), 1:length(a))
it dostuff can be vectorized or
arrayfun(#dostuff, a, 1:length(a))
otherwise.