Reputation: 197
Assuming we have a matrix M of size N^2 x N^2 elements (e.g., 9x9), what's the fastest way to split it into say 3x3 segments (each with 3x3 elements).
One way that comes to mind is the following:
M = magic(9);
N = 3;
m = mat2cell(M, N * ones(1, size(M, 1) / N), ...
N * ones(1, size(M, 2) / N));
I, however, do not prefer the use of cells. I was curious if there is a way to split the matrix and store the segments in the form of a 3D matrix using a column-major indexing for the segments (e.g., the first segment m{1} becomes m(:, :, 1) and the second segment m{2} becomes m(:, :, 2), and so on).
Upvotes: 8
Views: 137
Reputation: 30121
After using mat2cell
, you can concatenate the 2D matrices into a 3D matrix with
m = cat( 3, m{:} );
This will satisfy "m{1} becomes m(:, :, 1)" etc.
A different option would be to take each strip of N
rows and reshape
them directly into an NxNxN
matrix of segments, assigning them as the next chunk of the NxNx(N^2)
output matrix
m = NaN(N,N,N^2);
for ii = 1:N:N^2
m(:,:,ii:(ii+N-1)) = reshape( M(ii:(ii+N-1),:), N, N, N );
end
Note that the segments in these two options are in a different order (the first goes down then across for sub-matrices, the latter goes across then down). If you wanted this loop and reshape version to match the mat2cell
version exactly you would need to do a couple of transpose operations with the reshape
to get the ordering right
m = NaN(N,N,N^2);
for ii = 1:N:N^2
m(:,:,ii:(ii+N-1)) = pagetranspose( reshape( M(:,ii:(ii+N-1)).', N, N, N ) );
end
There is only a very small performance penalty for doing the additional transpose operations.
Update with benchmarking
I was curious which of the options above would be faster, so wrote the benchmark at the bottom here (wrapper for the code suggested in the answer above). Here is the resulting plot, so looping and assigning directly into a numeric matrix is faster than going via a cell regardless of N
:
I've also included chtz's answer which just uses reshape
and permute, and gives the same result as the mat2cell
and pagetranspose
options above much faster, and with less dependency on N
, so I would recommend their solution.
I've also included Luis' answer for completeness.
Benchmarking code:
Nmax = 17;
t = zeros(Nmax,2);
for N = 2:Nmax
M = (1:N^2) + 0.1*(1:N^2).';
t(N,1) = timeit( @() opt1(N,M) );
t(N,2) = timeit( @() opt2(N,M) );
t(N,3) = timeit( @() opt3(N,M) );
t(N,4) = timeit( @() opt4(N,M) );
t(N,5) = timeit( @() opt5(N,M) );
end
figure(1); clf; hold on; grid on; legend( 'show', 'fontsize', 14 );
plot( 1:Nmax, t(:,1), 'displayname', 'mat2cell and cat', 'linewidth', 2 );
plot( 1:Nmax, t(:,2), 'displayname', 'loop and reshape', 'linewidth', 2 );
plot( 1:Nmax, t(:,3), 'displayname', 'loop and reshape (2)', 'linewidth', 2 );
plot( 1:Nmax, t(:,4), 'displayname', 'reshape and permute', 'linewidth', 2 );
plot( 1:Nmax, t(:,5), 'displayname', 'im2col', 'linewidth', 2 );
ylabel( 'Time (sec)' );
xlabel( 'N' );
m = opt3( N, M );
function m = opt1( N, M )
m = mat2cell(M, N * ones(1, size(M, 1) / N), ...
N * ones(1, size(M, 2) / N));
m = cat( 3, m{:} );
end
function m = opt2( N, M )
m = NaN(N,N,N^2);
for ii = 1:N:N^2
m(:,:,ii:(ii+N-1)) = reshape( M(ii:(ii+N-1),:), N, N, N );
end
end
function m = opt3( N, M )
m = NaN(N,N,N^2);
for ii = 1:N:N^2
m(:,:,ii:(ii+N-1)) = pagetranspose( reshape( M(:,ii:(ii+N-1)).', N, N, N ) );
end
end
function m = opt4( N, M )
m = reshape(permute(reshape(M, N,N,N,N), [1,3, 2,4]), N,N, N*N);
end
function m = opt5( N, M )
m = reshape(im2col(M, [N N], 'distinct'), N, N, []);
end
Upvotes: 8
Reputation: 112749
If you have the Image Procressing Toolbox, you can do it easily with im2col
(but the code is probably slower than the other options):
S = reshape(im2col(M, [N N], 'distinct'), N, N, []);
Upvotes: 3
Reputation: 18827
You can combine reshape
with permute
, e.g. like so:
reshape(permute(reshape(M, N,N,N,N), [1,3, 2,4]), N,N, N*N)
The inner reshape
has your intended "cells" split into dimensions 1 and 3, which permute
shifts into dimensions 1 and 2. The outer reshape
may be omitted, if you are ok with having a 4D matrix.
Upvotes: 10