2012-10-25 5 views
5

Angesichts der Matrix:Unter der max von zusammenhängenden Matrix Chunks in MATLAB

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

Ich möchte die folgenden vier 2x2 Matrizen erhalten:

a1 = 
    1 1 
    1 1 

a2 = 
    2 2 
    2 2 

a3 = 
    3 3 
    3 3 

a4 = 
    4 4 
    4 4 

Von dort aus möchte ich die nehmen max jeder Matrix und dann das Ergebnis in eine Matrix 2x2 Ergebnis umformen, etwa so:

r = 
    1 2 
    3 4 

die Lage des Ergebnisses max va Lues relativ zu ihrer ursprünglichen Position in der Ausgangsmatrix ist wichtig.

Derzeit bin ich mit dem folgenden Code, dies zu erreichen:

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 

Dies funktioniert, aber es scheint, als gäbe es einen Weg geben muss, die nicht Iteration (Vektorisierung) beinhaltet.

ich versuchte, mit neu zu gestalten, wie so: reshape(max(max(reshape(A, w, w, []))), w, w, []) jedoch, dass der max des falschen Wert und Rückkehr nimmt:

ans = 
    3 4 
    3 4 

Gibt es eine Möglichkeit, dies ohne Iteration zu erreichen oder sonst mein iteratives Verfahren zu verbessern?

+0

Hier ist eine Methode, die eine Zeile 4-mal langsamer als die Schleife ist: 'Soln = cell2mat (cellfun (@Max, cellfun (@Max, mat2cell (A, [2 2], [2 2]), 'UniformOutput', false), 'UniformOutput', false)); '. Sie haben gefragt, wie man es ohne Iteration macht :-) Aber ich habe es zu einem Kommentar gemacht, nicht zu einer Antwort, weil die Iteration aus der Geschwindigkeitsperspektive wirklich vorzuziehen ist. –

+1

Brauchen Sie die intermediären 2x2 Matrizen für etwas anderes oder interessiert Sie nur die finale Matrix 'r'? –

+0

yep, das war auch mein Gedanke ... – bla

Antwort

2

nicht sehr allgemein, aber es funktioniert für a:

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

Die Allgemeine Version von diesem ist ein bisschen * 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) 

Es kann ein wenig mehr getan werden e elegant:

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) 

aber ich bezweifle, dass Gonna schneller ist ...

+0

Sie müssen das Ergebnis transponieren - OP sagte, dass die Reihenfolge wichtig ist. – angainor

+0

@angainor: Danke, bearbeitet. Können Sie sich vielleicht meine zweite Lösung anschauen, um zu sehen, ob die Generierung der Indizes effizienter ist? –

+0

Sieht aus, was ich vorhatte zu tun;) Obwohl ich bin mir sicher, dass es irgendwie kürzer gemacht werden kann. – angainor

2

Eine weitere Option: langsamer als der cell2mat (cellfun ...) Code, aber der Zwischenschritt gibt:

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 
3

UPDATE: Ich bin nicht sicher, wie ich mit den meisten Stimmen am Ende haben (Stand 2012-10- 28). Für alle, die dies lesen, lesen Sie bitte angainors oder Rodys Antworten für bessere Lösungen, die keine zusätzlichen Toolboxen benötigen.

Hier ist ein Pferderennen jeder Antwort so weit (Nates ohne - sorry, nicht die erforderliche Toolbox haben):

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 

Die Ergebnisse des Geschwindigkeitstest (die Schleife über z) sind:

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

Drat! Es scheint, dass Rody (+1) der Gewinner ist. :-)

UPDATE: Neueinsteiger zum Rennen angainor (+1) übernimmt die Führung!

+1

Soln2 -> Soln2 ' – bla

+0

@nate Ja, entdeckte das, obwohl ich es löste, indem ich die Reihenfolge der Schleife umschaltete. Entschuldigung dafür, dass du deine Methode nicht im Pferderennen eingibst, aber ich habe keine 'blockproc' Toolbox –

+0

Kein Problem! es hat Spaß gemacht. – bla

2

ich das Pferderennen mit einem anderen nicht-General beitreten werden (noch;) Lösung auf Basis von linearen Indizes

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

Die von Colins Code erhalten Zeiten, meine Methode zuletzt:

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. 

Das idx Array kann leicht zu größeren Fenstern und Systemgrößen verallgemeinert werden.

+0

Sehr ordentlich! Ich arbeitete gerade an einer ähnlichen Idee mit linearen Indizes, hatte es aber nicht geschafft, die Zeit unter Rodys zu bekommen. +1! Zeit für mich, wieder an die Arbeit zu gehen, denke ich ... –

+0

Wäre es möglich, die Erstellung der idx-Matrix zu verallgemeinern, um jede gegebene ursprüngliche Matrix- und Fenstergröße zu unterstützen? –

+0

@NickEwing Rody hat eine Methode in seiner erweiterten Antwort gezeigt. Die Version mit 'bsxfun' ist, was Sie suchen. – angainor

0

Hinweis: Nates Lösung verwendet die Image Processing Toolbox-Funktion | blockproc |. Ich würde das umschreiben:

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

Vergleich Timing auf verschiedenen Computern mit Schwierigkeiten behaftet ist, wie es einmal Timing Dinge, die in einem Bruchteil einer Sekunde passiert. Timeit wäre hier nützlich:

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

Aber Timing diese auf meinem Computer mit tic/toc nahm 0,008 Sekunden.

Prost, Brett

+0

Hallo Brett. Vereinbart, dass "Zeit" ist ein großartiges Werkzeug, wenn man Zeit Leistung richtig funktionieren will.Beachten Sie jedoch, dass wir oben jede Routine 1000 Mal wiederholen, nicht einmal, daher können die Ergebnisse als ein (unskalierter) arithmetischer Durchschnitt betrachtet werden, der die Dinge etwas glättet. Es wird definitiv nicht so robust sein wie "Zeit" und das Umschalten zwischen Computern bringt definitiv Lärm mit sich, aber manchmal, wenn Sie für eine SO-Frage hacken, ist es einfacher, sich nicht mit Funktionsgriffen zu befassen. –