如何高效提取指定位置的图像补丁?

How do I efficiently extract image patches at specified locations?

我需要从图像(3 个通道)中提取指定 2D 位置周围大小为 s x s x 3 的图像块。

如何在没有 for 循环的情况下高效地执行此操作?我知道我可以在 (x,y) 位置附近提取一个补丁:

apatch = I(y-s/2:y+s/2, x-s/2:x+s/2, :)

如何为多个补丁执行此操作?我知道我可以使用 MATLAB 的函数 blockproc 但我无法指定位置。

虽然这看起来出乎意料,但对我来说,天真的 for 循环实际上是最快的。不过,这可能取决于您的 MATLAB 版本,因为对于较新的版本,他们会不断改进 JIT 编译器。

常用数据:

A = rand(30, 30, 3); % Image
I = [5,2,3,21,24]; % I = y 
J = [3,7,5,20,22]; % J = x
s = 3; % Block size

天真的方法:(比 im2colarrayfun 快!)

Patches = cell(size(I));
steps = -(s-1)/2:(s-1)/2;
for k = 1:numel(Patches);
    Patches{k} = A(I(k)+steps, ...
                   J(k)+steps, ...
                   :);
end

使用 arrayfun 的方法:(比循环慢)

steps = -(s-1)/2:(s-1)/2;
Patches = arrayfun(@(ii,jj) A(ii+steps,jj+steps,:), I, J, 'UniformOutput', false);

您可以使用图像处理工具箱中的 im2col 将每个像素邻域转换为 单列 。像素邻域 selected 这样每个块都是在 列基础上选择的 ,这意味着块是通过首先向下遍历行构建的,然后继续下一栏并在那里获取社区。

你这样调用im2col

B = im2col(A, [M N]);

我假设您需要滑动/重叠的邻域而不是不同的邻域,这是执行任何类型的图像过滤时通常使用的邻域。 A 是您的图像,您想要找到 M x N 像素邻域转换为列。 B 将是输出,其中每个邻域都是一列并且 水平平铺 在一起。但是,您可能想要处理想要沿图像边界抓取像素邻域的情况。在这种情况下,您需要先 pad 图像。我们将假设 MN 是奇数以使填充更容易。具体来说,您要确保在图像顶部和底部填充 floor(M/2) 行,在图像左侧和右侧填充 floor(N/2) 列。因此,我们应该首先使用 padarray 填充 A。假设边界像素将被复制,这意味着填充的行和列将只是从顶行或底行或左右列中抓取的行和列,具体取决于我们需要填充的位置。因此:

Apad = padarray(A, floor([M N]/2), 'replicate');

对于下一部分,如果你想选择指定邻域,你可以使用 sub2ind 将你的二维坐标转换成线性索引,这样你就可以 select 正确的列来获得正确的像素块。但是,因为您有彩色图像,所以您需要在每个颜色通道上执行 im2col。不幸的是,im2col 仅适用于灰度图像,因此您必须对图像中的每个通道重复此操作。

因此,要准备好补丁采样,请执行以下操作:

B = arrayfun(@(x) im2col(Apad(:,:,x), [M N]), 1:size(A,3), 'uni', 0);
B = cat(3, B{:});

以上代码将创建 im2col 的 3D 版本,其中每个 3D 切片都是 im2col 为每个颜色通道生成的。现在,我们可以使用 sub2ind 将您的 (x,y) 坐标转换为线性索引,以便我们可以选择我们想要的像素邻域。因此,假设您的位置存储在向量 xy 中,您将执行如下操作:

%// Generate linear indices
ind = sub2ind([size(A,1) size(A,2)], y, x);

%// Select neighbourhoods
%// Should be shaped as a MN x len(ind) x 3 matrix
neigh = B(:,ind,:);

%// Create cell arrays for each patch
patches = arrayfun(@(x) reshape(B(:,x,:), [M N 3]), 1:numel(ind), 'uni', 0);

patches 将是一个元胞数组,其中每个元素在您指定的 (x,y) 的每个位置包含您想要的补丁。因此,patches{1} 将是位于 (x(1), y(1)) 的补丁,patches{2} 将是位于 (x(2), y(2)) 的补丁,等等。为了您的复制和粘贴乐趣,这就是我们拥有的:

%// Define image, M and N here
%//...
%//...

Apad = padarray(A, floor([M N]/2), 'replicate');
B = arrayfun(@(x) im2col(Apad(:,:,x), [M N]), 1:size(A,3), 'uni', 0);
B = cat(3, B{:});

ind = sub2ind([size(A,1) size(A,2)], y, x);
neigh = B(:,ind,:);
patches = arrayfun(@(x) reshape(neigh(:,x,:), [M N 3]), 1:numel(ind), 'uni', 0);