【问题标题】:Efficient way to extract neighbours pixel of thousands locations simultaneously同时提取数千个位置的相邻像素的有效方法
【发布时间】:2017-10-26 20:35:36
【问题描述】:

我尝试提取图像的 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);

【问题讨论】:

  • 你打算用这些块做什么?这听起来很可疑,就像您正在尝试以艰难的方式进行卷积。
  • 您对卷积的“硬”方式是正确的。由于我有数千个不同的内核,convn 将非常昂贵。我使用的方法只考虑块而不是整个图像。 convn(image,kernels) 会很耗时。或许你有什么建议?
  • 您应该首先使用conv2 对解决方案进行基准测试。除非您的图像很大并且您只使用少数社区,否则很难被击败。

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


【解决方案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,否则图像边缘的行列在尝试调用blocks = image(blockIdxs); 时会引发索引错误。您可以通过填充图像轻松克服这个问题(这里用nan's 填充):

% pad image edges with nans
padded = padarray(image,[1 1],nan,'both');
sz = size(padded);
% 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 (+1 because of the padding)
idxs = permute(sub2ind(sz,row + 1,column + 1),[1 3 2]);
% block indexes
blockIdxs = bsxfun(@plus,idxs,hoodIdxs);
% generate blocks
blocks = padded(blockIdxs);

【讨论】:

  • 这太棒了!谢谢!
【解决方案2】:

如果您有图像处理工具箱,一种选择是使用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) 邻域的值。下一个是索引8,或img(3,2),以此类推。偏移量是因为im2col 只给你有效的块。如果这是您想要的,您只需记住在使用sub2ind 查找C 中的列号之前从行/列下标中减去1

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) 相同的社区。​​p>

【讨论】:

  • 非常感谢您的详细回答!您的方法更快 user2999345 的,IF image 被假定为相同的几个循环,因此只调用一次 im2col,对我来说就是这种情况!所以我正在实施你的方法!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-11
  • 2020-11-28
  • 1970-01-01
  • 2020-05-21
  • 1970-01-01
  • 1970-01-01
  • 2019-07-24
相关资源
最近更新 更多