如何在matlab中执行快速卷积小补丁

时间:2015-01-20 17:37:56

标签: matlab image-processing matrix convolution

我有大量的2D图像补丁和一组2D滤镜。它们的大小相同。我想在应用大小为dxd的2D滤镜时得到图像补丁(dxd)的滤镜响应(1x1)。

快速的方法是什么?

图像块作为3D矩阵存储。补丁和过滤器的大小为15x15。我在matlab中尝试转换但似乎很慢。 (5000个补丁约20-30秒)。

我可以通过一些矩阵乘法来做吗?我只是将每个补丁和每个滤波器转换为一个D矢量并进行乘法运算?

4 个答案:

答案 0 :(得分:3)

不要使用conv2。这是为一般2D信号设计的。但是,根据您的描述,您可以在3D矩阵中存储15 x 15个图像补丁,以及15 x 15滤镜。 imfilter专门用于过滤图片,但这不会对您有所帮助,因为只支持fullsame输出。我的猜测是,使用conv2时,您使用的是valid标志,因此这将减少到1 x 1的输出。

我建议您展开图像色块,以便创建2D矩阵。每个补丁都适合一列,所有图像补丁都按列连接以创建2D矩阵。您还可以展开过滤器以使其适合单个列,然后使用bsxfun来实现对每个修补程序的过滤。

因此,如果你的图片补丁存储在A中,并且你的过滤器存储在K中,那就做这样的事情:

B = reshape(A, 15*15, []);
C = sum(bsxfun(@times, B, K(:)), 1);

B将您的3D矩阵重塑为2D矩阵,其中每列代表一个图像补丁。接下来,C是具有过滤器K的每个图像补丁的输出响应。 bsxfun允许您将展开的过滤器转换为列向量,并将过滤器复制为B中的列数。这导致另一个2D矩阵与B大小相同,但每列都是相同(即K)。使用此矩阵,我们进行逐元素乘法,然后沿着行求和滤波器的输出响应。因此,C的每个元素都会为您提供特定感兴趣的过滤器的图像补丁的响应。


请注意,此代码假定执行了关联。如果2D滤波器是对称的,则卷积与相关性相同。但是,如果要进行卷积并且滤波器不对称,则需要在应用卷积之前翻转每个维度。因此,您必须为每个过滤器执行此操作:

K = K(end:-1:1, end:-1:1);

然后,您将应用上面两行代码来实现过滤。


因为您有多个过滤器,所以您可以使用for循环,为您拥有的每个过滤器执行上述操作。你当然可以对它进行矢量化并使用bsxfun方法,但我的直觉是这会慢一些。我认为如果你单独处理每个过滤器,而不是试图找到每个图像补丁的所有过滤器的响应,那会更好。因此,如果您的过滤器存储在3D矩阵中......我们再次将其称为K,您可以执行以下操作:

B = reshape(A, 15*15, []);
num_patches = size(B, 2);
num_filters = size(K, 3);
C = zeros(num_filters, num_patches);

for idx = 1 : num_filters
    filt = K(:,:,idx);
    %filt = filt(end:-1:1, end:-1:1); %// Do this if the filter is not symmetric
    C(idx,:) = sum(bsxfun(@times, B, filt(:)), 1);
end

此处,C将是一个二维矩阵,其中每个表示每个图像补丁与特定滤镜的响应。

答案 1 :(得分:1)

另一种可能的方法是使用blockproc功能。

由于图像补丁和过滤器尺寸相同,而您似乎想要一个有效的'卷积,简化为简单地将图像补丁和滤波器的相应元素(。*)相乘,并对结果求和。这可以通过简单的sum(sum(image_patch.*filter_patch,1),2)完成。

设置一些数据:

numimg  = 1000;
numfilt = 100;
img = rand(15,15,numimg); % 1000 15x15 image patches
filt = rand(15,15,numfilt); % 100 15x15 filters

为了使用blockproc,我们首先展平三维图像

img_flat = reshape(img,15,[]);

为blockproc定义处理函数。

filter_func = @(block_struct) squeeze( sum(sum(repmat(block_struct.data,1,1,numfilt) .* filt,1),2));

使用blockproc进行处理。

out = blockproc(img_flat,[15 15],filter_func);

结果是numfilt x numimg矩阵,每个图像补丁上的每个滤镜都有滤波器输出。

在我的机器上,运行不到一秒钟。如果numfiltnumimg大得多,您可以考虑启用'useParallel'的{​​{1}}标记。

答案 2 :(得分:1)

这是@ rayryeng的回答。我(大多数)采用相同的符号。

可以应用单个矩阵乘法来解决此问题。这是可能的原因,为了获得图像I和过滤器F的过滤器响应,我们只需要采用IFflipped的标量积,我们定义Fflipped = F(end:-1:1,end:-1:1);。可以以各种方式计算该标量积,例如,使用:sum(Fflipped(:).*I(:)),但使用transpose(Fflipped(:))*I(:)的矩阵乘法更简单。这样做的最大好处是,您可以通过以正确的方式堆叠向量来一次计算多个标量产品:

M = [V(:,1).'; V(:,2).'; V(:,3).'; V(:,4).'] * [W(:,1) W(:,2) W(:,3)]

将为您提供M(i,j) == dot(V(:,i), W(:,j))V(:,i)组合的所有标量积的矩阵W(:,j)

<小时/> 因此,首先我们通过以下方式翻转所有过滤器:

K = K(end:-1:1,end:-1:1,:);

然后我们重新塑造3D图像阵列并将滤镜翻转为2D阵列,这些阵列将按照我们已经看到过的方式逐列存储数据。

psize = size(A,1); % Patch size
A = reshape(A, psize^2, []); % Images
K = reshape(K, psize^2, []); % Flipped filters

现在,我们通过使用单个矩阵乘法转换滤波器并执行所有标量积,以获得响应矩阵:

C = K.'*A;

这将使功能:

function Responses = getFilterResponses(Images, Filters)
Filters = Filters(end:-1:1,end:-1:1,:); %// Flip filters
psize = size(Images,1); %// Patch size
Images = reshape(Images, psize^2, []); %// Images
Filters = reshape(Filters, psize^2, []); %// Flipped filters
%%// Compute all scalar products via nifty matrix multiplication trick
Responses = Filters.'*Images; 

对于5000 15-by-15个过滤器,这比循环过滤器快约25倍。

答案 3 :(得分:0)

这取决于你拥有什么工具。据我所知,没有批处理函数,所以你必须在for循环中执行它。但是,如果安装了Parallel Computing Toolbox,则可以使用parfor代替for,这样可以提高性能。