% ## Copyright (C) 2000 Teemu Ikonen
% ##
% ## This program is free software; you can redistribute it and/or
% ## modify it under the terms of the GNU General Public License
% ## as published by the Free Software Foundation; either version 2
% ## of the License, or (at your option) any later version.
% ##
% ## This program is distributed in the hope that it will be useful, but
% ## WITHOUT ANY WARRANTY; without even the implied warranty of
% ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
% ## General Public License for more details.
% ##
% ## You should have received a copy of the GNU General Public License
% ## along with this program; if not, write to the Free Software
% ## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
% 

% ## -*- texinfo -*-
% ## @deftypefn {Function File} {} im_pad(@var{A}, @var{xpad}, @var{ypad}, [@var{padding}, [@var{const}]])
% ## Pad (augment) a matrix for application of image processing algorithms.
% ##
% ## Pads the input image @var{A} with @var{xpad}(1) elements from left, 
% ## @var{xpad}(2), elements from right, @var{ypad}(1) elements from above 
% ## and @var{ypad}(2) elements from below.
% ## Values of padding elements are determined from the optional arguments
% ## @var{padding} and @var{const}. @var{padding} is one of
% ##
% ## @table @samp
% ## @item "zeros"     
% ## pad with zeros (default)
% ##
% ## @item "ones"      
% ## pad with ones
% ##
% ## @item "constant"  
% ## pad with a value obtained from the optional fifth argument const
% ##
% ## @item "symmetric" 
% ## pad with values obtained from @var{A} so that the padded image mirrors 
% ## @var{A} starting from edges of @var{A}
% ## 
% ## @item "reflect"   
% ## same as symmetric, but the edge rows and columns are not used in the padding
% ##
% ## @item "replicate" 
% ## pad with values obtained from A so that the padded image 
% ## repeates itself in two dimensions
% ## 
% ## @end table
% ## @end deftypefn
% 
% ## Author: Teemu Ikonen <tpikonen@pcu.helsinki.fi>
% ## Created: 5.5.2000
% ## Keywords: padding image processing
% ## 2006-11-28 P. Cloetens <cloetens@esrf.fr>
% ## * add padding possibility 'extend'
% ## * rename to im_pad
% ## 2006-12-03 PC
% ## * extend with average of last value over extend_avg rows/columns
% 
% ## A nice test matrix for padding:
% ## A = 10*[1:5]' * ones(1,5) + ones(5,1)*[1:5]

function retval = im_pad(A, xpad, ypad, padding, const)

try empty_list_elements_ok_save = empty_list_elements_ok;
catch empty_list_elements_ok_save = 0;
end
try warn_empty_list_elements_save = warn_empty_list_elements;
catch warn_empty_list_elements_save = 0;
end
unwind_protect

if nargin < 4 
    padding = 'zeros'; 
end
if nargin < 5 
    const = 1; 
end

extend_avg = const; %# we use the same fifth argument

origx = size(A,2);
origy = size(A,1);

if isscalar(xpad)
    retx = xpad;
    xpad(1) = ceil((retx-origx)/2);
    xpad(2) = retx-origx-xpad(1);
else
    retx = origx + xpad(1) + xpad(2);
end

if isscalar(ypad)
    rety = ypad;
    ypad(1) = ceil((rety-origy)/2);
    ypad(2) = rety-origy-ypad(1);
else
    rety = origy + ypad(1) + ypad(2);
end
  
empty_list_elements_ok = 1;
warn_empty_list_elements = 0;

if(strcmp(padding, 'zeros'))
  retval = zeros(rety,retx);
  retval(ypad(1)+1 : ypad(1)+origy, xpad(1)+1 : xpad(1)+origx) = A;
  elseif(strcmp(padding,'ones'))
    retval = ones(rety,retx);
    retval(ypad(1)+1 : ypad(1)+origy, xpad(1)+1 : xpad(1)+origx) = A;
  elseif(strcmp(padding,'constant'))
    retval = const.*ones(rety,retx);
    retval(ypad(1)+1 : ypad(1)+origy, xpad(1)+1 : xpad(1)+origx) = A;
  elseif(strcmp(padding,'replicate'))
    y1 = origy-ypad(1)+1;
    x1 = origx-xpad(1)+1;    
    if(y1 < 1 || x1 < 1 || ypad(2) > origy || xpad(2) > origx)
      error('Too large padding for this padding type');
    else
      yrange1 = y1 : origy;
      yrange2 = 1 : ypad(2);
      xrange1 = x1 : origx;
      xrange2 = 1 : xpad(2);
      retval = [ A(yrange1, xrange1), A(yrange1, :), A(yrange1, xrange2);
                 A(:, xrange1),       A,             A(:, xrange2);
                 A(yrange2, xrange1), A(yrange2, :), A(yrange2, xrange2) ];
    end                        
  elseif(strcmp(padding,'symmetric'))
    y2 = origy-ypad(2)+1;
    x2 = origx-xpad(2)+1;
    if(ypad(1) > origy || xpad(1) > origx || y2 < 1 || x2 < 1)
      error('Too large padding for this padding type');
    else
      yrange1 = 1 : ypad(1);
      yrange2 = y2 : origy;
      xrange1 = 1 : xpad(1);
      xrange2 = x2 : origx;
      retval = [ fliplr(flipud(A(yrange1, xrange1))), flipud(A(yrange1, :)), fliplr(flipud(A(yrange1, xrange2)));
                 fliplr(A(:, xrange1)), A, fliplr(A(:, xrange2));
                 fliplr(flipud(A(yrange2, xrange1))), flipud(A(yrange2, :)), fliplr(flipud(A(yrange2, xrange2))) ];
    end      
   elseif(strcmp(padding,'reflect'))
    y2 = origy-ypad(2);
    x2 = origx-xpad(2);
    if(ypad(1)+1 > origy || xpad(1)+1 > origx || y2 < 1 || x2 < 1)
      error('Too large padding for this padding type');
    else
      yrange1 = 2 : ypad(1)+1;
      yrange2 = y2 : origy-1;
      xrange1 = 2 : xpad(1)+1;
      xrange2 = x2 : origx-1;
      retval = [ fliplr(flipud(A(yrange1, xrange1))), flipud(A(yrange1, :)), fliplr(flipud(A(yrange1, xrange2)));
                 fliplr(A(:, xrange1)), A, fliplr(A(:, xrange2));
                 fliplr(flipud(A(yrange2, xrange1))), flipud(A(yrange2, :)), fliplr(flipud(A(yrange2, xrange2))) ];
    end
   elseif(strcmp(padding,'extend'))
    if (extend_avg > 1)
        retval = [  repmat(mean2(A(1:extend_avg,1:extend_avg)),ypad(1),xpad(1)) , repmat(mean(A(1:extend_avg,:),1),ypad(1),1) , repmat(mean2(A(1:extend_avg,end-extend_avg+1:end)),ypad(1),xpad(2)) ;
            repmat(mean(A(:,1:extend_avg),2),1,xpad(1)) , A , repmat(mean(A(:,end-extend_avg+1:end),2),1,xpad(2)) ;
            repmat(mean2(A(end-extend_avg+1:end,1:extend_avg)),ypad(2),xpad(1)) , repmat(mean(A(end-extend_avg+1:end,:),1),ypad(2),1) , repmat(mean2(A(end-extend_avg+1:end,end-extend_avg+1:end)),ypad(2),xpad(2)) ];
    else
        retval = [  repmat(A(1,1),ypad(1),xpad(1)) , repmat(A(1,:),ypad(1),1) , repmat(A(1,end),ypad(1),xpad(2)) ;
            repmat(A(:,1),1,xpad(1)) , A , repmat(A(:,end),1,xpad(2)) ;
            repmat(A(end,1),ypad(2),xpad(1)) , repmat(A(end,:),ypad(2),1) , repmat(A(end,end),ypad(2),xpad(2)) ];
    end
  else    
    error('Unknown padding type');
  end

unwind_protect_cleanup
    empty_list_elements_ok = empty_list_elements_ok_save;
    warn_empty_list_elements = warn_empty_list_elements_save;
end_unwind_protect
      
      end