matlab - loop - if octave
Para matriz de bucle para dividir submatrices de igual tamaƱo (6)
Dada una matriz cuadrada de, digamos, tamaño 400x400
, ¿cómo podría dividir esto en 400x400
constituyentes de 20x20
usando un for-loop? ¡Ni siquiera puedo pensar por dónde empezar!
Imagino que quiero algo como:
[x,y] = size(matrix)
for i = 1:20:x
for j = 1:20:y
pero no estoy seguro de cómo procedería. ¿Pensamientos?
Aunque la pregunta es básicamente para matrices 2D, inspirada por la respuesta de A. Donda, me gustaría ampliar su respuesta a las matrices 3D para que esta técnica se pueda utilizar en el recorte de imágenes de color verdadero (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
La idea básica es hacer que la tercera dimensión no se vea afectada mientras se cambia la forma para que la imagen no se distorsione. Para lograr esto, se realizó permuta adicional para intercambiar las dimensiones 3 y 4. Una vez que se realiza el proceso, las dimensiones se restauran tal como estaban, permutando hacia atrás.
Resultados:
Imagen original
Subtramas (Particiones / Sub Matrices)
La ventaja de este método es que también funciona bien en imágenes 2D. Aquí hay un ejemplo de una imagen de escala de grises (2D). Ejemplo utilizado aquí es la imagen incorporada de MatLab ''cameraman.tif''
Bueno, sé que el cartel pidió explícitamente un ciclo, y la respuesta de Jeff Mather me proporcionó exactamente eso.
Pero todavía tengo curiosidad de si es posible descomponer una matriz en mosaicos (submatrices) de un tamaño dado sin un bucle. En caso de que alguien más tenga curiosidad, también, esto es lo que se me ocurrió:
T = permute(reshape(permute(reshape(A, size(A, 1), n, []), [2 1 3]), n, m, []), [2 1 3])
transforma una matriz bidimensional A
en una matriz tridimensional T
, donde cada 2d rebanada T(:, :, i)
es una de las fichas de tamaño m
x n
. El tercer índice enumera las teselas en el orden linearizado Matlab estándar, primero las filas de teselas.
La 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]);
convierte a T
una matriz tetradimensional en la que T(:, :, i, j)
da la 2d división con índices de mosaico i, j
.
Venir con estas expresiones se parece un poco a la resolución de un rompecabezas deslizante. ;-)
Con algunos muchos upvotes para la respuesta que hace que las llamadas anidadas se permute
, pensé en sincronizarla y compararla con la otra respuesta que hace uso de mat2cell
.
Es cierto que no devuelven exactamente lo mismo, pero:
- la célula se puede convertir fácilmente en una matriz como la otra (cronometré esto, mire más abajo);
- cuando surge este problema, es preferible (en mi experiencia) tener los datos en una celda, ya que más adelante uno querrá volver a juntar el original;
De todos modos, los he comparado con el siguiente guión. El código se ejecutó en Octave (versión 3.9.1) con JIT deshabilitado.
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) ./ [m n];
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
Tenga en cuenta que el eje Y está en escala de registro
Actuación
En cuanto al rendimiento, usar el permute anidado solo será más rápido para matrices más pequeñas donde los grandes cambios en el rendimiento relativo son en realidad cambios muy pequeños en el tiempo. Tenga en cuenta que el eje Y está en escala logarítmica , por lo que la diferencia entre las dos funciones para una matriz de 100x100 es de 0.02 segundos, mientras que para una matriz de 10000x10000 es de 100 segundos.
También he probado lo siguiente que convertirá la celda en una matriz para que los valores de retorno de las dos funciones sean los mismos:
function T = split_by_mat2cell (A, m, n)
l = size (A) ./ [m n];
T = mat2cell (A, repmat (m, l(1), 1), repmat (n, l (2), 1), 1);
T = reshape (cell2mat (T(:)''), [m n numel(T)]);
endfunction
Esto ralentiza un poco, pero no lo suficiente como para considerar (las líneas se cruzarán a 600x600 en lugar de 400x400).
Legibilidad
Es mucho más difícil entender el uso del permute anidado y la remodelación. Es una locura usarlo. Aumentará mucho el tiempo de mantenimiento (pero bueno, este es el lenguaje de Matlab, no se supone que sea elegante y reutilizable).
Futuro
Las llamadas anidadas para permutar no se expanden correctamente en N dimensiones. Supongo que requeriría un for loop por dimensión (lo que no ayudaría en absoluto al código ya bastante críptico). Por otro lado, haciendo 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 (y probado en Matlab también)
La cantidad de votos ascendentes en la respuesta que sugiere usar permute y remodelar me dio tanta curiosidad que decidí hacer esto en Matlab (R2010b). Los resultados fueron más o menos los mismos, es decir, su rendimiento es realmente pobre. Entonces, a menos que esta operación se realice muchas veces, en matrices que siempre serán pequeñas (menos de 300x300), y siempre habrá un gurú de Matlab alrededor para explicar lo que hace, no lo use.
Lamento que mi respuesta tampoco use un bucle for, pero esto también funcionaría:
cellOf20x20matrices = mat2cell(matrix, ones(1,20)*20, ones(1,20)*20)
A continuación, puede acceder a las celdas individuales como:
cellOf20x20matrices{i,j}(a,b)
donde i, j es la submatriz a buscar (y a, b es la indexación en esa matriz si es necesario)
Saludos
Pareces muy cerca. Simplemente usando el problema como lo describió (400 por 400, dividido en fragmentos de 20 por 20), ¿no haría esto lo que usted quiere?
[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
Si desea usar un ciclo for, puede hacer esto:
[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