Возможности для улучшения в этом коде - PullRequest
5 голосов
/ 19 мая 2011

Я написал следующий код в MATLAB для обработки больших изображений порядка 3000x2500 пикселей. В настоящее время операция занимает более получаса. Есть ли возможность улучшить код, чтобы он занимал меньше времени? Я слышал, что параллельная обработка может ускорить процесс, но я не знаю, как это реализовать. Как мне это сделать, учитывая следующий код?

function dirvar(subfn)
[fn,pn] = uigetfile({'*.TIF; *.tiff; *.tif; *.TIFF; *.jpg; *.bmp; *.JPG; *.png'}, ...
            'Select an image', '~/');
I = double(imread(fullfile(pn,fn)));
ld = input('Enter the lag distance = '); % prompt for lag distance
fh = eval(['@' subfn]); % Function handles
I2 = uint8(nlfilter(I, [7 7], fh));
imshow(I2); % Texture Layer Image
imwrite(I2,'result_mat.tif');

% Zero Degree Variogram
function [gamma] = ewvar(I)
    c = (size(I)+1)/2; % Finds the central pixel of moving window
    EW = I(c(1),c(2):end); % Determines the values from central pixel to margin of window
    h = length(EW) - ld; % Number of lags
    gamma = 1/(2 * h) * sum((EW(1:ld:end-1) - EW(2:ld:end)).^2);
end

Входное расстояние задержки обычно равно 1.

Ответы [ 3 ]

3 голосов
/ 19 мая 2011

Вам действительно нужно использовать профилировщик, чтобы получить от него некоторые улучшения.Мое первое предположение (так как я не запустил профилировщик, который вы, как уже предлагали), было бы использовать как можно меньше операций length.Поскольку вы обрабатываете каждое изображение с окном [7 7], вы можете предварительно рассчитать некоторые детали, чтобы не повторять эти действия

function dirvar(subfn)
[fn,pn] = uigetfile({'*.TIF; *.tiff; *.tif; *.TIFF; *.jpg; *.bmp; *.JPG; *.png'}, ...
            'Select an image', '~/');
I = double(imread(fullfile(pn,fn)));
ld = input('Enter the lag distance = '); % prompt for lag distance
fh = eval(['@' subfn]); % Function handles

%% precalculations
wind = [7 7];
center = (wind+1)/2; % Finds the central pixel of moving window
EWlength = (wind(2)+1)/2;
h = EWlength - ld; % Number of lags

%% calculations
I2 = nlfilter(I, wind, fh);
imshow(I2); % Texture Layer Image
imwrite(I2,'result_mat.tif');

% Zero Degree Variogram
function [gamma] = ewvar(I)
    EW = I(center(1),center(2):end); % Determines the values from central pixel to margin of window
    gamma = 1/(2 * h) * sum((EW(1:ld:end-1) - EW(2:ld:end)).^2);
end
end

Обратите внимание, что при этом вы торгуете производительностью для ясности вашихкод и связь (между функцией dirvar и вложенной функцией ewvar).Однако, поскольку я не профилировал ваш код (вы должны сделать это самостоятельно, используя свои собственные входные данные), вы можете найти, какая строка вашего кода потребляет больше всего времени.

Для пакетной обработки я бы также рекомендовал:пропустите любые input, imshow, imwrite и uigetfile.Это команды, которые вы обычно вызываете из более высокоуровневой функции / скрипта и которые заставят вас вводить эти входные данные, даже если вы хотите, чтобы они оставались неизменными.Поэтому вместо этого кода сделайте каждую из переменных, которые они создают (/ process), параметром (/ return value) для вашей функции.Таким образом, вы можете оставить MATLAB включенным в выходные дни для обработки всего (без необходимости вручную вводить все эти значения), даже если вы не можете ускорить код.

3 голосов
/ 19 мая 2011

Несколько трюков общего назначения:

1 - используйте профилировщик MATLAB , чтобы определить все вычислительные узкие места

2 - параллельная обработка может ускорить процесс, и вы можете использовать множество инструментов, но это зависит от того, как настроен весь ваш код и оптимизирован ли для него код. Безусловно, самый простой трюк для изучения - parfor , где вы можете заменить цикл for верхнего уровня на parfor. Это означает, что вы должны открыть пул MATLAB с помощью matlabpool open.

3 - Если у вас достаточно недавний графический процессор Nvidia , а также MATLAB 2011, вы также можете написать некоторый код CUDA .

Все 30 минут для меня - это арахис, так что не беспокойтесь слишком сильно.

1 голос
/ 19 мая 2011

Прежде всего, я настоятельно рекомендую вам следовать совету @Egon: написать отдельную функцию, которая собирает список файлов (отличный UIPICKFILES из FEX - ваш друг здесь), а затем запускаетВаш код фильтрации в цикле для каждого изображения.Обратите внимание, что вы обязательно должны сохранить вызов imwrite в своем фильтрующем коде: в случае сбоя анализа на изображении 48 (например, из-за сбоя питания) вы не хотите терять всю предыдущую работу.

Таким образом, выполнение в пакетном режиме имеет два больших преимущества: (1) вы можете запустить свой код и пойти домой на выходные, и (2) вы можете легко распараллелить этот внешний цикл, используя PARFOR .Однако при использовании только двухъядерной машины маловероятно, что вы получите какие-либо существенные улучшения от распараллеливания - ваша ОС также хочет время от времени запускать что-то, и издержки распараллеливания могут быть больше, чем выгода от работы двух рабочих.Кроме того, 2,5 ГБ ОЗУ серьезно ограничивают.

Что касается вашего конкретного кода: по моему опыту использование IM2COL часто быстрее, чем NLFILTER .im2col создает массив nElementsInMask-by-nMasks из вашего изображения, так что вы можете применить фильтрацию за одну операцию.С окном 7x7 вывод im2col будет 3000 *2500* 49 байтов, что близко к 400 МБ.Таким образом, это должно просто работать.Все, что вам нужно сделать, это переписать ewvar так, чтобы он работал на массиве пикселей 49x1, которые составляют пиксели вашей маски, что потребует некоторого жонглирования индексом, если я правильно понимаю ваш код.

...