Эффективная реализация conv2 (действует) в image_data - PullRequest
0 голосов
/ 20 ноября 2018

Я пытаюсь реализовать conv2 (2D-функция свертки в MATLAB) с аргументом 'valid', который возвращает только те части свертки, которые вычисляются без ребер с нулевой подкладкой, то есть ядро ​​не сканирует за пределамиinput.

Пока у меня есть этот код, который работает, но, как вы можете видеть, он кажется излишне сложным, и я планирую преобразовать его в фиксированную точку и реализовать его на оборудовании позже, а переменную SampleWindowпродолжает доставлять мне неприятности, потому что кодер назначает ему динамическую матрицу.

Поэтому я ищу более простую и / или эффективную реализацию функции.

function outConvAcc = convn(input, kernel, S)
% Get the input size in terms of rows and cols. The weights should have
% same depth as the input volume(image)
[rowsIn, colsIn, depthInput] = size(input);


% Get the kernel size, considering a square kernel always
F = size(kernel,1);
kernelf=rot90(squeeze(kernel),2);
%% Initialize outputs
sizeRowsOut = ((rowsIn-F)/S) + 1;
sizeColsOut = ((colsIn-F)/S) + 1;
outConvAcc = zeros(sizeRowsOut , sizeColsOut, depthInput);

%% Do the convolution
% Convolve each channel on the input with it's respective kernel channel,
% at the end sum all the channel results.

for r=1:S:(rowsIn-1)
    for c=1:S:(colsIn-1)
        % Avoid sampling out of the image.
        if (((c+F)-1) <= colsIn) && (((r+F)-1) <= rowsIn)
            % Select window on input volume (patch)
            sampleWindow = input(r:(r+F)-1,c:(c+F)-1);
            % Do the dot product                
            dotProd =(sampleWindow(:) .* kernelf(:));
            n=size(dotProd,1);
            dotProdc=0;
            for km=1:n   % Replace function Sum for code generation
            dotProdc=dotProd(km)+dotProdc;
            end
            % Store result
            outConvAcc(ceil(r/S),ceil(c/S),depthInput) = dotProdc;
        end
    end
end
end

1 Ответ

0 голосов
/ 20 ноября 2018

Прежде всего, если изображение не делится равномерно на S, вы получаете ошибку.Вам нужно добавить floor здесь:

sizeRowsOut = floor((rowsIn-F)/S) + 1;
sizeColsOut = floor((colsIn-F)/S) + 1;

Основной двойной цикл можно немного упростить.Вместо зацикливания входного изображения с шагом S и вычисления местоположения в выходном изображении путем деления на S, зацикливание выходного изображения, а затем вычисление местоположения во входном изображении:

for r=1:sizeRowsOut
    r_in = (r-1)*S;      % NOTE! the actual location is r_in+1
    for c=1:sizeColsOut
        c_in = (c-1)*S;
        sampleWindow = input(r_in+(1:F),c_in+(1:F));
        % ...
        outConvAcc(r,c,depthInput) = dotProdc;
    end
end

(Обратите внимание, что все это индексирование выглядит немного опрятнее с индексацией на основе 0, но, увы.)

Здесь вам больше не нужен if.input гарантированно будет достаточно большим, чтобы соответствовать этому ядру, путем вычисления индексов.


Далее, вам нужно знать порядок данных в памяти и выполнять циклчто вы получаете доступ к данным в таком порядке.Это оптимизирует использование кэша.MATLAB равен основной столбец , что означает, что каждый столбец сохраняется последовательно.Ваш внутренний цикл проходит по ряду (по столбцам), что означает, что вы выполняете цикл в неправильном порядке.Просто поменяйте местами циклы r и c для хорошего увеличения скорости (заметно только для больших изображений):

for c=1:sizeColsOut
    c_in = (c-1)*S;
    for r=1:sizeRowsOut
        r_in = (r-1)*S;

Наконец, бит внутри основного двойного цикла: это сложнее, чемэто должно быть из-за петли.В MATLAB вам это не нужно:

sampleWindow = input(r_in+(1:F),c_in+(1:F));
dotProd = sum(sampleWindow(:) .* kernelf(:));

или просто:

dotProd = dot(sampleWindow(:), kernelf(:));

или даже:

dotProd = sampleWindow(:).' * kernelf(:);

Но если вы хотите выписатьВнутренний цикл, также я рекомендую вам не копировать часть изображения, а напрямую обращаться к данным на изображении:

dotProd = 0;
for jj=1:F
    for ii=1:F
        dotProd = dotProd + input(r_in+ii,c_in+jj) * kernelf(ii,jj);
    end
end

Это намного чище и удобочитаемее (IMO), потому что естьменьше переменных для отслеживания.


О, и еще одна проблема: цветные изображения.Если depthInput>1, то вы читаете с первого канала и записываете на последний канал.Вы вообще не выполняете обработку цвета!

Поскольку измерение цвета сохраняется последним, наиболее эффективным способом является вызов этой свертки значения серого один раз для каждого цветового канала.

...