Как сравнить все патчи на двух изображениях в Matlab?
Автор вопроса: akhilcОтветов: 2
У меня есть два изображения, скажем, 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'.
Вы на самом деле сделали хорошую векторизацию. Кажется, что это не может быть значительно улучшено. Рассмотрим алгоритмические улучшения. Мол, вам действительно нужно сравнивать ВСЕ патчи со ВСЕМИ? Mikhail 25.03.2014
У вас есть GPU с двойной точностью? А какая у вас версия MATLAB? Rody Oldenhuis 25.03.2014
Я не знаю о GPU. Моя версия Matlab 2012b. akhilc 25.03.2014
@RodyOldenhuis: вы имеете в виду, что я должен сначала написать "для столбцов", а затем "для строк" при цикле? akhilc 25.03.2014
Что вы увидите, если наберете gpuDevice в командной строке? Rody Oldenhuis 25.03.2014
@RodyOldenhuis Я использую Matlab в Ubuntu. Когда я набрал gpuDevice, он показывает ошибку. Возникла проблема с драйвером CUDA, связанным с этим устройством GPU. Причина: Не удалось загрузить драйвер CUDA. Используемое имя библиотеки было "libcuda.so.1". Ошибка: libcuda.so.1: невозможно открыть общий объектный файл: нет такого файла или каталога akhilc 26.03.2014
Кстати, если вам не нужны ВСЕ расстояния и вы готовы пойти на компромисс для приближения для k ближайших соседей, вы можете найти PatchMatch очень полезен: Это приблизительный алгоритм k ближайших соседей, адаптированный для патчей изображений. Он очень эффективен с точки зрения использования памяти (пространства) и очень быстр.
Ответ 2 · рейтинг 3 · Rody Oldenhuis
Как упомянул Михаил, вы действительно проделали потрясающую работу. На самом деле не так уж много можно улучшить.
Есть только несколько незначительных вещей, которые вы можете сделать:
поменять местами порядок обработки матриц
поменять порядок циклов
Поскольку 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).