## Copyright (C) 2005 Søren Hauberg <soren@hauberg.org>
## Copyright (C) 2013 Carnë Draug <carandraug@octave.org>
##
## 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 3 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, see <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn  {Function File} {} imresize (@var{im}, @var{scale})
## @deftypefnx {Function File} {} imresize (@var{im}, [@var{M} @var{N}])
## @deftypefnx {Function File} {} imresize (@dots{}, @var{method})
## Resize image with interpolation
##
## Scales the image @var{im} by a factor @var{scale} or into the size @var{M}
## rows by @var{N} columns.  For example:
##
## @example
## @group
## imresize (im, 1);    # return the same image as input
## imresize (im, 1.5);  # return image 1.5 times larger
## imresize (im, 0.5);  # return image with half the size
## imresize (im, 2);    # return image with the double size
## imresize (im, [512 610]); # return image of size 512x610
## @end group
## @end example
##
## If @var{M} or @var{N} is @code{NaN}, it will be determined automatically so
## as to preserve aspect ratio.
##
## The optional argument @var{method} defines the interpolation method to be
## used.  All methods supported by @code{interp2} can be used.  By default, the
## @code{cubic} method is used.
##
## For @sc{matlab} compatibility, the methods @code{bicubic} (same as
## @code{cubic}), @code{bilinear} and @code{triangle} (both the same as
## @code{linear}) are also supported.
##
## @table @asis
## @item bicubic (default)
## Same as @code{cubic}.
##
## @item bilinear
## Same as @code{linear}.
##
## @item triangle
## Same as @code{linear}.
## @end table
##
## @seealso{imremap, imrotate, interp2}
## @end deftypefn

function im = imresize (im, scale, method = "cubic")

  if (nargin < 2 || nargin > 3)
    print_usage ();
  elseif (! isimage (im))
    error ("imresize: IM must be an image")
  elseif (! isnumeric (scale) || any (scale <= 0) || all (isnan (scale)))
    error ("imresize: SCALE or [M N] must be numeric positive values")
  elseif (! ischar (method))
    error ("imresize: METHOD must be a string with interpolation method")
  endif
  method = interp_method (method);

  in_rows = rows (im);
  in_cols = columns (im);

  if (isscalar (scale))
    out_rows = ceil (in_rows * scale);
    out_cols = ceil (in_cols * scale);
    scale_rows = scale;
    scale_cols = scale;
  elseif (numel (scale) == 2)
    out_rows = scale(1);
    out_cols = scale(2);

    ## maintain aspect ratio if requested
    if (isnan (out_rows))
      out_rows = in_rows * (out_cols / in_cols);
    elseif (isnan (out_cols))
      out_cols = in_cols * (out_rows / in_rows);
    endif

    scale_rows = out_rows / in_rows;
    scale_cols = out_cols / in_cols;
    out_rows = ceil (out_rows);
    out_cols = ceil (out_cols);
  else
    error ("imresize: SCALE argument must be a scalar or a 2 element vector");
  end

  ## calculate the new pixel indices in terms of the old pixel indices
  off_rows = 1 / scale_rows / 2;
  off_cols = 1 / scale_cols / 2;
  idx_rows = 0.5 + off_rows + (0:out_rows-1) / scale_rows;
  idx_cols = 0.5 + off_cols + (0:out_cols-1) / scale_cols;

  ## work around for imremap, valid for linear interpolation, probably bad
  ## for cubic interpolation. Limits indices to the bounds 1 and end.
  idx_rows = max (min (idx_rows, in_rows), 1);
  idx_cols = max (min (idx_cols, in_cols), 1);

  ## perform the actual resizing
  if (in_rows == out_rows && in_cols == out_cols)
    ## no resizing to do
  elseif (strcmpi (method, "nearest"))
    im = im(round(idx_rows), round(idx_cols));

  else
    idx_cols = repmat (idx_cols, [out_rows 1]);
    idx_rows = repmat (idx_rows', [1 out_cols]);
    im = imremap (im, idx_cols, idx_rows, method);
  endif
endfunction

## Test basic features.
%!test
%!
## Test scaling with 1:
%! in = [116  227  153   69  146  194   59  130  139  106
%!         2   47  137  249   90   75   16   24  158   44
%!       155   68   46   84  166  156   69  204   32  152
%!        71  221  137  230  210  153  192  115   30  118
%!       107  143  108   52   51   73  101   21  175   90
%!        54  158  143   77   26  168  113  229  165  225
%!         9   47  133  135  130  207  236   43   19   73];
%! assert (imresize (uint8 (in), 1, "nearest"), uint8 (in))
%! assert (imresize (uint8 (in), 1, "bicubic"), uint8 (in))
%!
## Test nearest neighbour with a scale factor of 2, also by aspect ratio preservation:
%! out = [116  116  227  227  153  153   69   69  146  146  194  194   59   59  130  130  139  139  106  106
%!        116  116  227  227  153  153   69   69  146  146  194  194   59   59  130  130  139  139  106  106
%!          2    2   47   47  137  137  249  249   90   90   75   75   16   16   24   24  158  158   44   44
%!          2    2   47   47  137  137  249  249   90   90   75   75   16   16   24   24  158  158   44   44
%!        155  155   68   68   46   46   84   84  166  166  156  156   69   69  204  204   32   32  152  152
%!        155  155   68   68   46   46   84   84  166  166  156  156   69   69  204  204   32   32  152  152
%!         71   71  221  221  137  137  230  230  210  210  153  153  192  192  115  115   30   30  118  118
%!         71   71  221  221  137  137  230  230  210  210  153  153  192  192  115  115   30   30  118  118
%!        107  107  143  143  108  108   52   52   51   51   73   73  101  101   21   21  175  175   90   90
%!        107  107  143  143  108  108   52   52   51   51   73   73  101  101   21   21  175  175   90   90
%!         54   54  158  158  143  143   77   77   26   26  168  168  113  113  229  229  165  165  225  225
%!         54   54  158  158  143  143   77   77   26   26  168  168  113  113  229  229  165  165  225  225
%!          9    9   47   47  133  133  135  135  130  130  207  207  236  236   43   43   19   19   73   73
%!          9    9   47   47  133  133  135  135  130  130  207  207  236  236   43   43   19   19   73   73];
%! assert (imresize (uint8 (in), 2, "nearest"), uint8 (out))
%! assert (imresize (uint8 (in), 2, "neAreST"), uint8 (out))
%! assert (imresize (uint8 (in), [14 NaN], "nearest"), uint8 (out))
%! assert (imresize (uint8 (in), [NaN 20], "nearest"), uint8 (out))
%!
## Test nearest neighbour with a scaling of 2 for x and 1 for y:
%! out = [116  116  227  227  153  153   69   69  146  146  194  194   59   59  130  130  139  139  106  106
%!          2    2   47   47  137  137  249  249   90   90   75   75   16   16   24   24  158  158   44   44
%!        155  155   68   68   46   46   84   84  166  166  156  156   69   69  204  204   32   32  152  152
%!         71   71  221  221  137  137  230  230  210  210  153  153  192  192  115  115   30   30  118  118
%!        107  107  143  143  108  108   52   52   51   51   73   73  101  101   21   21  175  175   90   90
%!         54   54  158  158  143  143   77   77   26   26  168  168  113  113  229  229  165  165  225  225
%!          9    9   47   47  133  133  135  135  130  130  207  207  236  236   43   43   19   19   73   73];
%! assert (imresize (uint8 (in), [7 20], "nearest"), uint8 (out))
%!
## Test nearest neighbour with a scaling of 1 for x and 2 for y:
%! out = [116  227  153   69  146  194   59  130  139  106
%!        116  227  153   69  146  194   59  130  139  106
%!          2   47  137  249   90   75   16   24  158   44
%!          2   47  137  249   90   75   16   24  158   44
%!        155   68   46   84  166  156   69  204   32  152
%!        155   68   46   84  166  156   69  204   32  152
%!         71  221  137  230  210  153  192  115   30  118
%!         71  221  137  230  210  153  192  115   30  118
%!        107  143  108   52   51   73  101   21  175   90
%!        107  143  108   52   51   73  101   21  175   90
%!         54  158  143   77   26  168  113  229  165  225
%!         54  158  143   77   26  168  113  229  165  225
%!          9   47  133  135  130  207  236   43   19   73
%!          9   47  133  135  130  207  236   43   19   73];
%! assert (imresize (uint8 (in), [14 10], "nearest"), uint8 (out))
%!
## Test equivalence of different input writing styles:
%! assert (imresize (uint8 (in), 1.5, "bicubic"), imresize (uint8 (in), 1.5, "cubic"))
%! assert (imresize (uint8 (in), [NaN, size(in,2)*1.5], "bicubic"), imresize (uint8 (in), 1.5, "cubic"))
%! assert (imresize (uint8 (in), [size(in,1)*1.5, NaN], "bicubic"), imresize (uint8 (in), 1.5, "cubic"))
%! assert (imresize (uint8 (in), 1.5, "linear"), imresize (uint8 (in), 1.5, "LIneAR"))
%! assert (imresize (uint8 (in), 1.5, "linear"), imresize (uint8 (in), 1.5, "triangle"))
%!
## nearest neighbour test, check that a pixel from the neighbour locations
## gets picked. The distance is the same for all neighbours here.
%! in = [116  227  153   69  146  194   59  130  139  106
%!         2   47  137  249   90   75   16   24  158   44
%!       155   68   46   84  166  156   69  204   32  152
%!        71  221  137  230  210  153  192  115   30  118
%!       107  143  108   52   51   73  101   21  175   90
%!        54  158  143   77   26  168  113  229  165  225
%!         9   47  133  135  130  207  236   43   19   73
%!       129   60   59  243   64  181  249   56   32   86];
%!
%! out = imresize (in, 0.5, "nearest");
%! for x = 1:columns (out)
%!     for y = 1:rows (out)
%!         x_in = 2 * (x-1) + 1;
%!         y_in = 2 * (y-1) + 1;
%!         sub = in(y_in:y_in+1, x_in:x_in+1);
%!         assert (any (any (sub == out(y,x))))
%!     endfor
%! endfor


## Test floating point range and and scaling of multi-channel images.
%!test
%!
## Do not enforce floating point images to be in the [0 1] range (bug #43846):
%! assert (imresize (repmat (5, [3 3]), 2), repmat (5, [6 6]), eps*100)
%!
## Similarly, do not enforce images to have specific dimensions and only
## expand on the first 2 dimensions:
%! assert (imresize (repmat (5, [3 3 2]), 2), repmat (5, [6 6 2]), eps*100)


## Test linear interpolations against some reference results from matlab
## without anti-aliasing with
## imresize (uint8 (in), <FACTOR>, 'bilinear', 'Antialiasing', false)
## The floating point error is less than 1e-13, but it affects the rounding
## to int. So a difference of 1 is acceptable.
%!test
%!
%! in = [116  227  153   69  146  194   59  130  139  106
%!         2   47  137  249   90   75   16   24  158   44
%!       155   68   46   84  166  156   69  204   32  152
%!        71  221  137  230  210  153  192  115   30  118
%!       107  143  108   52   51   73  101   21  175   90
%!        54  158  143   77   26  168  113  229  165  225
%!         9   47  133  135  130  207  236   43   19   73
%!       129   60   59  243   64  181  249   56   32   86];
%!
## Factor 1.5, gives an output of size 12 x 15 (without requiring to round the size)
%! out = [116  172  215  165  111   82  133  170  171   81   95  132  138  123  106
%!         59   98  138  144  152  152  125  127  119   54   58   89  137  112   75
%!         27   39   62  110  172  202  123   96   78   36   40   68  123  100   62
%!        129   97   64   62   87  119  146  148  128   74  117  154   73   94  134
%!        113  129  136  101  125  162  183  172  151  135  146  139   53   83  135
%!         77  143  195  145  166  197  186  162  146  171  138   92   62   84  113
%!        101  129  149  120   98   81   78   82   91  111   77   56  132  123   95
%!         81  116  147  130   96   61   43   80  119  109  116  132  162  164  158
%!         46   93  139  141  114   80   50  109  168  141  166  189  151  171  200
%!         16   41   77  123  130  123  115  157  204  214  145   69   48   71   98
%!         69   62   61   89  143  174  112  146  202  235  147   46   30   53   80
%!        129   95   60   59  151  213   94  123  192  238  153   52   36   59   86];
%! assert (max (max (abs (imresize (uint8 (in), 1.5, "bilinear") - uint8 (out)))) <= 1)
%!
## Factor 0.5, gives an output of size 4 x 5 (without requiring to round the size)
%! out = [ 98  152  126   57  112
%!        129  124  171  145   83
%!        116   95   80  116  164
%!         61  143  146  146   53];
%!
%! assert (max (max (abs (imresize (uint8 (in), 0.5, "bilinear") - uint8 (out)))) <= 1)
%!
## Factor 4/3, gives an output of size 10.6667 x 13.3333 rounded up to 11 x 14
%! out = [116  185  199  143   80  117  164  177   76  103  133  135  110  106
%!         45   89  126  148  177  138  114  109   43   52   97  141   78   67
%!         59   57   73  114  177  145  114   96   45   71   99  108   88   85
%!        145  109   76   63   96  146  166  147   93  152  133   47  134  148
%!         82  157  174  137  201  208  186  156  174  145   90   42  111  122
%!         94  143  152  119  119  114  108  107  131   86   80  119  104  101
%!         87  126  139  114   69   49   67  109  106  102  126  167  145  141
%!         48  108  143  135   91   56   89  167  134  177  184  154  199  206
%!         15   44   88  133  129  121  149  204  219  124   55   44   85   92
%!         84   66   67  102  189  132  127  198  237  123   42   34   74   81
%!        129   86   60   82  220  131  108  190  241  128   47   39   79   86];
%! assert (max (max (abs (imresize (uint8 (in), 4/3, "bilinear") - uint8 (out)))) <= 1)
%!
## Factor 1/3, gives an output of size 2.6667 x 3.3333 rounded up to 3 x 4
%! out = [ 47   90   24   44
%!        143   51   21   90
%!         60   64   56   86];
%! assert (max (max (abs (imresize (uint8 (in), 1/3, "bilinear") - uint8 (out)))) <= 1)


## Comparison to the reference result from matlab with cubic interpolation
## fails. This has to be fixed in imremap and here (remove the work around
## above).
%!xtest
%!
%! in = [116  227  153   69  146  194   59  130  139  106
%!         2   47  137  249   90   75   16   24  158   44
%!       155   68   46   84  166  156   69  204   32  152
%!        71  221  137  230  210  153  192  115   30  118
%!       107  143  108   52   51   73  101   21  175   90
%!        54  158  143   77   26  168  113  229  165  225
%!         9   47  133  135  130  207  236   43   19   73
%!       129   60   59  243   64  181  249   56   32   86];
%!
%! out = [116  187  237  171   94   61  135  191  187   75   91  142  140  124  108
%!         43   92  143  149  164  163  119  123  118   44   38   80  151  118   62
%!         13   21   47  107  195  228  115   81   70   24   19   56  137  105   48
%!        146   98   49   49   71  107  148  159  132   58  124  176   61   85  146
%!        118  139  144   92  116  168  201  188  159  140  167  158   27   69  153
%!         61  151  218  145  174  219  201  164  146  187  148   84   48   76  115
%!        102  132  151  119   90   72   72   72   83  114   60   31  144  130   80
%!         81  121  154  133   87   41   19   67  116   95  108  140  183  180  163
%!         37   95  152  150  117   73   35  108  179  130  174  214  153  176  219
%!          3   29   73  131  136  120  116  162  214  229  147   54   35   62   96
%!         67   54   51   83  153  187  111  141  210  255  149   22   13   42   74
%!        142   99   53   43  164  237   77  103  197  254  159   42   31   59   91];
%! assert (imresize (uint8 (in), 1.5, "bicubic"), uint8 (out))


## Reduce the size of a checkerboard, such that it turns into gray
%!test
%!
%! in = checkerboard (1, [2 2]);
%! out = [0.5  0.35
%!        0.5  0.35];
%! assert ( imresize (in, 0.5, 'bilinear'), out)
