如果给你一个大小为400x400
的方阵,你该如何使用for循环将其分割成由20x20
子矩阵组成的矩阵?我甚至不知道该从哪里开始!
我想我需要类似于以下的东西:
[x,y] = size(matrix)
for i = 1:20:x
for j = 1:20:y
但我不确定该如何继续。你有什么想法吗?
我知道,这个帖子明确要求使用for
循环,Jeff Mather的答案正好提供了这种方法。
但我还是很好奇是否可以在不使用循环的情况下将矩阵分解为给定大小的图块(子矩阵)。如果还有其他人也很好奇,那么这就是我想出来的方法:
T = permute(reshape(permute(reshape(A, size(A, 1), n, []), [2 1 3]), n, m, []), [2 1 3])
将二维数组A
转换为三维数组T
,其中每个2D切片T(:,:,i)
都是大小为m
x n
的一个图块。 第三个索引按照标准Matlab线性化顺序枚举图块,首先是图块行。
这个变量
T = permute(reshape(A, size(A, 1), n, []), [2 1 3]);
T = permute(reshape(T, n, m, [], size(T, 3)), [2 1 3 4]);
将T
转换为四维数组,其中T(:, :, i, j)
表示具有平铺索引i, j
的二维切片。
想出这些表达式有点像解决滑动拼图。;-)
blockproc
来迭代地将一个函数句柄应用于图像/矩阵的子部分。例如:blockproc(M, [20 20], @myAwesomeFunction)
。 - Jeff Mather很抱歉我的答案也没有使用 for 循环,但是这种方法也可以解决问题:
cellOf20x20matrices = mat2cell(matrix, ones(1,20)*20, ones(1,20)*20)
您可以按以下方式访问各个单元格:
cellOf20x20matrices{i,j}(a,b)
其中i,j表示要获取的子矩阵(如果需要,a,b则是该矩阵的索引)
敬礼
你似乎很接近答案了。只是根据你所描述的问题(400x400,分成20x20的块),这样做不就可以满足你的要求吗?
[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
虽然这个问题基本上是针对2D矩阵的,但受到A. Donda的回答的启发,我想将他的答案扩展到3D矩阵,以便可以在裁剪真彩色图像(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
原始图像
子图(分区/子矩阵)
这种方法的优点是,它也适用于2D图像。这里是一个灰度图像(2D)的示例。在这个示例中使用的是MatLab内置图像'cameraman.tif'
有人使用嵌套调用permute
的答案获得了很多点赞,我觉得可以对其进行时间测量并与使用mat2cell
的其他答案进行比较。
虽然它们返回的结果不完全相同,但是:
无论如何,我已经使用以下脚本将它们进行了比较。代码在Octave中运行(版本3.9.1),JIT已禁用。
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
在性能方面,使用嵌套的排列仅在较小的矩阵中更快,其中相对性能的大变化实际上是时间上的微小变化。请注意,Y轴是以对数刻度表示的,因此100x100矩阵的两个函数之间的差异为0.02秒,而10000x10000矩阵的差异为100秒。
我还测试了以下内容,它将单元格转换为矩阵,以便两个函数的返回值相同:
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
这样做会使程序变慢一些,但不足以考虑到(线条将在600x600而非400x400相交)。
嵌套的排列和重塑用法更难理解。使用它很疯狂。这会增加维护时间(但嘿,这是Matlab语言,它不应该优雅且可重用)。
嵌套调用permute在N维空间中并不能很好地展开。我想这需要按维数进行for循环(这对已经相当晦涩的代码没有帮助)。另一方面,可以利用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
回答建议使用permute和reshape的赞数让我非常好奇,所以我决定在Matlab(R2010b)中进行测试。结果几乎相同,即性能非常差。因此,除非这个操作将在小矩阵(小于300x300)中进行很多次,并且总是有一个Matlab专家解释它的作用,否则不要使用它。
[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
20x20
的子矩阵,是什么让你写数字30
? - Autonomousmat2cell
函数非常适合将矩阵分解为子矩阵。KlausCPH的回答是一个很好的例子。此外,也可以参考这里。 - chappjc