%==========================================================================
%
%   Active contour with Chen-Vese Method 
%   for image segementation
%
%   Implemented by Yue Wu (yue.wu@tufts.edu)
%   Tufts University
%   Feb 2009
%   http://sites.google.com/site/rexstribeofimageprocessing/
% 
%   all rights reserved 
%   Last update 02/26/2009
%--------------------------------------------------------------------------
%   Usage of varibles:
%   input: 
%       I           = any gray/double/RGB input image
%       mask        = initial mask, either customerlized or built-in
%       num_iter    = total number of iterations
%       mu          = weight of length term
%       method      = submethods pick from ('chen','vector','multiphase')
%
%   Types of built-in mask functions
%       'small'     = create a small circular mask
%       'medium'    = create a medium circular mask
%       'large'     = create a large circular mask
%       'whole'     = create a mask with holes around
%       'whole+small' = create a two layer mask with one layer small
%                       circular mask and the other layer with holes around
%                       (only work for method 'multiphase')
%   Types of methods
%       'chen'      = general CV method
%       'vector'    = CV method for vector image
%       'multiphase'= CV method for multiphase (2 phases applied here)
%
%   output: 
%       phi0        = updated level set function 
%
%--------------------------------------------------------------------------
%
% Description: This code implements the paper: "Active Contours Without
% Edges" by Chan and Vese for method 'chen', the paper:"Active Contours Without
% Edges for vector image" by Chan and Vese for method 'vector', and the paper
% "A Multiphase Level Set Framework for Image Segmentation Using the 
% Mumford and Shah Model" by Chan and Vese. 
%
%--------------------------------------------------------------------------
% Deomo: Please see HELP file for details
%==========================================================================

function seg = chenvese(I,mask,num_iter,mu,method)

%%
%-- Default settings
%   length term mu = 0.2 and default method = 'chan'
  if(~exist('mu','var')) 
    mu=0.2; 
  end
  
  if(~exist('method','var')) 
    method = 'chan'; 
  end

  Heaviside = @func.chan_vese.Heaviside;
  kappa = @func.chan_vese.kappa;
%   checkstop = @func.chan_vese.checkstop;

  checkstop = @(a,b,c) false; % just do not check for stop conditions
  
  showphi  = @func.chan_vese.showphi;
  
  orig_img_size = size(I, [1, 2]);
  
%-- End default settings

%%
%-- Initializations on input image I and mask
%  resize original image
   s = 200./min(size(I,1),size(I,2)); % resize scale
   if s<1
       I = imresize(I,s);
   end
  
%   auto mask settings
  if ischar(mask)
      switch lower (mask)
          case 'small'
              mask = func.chan_vese.maskcircle2(I,'small');
          case 'medium'
              mask = func.chan_vese.maskcircle2(I,'medium');
          case 'large'
              mask = func.chan_vese.maskcircle2(I,'large');              
          case 'whole'
              mask = func.chan_vese.maskcircle2(I,'whole'); 
              %mask = init_mask(I,30);      
          case 'whole+small'
              m1 = func.chan_vese.maskcircle2(I,'whole');
              m2 = func.chan_vese.maskcircle2(I,'small');
              mask = zeros(size(I,1),size(I,2),2);
              mask(:,:,1) = m1(:,:,1);
              mask(:,:,2) = m2(:,:,2);
          otherwise
              error('unrecognized mask shape name (MASK).');
      end
  else
      if s<1
          mask = imresize(mask,s);
      end
      if size(mask,1)>size(I,1) || size(mask,2)>size(I,2)
          error('dimensions of mask unmathch those of the image.')
      end
      switch lower(method)
          case 'multiphase'
              if  (size(mask,3) == 1)  
                  error('multiphase requires two masks but only gets one.')
              end
      end

  end       

  
switch lower(method)
    case 'chan'
        if size(I,3)== 3
            P = rgb2gray(uint8(I));
            P = double(P);
        elseif size(I,3) == 2
            P = 0.5.*(double(I(:,:,1))+double(I(:,:,2)));
        else
            P = double(I);
        end
        layer = 1;
        
    case 'vector'
        s = 200./min(size(I,1),size(I,2)); % resize scale
        I = imresize(I,s);
        mask = imresize(mask,s);
        layer = size(I,3);
        if layer == 1
            disp('only one image component for vector image')
        end
        P = double(I);
            
    case 'multiphase'
        layer = size(I,3);
        if size(I,1)*size(I,2)>200^2
            s = 200./min(size(I,1),size(I,2)); % resize scale
            I = imresize(I,s);
            mask = imresize(mask,s);
        end
            
        P = double(I);  %P store the original image
    otherwise
        error('!invalid method')
end
%-- End Initializations on input image I and mask

%%
%--   Core function
switch lower(method)
    case {'chan','vector'}
        %-- SDF
        %   Get the distance map of the initial mask
        
        mask = mask(:,:,1);
        phi0 = bwdist(mask)-bwdist(1-mask)+im2double(mask)-.5; 
        %   initial force, set to eps to avoid division by zeros
        force = eps; 
        %-- End Initialization

        %-- Display settings
          figure();
          subplot(2,2,1); imshow(I); title('Input Image');
          subplot(2,2,2); contour(flipud(phi0), [0 0], 'r','LineWidth',1); title('initial contour');
          subplot(2,2,3); title('Segmentation');
        %-- End Display original image and mask

        %-- Main loop
          for n=1:num_iter
              inidx = find(phi0>=0); % frontground index
              outidx = find(phi0<0); % background index
              force_image = 0; % initial image force for each layer 
              for i=1:layer
                  L = im2double(P(:,:,i)); % get one image component
                  c1 = sum(sum(L.*Heaviside(phi0)))/(length(inidx)+eps); % average inside of Phi0
                  c2 = sum(sum(L.*(1-Heaviside(phi0))))/(length(outidx)+eps); % verage outside of Phi0
                  force_image=-(L-c1).^2+(L-c2).^2+force_image; 
                  % sum Image Force on all components (used for vector image)
                  % if 'chan' is applied, this loop become one sigle code as a
                  % result of layer = 1
              end

              % calculate the external force of the image 
              force = mu*kappa(phi0)./max(max(abs(kappa(phi0))))+1/layer.*force_image;

              % normalized the force
              force = force./(max(max(abs(force))));

              % get stepsize dt
              dt=0.5;
              
              % get parameters for checking whether to stop
              old = phi0;
              phi0 = phi0+dt.*force;
              new = phi0;
              indicator = checkstop(old,new,dt);

              % intermediate output
              if(mod(n,20) == 0) 
                 showphi(I,phi0,n);  
              end
              if indicator % decide to stop or continue 
                  showphi(I,phi0,n);

                  %make mask from SDF
                  seg = phi0<=0; %-- Get mask from levelset

                  subplot(2,2,4); imshow(seg); title('Global Region-Based Segmentation');

                  return;
              end
          end
          showphi(I,phi0,n);

          %make mask from SDF
          seg = phi0<=0; %-- Get mask from levelset

          subplot(2,2,4); imshow(seg); title('Global Region-Based Segmentation');
    case 'multiphase'
        %-- Initializations
        %   Get the distance map of the initial masks
        mask1 = mask(:,:,1);
        mask2 = mask(:,:,2);
        phi1 = bwdist(mask1)-bwdist(1-mask1)+im2double(mask1)-.5;%Get phi1 from the initial mask 1
        phi2 = bwdist(mask2)-bwdist(1-mask2)+im2double(mask2)-.5;%Get phi1 from the initial mask 2
        
        %-- Display settings
        figure();
        subplot(2,2,1); 
        if layer ~= 1
            imshow(I); title('Input Image');
        else
            imagesc(P); axis image; colormap(gray);title('Input Image');
        end
        subplot(2,2,2);
        hold on
        contour(flipud(mask1),[0,0],'r','LineWidth',2.5); 
        contour(flipud(mask1),[0,0],'x','LineWidth',1);
        contour(flipud(mask2),[0,0],'g','LineWidth',2.5);
        contour(flipud(mask2),[0,0],'x','LineWidth',1);
        title('initial contour');
        hold off
        subplot(2,2,3); title('Segmentation');
        %-- End display settings
        
        %Main loop
        for n=1:num_iter
              %-- Narrow band for each phase
              nb1 = find(phi1<1.2 & phi1>=-1.2); %narrow band of phi1
              inidx1 = find(phi1>=0); %phi1 frontground index
              outidx1 = find(phi1<0); %phi1 background index

              nb2 = find(phi2<1.2 & phi2>=-1.2); %narrow band of phi2
              inidx2 = find(phi2>=0); %phi2 frontground index
              outidx2 = find(phi2<0); %phi2 background index
              %-- End initiliazaions on narrow band

              %-- Mean calculations for different partitions
              %c11 = mean (phi1>0 & phi2>0)
              %c12 = mean (phi1>0 & phi2<0)
              %c21 = mean (phi1<0 & phi2>0)
              %c22 = mean (phi1<0 & phi2<0)

              cc11 = intersect(inidx1,inidx2); %index belong to (phi1>0 & phi2>0)
              cc12 = intersect(inidx1,outidx2); %index belong to (phi1>0 & phi2<0)
              cc21 = intersect(outidx1,inidx2); %index belong to (phi1<0 & phi2>0)
              cc22 = intersect(outidx1,outidx2); %index belong to (phi1<0 & phi2<0)
              
              f_image11 = 0;
              f_image12 = 0;
              f_image21 = 0;
              f_image22 = 0; % initial image force for each layer 
              
              for i=1:layer
                  L = im2double(P(:,:,i)); % get one image component
          
              if isempty(cc11)
                  c11 = eps;
              else
                  c11 = mean(L(cc11));
              end
              
              if isempty(cc12)
                  c12 = eps;
              else
                  c12 = mean(L(cc12)); 
              end
              
              if isempty(cc21)
                  c21 = eps;
              else
                  c21 = mean(L(cc21));
              end
              
              if isempty(cc22)
                  c22 = eps;
              else
                  c22 = mean(L(cc22));
              end
              
              %-- End mean calculation

              %-- Force calculation and normalization
              % force on each partition
              
              
              
              f_image11=(L-c11).^2.*Heaviside(phi1).*Heaviside(phi2)+f_image11;
              f_image12=(L-c12).^2.*Heaviside(phi1).*(1-Heaviside(phi2))+f_image12;
              f_image21=(L-c21).^2.*(1-Heaviside(phi1)).*Heaviside(phi2)+f_image21;
              f_image22=(L-c22).^2.*(1-Heaviside(phi1)).*(1-Heaviside(phi2))+f_image22;
              end
              
                  % sum Image Force on all components (used for vector image)
                  % if 'chan' is applied, this loop become one sigle code as a
                  % result of layer = 1
 
              % calculate the external force of the image 
              
              % curvature on phi1
              curvature1 = mu*kappa(phi1);
              curvature1 = curvature1(nb1);
              % image force on phi1
              fim1 = 1/layer.*(-f_image11(nb1)+f_image21(nb1)-f_image12(nb1)+f_image22(nb1));
              fim1 = fim1./max(abs(fim1)+eps);

              % curvature on phi2
              curvature2 = mu*kappa(phi2);
              curvature2 = curvature2(nb2);
              % image force on phi2
              fim2 = 1/layer.*(-f_image11(nb2)+f_image12(nb2)-f_image21(nb2)+f_image22(nb2));
              fim2 = fim2./max(abs(fim2)+eps);

              % force on phi1 and phi2
              force1 = curvature1+fim1;
              force2 = curvature2+fim2;
              %-- End force calculation

              % detal t
              dt = 1.5;
              
              old(:,:,1) = phi1;
              old(:,:,2) = phi2;

              %update of phi1 and phi2
              phi1(nb1) = phi1(nb1)+dt.*force1;
              phi2(nb2) = phi2(nb2)+dt.*force2;
              
              new(:,:,1) = phi1;
              new(:,:,2) = phi2;
              
              indicator = checkstop(old,new,dt);
%               indicator = false;
              
              if indicator 
                 showphi(I, new, n);
                 %make mask from SDF
                 seg11 = (phi1>0 & phi2>0); %-- Get mask from levelset
                 seg12 = (phi1>0 & phi2<0);
                 seg21 = (phi1<0 & phi2>0);
                 seg22 = (phi1<0 & phi2<0);

                 se = strel('disk',1);
                 aa1 = imerode(seg11,se);
                 aa2 = imerode(seg12,se);
                 aa3 = imerode(seg21,se);
                 aa4 = imerode(seg22,se);
                 seg = aa1+2*aa2+3*aa3+4*aa4;
                
                 subplot(2,2,4); imagesc(seg);axis image;title('Global Region-Based Segmentation');

                  return
              end
              % re-initializations
              phi1 = func.chan_vese.reinitialization(phi1, 0.6);%sussman(phi1, 0.6);%
              phi2 = func.chan_vese.reinitialization(phi2, 0.6);%sussman(phi2,0.6);

              %intermediate output
              if(mod(n,20) == 0) 
                 phi(:,:,1) = phi1;
                 phi(:,:,2) = phi2;
                 showphi(I, phi, n);
              end
        end
          phi(:,:,1) = phi1;
          phi(:,:,2) = phi2;
          showphi(I, phi, n);
          %make mask from SDF
        seg11 = (phi1>0 & phi2>0); %-- Get mask from levelset
        seg12 = (phi1>0 & phi2<0);
        seg21 = (phi1<0 & phi2>0);
        seg22 = (phi1<0 & phi2<0);

        se = strel('disk',1);
        aa1 = imerode(seg11,se);
        aa2 = imerode(seg12,se);
        aa3 = imerode(seg21,se);
        aa4 = imerode(seg22,se);
        seg = aa1+2*aa2+3*aa3+4*aa4;
        %seg = bwlabel(seg);
        subplot(2,2,4); imagesc(seg);axis image;title('Global Region-Based Segmentation');
end


seg = imresize(seg, orig_img_size, 'nearest');

end


