我正在尝试使用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
答案 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,表示每一列都是连续存储的。您的内部循环沿行(跨列)进行,这意味着循环顺序错误。只需将r
和c
循环交换即可获得良好的速度提升(仅在较大的图像中可见):
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
,则从第一个通道读取,然后写入最后一个通道。您根本不进行颜色处理!
因为颜色维数最后存储,所以最有效的方法是为每个颜色通道调用一次灰度值卷积。