Как сравнить все патчи на двух изображениях в Matlab?

У меня есть два изображения, скажем, A и B, размеры которых не обязательно должны быть одинаковыми. A иметь размер 255×255 и B иметь размер 100×100. А размер патча для моей задачи 5х5. Что мне нужно сделать, так это сравнить все патчи в A со всеми патчами в B.

Каждый патч будет перекрываться с некоторыми соседними патчами. Чтобы очистить эту точку, первым патчем в A будет A(1:5,1:5) (нотация MATLAB). Второй патч A(2:6,1:5) и так далее, вплоть до A(251:255,1:5) в конце первой строки и до A(251:255,251:255) для последнего патча в A.

Мне нужно сравнить каждое из этих исправлений с всеми исправлениями в B. Как видите, есть 251*251 патчей в A и 96*96 патчей в B. Так что есть много сравнений, которые нужно сделать. А сравнение — это просто евклидово расстояние, т. е. я просто возьму разность двух участков и суммирую квадраты. Для каждого сравнения патчей я получу в результате скалярное значение.

Я реализовал это в MATLAB, но выполнение занимает несколько минут. Поэтому, пожалуйста, предложите мне самый быстрый способ реализовать это. Этот раздел является узким местом во всем моем проекте. Код, который я написал, приведен ниже. Я не эксперт, так что, пожалуйста, простите за ошибки.

row = size(A,1);    
col = size(A,2);

row2 = size(B,1);    
col2 = size(B,2);

patch_long = zeros(5,5,(row2-4)*(col2-4));

idx = 1;

for i = 1:row2-4    
    for j = 1:col2-4

        patch_long(:,:,idx) = B(i:i+4,j:j+4);

        idx = idx+1;

    end

end

%// I rearranged 'B' matrix as 3d matrix with each patch arranged like 
%// slide behind one by one

parfor m = 1:row-4    
    for n = 1:col-4

        temp1 = bsxfun(@minus,(A(m:m+4,n:n+4)),patch_long);
        temp2 = sum(sum(temp1.^2));

        count = sum(temp2 <=threshold);

        if count > 1 
            % Do xyzend
        end

    end
end

%// count counts how many patches in 'B' are close to a particular patch in 'A'.

person akhilc    schedule 25.03.2014    source источник
comment
Вы на самом деле сделали хорошую векторизацию. Кажется, что это не может быть значительно улучшено. Рассмотрим алгоритмические улучшения. Мол, вам действительно нужно сравнивать ВСЕ патчи со ВСЕМИ?   -  person Mikhail    schedule 25.03.2014
comment
У вас есть GPU с двойной точностью? А какая у вас версия MATLAB?   -  person Rody Oldenhuis    schedule 25.03.2014
comment
Я не знаю о GPU. Моя версия Matlab 2012b.   -  person akhilc    schedule 25.03.2014
comment
@RodyOldenhuis: вы имеете в виду, что я должен сначала написать «для столбцов», а затем «для строк» ​​при цикле?   -  person akhilc    schedule 25.03.2014
comment
Что вы увидите, если наберете gpuDevice в командной строке?   -  person Rody Oldenhuis    schedule 25.03.2014
comment
@RodyOldenhuis Я использую Matlab в Ubuntu. Когда я набрал gpuDevice, он показывает ошибку. Возникла проблема с драйвером CUDA, связанным с этим устройством GPU. Причина: Не удалось загрузить драйвер CUDA. Используемое имя библиотеки было «libcuda.so.1». Ошибка: libcuda.so.1: невозможно открыть общий объектный файл: нет такого файла или каталога   -  person akhilc    schedule 26.03.2014
comment
Да, это просто означает, что у вас нет графического устройства с поддержкой CUDA (карта NVIDIA с арифметикой DP), так что это не вариант.   -  person Rody Oldenhuis    schedule 26.03.2014


Ответы (2)


Вы можете использовать im2col для извлечения всех исправлений.

pA = im2col( A, [5 5], 'sliding' );
pB = im2bol( B, [5 5], 'sliding' );
% compute squared difference
D = sum( bsxfun( @minus, permute( pA, [2 3 1] ), permute( pB, [3 2 1] ) ).^2, 3 );

Кстати, если вам не нужны ВСЕ расстояния и вы готовы пойти на компромисс для приближения для k ближайших соседей, вы можете найти PatchMatch очень полезен:
Это приблизительный алгоритм k ближайших соседей, адаптированный для патчей изображений. Он очень эффективен с точки зрения использования памяти (пространства) и очень быстр.

person Shai    schedule 25.03.2014
comment
Глядя на мою реализацию im2col (сейчас я на R2010b), она не делает ничего отличного от того, что уже делал OP, за исключением того, что использует намного больше памяти... Кроме сокращения кода, я не могу добиться существенной разницы в скорости. . Возможно, реализация изменилась в более поздних версиях; Вы можете проверить ускорение с вашей (более новой) версией? - person Rody Oldenhuis; 25.03.2014
comment
@RodyOldenhuis У меня нет доступа к более новым версиям. Извините. - person Shai; 25.03.2014
comment
Мне нужно найти не более 10 патчей, удовлетворяющих пороговым критериям. Я имею в виду для патча, если нет патча с расстоянием меньше порогового, все в порядке. Но если патчей больше 10, то я выберу ближайшие 10 патчей. Но как мне найти 10 ближайших соседей, не сравнивая все патчи? Еще одно ограничение заключается в том, что порог представляет собой матрицу размером 251x251 (как в примере). Он имеет порог, соответствующий каждому патчу в A. - person akhilc; 25.03.2014
comment
@akhilc, в этом случае вам нужны только 10 ближайших соседей (NN) каждого патча: если они ближе порога; вы используете их все, если не используете только часть из них. Чтобы получить ТОЧНО 10 NN, вам нужно тщательно сравнить все остальные. НО, если вы хотите сделать некоторые приближения, вы можете получить (приблизительно) 10 NN намного быстрее. Взгляните на PatchMatch! - person Shai; 25.03.2014
comment
@ Шай, я не уверен, как приблизительный NN повлияет на весь проект. Позвольте мне попробовать. Спасибо. - person akhilc; 25.03.2014
comment
@akhilc - если вы экспериментируете с PatchMatch, не могли бы вы опубликовать некоторую информацию о ваших результатах: по крайней мере, с точки зрения времени выполнения. Насколько быстро работает PatchMatch по сравнению со сравнением патчей методом грубой силы. - person Shai; 25.03.2014
comment
Если я вас правильно понял, PatchMatch — это ранняя версия метода грубой силы. Это означает, что это все еще O(N²) наихудший случай (ни один, кроме самых последних патчей, не ниже порогового значения). Это правильно? - person Rody Oldenhuis; 25.03.2014
comment
@RodyOldenhuis Теоретически вы правы, PatchMatch будет выполнять свои итерации и достигать сложности O (N ^ 2). Но статистический анализ алгоритма позволяет ОЧЕНЬ досрочно завершить итерационный процесс при очень низкой стоимости аппроксимации. На практике (если я правильно помню) его сложность составляет всего O (N) при очень небольшой ошибке аппроксимации. - person Shai; 25.03.2014
comment
@Shai, я скачал Patchmatch, но не знаю, как его запустить. Внутри него много файлов. В нем также нет «readme.txt». И я попытался прочитать соответствующую статью, но это не так просто. Поэтому я не совсем уверен, смогу ли я действительно сделать это полезным. Однако, спасибо. - person akhilc; 26.03.2014
comment
@akhilc Я вижу файл readme.txt в версии, которую я скачал с здесь . - person Shai; 26.03.2014

Как упомянул Михаил, вы действительно проделали потрясающую работу. На самом деле не так уж много можно улучшить.

Есть только несколько незначительных вещей, которые вы можете сделать:

  1. поменять местами порядок обработки матриц
  2. поменять порядок циклов

Поскольку A меньше, чем B, изменение порядка обработки заставит bsxfun выполнять большую часть тяжелой работы. Поскольку это «ближе к кремнию», чем JIT-цикл MATLAB, это будет выгодно.

Выполнение простого теста tic...toc с A = rand(255) и B = rand(10) (у меня нет всего дня! :) дает

Elapsed time is 3.460361 seconds.  %// original processing order
Elapsed time is 1.629848 seconds.  %// inverted processing order 

В вашем случае разница будет меньше, конечно, но это может быть существенно.

Переключение порядка цикла (перебор строк во внутреннем цикле, столбцов во внешнем цикле) работает, потому что MATLAB хранит данные в порядке столбцов. Изменение порядка означает, что вычисление смещения указателя может быть повторно использовано во внутреннем цикле, что требует меньшего количества операций. Обычно это может иметь разницу до ~ 30%, однако последние версии MATLAB значительно улучшили это (до сих пор не знаю, как), и вы используете не 1 столбец, а 5... так что это, вероятно, не будет как значительный.

Выполнение того же теста с другим порядком цикла дает:

Elapsed time is 3.462599 seconds. %// original loop order
Elapsed time is 3.272909 seconds. %// inverted loop order

Соединяем все:

Elapsed time is 1.571496 seconds. %// Both optimizations implemented

Мой код:

%// random data for testing
A = rand(255);
B = rand(10);
threshold = rand;

[rowA, colA] = size(A);
[rowB, colB] = size(B);

patch_long = zeros(5,5, (rowA-4)*(colA-4));

tic

%// re-arrange A into 3D array of 5×5 patches 
idx = 1;
for jj = 1:colA-4     
    c = jj:jj+4;      %// re-use column indices
    for ii = 1:rowA-4 %// inner loop is over the rows

        patch_long(:,:,idx) = A(ii:ii+4, c);        
        idx = idx+1;

    end

end

%// The heavy lifting
for n = 1:colB-4     %// let bsxfun do more work; loop over smallest array here
    c = n:n+4;       %// re-use column indices
    for m = 1:rowB-4 %// inner loop is over the rows

        temp1 = bsxfun(@minus, B(m:m+4, c), patch_long);
        count = sum( sum(temp1.^2) <= threshold );

    end
end

toc

Обратите внимание, что я не использовал parfor, просто чтобы все было доступно для копирования для всех. Использование parfor в реальной задаче дает мне

Elapsed time is 387.802798 seconds. %// original version, but without parfor
Elapsed time is 362.991760 seconds. %// both optimizations, WITH parfor

но я думаю, что это в основном потому, что мой рабочий ПК довольно плохой (AMD-A6 3650 APU).

person Rody Oldenhuis    schedule 25.03.2014
comment
большое спасибо. позвольте мне попробовать это и вернуться к вам позже. - person akhilc; 25.03.2014
comment
Я запустил ваш оптимизированный код для A (255,255) и B (50,50). Потребовалось 30 секунд для метода «сначала строка, затем столбец» и 29,5 для предложенного вами метода зацикливания. Я думаю, что для большего изображения это может сэкономить мне значительное количество времени. Ваше второе предложение, хотя мне немного сложно включить. Я имею в виду использование цикла по «B». Это потому, что «порог» на самом деле является матрицей. Для каждого патча в «А» есть уникальное пороговое значение. Таким образом, «порог» представляет собой матрицу 251x251, и сравнение на самом деле является суммой (сумма (temp1. ^ 2 ‹ = порог (m, n). То есть, когда я перебираю «A», это легче написать. - person akhilc; 26.03.2014
comment
@akhilc: (y-x)² < z для матриц x, y и c можно записать как (y<x & y>x-sqrt(z)) | (y>=x & y<x+sqrt(z)). Таким образом, вы можете предварительно вычислить A-sqrt(c) и A+sqrt(c) в цикле A и использовать эти значения в цикле B. Однако эти сравнения должны быть выполнены с помощью ЧЕТЫРЕХ вызовов bsxfun, поэтому я не уверен, что это будет эффективно... Но попробовать стоит. - person Rody Oldenhuis; 26.03.2014