使用for循环将矩阵分割为相同大小的子矩阵

12

如果给你一个大小为400x400的方阵,你该如何使用for循环将其分割成由20x20子矩阵组成的矩阵?我甚至不知道该从哪里开始!

我想我需要类似于以下的东西:

[x,y] = size(matrix)

for i = 1:20:x
    for j = 1:20:y

但我不确定该如何继续。你有什么想法吗?


虽然你已经得到了答案,但出于好奇,如果你要将矩阵分成20x20的子矩阵,是什么让你写数字30 - Autonomous
mat2cell函数非常适合将矩阵分解为子矩阵。KlausCPH的回答是一个很好的例子。此外,也可以参考这里 - chappjc
@Parag 只是一个错误哈哈! - user3058703
相关问题:https://dev59.com/1mXWa4cB1Zd3GeqPNX_L - Amro
6个回答

24

我知道,这个帖子明确要求使用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的二维切片。

想出这些表达式有点像解决滑动拼图。;-)


3
如果你拥有图像处理工具箱,你可以使用blockproc来迭代地将一个函数句柄应用于图像/矩阵的子部分。例如:blockproc(M, [20 20], @myAwesomeFunction) - Jeff Mather
1
当然可以,但这样有什么乐趣呢?;-) - A. Donda
1
+1 我很好奇!实际上,我尝试了几分钟,然后放弃了 :-) - Luis Mendo
3
很棒的工作。+1 我将您的答案扩展到了三维矩阵。看一下! - Santhan Salai
4
@carandraug。你称我的回答“可怕”,这是一个相当强烈的词,而且你含蓄地称我为“大师”,这也不太好。说真的,这有什么大不了的?路易斯·门多和可能大多数其他投票者意识到的是,这是一种极端向量化的练习,这对我和显然其他人来说是有趣和娱乐性的。 - A. Donda
显示剩余12条评论

14

很抱歉我的答案也没有使用 for 循环,但是这种方法也可以解决问题:

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

您可以按以下方式访问各个单元格:

cellOf20x20matrices{i,j}(a,b)

其中i,j表示要获取的子矩阵(如果需要,a,b则是该矩阵的索引)

敬礼


10

你似乎很接近答案了。只是根据你所描述的问题(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

谢谢,这非常有帮助。还要感谢其他人提供的答案,但我在这种特殊情况下想使用for循环。讨论了一些非常有趣的想法! - user3058703
2
@user3058703,如果这个答案解决了你的问题,你应该接受它(点击外部勾号,让它变成绿色)。 - A. Donda

9

虽然这个问题基本上是针对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

基本思想是在重塑图像时不影响第三维,以避免图像失真。为了实现这一点,需要进行额外的置换来交换第三和第四维。一旦完成该过程,通过重新排列维度来恢复它们。

结果:

原始图像

enter image description here

子图(分区/子矩阵)

enter image description here


这种方法的优点是,它也适用于2D图像。这里是一个灰度图像(2D)的示例。在这个示例中使用的是MatLab内置图像'cameraman.tif'

enter image description here


6

有人使用嵌套调用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轴是对数刻度

将2D矩阵拆分为2个 将2D矩阵拆分为5个 将2D矩阵拆分为10个

性能

在性能方面,使用嵌套的排列仅在较小的矩阵中更快,其中相对性能的大变化实际上是时间上的微小变化。请注意,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

编辑(并在Matlab中进行测试)

回答建议使用permute和reshape的赞数让我非常好奇,所以我决定在Matlab(R2010b)中进行测试。结果几乎相同,即性能非常差。因此,除非这个操作将在小矩阵(小于300x300)中进行很多次,并且总是有一个Matlab专家解释它的作用,否则不要使用它。


0
如果你想使用for循环,可以这样做:
[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   

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接