Para loop para dividir matriz para sub-matrizes de tamanho igual

Dada uma matriz quadrada de, digamos, tamanho 400x400 , como eu iria dividir isso em sub-matrizes constituintes de 20x20 usando um loop for? Eu não posso nem pensar por onde começar!

Eu imagino que eu queira algo como:

 [x,y] = size(matrix) for i = 1:20:x for j = 1:20:y 

mas não tenho certeza de como prosseguiria. Pensamentos?

Bem, eu sei que o cartaz pediu explicitamente um loop, e a resposta de Jeff Mather forneceu exatamente isso.

Mas ainda fico curioso para saber se é possível decompor uma matriz em blocos (sub-matrizes) de um determinado tamanho sem um loop. Caso outra pessoa esteja curiosa também, aqui está o que eu sugeri:

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

transforma um arranjo bidimensional A em um arranjo tridimensional T , onde cada 2d fatia T(:, :, i) é um dos azulejos de tamanho m x n . O terceiro índice enumera os blocos na ordem linearizada padrão do Matlab, primeiro as linhas do mosaico.

A variante

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

faz de T uma matriz de quatro dimensões onde T(:, :, i, j) fornece a fatia de 2d com índices de bloco i, j .

Chegar com estas expressões parece um pouco como resolver um quebra-cabeça deslizante. 😉

Lamento que minha resposta também não use um loop for, mas isso também funcionaria:

 cellOf20x20matrices = mat2cell(matrix, ones(1,20)*20, ones(1,20)*20) 

Você pode acessar as células individuais como:

 cellOf20x20matrices{i,j}(a,b) 

onde i, j é a submatriz a buscar (e a, b é a indexação nessa matriz, se necessário)

Saudações

Você parece muito perto. Apenas usando o problema como você descreveu (400 por 400, dividido em 20 por 20 pedaços), isso não faria o que você quer?

 [x,y] = size(M); for i = 1:20:x for j = 1:20:y tmp = M(i:(i+19), j:(j+19)); % Do something interesting with "tmp" here. end end 

Embora a questão seja basicamente para matrizes 2D, inspirada na resposta de A. Donda, gostaria de expandir sua resposta para matrizes 3D para que essa técnica pudesse ser usada no corte de imagens True Color (3D)

 A = imread('peppers.png'); %// size(384x512x3) nCol = 4; %// number of Col blocks nRow = 2; %// number of Row blocks m = size(A,1)/nRow; %// Sub-matrix row size (Should be an integer) n = size(A,2)/nCol; %// Sub-matrix column size (Should be an integer) imshow(A); %// show original image out1 = reshape(permute(A,[2 1 4 3]),size(A,2),m,[],size(A,3)); out2 = permute(reshape(permute(out1,[2 1 3 4]),m,n,[],size(A,3)),[1 2 4 3]); figure; for i = 1:nCol*nRow subplot(nRow,nCol,i); imshow(out2(:,:,:,i)); end 

A idéia básica é fazer com que a 3ª dimensão não seja afetada durante a reformulação, para que a imagem não seja distorcida. Para conseguir isso, permutação adicional foi feita para trocar as dimensões 3 e 4. Depois que o processo é concluído, as dimensões são restauradas como estavam, permutando de volta.

Resultados:

Imagem original

insira a descrição da imagem aqui

Subtramas (Partições / Sub Matrizes)

insira a descrição da imagem aqui


A vantagem deste método é, funciona bem em imagens 2D também. Aqui está um exemplo de uma imagem da Gray Scale (2D). Exemplo usado aqui é a imagem embutida do MatLab 'cameraman.tif'

insira a descrição da imagem aqui

Com alguns muitos upvotes para a resposta que faz uso de chamadas aninhadas para permute , pensei em tempo e comparando com a outra resposta que faz uso de mat2cell .

É verdade que eles não retornam exatamente a mesma coisa, mas:

  • a célula pode ser facilmente convertida em uma matriz como a outra (eu calculei isso, veja abaixo);
  • quando esse problema surge, é preferível (em minha experiência) ter os dados em uma célula, pois, mais tarde, um deles geralmente quer juntar o original;

De qualquer forma, eu os comparei com o seguinte script. O código foi executado no Octave (versão 3.9.1) com o JIT desativado.

 function T = split_by_reshape_permute (A, m, n) T = permute (reshape (permute (reshape (A, size (A, 1), n, []), [2 1 3]), n, m, []), [2 1 3]); endfunction function T = split_by_mat2cell (A, m, n) l = size (A) ./ [mn]; T = mat2cell (A, repmat (m, l(1), 1), repmat (n, l (2), 1)); endfunction function t = time_it (f, varargin) t = cputime (); for i = 1:100 f(varargin{:}); endfor t = cputime () - t; endfunction Asizes = [30 50 80 100 300 500 800 1000 3000 5000 8000 10000]; Tsides = [2 5 10]; As = arrayfun (@rand, Asizes, "UniformOutput", false); for d = Tsides figure (); t1 = t2 = []; for A = As A = A{1}; s = rows (A) /d; t1(end+1) = time_it (@split_by_reshape_permute, A, s, s); t2(end+1) = time_it (@split_by_mat2cell, A, s, s); endfor semilogy (Asizes, [t1(:) t2(:)]); title (sprintf ("Splitting in %i", d)); legend ("reshape-permute", "mat2cell"); xlabel ("Length of matrix side (all squares)"); ylabel ("log (CPU time)"); endfor 

Observe que o eixo Y está na escala de log

dividindo matrizes 2D em 2dividindo matrizes 2D em 5dividindo matrizes 2D em 10

atuação

Em termos de desempenho, o uso da permutação aninhada só será mais rápido para matrizes menores, nas quais grandes mudanças no desempenho relativo são, na verdade, pequenas alterações no tempo. Observe que o eixo Y está na escala de log , portanto, a diferença entre as duas funções para uma matriz de 100×100 é de 0,02 segundos, enquanto para uma matriz de 10000×10000 é de 100 segundos.

Eu também testei o seguinte, que irá converter a célula em uma matriz para que os valores de retorno das duas funções sejam os mesmos:

 function T = split_by_mat2cell (A, m, n) l = size (A) ./ [mn]; T = mat2cell (A, repmat (m, l(1), 1), repmat (n, l (2), 1), 1); T = reshape (cell2mat (T(:)'), [mn numel(T)]); endfunction 

Isso diminui um pouco, mas não o suficiente para considerar (as linhas se cruzarão em 600×600 em vez de 400×400).

Legibilidade

É muito mais difícil entender o uso do permeado e remodelação nesteds. É louco usá-lo. Isso aumentará muito o tempo de manutenção (mas, ei, essa é a linguagem Matlab, não deve ser elegante e reutilizável).

Futuro

As chamadas aninhadas para permutação não se expandem muito bem em N dimensões. Eu acho que seria necessário um loop for por dimensão (o que não ajudaria em todo o código já bastante enigmático). Por outro lado, fazendo uso de mat2cell:

 function T = split_by_mat2cell (A, lengths) dl = arrayfun (@(l, s) repmat (l, s, 1), lengths, size (A) ./ lengths, "UniformOutput", false); T = mat2cell (A, dl{:}); endfunction 

Editar (e testado no Matlab também)

A quantidade de upvotes na resposta sugerindo usar permutar e remodelar me deixou tão curiosa que decidi fazer o teste em Matlab (R2010b). Os resultados foram praticamente os mesmos, ou seja, o desempenho é muito ruim. Então, a menos que essa operação seja feita muitas vezes, em matrizes que sempre serão pequenas (menos de 300×300), e sempre haverá um guru do Matlab para explicar o que ele faz, não usá-lo.

Se você quiser usar um loop for, você pode fazer isso:

 [x,y] = size(matrix) k=1; % counter for i = 1:20:x for j = 1:20:y subMatrix=Matrix(i:i+19, j:j+19); subMatrixCell{k}=subMatrix; % if you want to save all the % submatrices into a cell array k=k+1; end end