7

I have 100 images each of size 512 by 512 stored in a cell array.

I want to find the max value and indices for each pixel location by searching all the images.

Here is the sample representation: enter image description here

My code:

imgs = cell(1,5);
imgs{1} = [2,3,2;3,2,2;3,1,1];
imgs{2} = [2,3,1;4,2,3;2,2,1];
imgs{3} = [3,2,1;5,3,5;3,2,3];
imgs{4} = [4,4,2;5,3,4;4,2,2];
imgs{5} = [4,5,2;4,2,5;3,3,1];

[nrows, ncols] = size(imgs{1});
maxVal_Mat = zeros(nrows,ncols);
maxIdx_Mat = zeros(nrows,ncols);
for nrow = 1:nrows
    for ncol = 1:ncols
        [maxVal_Mat(nrow, ncol), maxIdx_Mat(nrow, ncol)] = max(cellfun(@(x) x(nrow, ncol) , imgs));
    end
end

maxVal_Mat =

     4     5     2
     5     3     5
     4     3     3

maxIdx_Mat =

     4     5     1
     3     3     3
     4     5     3

Any ideas on how to optimize this code to save execution time and memory.

Note: This is a sample demonstration of the problem, the original cell and matrices are quite large.

Thanks,

Gopi

Gopi
  • 369
  • 1
  • 17

2 Answers2

3

Since all of your images are the same size it makes more sense to store them in a 3D matrix than a cell array, which also greatly simplifies performing operations like this on them. You can convert imgs from a cell array to a 3D matrix and find the maxima and indices like so:

imgs = cat(3, imgs{:});  % Concatenate into a 3D matrix
[maxValue, index] = max(imgs, [], 3)  % Find max across third dimension

maxValue =

     4     5     2
     5     3     5
     4     3     3

index =

     4     5     1
     3     3     3
     4     5     3

There is some discussion of using cell arrays versus multidimensional arrays in this post. In general, a multidimensional array will give you better performance for many operations, but requires contiguous memory space for storage (which can cause you to hit memory limits quicker for increasing array size). Cell arrays don't require contiguous memory space and can therefore be more memory-efficient, but complicate certain operations.

gnovice
  • 125,304
  • 15
  • 256
  • 359
  • Thanks, works great. Any difference is execution time and memory usage between 3D matrix and cell usage for my case? – Gopi Jun 12 '18 at 18:46
  • ok, In this case I can use this to avoid memory limits and still use cells right? `[maxValue, index] = max(cat(3, adj_Imgs{slice_XIdxs}), [], 3);`. – Gopi Jun 12 '18 at 18:59
  • @Gopi: Intermediate copies of the data may still be made in that case. Your best option may be to just use a 3D array instead of a cell array to store your data. For 100 512-by-512 double precision images, it will use 200MB of storage. That's not much (you can check [`memory`](https://www.mathworks.com/help/matlab/ref/memory.html) to see what your limits are). – gnovice Jun 12 '18 at 19:02
  • Storing the images as a 3D array is convenient for this particular task, but (I think) not optimal in general. As [discussed before with OP](https://stackoverflow.com/q/50300427/7328782), many image processing pipelines would require copying the images in and out of the 3D stack because they can’t be applied to the slices in-place. I don’t think @obchardon’s loop is significantly more expensive than this, and surely cheaper if the 3D array needs to be constructed first. (Ping @Gopi) – Cris Luengo Jun 13 '18 at 13:20
2

I propose another solution that will probably:

  • Increase the execution time
  • Consume less memory

It is an option if your images are large and due to memory limitation you can not concatenate all the images.

Instead of loading all the images in a single 3D matrix, I compare the images by pairs.

If I take your example:

imgs = cell(1,5);
imgs{1} = [2,3,2;3,2,2;3,1,1];
imgs{2} = [2,3,1;4,2,3;2,2,1];
imgs{3} = [3,2,1;5,3,5;3,2,3];
imgs{4} = [4,4,2;5,3,4;4,2,2];
imgs{5} = [4,5,2;4,2,5;3,3,1];

% Only for the first image
Mmax = imgs{1};
Mind = ones(size(imgs{1}));

for ii = 2:numel(imgs)
    % 2 by 2 comparison
    [Mmax,ind] = max(cat(3,Mmax,imgs{ii}),[],3);
    Mind(ind == 2) = ii;
end

Results:

Mmax =

   4   5   2
   5   3   5
   4   3   3

Mind =

   4   5   1
   3   3   3
   4   5   3

In concrete terms the same code will look like:

% your list of images
file = {'a.img','b.img','c.img'}

I = imread(file{1}); 
Mmax = I;
Mind = ones(size(I));

for ii = 2:numel(file)
    I = imread(file{ii})
    [Mmax,ind] = max(cat(3,Mmax,I),[],3);
    Mind(ind == 2) = ii;
end
obchardon
  • 10,614
  • 1
  • 17
  • 33
  • I think this is the better approach. You can improve the first bit of code by moving the initialization out of the loop and looping from 2 to `numel` (I avoid `length`, also there is no need to assume a 1D cell array). – Cris Luengo Jun 13 '18 at 13:11
  • @CrisLuengo thanks for the input, I've edited my answer. – obchardon Jul 02 '18 at 15:54