1

I have a 3D (x,y,nframes) matrix/ movie ( named ch), and a logical mask (x,y). I want to do the mean of the mask pixels in each frame , at the end I get a vector of dim 1xnframes. And I want to do it with the reshape instead than frame by frame, but both results doesn't match and I don't understand why... Could you please let me know why???

for i=1:nframes
    ch_aux=ch(:,:,i);    
    Mean_for(i)= mean(ch_aux(mask));
end
% C with reshape
[row,col] = find(mask);
A=ch(row,col,:);
B=reshape(A,1,length(row).^2,nframes );
Mean_res=mean(B);


plot( Mean_for,'r')
hold on
plot( Mean_res(:))

legend({'for','reshape'})

thanks!

Shai
  • 111,146
  • 38
  • 238
  • 371
marina
  • 15
  • 2

2 Answers2

2

Solution:

Using reshape

r = reshape( ch, [], size(ch,3) );
Mean_res = mean( r(mask(:),: ), 2 );

Benchmarking (comparing this solution to the two proposed by Divakar) can be found here showing:

Shai
Elapsed time is 0.0234721 seconds.
Divakar 1
Elapsed time is 0.743586 seconds.
Divakar 2
Elapsed time is 0.025841 seconds.

bsxfun is significantly slower,


What caused the error in the original code?

I suspect your problem lies in the expression A=ch(row, col,:);:
Suppose ch is of size 2-by-2-by-n and mask = [ 1 0; 0 1];, in that case

[rox, col] = find(mask);

Results with

row = [1,2];
col = [1,2];

And, obviously, A=ch(row,col,:); results with A equals ch exactly, which is not what you want...

Community
  • 1
  • 1
Shai
  • 111,146
  • 38
  • 238
  • 371
  • That `ideone` runtimes seems different to me, can you run again? – Divakar Apr 28 '15 at 14:45
  • @Divakar I also get inconsistent results.... I guess it depends on loads on *ideone* server. It is clear, though, that `bsxfun` is not the way to go here. Mat-mult is the Matlab-ish way to accomplish this task. – Shai Apr 28 '15 at 14:48
  • 1
    Now, I can agree with you :) – Divakar Apr 28 '15 at 14:49
1

For efficiency, you could use another vectorized solution with bsxfun alongwith your favourite reshape -

Mean_bsxfun = sum(reshape(bsxfun(@times,ch,mask),[],size(ch,3)),1)./sum(mask(:))

Or even better, abuse the fast matrix multiplication in MATLAB -

Mean_matmult  = mask(:).'*reshape(ch,[],size(ch,3))./sum(mask(:))
Community
  • 1
  • 1
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • the result will be lower than the expected mean: you average over masked pixels, you should `sum(..., 1)/sum(mask(:));` – Shai Apr 28 '15 at 14:12
  • `mean` can be mean sometimes, if you are not careful ;) – Shai Apr 28 '15 at 14:15
  • @Shai haha joke of the day! ;) – Divakar Apr 28 '15 at 14:15
  • @Shai Ah yes! That was a hard-coded value, thanks for the edit. – Divakar Apr 28 '15 at 14:29
  • Joke aside, I LOVE `bsxfun`, but I suspect this time it is **significantly** slower than the other proposed solutions. – Shai Apr 28 '15 at 14:29
  • @Shai Yup, matrix-mult based approach must win this one. – Divakar Apr 28 '15 at 14:30
  • according to my small benchmark matrix mult is comparable to regular reshape - I suppose because it does not require any memory copies... – Shai Apr 28 '15 at 14:33
  • @Shai I would suggest trying on bigger datasizes, mat-mult wins hands down. Something like `1000 x 1000 x 100`. – Divakar Apr 28 '15 at 14:36
  • I suspect it will depend on the `nnz` in the mask - if most of the mask is `true` then mat-mult should win, otherwise, it should be quicker to hand-pick the relevant rows in the `reshape`d matrix... – Shai Apr 28 '15 at 14:39