How to efficiently extract image patches at specific locations?

I need to extract patches sized s x s x 3

around specific 2D locations from an image (3 channels).

How can I do this efficiently without a loop for

? I know that I can extract one patch in scope (x,y)

as:

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

      

How can I do this for many patches? I know I can use a MATLAB function blockproc

, but I cannot specify the locations.

+3


source to share


2 answers


You can use im2col

from the imaging toolkit to convert the neighborhoods of each pixel to a single column . Pixel regions are selected such that each block is selected based on a column , which means that the blocks are built by moving through the first rows and then moving to the next column and getting neighborhoods.

You call im2col

like this:

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

      

I am assuming you will want sliding / overlapping neighborhoods and not separate neighborhoods, which are commonly used when doing any kind of image filtering. A

is your image and you want to find pixel neighborhoods M x N

converted into columns. B

would be the result where each neighborhood is one column and horizontally tiled together. However, you probably want to handle the case when you want to capture pixel neighborhoods along the edges of an image. In this case, you first need to plot the image. We will assume that M

and are N

odd to simplify shim. In particular, you want to be sure that there are linesfloor(M/2)

filled over the image, as well as the bottom, as well as the columns floor(N/2)

to the left of the image, as well as to the right. So we must first put A

using padarray

. Suppose the pixels of the border are replicated, which means that the filled rows and columns will simply be grabbed from the top or bottom row, or in the left and right columns, depending on where we have to route. Therefore:

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

      

In the next part, if you want to select to specify neighborhoods, you can use sub2ind

to convert your 2D coordinates to linear indices so that you can select the right columns to get the correct pixel blocks. However, since you have a color image, you want to perform im2col

on each color channel. Unfortunately, im2col

it only works with grayscale images, so you'll have to repeat this for every channel in your image.



Thus, to prepare for sampling, do the following:

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

      

The above code will create a 3D version im2col

where each 3D slice will be what it im2col

creates for each color channel. We can now use sub2ind

to convert your codes (x,y)

to linear indices so that we can choose which pixel neighborhoods we want. So, assuming your positions are stored in vectors x

and y

, you would do something like this:

%// 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

will be a cell array with each item containing your desired patch at every location (x,y)

you specify. So there patches{1}

will be a patch located in (x(1), y(1))

, patches{2}

will be a patch located in (x(2), y(2))

, etc. For your copy and paste pleasure, this is what we have:

%// 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);

      

+6


source


Surprisingly, for me the naive for

-loop is actually the fastest. This may depend on your version of MATLAB, although as with newer versions, they continue to improve the JIT compiler.

General information:

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

      

Naive approach: (faster than im2col

u arrayfun

!)



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

      

Approach using arrayfun

: (slower than loop)

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

      

+1


source







All Articles