5

I have a matrix which consists of positive and negative values. I need to do these things.

Let u(i,j) denote the pixels of the matrix u.

  1. Calculate the zero crossing pixels. These are the pixels in the grid if u(i-1,j) and u(i+1,j) are of opposite signs or u(i,j-1) and u(i,j+1) are of opposite signs.
  2. Then I need to calculate the narrow band around these zero crossing pixels. The width of the narrow band is (2r+1)X(2r+1) for each pixel. I am taking r=1 so for it I have to actually get the 8 neighborhood pixels of each zero crossing pixels.

I have done this in a program. Please see it below.

%// calculate the zero crossing pixels  
front = isfront(u);
%// calculate the narrow band of around the zero crossing pixels
band  = isband(u,front,1);

I am also attaching the isfront and isband functions.

function front = isfront( phi )
%// grab the size of phi
[n, m] = size(phi);

%// create an boolean matrix whose value at each pixel is 0 or 1
%// depending on whether that pixel is a front point or not
front = zeros( size(phi) );

%// A piecewise(Segmentation) linear approximation to the front is contructed by
%// checking each pixels neighbour. Do not check pixels on border.
for i = 2 : n - 1;
  for j = 2 : m - 1;
    if (phi(i-1,j)*phi(i+1,j)<0) || (phi(i,j-1)*phi(i,j+1)<0)
        front(i,j) = 100;
    else
        front(i,j) = 0;
    end
  end
end

function band = isband(phi, front, width)
%// grab size of phi
[m, n] = size(phi);

%// width=r=1;
width = 1;

[x,y] = find(front==100);

%// create an boolean matrix whose value at each pixel is 0 or 1
%// depending on whether that pixel is a band point or not
band = zeros(m, n);

%// for each pixel in phi
for ii = 1:m
  for jj = 1:n
    for k = 1:size(x,1)
        if (ii==x(k)) && (jj==y(k))
            band(ii-1,jj-1) = 100;  band(ii-1,jj) = 100; band(ii-1,jj+1) = 100;
            band(ii  ,jj-1) = 100;  band(ii  ,jj) = 100; band(ii,jj+1) = 100;
            band(ii+1,jj-1) = 100;  band(ii+1,jj) = 100; band(ii+1,jj+1) = 100;
        end
    end
  end
end

The outputs are given below:, as well as the computation time:

Figure

%// Computation time

%// for isfront function
Elapsed time is 0.003413 seconds.

%// for isband function
Elapsed time is 0.026188 seconds.

When I run the code I do get the correct answers but the computation for the tasks is too much to my liking. Is there a better way to do it ? Especially the isband function? How can I optimize my code further ?

Thanks in advance.

roni
  • 1,443
  • 3
  • 28
  • 49
  • 3
    Have you considered morphological operations, say [`bwmorph`](http://www.mathworks.com/help/images/ref/bwmorph.html)? – Eitan T Sep 03 '13 at 11:29
  • 1
    Beware: Access to the bottom-right two neighboring pixels in your `isband` is coded incorrectly; you probably copy-pasted, and forgot to correct the -1 into a +1 and +0. – Rody Oldenhuis Sep 03 '13 at 12:13
  • @RodyOldenhuis thanks for the comment. Yeah I copy and pasted it so it was a mistake. Edited my question to correct it. – roni Sep 03 '13 at 15:18

2 Answers2

5

As suggested by EitanT, there is at least bwmorph that already does what you want.

If you do no have access to the image processing toolbox, or just insist on doing it yourself:

You can replace the triple-loop in isfront with the vectorized

front = zeros(n,m);

zero_crossers = ...
    phi(1:end-2,2:end-1).*phi(3:end,2:end-1) < 0 | ...
    phi(2:end-1,1:end-2).*phi(2:end-1,3:end) < 0;

front([...
                   false(1,m)
    false(n-2,1)  zero_crossers  false(n-2,1)
                   false(1,m)                 ]...
) = 100;

And you can replace isband by this single loop:

[n,m] = size(front);
band = zeros(n,m);
[x,y] = find(front);
for ii = 1:numel(x)
    band(...
        max(x(ii)-width,1) : min(x(ii)+width,n),...
        max(y(ii)-width,1) : min(y(ii)+width,m)) = 1;
end

Alternatively, as suggested by Milan, you can apply the image dilation through convolution:

kernel = ones(2*width+1);    
band = conv2(front, kernel);
band = 100 * (band(width+1:end-width, width+1:end-width) > 0);

which should be even faster.

And you can of course have some other minor optimizations (isband doesn't require phi as an argument, you can pass front as a logical array so that find is faster, etc.).

Rody Oldenhuis
  • 37,726
  • 7
  • 50
  • 96
  • A quick info I was trying to time it with my images which had a size of 75X79 and I timed both my function is_front and your function is_front. Here are the results :run1: Elapsed time is 0.003101 seconds. Elapsed time is 0.004594 seconds. run2: Elapsed time is 0.002801 seconds. Elapsed time is 0.004535 seconds. – roni Sep 03 '13 at 16:55
  • Then I resized my image to 4000X4000 and I get this timing results : run1: Elapsed time is 2.646254 seconds. Elapsed time is 0.766821 seconds. run2: Elapsed time is 2.853837 seconds. Elapsed time is 0.737387 seconds. Why is my code faster for smaller arrays and slower for larger arrays ? Can you explain ? Should not always the vectored code run much faster ? – roni Sep 03 '13 at 16:58
  • Similarly for function is_band run1 : my func: 0.006310 seconds. your func:0.004052 seconds. run2 : my func: 0.005674 seconds. your func: 0.004003 seconds. run3 : my func: 0.005919 seconds. your func:0.003908 seconds. – roni Sep 03 '13 at 17:06
  • When image is resized to 4000X4000: Similarly for function is_band run1 : my func: 4.978568 seconds. your func:0.072092 seconds. run2 : my func: 5.035173 seconds. your func: 0.072937 seconds. run3 : my func: 5.049851 seconds. your func:0.082769 seconds. – roni Sep 03 '13 at 17:11
  • 1
    @roni: this is often the case for vectorization; it starts being beneficial for larger arrays. This is because it has some overhead (for instance, all those index vectors need to be created and figured out by MATLAB), which translates into a non-zero time *before* actual computations start. During that time, the small (and JIT'ed) loop may already have begun computations. But the loop fails to be faster after a threshold size. In any case, I think you can live with 0.004 seconds of waiting, right? :) – Rody Oldenhuis Sep 04 '13 at 05:29
  • Yes I can do that. Actually I am working with images and even for image sizes 256X256 or 512X512 your vectorized code is much faster. Plus I run this code within a outer loop so you can well imagine how much computation time I will be saving. Thanks for your info. – roni Sep 04 '13 at 06:11
1

If you are only interested in r==1, look at makelut and the corresponding function bwloolup.

[EDIT]

% Let u(i,j) denote the pixels of the matrix u. Calculate the zero crossing
% pixels. These are the pixels in the grid if u(i-1,j) and u(i+1,j) are of
% opposite signs or u(i,j-1) and u(i,j+1) are of opposite signs.

% First, create a function which will us if a pixel is a zero crossing
% pixel, given its 8 neighbors. 

% uSign = u>0; % Note - 0 is treated as negative here. 

% uSign is 3x3, we are evaluating the center pixel uSign(2,2)
zcFun = @(uSign) (uSign(1,2)~=uSign(3,2)) || (uSign(2,1)~=uSign(2,3));

% Make a look up table which tells us what the output should be for the 2^9
% = 512 possible patterns of 3x3 arrays with 1's and 0's.
lut   = makelut(zcFun,3);

% Test image
im = double(imread('pout.tif'));
% Create positve and negative values
im = im -mean(im(:));

% Apply lookup table
imSign = im>0;
imZC   = bwlookup(imSign,lut);

imshowpair(im, imZC);
Ashish Uthama
  • 1,331
  • 1
  • 9
  • 14
  • can you show me how ? I looked through the material and it is not clear to me. I have very few ideas about morphological methods. Please provide me some tips on how to apply it. – roni Sep 03 '13 at 15:26