Code covered by the BSD License  

Highlights from
Snakes with class

image thumbnail
from Snakes with class by Cris Luengo
Implementation of snakes using a class to enable automatic display of the snake over an image.

dip_snake
%DIP_SNAKE   Creates an object of class DIP_SNAKE
%   S = DIP_SNAKE(X) creats a DIP_SNAKE object from the
%   Nx2 array X. X(ii,:) are the coordinates of control
%   point ii. The snake S is always a closed contour.
%   N must be at least 5!
%
%   S, by itself, plots the contour over the current
%   figure. DISP(S,H) displays the contour over the image
%   in the figure with handle H. H defaults to GCF.
%   If H is the handle to a line object, it updates the
%   line rather than drawing a new one.
%   H = DISP(S) returns the handle to the line object.
%
%   S = RESAMPLE(S,D,MODE) re-samples the contour with a
%   sample distance D. MODE can be 'linear' or 'cubic',
%   corresponding to the interpolation methods used by
%   INTERP1.
%
%   S(ii) returns the coordinates for control point ii.
%   S.X returns the x-coordinate for all control points.
%   S.Y returns the y-coordinate for all control points.
%
%   DIP_IMAGE(S) returns a binary image with the region
%   demarkated by the snake.
%
%   NOTE:
%     This object requires at least MATLAB version 7.6!

% (C) Copyright 2009, All rights reserved.
% Cris Luengo, Uppsala, 18 September 2009.

classdef dip_snake
   properties
      x = []
      y = []
      imsz = [];
   end 
   methods

      % DIP_SNAKE   Constructor metod
      function s = dip_snake(x)
         if nargin~=0
            if ~isnumeric(x)
               error('Numeric inputs expected.')
            end
            if size(x,2)~=2 || size(x,1) < 5
               error('The snake must be initialized with a Nx2 array, N>=5.')
            end
            x = double(x);
            s.x = x(:,1);
            s.y = x(:,2);
            s.imsz = ceil(max(x)+1);
         end
      end

      % RESAMPLE   Resamples the snake
      function s = resample(s,d,mode)
         if nargin<2
            d = 1;
         end
         if nargin<3
            mode = 'cubic';
         else
            mode = lower(mode);
            if ~any(strcmp(mode,{'linear','cubic'}))
               error('Illegal interpolation mode.');
            end
         end
         if strcmp(mode,'linear')
            r = 1; % for linear interpolation, replicate one value
         else
            r = 3; % for cubic spline interpolation, replicate 3 values
         end
         if length(s.x)<3
            error('Snake has too few control points.')
         end
         x = s.x; x = [x(end-r+1:end);x;x(1:r)];
         y = s.y; y = [y(end-r+1:end);y;y(1:r)];
         % p is the parameter along the snake
         p = [0;cumsum(sqrt( diff(x).^2 + diff(y).^2 ))];
         % the first control point should be at p=0
         p = p-p(r+1);
         % resample snake between first and last+1 control points
         x = interp1(p,x,0:d:p(end-r+1),mode);
         y = interp1(p,y,0:d:p(end-r+1),mode);
         % if the last new point is too close to the first one, remove it
         if norm([x(end),y(end)]-[x(1),y(1)]) < d/2
            x(end) = [];
            y(end) = [];
         end
         % ensure column vectors
         s.x = x(:);
         s.y = y(:);
         if length(s.x)<3
            error('Snake has become too small!')
         end
      end

      % DISP   Plots the snake on top of an image
      function oh = disp(s,h)
         if nargin<2
            h = gcf;
         end
         if ~ishandle(h)
            error('Figure does not exist!')
         end
         if strcmp(get(h,'type'),'line')
            lh = h;
            set(lh,'xdata',[s.x;s.x(1)],'ydata',[s.y;s.y(1)]);
         else
            if ~strcmp(get(h,'type'),'image')
               h = findobj(h,'type','image');
               if length(h)~=1
                  error('Cannot find an image in the figure.')
               end
            end
            h = get(h,'parent'); % axes handle
            os = get(h,'nextplot');
            set(h,'nextplot','add');
            lh = line([s.x;s.x(1)],[s.y;s.y(1)],'color',[0,0.8,0],'parent',h,'linewidth',1);
            set(h,'nextplot',os);
         end
         if nargout>0
            oh = lh;
         end
      end

      % DISPLAY   Overloaded method, calls DISP
      function display(s)
         if strcmp(dipgetpref('DisplayToFigure'),'on')
            h = disp(s);
            h = get(get(h,'parent'),'parent');
            disp(['Displayed in figure ',num2str(h)])
         else
            if isequal(get(0,'FormatSpacing'),'compact')
               disp([inputname(1),' ='])
               disp(['dip_snake object with ',num2str(length(s.x)),' points.'])
            else
               disp(' ')
               disp([inputname(1),' ='])
               disp(' ')
               disp(['dip_snake object with ',num2str(length(s.x)),' points.'])
               disp(' ')
            end
         end
      end

      % LENGTH   Overloaded method, returns length of snake
      function l = length(s)
         l = length(s.x);
      end

      % SIZE   Overloaded method, returns size of snake
      function l = size(s)
         l = [length(s.x),2];
      end

      % SUBSREF   Overloaded method, allows indexing      
      function o = subsref(s,ii)
         if length(ii)~=1
            error('Illegal indexing into object of class dip_snake.')
         end
         switch ii.type
         case '()'
            if length(ii.subs)~=1
               error('Illegal indexing.')
            end
            o = [s.x(ii.subs{:}),s.y(ii.subs{:})];
         case '.'
            switch ii.subs
            case 'x'
               o = s.x;
            case 'y'
               o = s.y;
            case 'imsz'
               o = s.imsz;
            otherwise
               error(['No appropriate method, property, or field ',ii.subs,' for class dip_snake.']);
            end
         otherwise
            error('Illegal indexing into object of class dip_snake.')
         end
      end
      
      % END   Overloaded method, returns index of last point
      function ii = end(s,k,n)
         if k~=1 || n~=1
            error('Illegal indexing.');
         end
         ii = length(s.x);
      end

      % DOUBLE   Overloaded method, returns coordinate array
      function o = double(s)
         o = [s.x,s.y];
      end
      
      % DIP_IMAGE   Overloaded method, returns binary image
      % (based on stuff in @dip_image/convhull.m)
      function o = dip_image(s)
         o = newim(s.imsz,'bin');
         strides = [s.imsz(2);1];
         indx = bresenham2([s.x(end),s.y(end)],[s.x(1),s.y(1)])*strides;
         for ii = 2:length(s)
            indx = [indx;bresenham2([s.x(ii-1),s.y(ii-1)],[s.x(ii),s.y(ii)])*strides];
         end
         indx = unique(indx);
         o(indx) = 1;
         o = ~dip_binarypropagation(o&0,~o,1,0,1); % Propagation from border, low connect.
      end

   end
end


%
% computes coordinates of points along line from pt1 to pt2
% (copied from @dip_image/convhull.m)
function line = bresenham2(pt1,pt2)
point = pt2-pt1;
N = max(abs(point)); % the number of pixels needed.
if N==0
   line = pt1;
   return;
end
ii = (0:N)';    % this is better than (0:N)/N, because of round-off errors.
x = ii*(point(1)/N)+pt1(1);
y = ii*(point(2)/N)+pt1(2);
line = round([x,y]);
end

Contact us