Average subset of a matrix in a loop in matlab
I am working with an image that I treat as a matrix.
I want to turn an 800 x 800 matrix (A) into a 400 x 400 matrix (B) where the average of 4 cells of matrix A = 1 cell of matrix B (I know this is not the correct line of code):
B[1,1] =mean2(A[1,1 + 1,2 + 2,1 + 2,2])
etc. for the whole matrix ...
B [1,2]=mean2(A[1,3 + 1,4 + 2,3 + 2,4 ])
I thought:
1) Change matrix A to a 2 x 320,000 matrix so that I get four cells that I need to average next to each other and after that it is easier to handle the row number.
Im4bis=reshape(permute(reshape(Im4,size(Im4,2),2,[]),[2,3,1]),2,[]);
2) Create a cell array with 4 cells that I need to average (subset) and calculate the average. What doesn't work there
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 a 400 x 400 empty matrix (actually a 1 x 160000 vector) and I wanted to fill it with the mean, but I got a 1 x 319,999 cell matrix with one cell of 2 blanks.
Looking for light
My input image:
source to share
Method 1
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 a 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
Checking the 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:
Final check by reading the already written image
B = imread('outputImage.bmp');
>> whos B
Name Size Bytes Class Attributes
B 400x400 160000 uint8
source to share
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 rearrangement way (which is probably faster), use this excellent answer to arrange the matrix in 2x2 blocks alternating in the third dimension, averaging over the first two dimensions, and change 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 2D Convolution ( conv2
) and then shrink. Convolution calculates more records than is actually needed (hence the downsampling), but on the other hand it can be done separatively , which helps speed things up to:
B = conv2(ones(m,1)/m, ones(1,n)/n ,A,'same');
B = B(m-1:m:end ,n-1:n:end);
source to share
One approach is based on this solution
using reshape
, sum
and squeeze
-
sublen = 2; %// subset length
part1 = reshape(sum(reshape(A,sublen,[])),size(A,1)/sublen,sublen,[]);
out = squeeze(sum(part1,2))/sublen^2;
Benchmarking
Install # 1
Following are the runtime comparisons for the approaches listed so far for data entry 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
Execution 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 runtime fairer, we can use a series of tests 1000
in addition to the fastest three approaches for the same data entry 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.
Install # 2
Following are the runtime comparisons for larger data entry 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.
source to share