the_wicked
the_wicked

Reputation: 197

How to divide a matrix in MATLAB into N^2 segments each with NxN elements?

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

Answers (4)

Wolfie
Wolfie

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 results

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

Luis Mendo
Luis Mendo

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

chtz
chtz

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

Till
Till

Reputation: 453

Could this help you?

m = reshape(M,N,N,N*N);

Upvotes: -2

Related Questions