MATLAB Answers

0

Finding the means of a large number of individual matrices.

Asked by Andrew McLean on 17 Feb 2017
Latest activity Answered by Image Analyst
on 17 Feb 2017
As part of an image processing task, I've split a 944x752 (grayscale) image into 11092 8x8 matrices. I've tried using the following for loop to calculate and individually store the means of each matrix:
start=0;
stop=11091;
for cell=start:1:stop
m=mean2(['out_' int2str(cell)]);
eval(['xbar_' int2str(cell) '=m'])
end
This is coming up with results, but the numbers are completely incorrect, for example if I manually enter;
k=mean2(out_3953)
Then k=148.3594, but if I call the result for the corresponding 8x8 block as produced by my for loop, the outcome is 81.375.
I'm new to Matlab and coding in general, so any help or advice would be greatly appreciated.

  1 Comment

"any help or advice would be greatly appreciated"
Accessing variables dynamically will be slow, buggy, and complicated. If you want to write fast, efficient, and easy to debug code then do not try to create and access variables like that.
Learn to use indexing. It really is that simple. ND arrays are great, simple to use. Cell arrays are easy too. Why choose the slowest, buggiest, and most obfuscated way to write your code?

Sign in to comment.

3 Answers

Answer by Guillaume
on 17 Feb 2017
Edited by Guillaume
on 17 Feb 2017
 Accepted Answer

You've really have numbered variables all the way up to out_11091? Wow!
In this forum, we keep telling people not to use eval just to avoid this issue. As soon as you start numbering variables, you're doing it wrong. Very wrong if your numbers go all the way to 11091. Use an array of variable (cell array) or matrices of higher dimension to store all these related variables.
In your case:
%someimage: 2D matrix that can be divided evenly into 8x8 blocks
assert(all(mod(size(someimage), 8) == 0), 'image cannot be divided evenly into 8x8 blocks')
blocks = mat2cell(someimage, repmat(8, 1, size(someimage, 1)/8), repmat(8, 1, size(someimage, 2)/8) %divide image into 8x8 blocks. Store blocks into cell array
xbar = zeros(size(blocks)); %predeclare for faster code
for ib = 1:numel(blocks)
xbar(ib) = mean2(blocks{ib});
end
See how easy the loop is. You don't even need the loop if you use cellfun:
%no need to predeclare xbar since it's created by cellfun
xbar = cellfun(@mean2, blocks);
edit: or as Adam suggest instead of a cell array use a 3D matrix. Easily obtained from the blocks cell array
blocks3D = cat(3, blocks{:});
and the mean is then:
xbar = mean(mean(blocks3D, 1), 2);
The downside of this is you loose the shape of the block matrix. You could always reshape xbar afterward:
xbar = reshape(xbar, size(someimage)/8)

  0 Comments

Sign in to comment.


Answer by Adam
on 17 Feb 2017

Put your 8*8 images into an 11092*8*8 double array instead of 11092 individual variables, that is insane!
Then calculating the means is easy as e.g.
mean( mean( myArray, 2 ), 3 );

  0 Comments

Sign in to comment.


Answer by Image Analyst
on 17 Feb 2017

Image Processing people would use blockproc() for this. You can do this with 2 lines of code if you have the Image Processing Toolbox. Here's a snippet from my demos:
% Block process the image to replace every pixel in the
% 4 pixel by 4 pixel block by the mean of the pixels in the block.
% The image is 256 pixels across which will give 256/4 = 64 blocks.
% Note that the size of the output block (2 by 2) does not need to be the size of the input block!
% Image will be the 128 x 128 since we are using ones(2, 2) and so for each of the 64 blocks across
% there will be a block of 2 by 2 output pixels, giving an output size of 64*2 = 128.
% We will still have 64 blocks across but each block will only be 2 output pixels across,
% even though we moved in steps of 4 pixels across the input image.
meanFilterFunction = @(theBlockStructure) mean2(theBlockStructure.data(:)) * ones(2,2, class(theBlockStructure.data));
blockSize = [4 4];
blockyImage64 = blockproc(grayImage, blockSize, meanFilterFunction);
For your case, replace the blockSize with [8,8] and it does what you want. I'm attaching more comprehensive full demos that do other things too like different resolutions and different operations (median, stdev, etc.).

  0 Comments

Sign in to comment.