1## Copyright (C) 2005 Søren Hauberg <soren@hauberg.org>
2## Copyright (C) 2013 Carnë Draug <carandraug@octave.org>
3##
4## This program is free software; you can redistribute it and/or modify it under
5## the terms of the GNU General Public License as published by the Free Software
6## Foundation; either version 3 of the License, or (at your option) any later
7## version.
8##
9## This program is distributed in the hope that it will be useful, but WITHOUT
10## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12## details.
13##
14## You should have received a copy of the GNU General Public License along with
15## this program; if not, see <http://www.gnu.org/licenses/>.
16
17## -*- texinfo -*-
18## @deftypefn  {Function File} {} imresize (@var{im}, @var{scale})
19## @deftypefnx {Function File} {} imresize (@var{im}, [@var{M} @var{N}])
20## @deftypefnx {Function File} {} imresize (@dots{}, @var{method})
21## Resize image with interpolation
22##
23## Scales the image @var{im} by a factor @var{scale} or into the size @var{M}
24## rows by @var{N} columns.  For example:
25##
26## @example
27## @group
28## imresize (im, 1);    # return the same image as input
29## imresize (im, 1.5);  # return image 1.5 times larger
30## imresize (im, 0.5);  # return image with half the size
31## imresize (im, 2);    # return image with the double size
32## imresize (im, [512 610]); # return image of size 512x610
33## @end group
34## @end example
35##
36## If @var{M} or @var{N} is @code{NaN}, it will be determined automatically so
37## as to preserve aspect ratio.
38##
39## The optional argument @var{method} defines the interpolation method to be
40## used.  All methods supported by @code{interp2} can be used.  By default, the
41## @code{cubic} method is used.
42##
43## For @sc{matlab} compatibility, the methods @code{bicubic} (same as
44## @code{cubic}), @code{bilinear} and @code{triangle} (both the same as
45## @code{linear}) are also supported.
46##
47## @table @asis
48## @item bicubic (default)
49## Same as @code{cubic}.
50##
51## @item bilinear
52## Same as @code{linear}.
53##
54## @item triangle
55## Same as @code{linear}.
56## @end table
57##
58## @seealso{imremap, imrotate, interp2}
59## @end deftypefn
60
61function im = imresize (im, scale, method = "cubic")
62
63  if (nargin < 2 || nargin > 3)
64    print_usage ();
65  elseif (! isimage (im))
66    error ("imresize: IM must be an image")
67  elseif (! isnumeric (scale) || any (scale <= 0) || all (isnan (scale)))
68    error ("imresize: SCALE or [M N] must be numeric positive values")
69  elseif (! ischar (method))
70    error ("imresize: METHOD must be a string with interpolation method")
71  endif
72  method = interp_method (method);
73
74  inRows = rows (im);
75  inCols = columns (im);
76
77  ## we may be able to use clever indexing instead of interpolation
78  int_row_scale = false;
79  int_col_scale = false;
80  if (isscalar (scale))
81    outRows = ceil (rows (im) * scale);
82    outCols = ceil (columns (im) * scale);
83
84    ## check if we can use clever indexing
85    scale_rows = scale_cols = scale;
86    int_row_scale = int_col_scale = is_for_integer (scale);
87  elseif (numel (scale) == 2)
88    outRows = scale(1);
89    outCols = scale(2);
90    ## maintain aspect ratio if requested
91    if (isnan (outRows))
92      outRows = inRows * (outCols / inCols);
93    elseif (isnan (outCols))
94      outCols = inCols * (outRows / inRows);
95    endif
96    outRows = ceil (outRows);
97    outCols = ceil (outCols);
98
99    ## we will need this to use clever indexing. In this case, we will also need
100    ## to check that we are changing the rows and columns of the image in the
101    ## same direction
102    scale_rows = (outRows/inRows);
103    scale_cols = (outCols/inCols);
104    int_row_scale = is_for_integer (scale_rows);
105    int_col_scale = is_for_integer (scale_cols);
106  else
107    error ("imresize: SCALE argument must be a scalar or a 2 element vector");
108  end
109
110  ## Perform the actual resizing
111  if (inRows == outRows && inCols == outCols)
112    ## no resizing to do
113  elseif (strcmpi (method, "nearest") && all ([int_row_scale int_col_scale]))
114    ## we are matlab incompatible here on purpose. We can the stuff here in 2
115    ## ways. With interp2 or by clever indexing. Indexing is much much faster
116    ## than interp2 but they return different results (the way we are doing it
117    ## at least). Matlab does the same as we are doing if the both columns and
118    ## rows go the same direction but if they increase one and decrease the
119    ## other, then they return the same as if we were using interp2. We are
120    ## smarter and use indexing even in that case but then the results differ
121    if (int_row_scale == 1)
122      row_idx = (1:rows (im))(ones (1, scale_rows), :);
123    elseif (int_row_scale == -1)
124      row_idx = ceil (linspace (floor (1/(scale_rows * 2)) + 1, inRows, outRows));
125    endif
126    if (int_col_scale == 1)
127      col_idx = (1:columns (im))(ones (scale_cols, 1), :);
128    elseif (int_col_scale == -1)
129      col_idx = ceil (linspace (floor (1/(scale_cols * 2)) + 1, inCols, outCols));
130    endif
131    im = im(row_idx, col_idx);
132
133  else
134    [XI, YI] = meshgrid (linspace (1, inCols, outCols), linspace (1, inRows, outRows));
135    im = imremap (im, XI, YI, method);
136  endif
137endfunction
138
139function retval = is_for_integer (scale)
140  retval = false;
141  if (fix (scale) == scale)
142    retval = 1;
143  elseif (fix (1/scale) == (1/scale))
144    ## if scale/n is an integer then we are resizing to one half, one third, etc
145    ## and we can also use clever indexing
146    retval = -1;
147  endif
148endfunction
149
150%!test
151%! in = [116  227  153   69  146  194   59  130  139  106
152%!         2   47  137  249   90   75   16   24  158   44
153%!       155   68   46   84  166  156   69  204   32  152
154%!        71  221  137  230  210  153  192  115   30  118
155%!       107  143  108   52   51   73  101   21  175   90
156%!        54  158  143   77   26  168  113  229  165  225
157%!         9   47  133  135  130  207  236   43   19   73];
158%! assert (imresize (uint8 (in), 1, "nearest"), uint8 (in))
159%! assert (imresize (uint8 (in), 1, "bicubic"), uint8 (in))
160%!
161%! out = [116  116  227  227  153  153   69   69  146  146  194  194   59   59  130  130  139  139  106  106
162%!        116  116  227  227  153  153   69   69  146  146  194  194   59   59  130  130  139  139  106  106
163%!          2    2   47   47  137  137  249  249   90   90   75   75   16   16   24   24  158  158   44   44
164%!          2    2   47   47  137  137  249  249   90   90   75   75   16   16   24   24  158  158   44   44
165%!        155  155   68   68   46   46   84   84  166  166  156  156   69   69  204  204   32   32  152  152
166%!        155  155   68   68   46   46   84   84  166  166  156  156   69   69  204  204   32   32  152  152
167%!         71   71  221  221  137  137  230  230  210  210  153  153  192  192  115  115   30   30  118  118
168%!         71   71  221  221  137  137  230  230  210  210  153  153  192  192  115  115   30   30  118  118
169%!        107  107  143  143  108  108   52   52   51   51   73   73  101  101   21   21  175  175   90   90
170%!        107  107  143  143  108  108   52   52   51   51   73   73  101  101   21   21  175  175   90   90
171%!         54   54  158  158  143  143   77   77   26   26  168  168  113  113  229  229  165  165  225  225
172%!         54   54  158  158  143  143   77   77   26   26  168  168  113  113  229  229  165  165  225  225
173%!          9    9   47   47  133  133  135  135  130  130  207  207  236  236   43   43   19   19   73   73
174%!          9    9   47   47  133  133  135  135  130  130  207  207  236  236   43   43   19   19   73   73];
175%! assert (imresize (uint8 (in), 2, "nearest"), uint8 (out))
176%! assert (imresize (uint8 (in), 2, "neAreST"), uint8 (out))
177%! assert (imresize (uint8 (in), [14 NaN], "nearest"), uint8 (out))
178%! assert (imresize (uint8 (in), [NaN 20], "nearest"), uint8 (out))
179%!
180%! out = [116  116  227  227  153  153   69   69  146  146  194  194   59   59  130  130  139  139  106  106
181%!          2    2   47   47  137  137  249  249   90   90   75   75   16   16   24   24  158  158   44   44
182%!        155  155   68   68   46   46   84   84  166  166  156  156   69   69  204  204   32   32  152  152
183%!         71   71  221  221  137  137  230  230  210  210  153  153  192  192  115  115   30   30  118  118
184%!        107  107  143  143  108  108   52   52   51   51   73   73  101  101   21   21  175  175   90   90
185%!         54   54  158  158  143  143   77   77   26   26  168  168  113  113  229  229  165  165  225  225
186%!          9    9   47   47  133  133  135  135  130  130  207  207  236  236   43   43   19   19   73   73];
187%! assert (imresize (uint8 (in), [7 20], "nearest"), uint8 (out))
188%!
189%! assert (imresize (uint8 (in), 1.5, "bicubic"), imresize (uint8 (in), 1.5, "cubic"))
190%! assert (imresize (uint8 (in), [NaN, size(in,2)*1.5], "bicubic"), imresize (uint8 (in), 1.5, "cubic"))
191%! assert (imresize (uint8 (in), [size(in,1)*1.5, NaN], "bicubic"), imresize (uint8 (in), 1.5, "cubic"))
192%! assert (imresize (uint8 (in), 1.5, "linear"), imresize (uint8 (in), 1.5, "LIneAR"))
193%! assert (imresize (uint8 (in), 1.5, "linear"), imresize (uint8 (in), 1.5, "triangle"))
194%!
195%! out = [ 47  249   75   24   44
196%!        221  230  153  115  118
197%!        158   77  168  229  225
198%!        47   135  207   43   73];
199%! assert (imresize (uint8 (in), 0.5, "nearest"), uint8 (out))
200
201## Do not enforce floating point images to be in the [0 1] range (bug #43846)
202%!assert (imresize (repmat (5, [3 3]), 2), repmat (5, [6 6]), eps*100)
203
204## Similarly, do not enforce images to have specific dimensions and only
205## expand on the first 2 dimensions.
206%!assert (imresize (repmat (5, [3 3 2]), 2), repmat (5, [6 6 2]), eps*100)
207
208## The following are the matlab results. We have slighlty different
209## results but not by much. If there's would be any fixes, they
210## would have to be on interp2 or maybe in imremap.
211
212%!shared in
213%! in = [116  227  153   69  146  194   59  130  139  106
214%!         2   47  137  249   90   75   16   24  158   44
215%!       155   68   46   84  166  156   69  204   32  152
216%!        71  221  137  230  210  153  192  115   30  118
217%!       107  143  108   52   51   73  101   21  175   90
218%!        54  158  143   77   26  168  113  229  165  225
219%!         9   47  133  135  130  207  236   43   19   73];
220
221%!xtest
222%! out = [116  185  235  171   96   64  134  189  186   74   90   141  140  124  108
223%!         44   92  143  149  164  163  119  123  118   44   38    80  151  118   63
224%!         14   21   47  107  195  228  115   81   70   24   19    56  137  105   49
225%!        145   98   49   49   71  107  148  159  132   58  124   176   61   85  145
226%!        118  139  144   92  116  168  201  188  159  140  167   158   27   69  152
227%!         62  151  218  145  174  219  201  164  146  187  148    84   48   76  115
228%!        102  132  151  119   90   72   72   72   83  114   60    31  144  130   81
229%!         82  121  154  133   87   41   19   67  116   95  108   140  183  180  164
230%!         40   96  152  149  117   74   34  108  179  131  175   215  153  177  219
231%!         11   33   73  127  137  125  113  158  212  229  148    55   35   63   96
232%!          4   17   53  121  141  138  133  171  220  253  141    16    7   36   67];
233%! assert (imresize (uint8 (in), 1.5, "bicubic"), uint8 (out))
234
235%!xtest
236%! out = [116  172  215  165  111   82  133  170  171   81   95   132  138  123  106
237%!         59   98  138  144  152  152  125  127  119   54   58    89  137  112   75
238%!         27   39   62  110  172  202  123   96   78   36   40    68  123  100   62
239%!        129   97   64   62   87  119  146  148  128   74  117   154   73   94  134
240%!        113  129  136  101  125  162  183  172  151  135  146   139   53   83  135
241%!         77  143  195  145  166  197  186  162  146  171  138    92   62   84  113
242%!        101  129  149  120   98   81   78   82   91  111   77    56  132  123   95
243%!         81  116  147  130   96   61   43   80  119  109  116   132  162  164  158
244%!         46   93  139  141  114   80   50  109  168  141  166   189  151  171  200
245%!         16   41   77  123  130  123  115  157  204  214  145    69   48   71   98
246%!          9   28   61  119  134  134  131  169  212  231  140    39   23   46   73];
247%! assert (imresize (uint8 (in), 1.5, "bilinear"), uint8 (out))
248
249%!xtest
250%! out = [108  136  125   89  107
251%!        111  132  143  114   99
252%!        106  110  106  127  136
253%!         47  121  163  138   68];
254%! assert (imresize (uint8 (in), 0.5, "bilinear"), uint8 (out))
255
256%!xtest
257%! out = [103  141  124   78  110
258%!        111  134  153  114   91
259%!        115  108   93  128  146
260%!         38  124  175  143   54];
261%! assert (imresize (uint8 (in), 0.5, "bicubic"), uint8 (out))
262
263%!xtest
264%! ## bug #55202
265%! assert (imresize (zeros (1, 20), [1 30], "nearest"), zeros (1, 30))
266
267%!test
268%! ## a test for bug #55780
269%! a = zeros (7, "uint8");
270%! a (4,4) = 255;
271%! c = [   4   13   24   31   31   24   13    4;
272%!        13   44   79  103  103   79   44   13;
273%!        24   79  140  182  182  140   79   24;
274%!        31  103  182  238  238  182  103   31;
275%!        31  103  182  238  238  182  103   31;
276%!        24   79  140  182  182  140   79   24;
277%!        13   44   79  103  103   79   44   13;
278%!         4   13   24   31   31   24   13    4];
279%! b = imresize (a, 4, "bicubic");
280%! assert (b(11:18,11:18), c, eps)
281
282