有效地同时提取数千个位置的邻居像素的方法

时间:2017-05-26 03:50:55

标签: matlab loops for-loop image-processing matrix

我试图提取图像的8个邻居像素。但是,我想同时对数百个位置(函数中的rowcolumn)执行此操作。这意味着输出Block是一个3D矩阵,3D矩阵的每个切片都对应于单个位置。

function Block = getNeighbours(image, row, column)
% Create a 3x3 matrix that contains the neighbors of the point (row,column)
    row=round(row(:));
    column=round(column(:));
    neighbors_x = [row(:,1)-1 row(:,1) row(:,1)+1];
    neighbors_y = [column(:,1)-1 column(:,1) column(:,1)+1];
    Block = zeros(3,3,size(row,1));
    for i=1:size(row,1)
    Block(:,:,i) = image(neighbors_x(i,:), neighbors_y(i,:)); %can I avoid this loop?
    end
end

从上面的代码中,我需要循环来完成这项工作。它似乎是这个功能的瓶颈,对于数千个地点来说,这绝对不是有效的。有什么方法可以避免这种情况吗?

您可以通过以下方式尝试此功能:

image=randi([1 255], [300 300]);
row=randi([1 200], [1 1000]);
column=randi([1 200], [1 1000]);

block=getNeighbours(image, row, column);

2 个答案:

答案 0 :(得分:1)

您可以在行列索引上使用sub2ind,然后添加一般的9邻域索引以形成块索引。对于大量的行 - 列对,它比使用循环快约10倍:

image=randi([1 255], [300 300]);
row=randi([2 200], [1 10000]);
column=randi([2 200], [1 10000]);
tic;
sz = size(image);
% general 9-neighberhood indexes
hoodIdxs = [-sz(2)-1,-1,sz(2)-1;-sz(2),0,sz(2);-sz(2)+1,1,sz(2)+1];
% linear indexes of requested pixels
idxs = permute(sub2ind(sz,row,column),[1 3 2]);
% block indexes
blockIdxs = bsxfun(@plus,idxs,hoodIdxs);
% generate blocks
blocks = image(blockIdxs);
toc;
% OP's function
tic;
blocks_loop = getNeighbours(image, row, column); % ~10 times slower
toc
% check
isequal(blocks, blocks_loop) % yes

请注意,在生成随机行和列时,我将randi间隔的下端设置为2,否则图像边缘上的行列会在尝试调用时抛出索引错误{ {1}}。您可以通过填充图像(此处填充blocks = image(blockIdxs);)轻松克服此问题:

nan

答案 1 :(得分:1)

如果您有图像处理工具箱,一个选项是使用im2col将图像预处理为块,一次生成所有邻域。如果您需要多次访问块,这将特别有效。

给出一张图片:

>> img = reshape(1:25,5,5)

img =

    1    6   11   16   21
    2    7   12   17   22
    3    8   13   18   23
    4    9   14   19   24
    5   10   15   20   25

im2col将每个有效块(这很重要......)转换为矩阵列:

C = im2col(img,[3,3])

C =

    1    2    3    6    7    8   11   12   13
    2    3    4    7    8    9   12   13   14
    3    4    5    8    9   10   13   14   15
    6    7    8   11   12   13   16   17   18
    7    8    9   12   13   14   17   18   19
    8    9   10   13   14   15   18   19   20
   11   12   13   16   17   18   21   22   23
   12   13   14   17   18   19   22   23   24
   13   14   15   18   19   20   23   24   25

如您所见,C的第一列具有索引7img(2,2)的邻域值。下一个是索引8img(3,2),依此类推。偏移是因为im2col仅为您提供有效的块。如果这是你想要的,你只需要记住从行/列下标中减去1,然后使用sub2indC中找到列号:

C_idx = sub2ind(size(img), 2-1, 2-1)

C_idx =  1

(如果填充图像,则索引和下标与原始图像中的相同。)

因此img(2,2)邻域的列索引为1

你实际上可以在这里停下来。如果您将相同的内核应用于所有查询的邻域,您可以将内核变为行向量并相乘,选择要考虑的C列:

kernel = ones(3)/9
kernel =

   0.11111   0.11111   0.11111
   0.11111   0.11111   0.11111
   0.11111   0.11111   0.11111

kernel(:).' * C
ans =

    7.0000    8.0000    9.0000   12.0000   13.0000   14.0000   17.0000   18.0000   19.0000

C_idx = [1:9];   % or randi(9, 1, 4), or any valid list of column numbers
k_vec = kernel(:).';   % turn kernel into a row vector
result = k_vec * C(:, C_idx);

result =

    7.0000    8.0000    9.0000   12.0000   13.0000   14.0000   17.0000   18.0000   19.0000

如果你不喜欢邻域是列向量并且真的希望它们是3x3块,你可以将C矩阵重塑为3D矩阵:

D = reshape(C, 3, 3, []);

D =

ans(:,:,1) =

    1    6   11
    2    7   12
    3    8   13

ans(:,:,2) =

    2    7   12
    3    8   13
    4    9   14

ans(:,:,3) =

    3    8   13
    4    9   14
    5   10   15

ans(:,:,4) =

    6   11   16
    7   12   17
    8   13   18

ans(:,:,5) =

    7   12   17
    8   13   18
    9   14   19

ans(:,:,6) =

    8   13   18
    9   14   19
   10   15   20

ans(:,:,7) =

   11   16   21
   12   17   22
   13   18   23

ans(:,:,8) =

   12   17   22
   13   18   23
   14   19   24

ans(:,:,9) =

   13   18   23
   14   19   24
   15   20   25

从这里开始,您使用与之前用于列的索引相同的索引,但现在它们是第三维的索引,例如:

>> D(:, :, 1)
ans =

    1    6   11
    2    7   12
    3    8   13

为您提供与C(:, 1)相同的社区。