image_data中conv2的有效实现(有效)

时间:2018-11-20 10:39:17

标签: matlab convolution hardware-programming

我正在尝试使用conv2参数实现'valid'(MATLAB中的2D卷积函数),该参数仅返回卷积中没有零填充边的部分,这意味着内核可以不会扫描超出输入的范围。

到目前为止,我有这段代码可以正常工作,但是您似乎看到它似乎不必要地复杂,因此我计划转换到定点并稍后在硬件上实现它,而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 :(得分:0)

首先,如果图像没有被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是column-major,表示每一列都是连续存储的。您的内部循环沿行(跨列)进行,这意味着循环顺序错误。只需将rc循环交换即可获得良好的速度提升(仅在较大的图像中可见):

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

最后,是主double循环中的位:由于循环,它比需要的要复杂。在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,则从第一个通道读取,然后写入最后一个通道。您根本不进行颜色处理!

因为颜色维数最后存储,所以最有效的方法是为每个颜色通道调用一次灰度值卷积。