2012-10-25 9 views
5

Biorąc pod uwagę macierz:Biorąc max sąsiadujących fragmentów macierzy w programie MATLAB

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

Chciałbym uzyskać następujące cztery macierze 2x2:

a1 = 
    1 1 
    1 1 

a2 = 
    2 2 
    2 2 

a3 = 
    3 3 
    3 3 

a4 = 
    4 4 
    4 4 

Stamtąd chciałbym podjąć max każdej macierzy, a następnie przekształć wynik na macierz wyników 2x2, tak jak:

r = 
    1 2 
    3 4 

Położenie wyniku max va luki w stosunku do ich pierwotnego położenia w początkowej macierzy są ważne.

Obecnie używam następujący kod do osiągnięcia tego celu:

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 

To działa, ale wydaje się, że musi być jakiś sposób, który nie wiąże się z iteracji (wektoryzacji).

Próbowałem za pomocą przekształcenia tak: reshape(max(max(reshape(A, w, w, []))), w, w, []) jednak, że bierze max niewłaściwych wartości i zwrotów:

ans = 
    3 4 
    3 4 

Czy istnieje jakiś sposób, aby osiągnąć ten cel bez iteracji lub w inny sposób poprawić swój iteracyjny sposób?

+0

tu jest sposób jeden przewód, który jest 4 razy wolniej niż pętli: 'roztw = cell2mat (cellfun (@max, cellfun (@max, mat2cell (A [2 2] [2 2]), 'UniformOutput', false), 'UniformOutput', false)); '. Poprosiłeś o to, jak to zrobić bez iteracji :-) Ale zrobiłem to jako komentarz, a nie odpowiedź, ponieważ iteracja jest naprawdę lepsza z perspektywy szybkości. –

+1

Czy potrzebujesz pośrednich matryc 2x2 dla czegoś innego, czy tylko interesuje cię ostatnia macierz 'r'? –

+0

Tak, to też była moja myśl ... – bla

Odpowiedz

2

Nie bardzo ogólne, ale to działa na a:

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

ogólna wersja tego jest nieco * 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) 

Można zrobić trochę więcej e elegancko:

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) 

ale wątpię, że to będzie szybciej ...

+0

Trzeba przetransponować wynik - OP powiedział, że kolejność jest ważna. – angainor

+0

@angainor: Dzięki, edytowane. Czy możesz ewentualnie przyjrzeć się mojemu drugiemu rozwiązaniu, czy generowanie indeksów można wykonać bardziej efektywnie? –

+0

Wygląda, co planowałem zrobić;) Chociaż jestem pewien, że można to zrobić w jakiś sposób krótszy .. – angainor

2

Inna opcja: wolniej niż cell2mat (cellfun ...) kod, ale daje pośredni krok:

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: Nie jestem pewien, jak ja skończyliśmy z największą liczbą głosów (stan na 2012-10- 28). Dla każdego, kto to czyta, zobacz odpowiedzi Angainora lub Rody na lepsze rozwiązania, które nie wymagają żadnych dodatkowych narzędzi.

Oto wyścigów konnych z każdej odpowiedzi do tej pory (z wyłączeniem nates - przepraszam, nie mają wymaganego Toolbox):

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 

Wyniki testu prędkości (pętla nad z) są:

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! Wygląda na to, że zwyciężył Rody (+1). :-)

AKTUALIZACJA: Nowy uczestnik wyścigu angainor (+1) przejmuje inicjatywę!

+1

@Narn2 -> Soln2 ' – bla

+0

@poprawny Yep, zauważyłem, że chociaż rozwiązałem go, przełączając kolejność pętli. Przepraszam, że nie uwzględniłem twojej metody w wyścigu konnym, ale nie mam zestawu narzędzi 'blockproc'a –

+0

Bez obaw! to była zabawa. – bla

2

Dołączę koniem wyścigu z innymi non Generalny (jeszcze;) rozwiązania, oparte na indeksach liniowych

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

uzyskane przez kod Colins razy, moja metoda ostatniego:

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. 

Tablicę idx można łatwo uogólnić na większe okna i rozmiary systemu.

+0

Bardzo miłe! Właśnie pracowałem nad podobnym pomysłem z liniowymi wskaźnikami, ale nie zdążyłem znaleźć czasu poniżej Rody'ego. +1! Czas na powrót do pracy, myślę, że ... –

+0

Czy istnieje sposób na uogólnienie procesu tworzenia macierzy IDX w celu obsługi dowolnego oryginalnego rozmiaru matrycy i okna? –

+0

@NickEwing Rody pokazał metodę w swojej rozszerzonej odpowiedzi. Wersja z 'bsxfun' jest tym, czego szukasz. – angainor

0

Uwaga: Rozwiązanie Nate wykorzystuje funkcję Image Processing Toolbox | blockproc |. Chciałbym przepisać że:

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

Porównując rozrządu na różnych komputerach jest najeżona trudnościami, jak raz taktowania rzeczy, które dzieją się w ułamku sekundy. Timeit byłyby użyteczne tutaj:

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

Ale rozrządu to na moim komputerze z tic toc/czas: 0.008 sek.

zdrowie, Brett

+0

Witam Bretta. Uzgodniono, że 'timeit' to świetne narzędzie, jeśli chce się odpowiednio poprawić wydajność funkcji.Należy jednak pamiętać, że powyżej pętamy ponad każdą rutynę 1000 razy, ani razu, dlatego wyniki mogą być uważane za (nieskalowaną) średnią arytmetyczną, która powinna trochę wygładzić. Zdecydowanie nie będzie tak solidny, jak "timeit", a przełączanie się między komputerami zdecydowanie dodaje szum, ale czasami, gdy hakujesz na pytanie SO, po prostu łatwiej nie musisz zawracać sobie głowy uchwytami funkcji itp :-) –

Powiązane problemy