2

I work with an image that I consider as a matrix.

I want to turn a 800 x 800 matrix (A) into a 400 x 400 matrix (B) where the mean of 4 cells of the A matrix = 1 cell of the B matrix (I know this not a right code line) :

B[1,1] =mean2(A[1,1 + 1,2 + 2,1 + 2,2]) 

and so on for the whole matrix ...

B [1,2]=mean2(A[1,3 + 1,4 + 2,3 + 2,4 ])

I thought to :

1) Reshape the A matrix into a 2 x 320 000 matrix so I get the four cells I need to average next to each other and it is easier to deal with the row number afterwards.

Im4bis=reshape(permute(reshape(Im4,size(Im4,2),2,[]),[2,3,1]),2,[]);

2) Create a cell-array with the 4 cells I need to average (subsetted) and calculate the mean of it. That's where it doesn't work

I{1,160000}=ones,
for k=drange(1:2:319999)
    for n=1:160000
        I{n}=mean2(Im4bis(1:2,k:k+1));
    end
end

I created an empty matrix of 400 x 400 cells (actually a vector of 1 x 160000) and I wanted to fill it with the mean but I get a matrix of 1 x 319 999 cells with one cell out of 2 empty.

Looking for light

My Input Image:

enter image description here

Santhan Salai
  • 3,888
  • 19
  • 29
Sarahdata
  • 309
  • 3
  • 15

3 Answers3

3

Let A denote your matrix and

m = 2; %// block size: rows
n = 2; %// block size: columns

Method 1

Use blockproc:

B = blockproc(A, [m n], @(x) mean(x.data(:)));

Example:

>> A = magic(6)
A =
    35     1     6    26    19    24
     3    32     7    21    23    25
    31     9     2    22    27    20
     8    28    33    17    10    15
    30     5    34    12    14    16
     4    36    29    13    18    11
>> B = blockproc(A, [m n], @(x) mean(x.data(:)))
B =
   17.7500   15.0000   22.7500
   19.0000   18.5000   18.0000
   18.7500   22.0000   14.7500

Method 2

If you prefer the reshaping way (which is probably faster), use this great answer to organize the matrix into 2x2 blocks tiled along the third dimension, average along the first two dimensions, and reshape the result:

T = permute(reshape(permute(reshape(A, size(A, 1), n, []), [2 1 3]), n, m, []), [2 1 3]);
B = reshape(mean(mean(T,1),2), size(A,1)/m, size(A,2)/n);

Method 3

Apply a 2D convolution (conv2) and then downsample. The convolution computes more entries than are really necessary (hence the downsampling), but on the other hand it can be done separably, which helps speed things up:

B = conv2(ones(m,1)/m, ones(1,n)/n ,A,'same');
B = B(m-1:m:end ,n-1:n:end);
Community
  • 1
  • 1
Luis Mendo
  • 110,752
  • 13
  • 76
  • 147
  • Method 1 and method 2 give me the same result, a 400 x 400 variable in matlab workspace but a 1366 x 652 pixels image with I save it as BMP. My initial wish was to get a 400 x 400 pixels image... I don't get it. In method 2, why do you apply `mean` twice ? Because we are in 2 D ? – Sarahdata May 16 '15 at 05:54
  • My three methods (and the other answers) give me a 400x400 output when I use a 800x800 input. Yes, `mean` is applied twice to average along the two dimensions of each block – Luis Mendo May 16 '15 at 15:13
3

Method 1

Using mat2cell and cellfun

AC = mat2cell(A, repmat(2,size(A,1)/2,1), repmat(2,size(A,2)/2,1));

out = cellfun(@(x) mean(x(:)), AC);

Method 2

using im2col

out = reshape(mean(im2col(A,[2 2],'distinct')),size(A)./2);

Method 3

Using simple for loop

out(size(A,1)/2,size(A,2)/2) = 0;
k = 1;
for i = 1:2:size(A,1)
    l = 1;
    for j = 1:2:size(A,2)
        out(k,l) = mean(mean(A(i:i+1,j:j+1)));
        l = l+1;
    end
    k = k+1;
end

Test on input image:

A = rgb2gray(imread('inputImage.png'));

%// Here, You could use any of the method from any answers 
%// or you could use the best method from the bench-marking tests done by Divakar
out = reshape(mean(im2col(A,[2 2],'distinct')),size(A)./2);  

imshow(uint8(out));  

imwrite(uint8(out),'outputImage.bmp');

Output Image:

enter image description here

Final check by reading the already written image

B = imread('outputImage.bmp');

>> whos B

Name        Size              Bytes  Class    Attributes

B         400x400            160000  uint8              
Santhan Salai
  • 3,888
  • 19
  • 29
  • Nice approach, very clean – Luis Mendo May 15 '15 at 15:55
  • Thanks and you too as usual :) @LuisMendo – Santhan Salai May 15 '15 at 15:56
  • All the 3 methods are giving me same result, a 400 x 400 matrix in the matlab workspace but a 574 x 495 pixel images when I sace it as BMP. So, this is the same issue as the methods proposed by @Luis Mendo. I do not know what is the difference between the matrix size and image size ?? I know I need 400 x 400 pixels/cells because I will compute this image. – Sarahdata May 16 '15 at 06:08
  • Moreover, I found that the result is not as I expected. My initial image is a fractal black/white image so I would guess that when we average 4 pixels, I would get a gray image, isn't ? I read something about the type of image, the difference could come from the fact that it is a 1 - 0 image and not a 255 - 0 ? – Sarahdata May 16 '15 at 06:10
  • 1
    @Sarahdata Give us "whos(A)" information when the input A is a bitmap image that is outputting `574 x 495` as output image size ? – Divakar May 16 '15 at 06:20
  • @Divakar A is a 800 x 800 uint8 and 640 000 bytes after I turned the initial image of 800 x 800 x 3 uint8 to 800 x 800 with `rgb2gray`. – Sarahdata May 16 '15 at 06:27
  • 1
    @Sarahdata could you add the original image (may be as an edit to the question)? I guess you have enough rep to upload a picture – Santhan Salai May 16 '15 at 06:28
  • [https://www.dropbox.com/s/a7zkuyci2phwqir/4.png?dl=0] I think this should work... – Sarahdata May 16 '15 at 06:56
  • I read your edit. Now the figure looks like it should look like AND its size in BMP format is 400x400 ! When I save as on the matlab figure, it still gives me a 574 x 495 size but I guess this is just not the right way to save figure. And I guess I should have convert the double image in uint8. Thank you all ! – Sarahdata May 16 '15 at 07:35
3

One approach based on this solution using reshape, sum & squeeze -

sublen = 2; %// subset length
part1 = reshape(sum(reshape(A,sublen,[])),size(A,1)/sublen,sublen,[]);
out = squeeze(sum(part1,2))/sublen^2;

Benchmarking

Set #1

Here are the runtime comparisons for the approaches listed so far for a input datasize of 800x 800 -

%// Input
A = rand(800,800);

%// Warm up tic/toc.
for k = 1:50000
    tic(); elapsed = toc();
end

disp('----------------------- With  RESHAPE + SUM + SQUEEZE')
tic
sublen = 2; %// subset length
part1 = reshape(sum(reshape(A,sublen,[])),size(A,1)/sublen,sublen,[]);
out = squeeze(sum(part1,2))/sublen^2;
toc, clear sublen part1 out

disp('----------------------- With  BLOCKPROC')
tic
B = blockproc(A, [2 2], @(x) mean(x.data(:))); %// [m n]
toc, clear B

disp('----------------------- With  PERMUTE + MEAN + RESHAPE')
tic
m = 2;n = 2;
T = permute(reshape(permute(reshape(A, size(A, 1), n, []),...
      [2 1 3]), n, m, []), [2 1 3]);
B = reshape(mean(mean(T,1),2), size(A,1)/m, size(A,2)/m);
toc, clear B T m n

disp('----------------------- With  CONVOLUTION')
tic
m = 2;n = 2;
B = conv2(ones(m,1)/m, ones(1,n)/n ,A,'same');
B = B(m-1:m:end ,n-1:n:end);
toc, clear m n B

disp('----------------------- With  MAT2CELL')
tic
AC = mat2cell(A, repmat(2,size(A,1)/2,1), repmat(2,size(A,2)/2,1));
out = cellfun(@(x) mean(x(:)), AC);
toc

disp('----------------------- With  IM2COL')
tic
out = reshape(mean(im2col(A,[2 2],'distinct')),size(A)./2);
toc

Runtime results -

----------------------- With  RESHAPE + SUM + SQUEEZE
Elapsed time is 0.004702 seconds.
----------------------- With  BLOCKPROC
Elapsed time is 6.039851 seconds.
----------------------- With  PERMUTE + MEAN + RESHAPE
Elapsed time is 0.006015 seconds.
----------------------- With  CONVOLUTION
Elapsed time is 0.002174 seconds.
----------------------- With  MAT2CELL
Elapsed time is 2.362291 seconds.
----------------------- With  IM2COL
Elapsed time is 0.239218 seconds.

To make the runtimes more fair, we can use a number of trials of 1000 on top of the fastest three approaches for the same input datasize of 800 x 800, giving us -

----------------------- With  RESHAPE + SUM + SQUEEZE
Elapsed time is 1.264722 seconds.
----------------------- With  PERMUTE + MEAN + RESHAPE
Elapsed time is 3.986038 seconds.
----------------------- With  CONVOLUTION
Elapsed time is 1.992030 seconds.

Set #2

Here are the runtime comparisons for a larger input datasize of 10000x 10000 for the fastest three approaches -

----------------------- With  RESHAPE + SUM + SQUEEZE
Elapsed time is 0.158483 seconds.
----------------------- With  PERMUTE + MEAN + RESHAPE
Elapsed time is 0.589322 seconds.
----------------------- With  CONVOLUTION
Elapsed time is 0.307836 seconds.
Community
  • 1
  • 1
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • 1
    Nice! And could be easily generalized to rectangular blocks – Luis Mendo May 15 '15 at 16:11
  • Thanks for runtime results :). Only think left is a simple `for` loop. – Santhan Salai May 15 '15 at 16:26
  • @SanthanSalai Don't think JIT could play any part here! – Divakar May 15 '15 at 16:28
  • 1
    why? are there any related posts to that? – Santhan Salai May 15 '15 at 16:31
  • Sry, i left a typo in my 1st comment. *thing (instead of think :P). I didn't meant to say it would beat any of those already proposed solution. i meant it would be nice to know where it stands in the test :) – Santhan Salai May 15 '15 at 17:11
  • 1
    @SanthanSalai Well for a `2 x 2`, my gut feeling says JIT would get tired of indexing (there would be lots then!) and would give up :) – Divakar May 15 '15 at 17:21
  • @SanthanSalai This is really embarrassing, but how do you write a for-loop to do this? Damn! Ah if some JIT lover out there posts something on that, I would love to include that into the benchmarks, but writing a for-loop is too much of work! :) – Divakar May 15 '15 at 17:27
  • Not a JIT lover but still tried as per your request :) check the results if that works :P – Santhan Salai May 15 '15 at 17:48
  • 1
    @SanthanSalai Tried it and results are just too bad for JIT, just really really bad! – Divakar May 15 '15 at 17:54