【问题标题】:Taking the max of contiguous matrix chunks in MATLAB在 MATLAB 中取最大的连续矩阵块
【发布时间】:2012-10-25 07:17:39
【问题描述】:

给定矩阵:

a =
   1   1   2   2
   1   1   2   2
   3   3   4   4
   3   3   4   4

我想得到以下四个 2x2 矩阵:

a1 =
   1   1
   1   1

a2 =
   2   2
   2   2

a3 =
   3   3
   3   3

a4 =
   4   4
   4   4

从那里,我想取每个矩阵的最大值,然后将结果重塑为 2x2 结果矩阵,如下所示:

r =
   1   2
   3   4

结果最大值相对于它们在初始矩阵中的原始位置的位置很重要。

目前,我正在使用以下代码来完成此操作:

w = 2
S = zeros(size(A, 1)/w);
for i = 1:size(S)
  for j = 1:size(S)
    Window = A(i*w-1:i*w, j*w-1:j*w);
    S(i, j) = max(max(Window));
  end
end

这可行,但似乎必须有一种不涉及迭代(矢量化)的方法。

我尝试像这样使用 reshape: reshape(max(max(reshape(A, w, w, []))), w, w, []) 然而,这需要错误值的最大值并返回:

ans =
   3   4
   3   4

有没有什么方法可以在不进行迭代的情况下完成此任务或以其他方式改进我的迭代方法?

【问题讨论】:

  • 这是一种比循环慢 4 倍的单行方法:Soln = cell2mat(cellfun(@max, cellfun(@max, mat2cell(A, [2 2], [2 2]), 'UniformOutput', false), 'UniformOutput', false));。你确实问过如何在没有迭代的情况下做到这一点 :-) 但我将其作为评论,而不是答案,因为从速度的角度来看,迭代确实更可取。
  • 您是否需要中间 2x2 矩阵来做其他事情,或者您只是对最终矩阵 r 感兴趣?
  • 是的,我也是这么想的……
  • 另外,数组有多大?总是 4x4 和 2x2 窗口?
  • 我不需要中间的 2x2 矩阵,只需要最终矩阵。矩阵几乎总是大于 4x4,最大值可以用于原始矩阵上的任何矩阵平铺。对于 60x60 矩阵,平铺可以是 2x2、3x3、4x4 等。

标签: matlab matrix vectorization


【解决方案1】:

更新:我不确定我是如何获得最多票的(截至 2012 年 10 月 28 日)。对于阅读本文的任何人,请参阅 angainor 或 Rody 的答案以获得不需要任何额外工具箱的更好解决方案。

到目前为止,这是每个答案的赛马(不包括 Nates - 抱歉,没有必要的工具箱):

Z = 1000;

A = [1 1 2 2; 1 1 2 2; 3 3 4 4; 3 3 4 4];
w = 2;

%Method 1 (OP method)
tic
for z = 1:Z
S = zeros(size(A, 1)/w);
for i = 1:size(S)
  for j = 1:size(S)
    Window = A(i*w-1:i*w, j*w-1:j*w);
    S(i, j) = max(max(Window));
  end
end
end
toc

%Method 2 (My double loop with improved indexing)
tic
for z = 1:Z
wm = w - 1;
Soln2 = NaN(w, w);
for m = 1:w:size(A, 2)
    for n = 1:w:size(A, 1)
        Soln2((m+1)/2, (n+1)/2) = max(max(A(n:n+wm, m:m+wm)));
    end
end
Soln2 = Soln2';
end
toc


%Method 3 (My one line method)
tic
for z = 1:Z
Soln = cell2mat(cellfun(@max, cellfun(@max, mat2cell(A, [w w], [w w]), 'UniformOutput', false), 'UniformOutput', false));
end
toc

%Method 4 (Rody's method)
tic
for z = 1:Z
b = [A(1:2,:) A(3:4,:)];
reshape(max(reshape(b, 4,[])), 2,2);
end
toc

速度测试(z上的循环)的结果是:

Elapsed time is 0.042246 seconds.
Elapsed time is 0.019071 seconds.
Elapsed time is 0.165239 seconds.
Elapsed time is 0.011743 seconds.

该死!看来罗迪(+1)是赢家。 :-)

更新:新的参赛者(+1)领跑!

【讨论】:

  • @nate 是的,发现了这一点,尽管我通过切换循环的顺序来解决它。很抱歉没有在赛马中包含你的方法,但我没有blockproc 工具箱
  • @nate 糟糕,我的切换方法不起作用!我改用了你建议的转置。干杯!
  • 看起来我们有同一台电脑,科林 :)
  • 注意到了一些有趣的事情:在 matlab 2010 上,我得到了大致相同的结果,但在 2008 年,方法 2 明显变慢(与方法 1 大致相同)
  • @DennisJaheruddin 在过去的 4 年里,JIT 加速器有了一些显着的改进。在跑马之前输入feature accel off,你就会明白我的意思了。特别要注意的是,方法 1 和 2 在 R2012b 中在关闭 JIT 加速器的情况下具有相似的性能。
【解决方案2】:

不是很通用,但它适用于a

b = [a(1:2,:) a(3:4,:)];
reshape(max(reshape(b, 4,[])), 2,2).'

这个的一般版本有点 *ahum* fuglier:

% window size
W = [2 2];

% number of blocks (rows, cols)
nW = size(a)./W;


% indices to first block
ids = bsxfun(@plus, (1:W(1)).', (0:W(2)-1)*size(a,1));

% indices to all blocks in first block-column
ids = bsxfun(@plus, ids(:), (0:nW(1)-1)*W(1));

% indices to all blocks
ids = reshape(bsxfun(@plus, ids(:), 0:nW(1)*prod(W):numel(a)-1), size(ids,1),[]);

% maxima
M = reshape(max(a(ids)), nW)

可以做得更优雅一点:

b = kron(reshape(1:prod(nW), nW), ones(W));    
C = arrayfun(@(x) find(b==x), 1:prod(nW), 'uni', false);    
M = reshape(max(a([C{:}])), nW)

但我怀疑这会更快......

【讨论】:

  • 您必须转置结果 - OP 说顺序很重要。
  • @angainor:谢谢,已编辑。您能否看看我的第二个解决方案,看看索引的生成是否可以更有效地完成?
  • 看起来我打算做什么;)虽然我确信它可以以某种方式缩短..
【解决方案3】:

另一种选择:比 cell2mat(cellfun...) 代码慢,但给出了中间步骤:

fun = @(block_struct) reshape((block_struct.data), [],1);
B = reshape(blockproc(A,[2 2],fun),2,2,[])
r=reshape(max(max(B)) ,2,[])

B(:,:,1) =

 1     1
 1     1


B(:,:,2) =

 3     3
 3     3


B(:,:,3) =

 2     2
 2     2


B(:,:,4) =

 4     4
 4     4

r =

 1     2
 3     4

【讨论】:

    【解决方案4】:

    我将加入另一个基于线性索引的非通用(尚未)解决方案

    idx = [1 2 5 6; 3 4 7 8]';
    splita = [A(idx) A(idx+8)];
    reshape(max(splita), 2, 2);
    

    Colins代码得到的次数,我的方法最后:

    Elapsed time is 0.039565 seconds.
    Elapsed time is 0.021723 seconds.
    Elapsed time is 0.168946 seconds.
    Elapsed time is 0.011688 seconds.
    Elapsed time is 0.006255 seconds.
    

    idx 数组可以轻松推广到更大的窗口和系统大小。

    【讨论】:

    • 非常整洁!我只是在用线性索引研究类似的想法,但没有设法让时间低于罗迪的时间。 +1!我想是时候回去工作了……
    • 是否有一种方法可以泛化 idx 矩阵的创建以支持任何给定的原始矩阵和窗口大小?
    • @NickEwing Rody 在他的扩展答案中展示了一种方法。带有bsxfun 的版本就是您要查找的版本。
    【解决方案5】:

    注意:Nate 的解决方案使用图像处理工具箱函数 |blockproc|。我会重写:

    fun = @(x) max(max(x.data));
    r = blockproc(A,[2 2],fun)
    

    比较不同计算机之间的时间是充满困难的,就像在几分之一秒内发生的事情一样。 TIMEIT 在这里很有用:

    http://www.mathworks.com/matlabcentral/fileexchange/18798

    但是在我的电脑上用 tic/toc 计时需要 0.008 秒。

    干杯, 布雷特

    【讨论】:

    • 嗨,布雷特。同意timeit 是一个很好的工具,如果你想正确地计时功能性能。但是,请注意,上面我们实际上是在每个例程上循环了 1000 次,而不是一次,因此可以将结果视为(未缩放的)算术平均值,它应该会使事情变得平滑一点。它肯定不会像timeit 那样健壮,并且在计算机之间切换肯定会增加噪音,但有时当你为一个 SO 问题四处乱窜时,不必为函数句柄等而烦恼:-)跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多