2
votes

Is there any built-in function for doing convolution only for a subset of pixels on the image?

Basically, I know the coordinates of these points and I want to get the results of applying convolution kernel centered at these points.

I want to use it in my implementation of Hessian-Laplace feature detector. I don't want to build the whole scale space cube, I want to apply Laplacian only to interest points found by Hessian detector.

Thank you.

Edit:

I am searching for a function that will have the following signature:

function [ results ] = selected_conv( input_matrix, ...
coords, kernel, border_treatment_mode )

Example of usage:

% define the kernel and the input image
h = [1 2 3;
     0 0 0;
     6 5 4];
I = [1 5 2 3;
     8 7 3 6;
     3 3 9 1]

% Points coordinates in original image to convolve.
points_coords_to_convolve = [[2, 2]; [2, 3]];
% The third parameter is a parameter like for padarray(): 'zeros', 'replicate', 'symmetric'.
result = selected_conv(I, h, 'zeros')

Output:

[65, 76]

Break-down of the above code:

  1. Kernel matrix is always of uneven size. Rotate our kernel matrix 180 degrees. (How it is usually done with convolution). Result for our code:

     h = [4 5 6;
          0 0 0;
          3 2 1];
    
  2. We check if for all specified points the kernel fits into the matrix. Otherwise, we pad our matrix with one of possible padding techniques: 'zeros', 'replicate', 'symmetric'. The process of padding is identical to padarray() function in matlab.

  3. Center the rotated kernel on each of the specified points of the original image and compute the response. Proceed the same way for all the specified points. In our example [[2, 2]; [2, 3]]. The first number of each row is a row number, and the second one is the column number. In our case it will be numbers 7 and 3 or the original matrix.
  4. The response for the first number is 4 + 5*5 + 6*2 + 3*3 + 2*3 + 9 = 65.

My code for nlfileter():

function [ results ] = selected_nlfilter( input_matrix, coords, ...
func_handler, sliding_window_size, border_treatment_mode ) 

    Kernel_x = sliding_window_size(1);
    Kernel_y = sliding_window_size(2);

    pad_row = floor(Kernel_x/2);
    pad_col = floor(Kernel_y/2);

    padded_matrix = pad_matrix(input_matrix, pad_row, pad_col, border_treatment_mode);

    results  = zeros(size(coords, 1), 1, 'double');

    amount_of_coords = size(coords, 1);

    for coord_count = 1:amount_of_coords

        row = coords(coord_count, 1);
        col = coords(coord_count, 2);

        frame = padded_matrix(row:row+(2*pad_row),col:col+(2*pad_col));

        sliding_window_size;
        results(coord_count) = func_handler(frame);

    end 
  end

I've just applied it with already rotated kernel matrix.

1
Show your code on how you would use on few points? - Divakar
@Divakar, do you want a signature or a specific use-case? - warmspringwinds
Use some sample minimal input image data, an array of points, sample kernel and show the desired output. Explain how must that behave around borders. Would be nice to see these covered in the question. - Divakar
@Divakar, I've added it. Actually, it's the second time I have to use some function like this. I've already implemented one similar function before but it wan't that fast. nlfilter only for selected pixels will also be enough. But I have found no built-in functions. - warmspringwinds
Could you add how you got the desired output? Basically we are looking to have a minimal reproducible code that takes in the input data (as you have already added in the question) and produces the desired output, assuming of course that you have tried something at your end. - Divakar

1 Answers

1
votes

This is a function code that does zero-padding for points around the boundaries and achieves the "selective convolution" -

function out = selected_conv(I,pts,h)

%// Parameters
hsz = size(h);
bxr = (hsz-1)/2;
Isz = size(I);

%// Get padding lengths across all 4 sides
low_padlens = max(bsxfun(@minus,bxr+1,pts),[],1);
low_padlens = (low_padlens + abs(low_padlens))./2;
high_padlens = bxr - min(bsxfun(@minus,Isz,pts),[],1);
high_padlens = (high_padlens + abs(high_padlens))./2;

%// Get zeros padded array
Ip = zeros(Isz + high_padlens + low_padlens);
Ip(low_padlens(1)+1:Isz(1)+low_padlens(1),...
    low_padlens(2)+1:Isz(2)+low_padlens(2)) = I;

pts = bsxfun(@plus,pts,low_padlens); %// modified points based on padding

lin_idx = sub2ind(size(Ip),pts(:,1),pts(:,2)); %//'#linear indices of points

%// Calculate neighborhood offsets and then the actual neighboring elements
off1 = bsxfun(@plus,[-bxr(1):bxr(1)]',[-bxr(2):bxr(2)]*size(Ip,1)); %//'
all_idx = bsxfun(@plus,off1(:),lin_idx(:).'); %//'# all neighbouring indices
vals = Ip(all_idx);  %// all neighbouring elements
out = h(:).'*vals; %//'# Finally get the weighted output

return;

Sample Usage

h = [4 5 6;
    0 0 0;
    3 2 1];
I = [1 5 2 3;
    8 7 3 6;
    3 3 9 1]

pts = [[2, 2]; [2, 3]]

out = selected_conv(I,pts,h)

Output -

out =
    65    76