changeset 889:ea45a8e63169

maint: Periodic merge of stable to default.
author Carnë Draug <carandraug@octave.org>
date Tue, 02 Sep 2014 15:01:16 +0100
parents 33c44c229f74 (diff) d316de4780ce (current diff)
children 3d1d76c830c6
files
diffstat 28 files changed, 809 insertions(+), 1408 deletions(-) [+]
line wrap: on
line diff
--- a/COPYING
+++ b/COPYING
@@ -1,9 +1,7 @@
 inst/private/analyze75filename.m        GPLv3+
 inst/private/handle_colorspec.m         GPLv3+
 inst/private/im2col_check.m             GPLv3+
-inst/private/im2float.m                 GPLv3+
 inst/private/imarithmetics.m            GPLv3+
-inst/private/imconversion.m             GPLv3+
 inst/private/interp_method.m            GPLv3+
 inst/private/is_double_image.m          GPLv3+
 inst/private/isimage.m                  GPLv3+
@@ -73,6 +71,7 @@
 inst/imadd.m                            GPLv3+
 inst/imadjust.m                         GPLv3+
 inst/imbothat.m                         GPLv3+
+inst/imcast.m                           GPLv3+
 inst/imclose.m                          GPLv3+
 inst/imcomplement.m                     GPLv3+
 inst/imcrop.m                           GPLv3+
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,5 +1,5 @@
 Name: Image
-Version: 2.2.1
+Version: 2.3.0
 Date: 2014-03-08
 Author: various authors
 Maintainer: Carnë Draug <carandraug+dev@gmail.com>
@@ -9,6 +9,6 @@
  The package also provides functions for feature extraction, image
  statistics, spatial and geometric transformations, morphological
  operations, linear filtering, and much more.
-Depends: octave (>= 3.8.0), signal (>= 1.2.0), general (>= 1.3.0)
+Depends: octave (>= 3.8.0), general (>= 1.3.0)
 License: GPLv3+, MIT, FreeBSD
 Url: http://octave.sf.net
--- a/INDEX
+++ b/INDEX
@@ -133,6 +133,7 @@
  im2single
  im2uint8
  im2uint16
+ imcast
  imdither
  isbw
  isgray
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,48 @@
+ Summary of important user-visible changes for image 2.4.0 (yyyy/mm/dd):
+-------------------------------------------------------------------------
+
+ ** The following functions are new:
+
+      imcast
+
+ ** The implementation of normxcorr2 has been changed. The new method is
+    Matlab compatible and will return values in the range [-1 1].
+
+ ** The image package is no longer dependent on the signal package.
+
+ ** The disk shaped filter of fspecial has been changed for Matlab
+    compatibility. The elements on the border of the disk are now
+    weighted by how much of them is covered by the disk. Note that
+    this change is backwards incompatible.
+
+ ** The following functions will display the output image as a figure
+    instead of printing to the command line, when there are no output
+    arguments:
+
+      im2bw
+
+ ** For better compatibility with Matlab, the imrotate, imremap, and
+    imperspectivewarp functions now use 0 instead of NA for the default
+    extrapolation value.
+
+ ** Deprecated functions.
+
+    The following functions were deprecated in image 2.2.0 and have been
+    removed from image 2.4.0.
+
+      bwborder              iptchecknargin        readexif
+      impad                 iptcheckstrs
+      imrotate_Fourier      uintlut
+
+ ** Other functions that have been changed for smaller bugfixes, increased
+    Matlab compatibility, or performance:
+
+      im2double
+      im2int16
+      im2single
+      im2uint8
+      im2uint16
+
  Summary of important user-visible changes for image 2.2.1 (2014/03/08):
 -------------------------------------------------------------------------
 
@@ -15,6 +60,7 @@
 
  ** Fix regression in bwdist when calculating the closest pixel map.
 
+
  Summary of important user-visible changes for image 2.2.0 (2014/01/08):
 -------------------------------------------------------------------------
 
deleted file mode 100644
--- a/inst/bwborder.m
+++ /dev/null
@@ -1,51 +0,0 @@
-## Copyright (C) 2000 Etienne Grossmann <etienne@egdn.net>
-##
-## 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} {@var{b} = } bwborder (@var{im})
-## Finds the borders of foreground objects in a binary image.
-##
-## @code{bwborder} has been deprecated in favor of
-## @code{bwmorph (@var{im},"remove")}.  This function will be removed from
-## future versions of the `image' package.
-##
-## @var{b} is the borders in the 0-1 matrix @var{im}. 4-neighborhood is considered.
-## 
-## A pixel is on the border if it is set in @var{im}, and it has at least one
-## neighbor that is not set.
-## @end deftypefn
-
-function b = bwborder(im)
-
-  ## Deprecate bwborder because bwmorph does the same job, works for any
-  ## number of dimensions, performs faster, and exist in Matlab.
-  persistent warned = false;
-  if (! warned)
-    warned = true;
-    warning ("Octave:deprecated-function",
-             ["`bwborder' has been deprecated in favor of " ...
-              "`bwmorph (IM,\"remove\")'.  This function will be removed " ...
-              "from future versions of the `image' package"]);
-  endif
-
-[R,C]=size(im);
-
-b = im & ...
-    !([im(2:R,:) ;  zeros(1,C) ] & ...
-      [zeros(1,C); im(1:R-1,:) ] & ...
-      [im(:,2:C) ,  zeros(R,1) ] & ...
-      [zeros(R,1),  im(:,1:C-1)] ) ;
-
-endfunction
--- a/inst/fspecial.m
+++ b/inst/fspecial.m
@@ -14,79 +14,99 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} {@var{filter} = } fspecial(@var{type}, @var{arg1}, @var{arg2})
+## @deftypefn {Function File} {} fspecial(@var{type}, @var{arg1}, @var{arg2})
 ## Create spatial filters for image processing.
 ##
 ## @var{type} determines the shape of the filter and can be
-## @table @t
-## @item "average"
+## @table @asis
+## @item @qcode{"average"}
 ## Rectangular averaging filter. The optional argument @var{arg1} controls the
 ## size of the filter. If @var{arg1} is an integer @var{N}, a @var{N} by @var{N}
 ## filter is created. If it is a two-vector with elements @var{N} and @var{M}, the
 ## resulting filter will be @var{N} by @var{M}. By default a 3 by 3 filter is
 ## created.
-## @item "disk"
+##
+## @item @qcode{"disk"}
 ## Circular averaging filter. The optional argument @var{arg1} controls the
-## radius of the filter. If @var{arg1} is an integer @var{N}, a 2 @var{N} + 1
-## filter is created. By default a radius of 5 is used.
-## @item "gaussian"
+## radius of the filter. If @var{arg1} is an integer @var{R}, a 2 @var{R} + 1
+## filter is created. By default a radius of 5 is used. If the returned matrix
+## corresponds to a cartesian grid, each element of the matrix is weighted by
+## how much of the corresponding grid square is covered by a disk of radius
+## @var{R} and centered at the middle of the element @var{R}+1,@var{R}+1.
+##
+## @item @qcode{"gaussian"}
 ## Gaussian filter. The optional argument @var{arg1} controls the size of the
 ## filter. If @var{arg1} is an integer @var{N}, a @var{N} by @var{N}
 ## filter is created. If it is a two-vector with elements @var{N} and @var{M}, the
 ## resulting filter will be @var{N} by @var{M}. By default a 3 by 3 filter is
 ## created. The optional argument @var{arg2} sets spread of the filter. By default
 ## a spread of @math{0.5} is used.
-## @item "log"
+##
+## @item @qcode{"log"}
 ## Laplacian of Gaussian. The optional argument @var{arg1} controls the size of the
 ## filter. If @var{arg1} is an integer @var{N}, a @var{N} by @var{N}
 ## filter is created. If it is a two-vector with elements @var{N} and @var{M}, the
 ## resulting filter will be @var{N} by @var{M}. By default a 5 by 5 filter is
 ## created. The optional argument @var{arg2} sets spread of the filter. By default
 ## a spread of @math{0.5} is used.
-## @item "laplacian"
+##
+## @item @qcode{"laplacian"}
 ## 3x3 approximation of the laplacian. The filter is approximated as
+##
 ## @example
-## (4/(@var{alpha}+1))*[@var{alpha}/4,     (1-@var{alpha})/4, @var{alpha}/4; ...
-##                (1-@var{alpha})/4, -1,          (1-@var{alpha})/4;  ...
-##                @var{alpha}/4,     (1-@var{alpha})/4, @var{alpha}/4];
+## (4/(@var{alpha}+1)) * [   @var{alpha}/4   (1-@var{alpha})/4     @var{alpha}/4
+##                  (1-@var{alpha})/4   -1          (1-@var{alpha})/4
+##                     @var{alpha}/4   (1-@var{alpha})/4     @var{alpha}/4 ];
 ## @end example
+##
 ## where @var{alpha} is a number between 0 and 1. This number can be controlled
 ## via the optional input argument @var{arg1}. By default it is @math{0.2}.
-## @item "unsharp"
+##
+## @item @qcode{"unsharp"}
 ## Sharpening filter. The following filter is returned
 ## @example
-## (1/(@var{alpha}+1))*[-@var{alpha},   @var{alpha}-1, -@var{alpha}; ...
-##                 @var{alpha}-1, @var{alpha}+5,  @var{alpha}-1; ...
-##                -@var{alpha},   @var{alpha}-1, -@var{alpha}];
+## (1/(@var{alpha}+1)) * [-@var{alpha}   @var{alpha}-1 -@var{alpha}
+##                   @var{alpha}-1 @var{alpha}+5  @var{alpha}-1
+##                  -@var{alpha}   @var{alpha}-1 -@var{alpha}];
 ## @end example
+##
 ## where @var{alpha} is a number between 0 and 1. This number can be controlled
 ## via the optional input argument @var{arg1}. By default it is @math{0.2}.
-## @item "motion"
+##
+## @item @qcode{"motion"}
 ## Moion blur filter of width 1 pixel. The optional input argument @var{arg1}
 ## controls the length of the filter, which by default is 9. The argument @var{arg2}
 ## controls the angle of the filter, which by default is 0 degrees.
-## @item "sobel"
+##
+## @item @qcode{"sobel"}
 ## Horizontal Sobel edge filter. The following filter is returned
+##
 ## @example
-## [ 1,  2,  1;
-##   0,  0,  0;
-##  -1, -2, -1 ]
+## [ 1  2  1
+##   0  0  0
+##  -1 -2 -1 ]
 ## @end example
-## @item "prewitt"
+##
+## @item @qcode{"prewitt"}
 ## Horizontal Prewitt edge filter. The following filter is returned
+##
 ## @example
-## [ 1,  1,  1;
-##   0,  0,  0;
-##  -1, -1, -1 ]
+## [ 1  1  1
+##   0  0  0
+##  -1 -1 -1 ]
 ## @end example
+##
 ## @item "kirsch"
 ## Horizontal Kirsch edge filter. The following filter is returned
-## @example
-## [ 3,  3,  3;
-##   3,  0,  3;
-##  -5, -5, -5 ]
-## @end example
+##
+## @verbatim
+## [ 3  3  3
+##   3  0  3
+##  -5 -5 -5 ]
+## @end verbatim
 ## @end table
+##
+## @seealso{conv2, convn, filter2, imfilter}
 ## @end deftypefn
 
 ## Remarks by Søren Hauberg (jan. 2nd 2007)
@@ -95,11 +115,14 @@
 ## http://www.csse.uwa.edu.au/~pk/research/matlabfns/OctaveCode/fspecial.m
 
 function f = fspecial (type, arg1, arg2)
-  if (!ischar (type))
+
+  if (nargin < 1)
+    print_usage ();
+  elseif (! ischar (type))
     error ("fspecial: first argument must be a string");
   endif
-  
-  switch lower(type)
+
+  switch lower (type)
     case "average"
       ## Get filtersize
       if (nargin > 1 && isreal (arg1) && length (arg1 (:)) <= 2)
@@ -114,17 +137,91 @@
 
     case "disk"
       ## Get the radius
-      if (nargin > 1 && isreal (arg1) && length (arg1 (:)) == 1)
-        radius = arg1;
+      if (nargin > 1 && isreal (arg1) && isscalar (arg1))
+        r = arg1;
       else
-        radius = 5;
+        r = 5;
       endif
       ## Create the filter
-      [x, y] = meshgrid (-radius:radius, -radius:radius);
-      r = sqrt (x.^2 + y.^2);
-      f = (r <= radius);
-      ## Normalize the filter to integral 1
-      f = f / sum (f (:));
+      if (r == 0)
+        f = 1;
+      else
+        ax = r + 1; # index of the "x-axis" and "y-axis"
+        corner = floor (r / sqrt (2)+0.5)-0.5; # corner corresponding to 45 degrees
+        rsq = r*r;
+        ## First set values for points completely covered by the disk
+        [X, Y] = meshgrid (-r:r, -r:r);
+        rhi = (abs (X) +0.5).^2 + (abs (Y)+0.5).^2;
+        f = (rhi <= rsq) / 1.0;
+        xx = linspace (0.5, r - 0.5, r);
+        ii = sqrt (rsq - xx.^2); # intersection points for sqrt (r^2 - x^2)
+        ## Set the values at the axis caps
+        tmp = sqrt (rsq -0.25);
+        rint = (0.5*tmp + rsq * atan (0.5/tmp))/2; # value of integral on the right
+        cap = 2*rint - r+0.5; # at the caps, lint = rint
+        f(ax  ,ax+r) = cap;
+        f(ax  ,ax-r) = cap;
+        f(ax+r,ax  ) = cap;
+        f(ax-r,ax  ) = cap;
+        if (r == 1)
+          y = ii(1);
+          lint = rint;
+          tmp = sqrt (rsq - y^2);
+          rint = (y*tmp + rsq * atan (y/tmp))/2;
+          val  = rint - lint - 0.5 * (y-0.5);
+          f(ax-r,ax-r) = val;
+          f(ax+r,ax-r) = val;
+          f(ax-r,ax+r) = val;
+          f(ax+r,ax+r) = val;
+        else
+          ## Set the values elsewhere on the rim
+          idx = 1; # index in the vector ii
+          x   = 0.5; # bottom left corner of the current square
+          y   = r-0.5;
+          rx  = 0.5; # x on the right of the integrable region
+          ybreak = false; # did we change our y last time
+          do
+            i = x +0.5;
+            j = y +0.5;
+            lint = rint;
+            lx = rx;
+            if (ybreak)
+              ybreak = false;
+              val = lx-x;
+              idx++;
+              x++;
+              rx = x;
+              val -= y*(x-lx);
+            elseif (ii(idx+1) < y)
+              ybreak = true;
+              y--;
+              rx  = ii(y+1.5);
+              val = (y+1) * (x-rx);
+            else
+              val = -y;
+              idx++;
+              x++;
+              rx = x;
+              if (floor (ii(idx)-0.5) == y)
+                y++;
+              endif
+            endif
+            tmp  = sqrt (rsq - rx*rx);
+            rint = (rx*tmp + rsq * atan (rx/tmp))/2;
+            val += rint - lint;
+            f(ax+i, ax+j) = val;
+            f(ax+i, ax-j) = val;
+            f(ax-i, ax+j) = val;
+            f(ax-i, ax-j) = val;
+            f(ax+j, ax+i) = val;
+            f(ax+j, ax-i) = val;
+            f(ax-j, ax+i) = val;
+            f(ax-j, ax-i) = val;
+          until (y < corner || x > corner)
+        endif
+        # Normalize
+        f /= pi * rsq;
+      endif
 
     case "gaussian"
       ## Get hsize
@@ -250,3 +347,33 @@
       error ("fspecial: filter type '%s' is not supported", type);
   endswitch
 endfunction
+
+
+## Test that the disk filter's error does not grow unreasonably large
+%!test
+%! for i = 1:9
+%!   n = 2^i;
+%!   assert (sum (fspecial ("disk", n)(:)), 1, eps*n*n);
+%! endfor
+
+## Test that all squares completely under the disk or completely out of it are
+## being assigned the correct values.
+%!test
+%! for r = [3 5 9 17]
+%!   f = fspecial ("disk", r);
+%!   [X, Y] = meshgrid (-r:r, -r:r);
+%!   rhi = (abs (X) + 0.5).^2 + (abs (Y) + 0.5).^2;
+%!   rlo = (abs (X) - 0.5).^2 + (abs (Y) - 0.5).^2;
+%!   fhi = (rhi <= (r^2));
+%!   flo = (rlo >= (r^2));
+%!   for i = 1:(2*r+1)
+%!     for j = 1:(2*r+1)
+%!       if (fhi(i,j))
+%!         assert (f(i,j), 1/(pi*r^2), eps);
+%!       endif
+%!       if (flo(i,j))
+%!         assert (f(i,j), 0);
+%!       endif
+%!     endfor
+%!   endfor
+%! endfor
--- a/inst/im2bw.m
+++ b/inst/im2bw.m
@@ -59,37 +59,44 @@
 
   if (islogical (img))
     warning ("im2bw: IMG is already binary so nothing is done");
-    BW = img;
-    return
-  endif
+    tmp = img;
+
+  else
+    ## Convert img to gray scale
+    if (nargin == 3)
+      ## indexed image (we already checked that is indeed indexed earlier)
+      img = ind2gray (img, cmap);
+    elseif (isrgb (img))
+      img = rgb2gray (img);
+    else
+      ## Everything else, we do nothing, no matter how many dimensions
+    endif
 
-  ## Convert img to gray scale
-  if (nargin == 3)
-    ## indexed image (we already checked that is indeed indexed earlier)
-    img = ind2gray (img, cmap);
-  elseif (isrgb (img))
-    img = rgb2gray (img);
-  else
-    ## Everything else, we do nothing, no matter how many dimensions
+    ## Convert the threshold value to same image class to do the thresholding which
+    ## is faster than converting the image to double and keep the threshold value
+    switch (class (img))
+      case {"double", "single", "logical"}
+        ## do nothing
+      case {"uint8"}
+        thres = im2uint8 (thres);
+      case {"uint16"}
+        thres = im2uint16 (thres);
+      case {"int16"}
+        thres = im2int16 (thres);
+      otherwise
+        ## we should have never got here in the first place anyway
+        error("im2bw: unsupported image class");
+    endswitch
+
+    tmp = (img > thres); # matlab compatible (not "greater than or equal")
   endif
 
-  ## Convert the threshold value to same image class to do the thresholding which
-  ## is faster than converting the image to double and keep the threshold value
-  switch (class (img))
-    case {"double", "single"}
-      ## do nothing
-    case {"uint8"}
-      thres = im2uint8 (thres);
-    case {"uint16"}
-      thres = im2uint16 (thres);
-    case {"int16"}
-      thres = im2int16 (thres);
-    otherwise
-      ## we should have never got here in the first place anyway
-      error("im2bw: unsupported image class");
-  endswitch
+  if (nargout > 0)
+    BW = tmp;
+  else
+    imshow (tmp);
+  endif
 
-  BW = (img > thres); # matlab compatible (not "greater than or equal")
 endfunction
 
 %!assert(im2bw ([0 0.4 0.5 0.6 1], 0.5), logical([0 0 0 1 1])); # basic usage
--- a/inst/im2double.m
+++ b/inst/im2double.m
@@ -1,5 +1,5 @@
 ## Copyright (C) 2007 Søren Hauberg <soren@hauberg.org>
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com>
 ##
 ## 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
@@ -15,32 +15,58 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{im2} = im2double (@var{im1})
-## @deftypefnx {Function File} @var{im2} = im2double (@var{im1}, "indexed")
-## Convert input image @var{im1} to double precision.
+## @deftypefn  {Function File} {} im2double (@var{img})
+## @deftypefnx {Function File} {} im2double (@var{img}, "indexed")
+## Convert image to double precision.
 ##
-## The following images type are supported: double, single, uint8, uint16, int16,
-## binary (logical), indexed. If @var{im1} is an indexed images, the second
-## argument must be a string with the value `indexed'.
+## The conversion of @var{img} to double precision, is dependent
+## on the type of input image.  The following input classes are supported:
 ##
-## Processing will depend on the class of the input image @var{im1}:
-## @itemize @bullet
-## @item uint8, uint16, int16 - output will be rescaled for the interval [0 1]
-## with the limits of the class;
-## @item double - output will be the same as input;
-## @item single - output will have the same values as input but the class will
-## double;
-## @item indexed, logical - converted to double class.
+## @table @samp
+## @item uint8, uint16, and int16
+## The whole range of values from the class (see @code{getrangefromclass})
+## are scaled for the interval [0 1], e.g., if input image was uint8,
+## intensity values of 0, 127, and 255, are converted to intensity of
+## 0, 0.498, and 1.
+##
+## @item logical
+## True and false values are assigned a value of 0 and 1 respectively.
+##
+## @item single
+## Values are cast to single precision.
+##
+## @item double
+## Returns the same image.
 ## @end itemize
 ##
-## @seealso{im2bw, im2int16, im2single, im2uint8, im2uint16}
+## If the second argument is the string @qcode{"indexed"}, then values are
+## cast to double precision, and a +1 offset is applied if input is
+## an integer class.
+##
+## @seealso{im2bw, imcast, im2uint8, im2int16, im2single, im2uint16}
 ## @end deftypefn
 
-function im = im2double (im, indexed = false)
-  im = im2float ("double", nargin, im, indexed);
+function imout = im2double (img, varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed"))
+    error ("im2double: second input argument must be the string \"indexed\"");
+  endif
+  imout = imcast (img, "double", varargin{:});
 endfunction
 
-%!assert(im2double([1 2 3]), [1 2 3]);                  # double returns the same
-%!assert(im2double(uint8([0 255])), [0 1]);             # basic usage
-%!assert(im2double(uint8([1 25]), "indexed"), [2 26]);  # test indexed
-%!assert(im2double(int16([-32768 32768])), [0 1]);      # test signed integer
+%!assert (im2double ([1 2 3]), [1 2 3]);
+%!assert (im2double (single ([1 2 3])), [1 2 3]);
+%!assert (im2double (uint8 ([0 127 128 255])), [0 127/255 128/255 1]);
+%!assert (im2double (uint16 ([0 127 128 65535])), [0 127/65535 128/65535 1]);
+%!assert (im2double (int16 ([-32768 -32767 -32766 32767])), [0 1/65535 2/65535 1]);
+
+%!assert (im2double (uint8 ([0 1 255]), "indexed"), [1 2 256]);
+%!assert (im2double (uint16 ([0 1 2557]), "indexed"), [1 2 2558]);
+%!assert (im2double ([3 25], "indexed"), [3 25]);
+
+%!error <indexed> im2double (single ([0 1 2]), "indexed");
+%!error <indexed> im2double (int16 ([17 8]), "indexed");
+%!error <indexed> im2double (int16 ([-7 8]), "indexed");
+%!error <indexed> im2double ([false true], "indexed");
+
--- a/inst/im2int16.m
+++ b/inst/im2int16.m
@@ -1,4 +1,4 @@
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com>
 ##
 ## 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
@@ -14,55 +14,47 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{im2} = im2int16 (@var{im1})
-## Convert input image @var{im1} to int16 precision.
+## @deftypefn {Function File} {} im2int16 (@var{img})
+## Convert image to int16.
 ##
-## The following images type are supported: double, single, uint8, uint16, int16,
-## binary (logical).
+## The conversion of @var{img} to a 16-bit signed integer, is dependent
+## on the type of input image.  The following input classes are supported:
+##
+## @table @samp
+## @item uint8 or uint16
+## Values are rescaled to the range of the int16 class [-32768 32767].
 ##
-## Processing will depend on the class of the input image @var{im1}:
-## @itemize @bullet
-## @item int16 - returns the same as input
-## @item uint8, double, single, uint16, logical - output will be rescaled for the
-## interval of the uint16 class [0 65535]
+## @item logical
+## True and false values are assigned a value of 32767 and -32768 respectively.
+##
+## @item double or single
+## Values are truncated to the interval [0 1] and then rescaled to the range
+## of values of the int16 class [-32768 32767].
+##
+## @item int16
+## Returns the same image.
 ## @end itemize
 ##
-## @seealso{im2bw, im2double, im2single, im2uint8, im2uint16}
+## @seealso{im2bw, imcast, im2uint8, im2double, im2single, im2uint16}
 ## @end deftypefn
 
-function im = im2int16 (im)
-
-  ## unlike the others for imconversion, this only accepts 1 argument so we check here
+function imout = im2int16 (img)
   if (nargin != 1)
-    print_usage;
+    print_usage ();
   endif
-  ## Input checking (private function that is used for all im2class functions)
-  im_class = imconversion (nargin, "im2int16", false, im);
-
-  ## for those who may wonder, 32767 = intmax ("int16")
-  ## for those who may wonder, 65535 = intmax ("uint16")
-    switch im_class
-    case "int16"
-      ## do nothing, return the same
-    case {"single", "double"}
-      im = int16 (double (im * uint16(65535)) - 32767 - 1);
-    case "logical"
-      im(im)  = intmax ("int16");
-      im(!im) = intmin ("int16");
-    case "uint8"
-        ## 257 is the ratio between the max of uint8 and uint16
-        ## double (intmax ("uint16")) / double (intmax ("uint8")) == 257
-        im = int16 (double (257 * uint16 (im)) - 32767 - 1);
-    case "uint16"
-      im = int16 (double (im) - 32767 - 1);
-    otherwise
-      error ("unsupported image class %s", im_class);
-  endswitch
-
+  imout = imcast (img, "int16");
 endfunction
 
-%!assert(im2int16(int16([-2 2 3])), int16([-2 2 3]));           # uint16 returns the same
-%!assert(im2int16(uint8([0 255])), int16([-32768 32767]));      # basic usage with uint8
-%!assert(im2int16(uint16([0 65535])), int16([-32768 32767]));   # basic usage with uint16
-%!assert(im2int16([0 0.5 1]), int16([-32768 0 32767]));         # basic usage with double
-%!assert(im2int16([1 2]), int16([32767 32767]));                # for double, above 1 is same as 1
+%!assert (im2int16 (int16 ([-2 2 3])),    int16 ([-2 2 3]));
+%!assert (im2int16 (uint16 ([0 65535])),  int16 ([-32768 32767]));
+%!assert (im2int16 ([false true]),        int16 ([-32768 32767]));
+%!assert (im2int16 ([true false]),        int16 ([32767 -32768]));
+%!assert (im2int16 (uint8 ([0 127 128 255])), int16 ([-32768 -129 128 32767]));
+
+%!assert (im2int16 ([0 1.4/65535 1.5/65535 2/65535 1]), int16 ([-32768 -32767 -32766 -32766 32767]));
+
+%!assert (im2int16 ([0 0.5 1]),  int16 ([-32768 0 32767]));
+%!assert (im2int16 ([-1 0 1 2]), int16 ([-32768 -32768 32767 32767]));
+
+%!error im2int16 ([1 2], "indexed");
+
--- a/inst/im2single.m
+++ b/inst/im2single.m
@@ -1,4 +1,4 @@
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com>
 ##
 ## 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
@@ -14,32 +14,58 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{im2} = im2single (@var{im1})
-## @deftypefnx {Function File} @var{im2} = im2single (@var{im1}, "indexed")
-## Convert input image @var{im1} to single precision.
+## @deftypefn  {Function File} {} im2single (@var{img})
+## @deftypefnx {Function File} {} im2single (@var{img}, "indexed")
+## Convert image to single precision.
 ##
-## The following images type are supported: double, single, uint8, uint16, int16,
-## binary (logical), indexed. If @var{im1} is an indexed images, the second
-## argument must be a string with the value `indexed'.
+## The conversion of @var{img} to single precision, is dependent
+## on the type of input image.  The following input classes are supported:
 ##
-## Processing will depend on the class of the input image @var{im1}:
-## @itemize @bullet
-## @item uint8, uint16, int16 - output will be rescaled for the interval [0 1]
-## with the limits of the class;
-## @item single - output will be the same as input;
-## @item double - output will have the same values as input but the class will
-## single;
-## @item indexed, logical - converted to single class.
+## @table @samp
+## @item uint8, uint16, and int16
+## The whole range of values from the class (see @code{getrangefromclass})
+## are scaled for the interval [0 1], e.g., if input image was uint8,
+## intensity values of 0, 127, and 255, are converted to intensity of
+## 0, 0.498, and 1.
+##
+## @item logical
+## True and false values are assigned a value of 0 and 1 respectively.
+##
+## @item double
+## Values are cast to double precision.
+##
+## @item single
+## Returns the same image.
 ## @end itemize
 ##
-## @seealso{im2bw, im2double, im2int16, im2uint8, im2uint16}
+## If the second argument is the string @qcode{"indexed"}, then values are
+## cast to single precision, and a +1 offset is applied if input is
+## an integer class.
+##
+## @seealso{im2bw, imcast, im2uint8, im2double, im2int16, im2uint16}
 ## @end deftypefn
 
-function im = im2single (im, indexed = false)
-  im = im2float ("single", nargin, im, indexed);
+function imout = im2single (img, varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed"))
+    error ("im2single: second input argument must be the string \"indexed\"");
+  endif
+  imout = imcast (img, "single", varargin{:});
 endfunction
 
-%!assert(im2single([1 2 3]), single([1 2 3]));                  # double returns the same
-%!assert(im2single(uint8([0 255])), single([0 1]));             # basic usage
-%!assert(im2single(uint8([1 25]), "indexed"), single([2 26]));  # test indexed
-%!assert(im2single(int16([-32768 32768])), single([0 1]));      # test signed integer
+%!assert (im2single (single ([1 2 3])), single ([1 2 3]));
+%!assert (im2single ([1 2 3]), single ([1 2 3]));
+%!assert (im2single (uint8 ([0 127 128 255])), single ([0 127/255 128/255 1]));
+%!assert (im2single (uint16 ([0 127 128 65535])), single ([0 127/65535 128/65535 1]));
+%!assert (im2single (int16 ([-32768 -32767 -32766 32767])), single ([0 1/65535 2/65535 1]));
+
+%!assert (im2single (uint8 ([0 1 255]), "indexed"), single ([1 2 256]));
+%!assert (im2single (uint16 ([0 1 2557]), "indexed"), single ([1 2 2558]));
+%!assert (im2single ([3 25], "indexed"), single ([3 25]));
+
+%!error <indexed> im2single ([0 1 2], "indexed");
+%!error <indexed> im2single (int16 ([17 8]), "indexed");
+%!error <indexed> im2single (int16 ([-7 8]), "indexed");
+%!error <indexed> im2single ([false true], "indexed");
+
--- a/inst/im2uint16.m
+++ b/inst/im2uint16.m
@@ -1,5 +1,5 @@
 ## Copyright (C) 2007 Søren Hauberg <soren@hauberg.org>
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com>
 ##
 ## 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
@@ -15,67 +15,63 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{im2} = im2uint16 (@var{im1})
-## @deftypefnx {Function File} @var{im2} = im2uint16 (@var{im1}, "indexed")
-## Convert input image @var{im1} to uint16 precision.
+## @deftypefn  {Function File} {} im2uint16 (@var{img})
+## @deftypefnx {Function File} {} im2uint16 (@var{img}, "indexed")
+## Convert image to uint16.
 ##
-## The following images type are supported: double, single, uint8, uint16, int16,
-## binary (logical), indexed. If @var{im1} is an indexed images, the second
-## argument must be a string with the value `indexed'.
+## The conversion of @var{img} to a 16-bit unsigned integer, is dependent
+## on the type of input image.  The following input classes are supported
+## for non-indexed images:
 ##
-## Processing will depend on the class of the input image @var{im1}:
-## @itemize @bullet
-## @item uint16 - returns the same as input
-## @item uint8, double, single, int16, logical - output will be rescaled for the
-## interval of the uint16 class [0 65535]
-## @item indexed - depends on the input class. If double, no value can be above
-## the max of the uint16 class (65535).
+## @table @samp
+## @item int16 or uint8
+## Values are rescaled to the range of the uint16 class [0 65535].
+##
+## @item logical
+## True and false values are assigned a value of 0 and 255 respectively.
+##
+## @item double or single
+## Values are truncated to the interval [0 1] and then rescaled to the range
+## of values of the int16 class [0 255].
+##
+## @item uint16
+## Returns the same image.
 ## @end itemize
 ##
-## @seealso{im2bw, im2double, im2int16, im2single, im2uint8}
+## If the second argument is the string @qcode{"indexed"}, then values are
+## cast to uint16, and a -1 offset is applied if input is
+## a floating point class.  Input checking is performed and an error will
+## be throw is the range of values in uint16 is not enough for all the
+## image indices.
+##
+## @seealso{im2bw, imcast, im2uint8, im2double, im2int16, im2single}
 ## @end deftypefn
 
-function im = im2uint16 (im, indexed = false)
-
-  ## Input checking (private function that is used for all im2class functions)
-  im_class = imconversion (nargin, "im2uint16", indexed, im);
-
-  ## for those who may wonder, 65535 = intmax ("uint16")
-  switch im_class
-    case "uint16"
-      ## do nothing, return the same
-    case {"single", "double"}
-      if (indexed)
-        imax = max (im(:));
-        if ( imax > 65535)
-          error ("Too many colors '%d' for an indexed uint16 image", imax);
-        endif
-        im = uint16 (im) - 1;
-      else
-        im = uint16 (im * 65535);
-      endif
-    case "logical"
-      im = uint16 (im) * uint16 (65535);
-    case "uint8"
-      if (indexed)
-        im = uint16 (im);
-      else
-        ## 257 is the ratio between the max of uint8 and uint16
-        ## double (intmax ("uint16")) / double (intmax ("uint8")) == 257
-        im = 257 * uint16 (im);
-      endif
-    case "int16"
-      im = uint16 (double (im) + double (intmax (im_class)) + 1);
-    otherwise
-      error ("unsupported image class %s", im_class);
-  endswitch
-
+function imout = im2uint16 (im, varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed"))
+    error ("im2uint16: second input argument must be the string \"indexed\"");
+  endif
+  imout = imcast (im, "uint16", varargin{:});
 endfunction
 
-%!assert(im2uint16(uint16([1 2 3])), uint16([1 2 3]));          # uint16 returns the same
-%!assert(im2uint16(uint8([0 255])), uint16([0 65535]));         # basic usage with uint8
-%!assert(im2uint16([0 0.5 1]), uint16([0 32768 65535]));        # basic usage with double
-%!assert(im2uint16([1 2]), uint16([65535 65535]));              # for double, above 1 is same as 1
-%!assert(im2uint16(uint8([3 25]), "indexed"), uint16([3 25]));  # test indexed uint8
-%!assert(im2uint16([3 25], "indexed"), uint16([2 24]));         # test indexed double
-%!assert(im2uint16(int16([-32768 32768])), uint16([0 65535]));  # test signed integer
+%!assert (im2uint16 (uint16 ([1 2 3])), uint16 ([1 2 3]));
+%!assert (im2uint16 (uint8 ([0 127 128 255])), uint16 ([0 32639 32896 65535]));
+%!assert (im2uint16 ([0 0.5 1]), uint16 ([0 32768 65535]));
+%!assert (im2uint16 ([0 1/65535 1.4/65535 1.5/65535 1]), uint16 ([0 1 1 2 65535]));
+%!assert (im2uint16 ([1 2]), uint16 ([65535 65535]));
+%!assert (im2uint16 ([-1 0 0.5 1]), uint16 ([0 0 32768 65535]));
+%!assert (im2uint16 (int16 ([-32768 -1 0 32768])), uint16 ([0 32767 32768 65535]));
+%!assert (im2uint16 ([false true]), uint16 ([0 65535]));
+%!assert (im2uint16 ([true false]), uint16 ([65535 0]));
+
+%!assert (im2uint16 (uint8 ([3 25]), "indexed"), uint16 ([3 25]));
+%!assert (im2uint16 ([1 3 25], "indexed"), uint16 ([0 2 24]));
+
+%!error <indexed> im2uint16 ([0 1 2], "indexed");
+%!error <indexed> im2uint16 (int16 ([17 8]), "indexed");
+%!error <indexed> im2uint16 (int16 ([-7 8]), "indexed");
+%!error <indexed> im2uint16 ([false true], "indexed");
+%!error <range of values> im2uint16 (65537, "indexed");
+
--- a/inst/im2uint8.m
+++ b/inst/im2uint8.m
@@ -1,5 +1,5 @@
 ## Copyright (C) 2007 Søren Hauberg <soren@hauberg.org>
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2012-2014 Carnë Draug <carandraug+dev@gmail.com>
 ##
 ## 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
@@ -15,70 +15,64 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{im2} = im2uint8 (@var{im1})
-## @deftypefnx {Function File} @var{im2} = im2uint8 (@var{im1}, "indexed")
-## Convert input image @var{im1} to uint16 precision.
+## @deftypefn  {Function File} {} im2uint8 (@var{img})
+## @deftypefnx {Function File} {} im2uint8 (@var{img}, "indexed")
+## Convert image to uint8.
 ##
-## The following images type are supported: double, single, uint8, uint16, int16,
-## binary (logical), indexed. If @var{im1} is an indexed images, the second
-## argument must be a string with the value `indexed'.
+## The conversion of @var{img} to an 8-bit unsigned integer, is dependent
+## on the type of input image.  The following input classes are supported
+## for non-indexed images:
 ##
-## Processing will depend on the class of the input image @var{im1}:
-## @itemize @bullet
-## @item uint8 - returns the same as input
-## @item uint16, double, single, int16, logical - output will be rescaled for the
-## interval of the uint8 class [0 255]
-## @item indexed - depends on the input class. No value can be above the max of
-## the uint8 class (255).
+## @table @samp
+## @item int16 or uint16
+## Values are rescaled to the range of the uint8 class [0 255].
+##
+## @item logical
+## True and false values are assigned a value of 0 and 255 respectively.
+##
+## @item double or single
+## Values are truncated to the interval [0 1] and then rescaled to the range
+## of values of the int16 class [0 255].
+##
+## @item uint8
+## Returns the same image.
 ## @end itemize
 ##
-## @seealso{im2bw, im2double, im2int16, im2single, im2uint16}
+## If the second argument is the string @qcode{"indexed"}, then values are
+## cast to uint8, and a -1 offset is applied if input is
+## a floating point class.  Input checking is performed and an error will
+## be throw is the range of values in uint8 is not enough for all the
+## image indices.
+##
+## @seealso{im2bw, imcast, im2double, im2int16, im2single, im2uint16}
 ## @end deftypefn
 
-function im = im2uint8 (im, indexed = false)
-
-  ## Input checking (private function that is used for all im2class functions)
-  im_class = imconversion (nargin, "im2uint8", indexed, im);
-
-  if (indexed)
-    imax = max (im(:));
-    if (imax > 255)
-      error ("Too many colors '%d' for an indexed uint8 image", imax);
-    endif
+function imout = im2uint8 (img, varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  elseif (nargin == 2 && ! strcmpi (varargin{1}, "indexed"))
+    error ("im2uint8: second input argument must be the string \"indexed\"");
   endif
-
-  ## for those who may wonder, 255 = intmax ("uint8")
-  switch im_class
-    case "uint8"
-      ## do nothing, return the same
-    case {"single", "double"}
-      if (indexed)
-        im = uint8 (im) - 1;
-      else
-        im = uint8 (im * 255);
-      endif
-    case "logical"
-      im = uint8 (im) * uint8 (255);
-    case "uint16"
-      if (indexed)
-        im = uint8 (im);
-      else
-        ## 257 is the ratio between the max of uint8 and uint16
-        ## double (intmax ("uint16")) / double (intmax ("uint8")) == 257
-        im = uint8 (im / 257);
-      endif
-    case "int16"
-      im = uint8 ((double (im) + double (intmax (im_class)) + 1) / 257);
-    otherwise
-      error ("unsupported image class %s", im_class);
-  endswitch
-
+  imout = imcast (img, "uint8", varargin{:});
 endfunction
 
-%!assert(im2uint8(uint8([1 2 3])), uint8([1 2 3]));               # uint16 returns the same
-%!assert(im2uint8(uint16([0 65535])), uint8([0 255]));            # basic usage with uint16
-%!assert(im2uint8([0 0.5 1]), uint8([0 128 255]));                # basic usage with double
-%!assert(im2uint8([1 2]), uint8([255 255]));                      # for double, above 1 is same as 1
-%!assert(im2uint8(uint16([3 25]), "indexed"), uint8([3 25]));     # test indexed uint8
-%!assert(im2uint8([3 25], "indexed"), uint8([2 24]));             # test indexed double
-%!assert(im2uint8(int16([-32768 0 32768])), uint8([0 128 255]));  # test signed integer
+%!assert (im2uint8 (uint8 ([1 2 3])), uint8 ([1 2 3]));
+%!assert (im2uint8 (uint16 ([0 65535])), uint8 ([0 255]));
+%!assert (im2uint8 ([0 0.5 1]), uint8 ([0 128 255]));
+%!assert (im2uint8 ([1 2]), uint8 ([255 255]));
+%!assert (im2uint8 ([-1 0 0.5 1 2]), uint8 ([0 0 128 255 255]));
+%!assert (im2uint8 (int16 ([-32768 0 32768])), uint8 ([0 128 255]));
+%!assert (im2uint8 ([false true]), uint8 ([0 255]));
+%!assert (im2uint8 ([true false]), uint8 ([255 0]));
+
+%!assert (im2uint8 ([1 256], "indexed"), uint8 ([0 255]));
+%!assert (im2uint8 ([3 25], "indexed"), uint8 ([2 24]));
+%!assert (im2uint8 (uint16 ([3 25]), "indexed"), uint8 ([3 25]));
+
+%!error <indexed> im2uint8 ([0 1 2], "indexed");
+%!error <indexed> im2uint8 (int16 ([17 8]), "indexed");
+%!error <indexed> im2uint8 (int16 ([-7 8]), "indexed");
+%!error <indexed> im2uint8 ([false true], "indexed");
+%!error <range of values> im2uint8 (uint16 (256), "indexed");
+%!error <range of values> im2uint8 (257, "indexed");
+
new file mode 100644
--- /dev/null
+++ b/inst/imcast.m
@@ -0,0 +1,184 @@
+## Copyright (C) 2014 Carnë Draug <carandraug+dev@gmail.com>
+##
+## 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} {} imcast (@var{img}, @var{type})
+## @deftypefnx {Function File} {} imcast (@var{img}, @var{type}, "indexed")
+## Convert image to specific data type @var{type}.
+##
+## Converts a valid image @var{img} into another class @var{type}.  A valid
+## image must be of class logical, uint8, uint16, int16, single, or double.
+## Conversion of images of class logical is valid, but not the inverse, i.e.,
+## conversion of images into logical class is not supported. Use of
+## @code{im2bw} is recommended for such cases.
+##
+## If the image is indexed, the last argument may be the string
+## @qcode{"indexed"}.  An indexed image may not be of class int16 or
+## single (see @code{isind} for details).
+##
+## Details on how the conversion is performed is class dependent, and can
+## be seen on the help text of @code{im2double}, @code{im2single},
+## @code{im2uint8}, @code{im2uint16}, and @code{im2int16}.
+##
+## @seealso{im2bw, im2uint8, im2double, im2int16, im2single, im2uint16}
+## @end deftypefn
+
+function imout = imcast (img, outcls, varargin)
+
+  if (nargin < 2 || nargin > 3)
+    print_usage ();
+  elseif (nargin == 3 && ! strcmpi (varargin{1}, "indexed"))
+    error ("imcast: third argument must be the string \"indexed\"");
+  endif
+
+  incls = class (img);
+  if (strcmp (outcls, incls))
+    imout = img;
+    return
+  endif
+
+  ## we are dealing with indexed images
+  if (nargin == 3)
+    if (! isind (img))
+      error ("imcast: input should have been an indexed image but it is not.");
+    endif
+
+    ## Check that the new class is enough to hold all the previous indices
+    ## If we are converting to floating point, then we don't bother
+    ## check the range of indices. Also, note that indexed images of
+    ## integer class are always unsigned.
+
+    ## we will be converting a floating point image to integer class
+    if (strcmp (outcls, "single") || strcmp (outcls, "double"))
+      if (isinteger (img))
+        imout = cast (img, outcls) +1;
+      else
+        imout = cast (img, outcls);
+      endif
+
+    ## we will be converting an indexed image to integer class
+    else
+      if (isinteger (img) && intmax (incls) > intmax (outcls) && max (img(:)) > intmax (outcls))
+          error ("imcast: IMG has too many colours '%d' for the range of values in %s",
+            max (img(:)), outcls);
+      elseif (isfloat (img))
+        imax = max (img(:)) -1;
+        if (imax > intmax (outcls))
+          error ("imcast: IMG has too many colours '%d' for the range of values in %s",
+            imax, outcls);
+        endif
+        img -= 1;
+      endif
+      imout = cast (img, outcls);
+    endif
+
+  ## we are dealing with "normal" images
+  else
+    problem = false; # did we found a bad conversion?
+    switch (incls)
+
+      case {"double", "single"}
+        switch (outcls)
+          case "uint8",  imout = uint8  (img * 255);
+          case "uint16", imout = uint16 (img * 65535);
+          case "int16",  imout = int16 (double (img * uint16 (65535)) -32768);
+          case {"double", "single"}, imout = cast (img, outcls);
+          otherwise, problem = true;
+        endswitch
+
+      case {"uint8"}
+        switch (outcls)
+          case "double", imout = double (img) / 255;
+          case "single", imout = single (img) / 255;
+          case "uint16", imout = uint16 (img) * 257; # 257 comes from 65535/255
+          case "int16",  imout = int16 ((double (img) * 257) -32768); # 257 comes from 65535/255
+          otherwise, problem = true;
+        endswitch
+
+      case {"uint16"}
+        switch (outcls)
+          case "double", imout = double (img) / 65535;
+          case "single", imout = single (img) / 65535;
+          case "uint8",  imout = uint8 (img / 257); # 257 comes from 65535/255
+          case "int16",  imout = int16 (double (img) -32768);
+          otherwise, problem = true;
+        endswitch
+
+      case {"logical"}
+        switch (outcls)
+          case {"double", "single"}
+            imout = cast (img, outcls);
+          case {"uint8", "uint16", "int16"}
+            imout = repmat (intmin (outcls), size (img));
+            imout(img) = intmax (outcls);
+          otherwise
+            problem = true;
+        endswitch
+
+      case {"int16"}
+        switch (outcls)
+          case "double", imout = (double (img) + 32768) / 65535;
+          case "single", imout = (single (img) + 32768) / 65535;
+          case "uint8",  imout = uint8 ((double (img) + 32768) / 257); # 257 comes from 65535/255
+          case "uint16", imout = uint16 (double (img) + 32768);
+          otherwise, problem = true;
+        endswitch
+
+      otherwise
+        error ("imcast: unknown image of class \"%s\"", incls);
+
+    endswitch
+    if (problem)
+      error ("imcast: unsupported TYPE \"%s\"", outcls);
+    endif
+  endif
+
+endfunction
+
+%!test
+%! im = randi ([0 255], 40, "uint8");
+%! assert (imcast (im, "uint8"), im2uint8 (im))
+%! assert (imcast (im, "uint16"), im2uint16 (im))
+%! assert (imcast (im, "single"), im2single (im))
+%! assert (imcast (im, "uint8", "indexed"), im2uint8 (im, "indexed"))
+%! assert (imcast (im, "uint16", "indexed"), im2uint16 (im, "indexed"))
+%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed"))
+
+%!test
+%! im = randi ([1 256], 40, "double");
+%! assert (imcast (im, "uint8"), im2uint8 (im))
+%! assert (imcast (im, "uint8", "indexed"), im2uint8 (im, "indexed"))
+%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed"))
+
+%!test
+%! im = randi ([0 65535], 40, "uint16");
+%! assert (imcast (im, "uint8"), im2uint8 (im))
+%! assert (imcast (im, "single"), im2single (im))
+%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed"))
+
+%!test
+%! im = randi ([1 255], 40, "double");
+%! assert (imcast (im, "uint8", "indexed"), im2uint8 (im, "indexed"))
+%! assert (imcast (im, "single", "indexed"), im2single (im, "indexed"))
+
+%!test
+%! im = rand (40);
+%! assert (imcast (im, "uint8"), im2uint8 (im))
+
+%!error <unknown image of class> imcast (randi (127, 40, "int8"), "uint8")
+%!error <unsupported TYPE> imcast (randi (255, 40, "uint8"), "uint32")
+%!error <unsupported TYPE> imcast (randi (255, 40, "uint8"), "not a class")
+%!error <range of values> imcast (randi ([0 65535], 40, "uint16"), "uint8", "indexed")
+
--- a/inst/imnoise.m
+++ b/inst/imnoise.m
@@ -105,16 +105,13 @@
   endswitch
 
   if (fix_class)
-    ## we probably should do this in a safer way... but hardcoding the list of
-    ## im2xxxx functions might not be a good idea since it then it requires to
-    ## be added here if a new im2xxx function is implemented
-    A = feval (["im2" in_class], A);
+    A = imcast (A, in_class);
   elseif (isfloat (A))
     ## this includes not even cases where the noise made it go outside of the
     ## [0 1] range, but also images that were already originally outside that
     ## range. This is by design and matlab compatibility. And we do this after
-    ## fixing class because the im2XX functions already take care of such
-    ## adjustemt
+    ## fixing class because the imcast functions already take care of such
+    ## adjustment
     A(A < 0) = cast (0, class (A));
     A(A > 1) = cast (1, class (A));
   endif
deleted file mode 100644
--- a/inst/impad.m
+++ /dev/null
@@ -1,180 +0,0 @@
-## Copyright (C) 2000 Teemu Ikonen <tpikonen@pcu.helsinki.fi>
-##
-## 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} {} impad(@var{A}, @var{xpad}, @var{ypad}, [@var{padding}, [@var{const}]])
-## Pad (augment) a matrix for application of image processing algorithms.
-##
-## This function has been deprecated in favor of @code{padarray} and will be
-## removed from future versions of image package.  Notes when making
-## the conversion:
-##
-## @itemize @bullet
-## @item
-## padarray will take the order of the padding arguments by order of dimension,
-## i.e., swap @var{xpad} and @var{ypad}.  Use
-## @code{padarray (@var{A}, [@var{ypad} @var{xpad}], @dots{})}
-##
-## @item
-## There is no @qcode{"ones"} as @var{padding} option.  Simply set the value
-## 1 as padding value.
-##
-## @item
-## If @var{xpad} and @var{ipad} are 2 element vectors with different values,
-## they will need to be replaced by two calls to padarray as follow:
-## @group
-## @example
-## B = padarray (A, [ypad(1) xpad(1)], "pre");
-## B = padarray (B, [ypad(2) xpad(2)], "post");
-## @end example
-## @end group
-##
-## @item
-## The @qcode{"reflect"} @var{padding} option of @code{padarray} is different
-## from @code{impad}.  Use @qcode{"circular"} instead.
-## @end itemize
-##
-## 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
-
-## A nice test matrix for padding:
-## A = 10*[1:5]' * ones(1,5) + ones(5,1)*[1:5]
-
-function retim = impad(im, xpad, ypad, padding = "zeros", const = 1)
-
-  persistent warned = false;
-  if (! warned)
-    warned = true;
-    ## We are deprecating impad because padarray already does the same thing
-    ## (we don't want multiple functions with the same purpose), padarray
-    ## already exists in Matlab so it must stay, padarray works for matrices
-    ## with any number of dimensions, and is now also working faster.
-    warning ("Octave:deprecated-function",
-             "`impad' has been deprecated in favor of `padarray (IM, [YPAD XPAD], \"PADDING\")'. This function will be removed from future versions of the `image' package");
-  endif
-
-
-  ## Input checking
-  if (!ismatrix(im) || ndims(im) < 2 || ndims(im) > 3)
-    error("impad: first input argument must be an image");
-  endif
-  if (isscalar(xpad) && xpad >= 0)
-    xpad(2) = xpad;
-  elseif (!isreal(xpad) || numel(xpad) != 2 || any(xpad < 0))
-    error("impad: xpad must be a positive scalar or 2-vector");
-  endif
-  if (isscalar(ypad) && ypad >= 0)
-    ypad(2) = ypad;
-  elseif (!isreal(ypad) || numel(ypad) != 2 || any(ypad < 0))
-    error("impad: ypad must be a positive scalar or 2-vector");
-  endif
-  if (!isscalar(const))
-    error("impad: fifth input argument must be a scalar");
-  endif
-
-  origx = size(im,2);
-  origy = size(im,1);
-  retx = origx + xpad(1) + xpad(2);
-  rety = origy + ypad(1) + ypad(2);
-  cl = class(im);
-
-  for iter = size(im,3):-1:1
-    A = im(:,:,iter);
-    switch (lower(padding))
-      case "zeros"
-        retval = zeros(rety, retx, cl);
-        retval(ypad(1)+1 : ypad(1)+origy, xpad(1)+1 : xpad(1)+origx) = A;
-      case "ones"
-        retval = ones(rety, retx, cl);
-        retval(ypad(1)+1 : ypad(1)+origy, xpad(1)+1 : xpad(1)+origx) = A;
-      case "constant"
-        retval = const.*ones(rety, retx, cl);
-        retval(ypad(1)+1 : ypad(1)+origy, xpad(1)+1 : xpad(1)+origx) = A;
-      case "replicate"
-        y1 = origy-ypad(1)+1;
-        x1 = origx-xpad(1)+1;
-        if (y1 < 1 || x1 < 1 || ypad(2) > origy || xpad(2) > origx)
-          error("impad: 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) ];
-        endif                        
-      case "symmetric"
-        y2 = origy-ypad(2)+1;
-        x2 = origx-xpad(2)+1;
-        if (ypad(1) > origy || xpad(1) > origx || y2 < 1 || x2 < 1)
-          error("impad: 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))) ];
-        endif      
-      case "reflect"
-        y2 = origy-ypad(2);
-        x2 = origx-xpad(2);
-        if (ypad(1)+1 > origy || xpad(1)+1 > origx || y2 < 1 || x2 < 1)
-          error("impad: 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))) ];
-        endif
-      otherwise   
-        error("impad: unknown padding type");
-    endswitch
-    
-    retim(:,:,iter) = retval;
-  endfor
-endfunction
--- a/inst/imperspectivewarp.m
+++ b/inst/imperspectivewarp.m
@@ -41,8 +41,7 @@
 ## @end table
 ##
 ## All values of the result that fall outside the original image will
-## be set to @var{extrapval}. For images of class @code{double} @var{extrapval}
-## defaults to @code{NA} and for other classes it defaults to 0.
+## be set to @var{extrapval}.  The default value of @var{extrapval} is 0.
 ##
 ## The optional output @var{valid} is a matrix of the same size as @var{warped}
 ## that contains the value 1 in pixels where @var{warped} contains an interpolated
@@ -50,7 +49,7 @@
 ## @seealso{imremap, imrotate, imresize, imshear, interp2}
 ## @end deftypefn
 
-function [warped, valid] = imperspectivewarp(im, P, interp = "linear", bbox = "loose", extrapolation_value = NA)
+function [warped, valid] = imperspectivewarp(im, P, interp = "linear", bbox = "loose", extrapolation_value = 0)
 
   if (nargin < 2 || nargin > 5)
     print_usage ();
--- a/inst/imremap.m
+++ b/inst/imremap.m
@@ -37,8 +37,7 @@
 ## @code{linear}) are also supported.
 ##
 ## All values of the result that fall outside the original image will
-## be set to @var{extrapval}. For images of class @code{double} @var{extrapval}
-## defaults to @code{NA} and for other classes it defaults to 0.
+## be set to @var{extrapval}.  The default value of @var{extrapval} is 0.
 ##
 ## The optional output @var{valid} is a matrix of the same size as @var{warped}
 ## that contains the value 1 in pixels where @var{warped} contains an interpolated
@@ -46,7 +45,7 @@
 ## @seealso{imperspectivewarp, imrotate, imresize, imshear, interp2}
 ## @end deftypefn
 
-function [warped, valid] = imremap(im, XI, YI, interp = "linear", extrapval = NA)
+function [warped, valid] = imremap(im, XI, YI, interp = "linear", extrapval = 0)
 
   if (nargin < 3 || nargin > 5)
     print_usage ();
@@ -63,10 +62,10 @@
   
   ## Interpolate
   if (size (im, 3) == 1) # Gray
-    warped = grayinterp(im, XI, YI, interp, NA);
+    warped = grayinterp(im, XI, YI, interp, 0);
   else # rgb image
     for i = 3:-1:1
-      warped(:,:,i) = grayinterp(im(:,:,i), XI, YI, interp, NA);
+      warped(:,:,i) = grayinterp(im(:,:,i), XI, YI, interp, 0);
     endfor
   endif
   valid = !isna(warped);
@@ -79,9 +78,9 @@
 
 function [warped, valid] = grayinterp(im, XI, YI, interp, extrapval)
   if (strcmp(interp, "cubic"))
-    warped = graybicubic(double(im), XI, YI, NA);
+    warped = graybicubic(double(im), XI, YI, 0);
   else
-    warped = interp2(double(im), XI, YI, interp, NA);
+    warped = interp2(double(im), XI, YI, interp, 0);
   endif
   valid = !isna(warped);
   warped(!valid) = extrapval;
@@ -97,7 +96,7 @@
 ## @seealso{interp2}
 ## @end deftypefn
 
-function ZI = graybicubic (Z, XI, YI, extrapval = NA)
+function ZI = graybicubic (Z, XI, YI, extrapval = 0)
   
   ## Allocate output
   [X, Y] = meshgrid(1:columns(Z), 1:rows(Z));
--- a/inst/imrotate.m
+++ b/inst/imrotate.m
@@ -41,8 +41,7 @@
 ##     @end itemize
 ##
 ##   @var{extrapval} sets the value used for extrapolation. The default value
-##      is @code{NA} for images represented using doubles, and 0 otherwise.
-##      This argument is ignored of Fourier interpolation is used.
+##      is 0.  This argument is ignored of Fourier interpolation is used.
 ##
 ## Output parameters:
 ##
@@ -57,7 +56,7 @@
 ##                  not available if Fourier interpolation is used.
 ## @end deftypefn
 
-function [imgPost, H, valid] = imrotate (imgPre, thetaDeg, interp = "nearest", bbox = "loose", extrapval = NA)
+function [imgPost, H, valid] = imrotate (imgPre, thetaDeg, interp = "nearest", bbox = "loose", extrapval = 0)
 
   if (nargin < 2 || nargin > 5)
     print_usage ();
@@ -154,10 +153,7 @@
     endif
     valid = NA;
 
-    ## we probably should do this in a safer way... but hardcoding the list of
-    ## im2xxxx functions might not be a good idea since it then it requires to
-    ## be added here if a new im2xxx function is implemented
-    imgPost = feval (["im2" in_class], imgPost);
+    imgPost = imcast (imgPost, in_class);
   else
     [imgPost, valid] = imperspectivewarp(imgPre, H, interp, bbox, extrapval);
   endif
deleted file mode 100644
--- a/inst/imrotate_Fourier.m
+++ /dev/null
@@ -1,37 +0,0 @@
-## Copyright (C) 2002 Jeff Orchard <jorchard@cs.uwaterloo.ca>
-##
-## 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} {} imrotate_Fourier (@var{M}, @var{theta}, @var{method}, @var{bbox})
-## Rotation of a 2D matrix.
-##
-## @emph{This function has been deprecated and will be removed. Instead, use
-## @code{imrotate} and select the @code{fourier} method. This function is
-## actually just a wrapper to that function.}
-##
-## @seealso{imrotate}
-## @end deftypefn
-
-function fs = imrotate_Fourier (f, theta, method = "fourier", bbox = "loose")
-
-  persistent warned = false;
-  if (! warned)
-    warned = true;
-    warning ("Octave:deprecated-function",
-             "`imrotate_Fourier' has been deprecated in favor of `imrotate (M, theta, \"fourier\")'. This function will be removed from future versions of the `image' package");
-  endif
-  fs = imrotate (f, theta, "fourier", bbox)
-
-endfunction
deleted file mode 100644
--- a/inst/iptchecknargin.m
+++ /dev/null
@@ -1,81 +0,0 @@
-## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com>
-##
-## 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} {} iptchecknargin (@var{low}, @var{high}, @var{in}, @var{func_name})
-## Checks for correct number of arguments.
-##
-## This function has been deprecated.  For an exact replacement, use
-## @code{narginchk (@var{low}, @var{high})} instead.  Alternatively,
-## @code{print_usage} is able to provide an even better error message
-## provided that there is documentation for the function:
-##
-## @example
-## @group
-## if (nargin < min_inputs || nargin > max_inputs)
-##   print_usage ();
-## endif
-## @end group
-## @end example
-##
-## This function returns an error unless @var{in} is between the values of
-## @var{low} and @var{high}. It does nothing otherwise. They all must be non
-## negative scalar integers. @var{high} can also be Inf.
-##
-## @var{func_name} is the name of the function to be used on the error message.
-##
-## @seealso{error, nargin, nargout, narginchk, nargoutchk}
-## @end deftypefn
-
-function iptchecknargin (low, high, in, func_name)
-
-  persistent warned = false;
-  if (! warned)
-    warned = true;
-    warning ("Octave:deprecated-function",
-             "iptchecknargin is obsolete and will be removed from a future version of the image package, please use narginchk instead");
-  endif
-
-  if (nargin != 4)
-    print_usage;
-  elseif (!isnumeric (low) || !isscalar (low) || !isreal (low) || low < 0 || !isfinite (low) || rem (low, 1) != 0)
-    error ("Argument 'low' must be a non-negative scalar integer");
-  elseif (!isnumeric (high) || !isscalar (high) || !isreal (high) || low < 0 || (isfinite (high) && rem (low, 1) != 0))
-    error ("Argument 'high' must be a non-negative scalar integer or Inf");
-  elseif (!isnumeric (in) || !isscalar (in) || !isreal (in) || in < 0 || !isfinite (in) || rem (in, 1) != 0)
-    error ("Argument 'in' must be a non-negative scalar integer");
-  elseif (!ischar (func_name))
-    error ("Argument 'func_name' must be a string");
-  elseif (low > high)
-    error ("Minimun number of arguments cannot be larger than maximum number of arguments")
-  endif
-
-  ## error ends in \n so the back trace of the error is not show. This is on
-  ## purpose since the whole idea of this function is already to give a properly
-  ## formatted error message
-  if (in < low)
-    error ("Function %s expected at least %d input arguments(s) but was called instead with %d input argument(s).\n", ...
-           func_name, low, in);
-  elseif (in > high)
-    error ("Function %s expected at most %d input argument(s) but was called instead with %d input argument(s).\n", ...
-           func_name, high, in);
-  endif
-
-endfunction
-
-%!test ('iptchecknargin (0, 2, 1, "func")');    # check simple works
-%!test ('iptchecknargin (0, Inf, 1, "func")');  # check Inf on max
-%!fail ('iptchecknargin (3, 2, 1, "func")');    # check fail min >max
-%!fail ('iptchecknargin (2, 3, 1, "func")');    # check fail in out of range
deleted file mode 100644
--- a/inst/iptcheckstrs.m
+++ /dev/null
@@ -1,76 +0,0 @@
-## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com>
-##
-## 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} {@var{valid} =} iptcheckstrs (@var{in}, @var{valid_str}, @var{func_name}, @var{var_name}, @var{pos})
-## Check if argument is a valid string.
-##
-## This function has been deprecated.  Use @code{validatestring} instead.
-##
-## If @var{in} is not a string, present in the cell array of strings
-## @var{valid_str} gives a properly formatted error message. Otherwise,
-## @var{valid} is the matched string. The performed matching is case-insensitive.
-##
-## @var{func_name} is the name of the function to be used on the error message,
-## @var{var_name} the name of the argument being checked (for the error message),
-## and @var{pos} the position of the argument in the input.
-##
-## @seealso{strcmp, strcmpi, find, validatestring}
-## @end deftypefn
-
-function out = iptcheckstrs (in, valid_str, func_name, var_name, pos)
-
-  persistent warned = false;
-  if (! warned)
-    warned = true;
-    warning ("Octave:deprecated-function",
-             "iptcheckstrs is obsolete and will be removed from a future version of the image package, please use validatestring instead");
-  endif
-
-  if (nargin != 5)
-    print_usage;
-  elseif (!ischar (in))
-    error ("Argument 'in' must be a string.");
-  elseif (!iscellstr (valid_str))
-    error ("Argument 'valid_str' must be a cell array of strings.");
-  elseif (!ischar (func_name))
-    error ("Argument 'func_name' must be a string");
-  elseif (!ischar (var_name))
-    error ("Argument 'var_name' must be a string");
-  elseif (!isnumeric (pos) || !isscalar (pos) || !isreal (pos) || pos <= 0 || rem (pos, 1) != 0)
-    error ("Argument 'pos' must be a real positive integer");
-  endif
-
-  idx = find (strcmpi (valid_str, in) == 1, 1, "first");
-
-  ## error ends in \n so the back trace of the error is not show. This is on
-  ## purpose since the whole idea of this function is already to give a properly
-  ## formatted error message
-  if (isempty (idx))
-    valid_str = cellfun (@(x) cstrcat (x, ", "), valid_str, "UniformOutput", false);
-    valid_str = cstrcat (valid_str{:});
-    error("Function %s expected its %s input argument, %s, to match one of these strings:\n\
-         %s\n\
-       The input, '%s', did not match any of the valid strings.\n",
-      func_name, iptnum2ordinal (pos), var_name, valid_str(1:end-2), in);
-  else
-    out = valid_str{idx};
-  endif
-
-endfunction
-
-%!assert (iptcheckstrs ("two", {"one", "two", "three"}, "func", "var", 1) == "two" );    # check simple works
-%!assert (iptcheckstrs ("Two", {"one", "two", "three"}, "func", "var", 1) == "two" );    # check case insensitive
-%!fail ('iptcheckstrs ("four", {"one", "two", "three"}, "func", "var", 1)');             # check failure if not found
--- a/inst/normxcorr2.m
+++ b/inst/normxcorr2.m
@@ -1,4 +1,4 @@
-## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2014 Benjamin Eltzner <b.eltzner@gmx.de>
 ##
 ## 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
@@ -14,27 +14,127 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn  {Function File} {} normxcorr2 (@var{template}, @var{img})
-## @deftypefnx {Function File} {} normxcorr2 (@var{template}, @var{img})
-## Compute the normalized 2D cross-correlation.
+## @deftypefn {Function File} {} normxcorr2 (@var{template}, @var{img})
+## Compute normalized cross-correlation.
+##
+## Returns the the cross-correlation coefficient of matrices @var{template}
+## and @var{img}, a matrix of the same size as @var{img} with values ranging
+## between -1 and 1.
+##
+## Normalized correlation is mostly used for template matching, finding an
+## object or pattern, @var{template}, withing an image @var{img}.  Higher
+## values on the output show their locations, even in the presence of noise.
 ##
-## Returns the normalized cross correlation matrix of @var{template} and
-## @var{img} so that a value of 1 corresponds to the positions of @var{img} that
-## match @var{template} perfectly.
+## @group
+## @example
+## img = randi (255, 600, 400);
+## template = imnoise (img(100:150, 300:320), "gaussian");
+## cc = normxcorr2 (template, img);
+## [r, c] = find (cc == max (cc(:)))
+## @result{r} 150
+## @result{c} 320
+## @end example
+## @end group
 ##
-## @emph{Note}: this function exists only for @sc{matlab} compatibility and is
-## just a wrapper to the @code{coeff} option of @code{xcorr2} with the arguments
-## inverted.  See the @code{xcorr2} documentation for more details. Same results
-## can be obtained with @code{xcorr2 (img, template, "coeff")}
+## Despite the function name, this function will accept input with an arbitrary
+## number of dimensions.
 ##
-## @seealso{conv2, corr2, xcorr2}
+## @seealso{conv2, convn, corr2, xcorr, xcorr2}
 ## @end deftypefn
 
-function cc = normxcorr2 (temp, img)
+## Author: Benjamin Eltzner <b.eltzner@gmx.de>
+
+function c = normxcorr2 (a, b)
   if (nargin != 2)
     print_usage ();
-  elseif (rows (temp) > rows (img) || columns (temp) > columns (img))
-    error ("normxcorr2: template must be same size or smaller than image");
+  endif
+
+  ## If this happens, it is probably a mistake
+  if (ndims (a) > ndims (b) || any (postpad (size (a), ndims (b)) > size (b)))
+    warning ("normxcorr2: TEMPLATE larger than IMG. Arguments may be swapped.");
   endif
-  cc = xcorr2 (img, temp, "coeff");
+
+  a = double (a) - mean (a(:));
+  b = double (b) - mean (b(:));
+
+  a1 = ones (size (a));
+  ar = reshape (a(end:-1:1), size (a));
+
+  c = convn (b, conj (ar));
+  b = convn (b.^2, a1) .- convn (b, a1).^2 ./ (prod (size (a)));
+  a = sumsq (a(:));
+  c = reshape (c ./ sqrt (b * a), size (c));
+
+  c(isnan (c)) = 0;
 endfunction
+
+%!function offsets = get_max_offsets (c)
+%!  l = find (c == max (c(:)));
+%!  offsets = nthargout (1:ndims (c), @ind2sub, size (c), l);
+%!endfunction
+
+## test basic usage
+%!test
+%! row_shift = 18;
+%! col_shift = 20;
+%! a = randi (255, 30, 30);
+%! b = a(row_shift-10:row_shift, col_shift-7:col_shift);
+%! c = normxcorr2 (b, a);
+%! ## should return exact coordinates
+%! assert (get_max_offsets (c), {row_shift col_shift});
+%!
+%! ## Even with some small noise, should return exact coordinates
+%! b = imnoise (b, "gaussian");
+%! c = normxcorr2 (b, a);
+%! assert (get_max_offsets (c), {row_shift col_shift});
+
+## The value for a "perfect" match should be 1. However, machine precision
+## creeps in most of the times.
+%!test
+%! a = rand (10, 10);
+%! c = normxcorr2 (a(5:7, 6:9), a);
+%! assert (c(7, 9), 1, eps*2);
+
+## coeff of autocorrelation must be same as negative of correlation
+## by additive inverse
+%!test
+%! a = 10 * randn (100, 100);
+%! auto = normxcorr2 (a, a);
+%! add_in = normxcorr2 (a, -a);
+%! assert (auto, -add_in);
+
+## Normalized correlation should be independent of scaling and shifting
+## up to rounding errors
+%!test
+%! a = 10 * randn (50, 50);
+%! b = 10 * randn (100, 100);
+%! do
+%!   scale = 100 * rand ();
+%! until (scale != 0)
+%!
+%! assert (max ((normxcorr2 (scale*a,b) .- normxcorr2 (a,b))(:)), 0, 1e-10);
+%! assert (max ((normxcorr2 (a,scale*b) .- normxcorr2 (a,b))(:)), 0, 1e-10);
+%!
+%! a_shift1 = a .+ scale * ones (size (a));
+%! b_shift1 = b .+ scale * ones (size (b));
+%! a_shift2 = a .- scale * ones (size (a));
+%! b_shift2 = b .- scale * ones (size (b));
+%! assert (max ((normxcorr2 (a_shift1,b) .- normxcorr2 (a,b))(:)), 0, 1e-10);
+%! assert (max ((normxcorr2 (a,b_shift1) .- normxcorr2 (a,b))(:)), 0, 1e-10);
+%! assert (max ((normxcorr2 (a_shift2,b) .- normxcorr2 (a,b))(:)), 0, 1e-10);
+%! assert (max ((normxcorr2 (a,b_shift2) .- normxcorr2 (a,b))(:)), 0, 1e-10);
+
+## test n dimensional input
+%!test
+%! a = randi (100, 15, 15, 15);
+%! c = normxcorr2 (a(5:10, 2:6, 3:7), a);
+%! assert (get_max_offsets (c), {10 6 7});
+%!
+%! a = randi (100, 15, 15, 15);
+%! c = normxcorr2 (a(5:10, 2:6, 1:1), a);
+%! assert (get_max_offsets (c), {10 6 1});
+
+%!warning <swapped> normxcorr2 (rand (20), rand (5));
+%!error normxcorr2 (rand (5));
+%!error normxcorr2 (rand (5), rand (20), 2);
+
deleted file mode 100644
--- a/inst/private/im2float.m
+++ /dev/null
@@ -1,43 +0,0 @@
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
-##
-## 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/>.
-
-## im2double and im2single are very similar so here's the common code,
-## which is prety much all of it.
-
-function im = im2float (out_class, caller_nargin, im, indexed = false)
-
-  ## Input checking (private function that is used for all im2class functions)
-  im_class = imconversion (caller_nargin, ["im2" out_class], indexed, im);
-
-  converter = eval (["@" out_class]);
-  switch im_class
-    case {"single", "double", "logical"}
-      if (strcmp (im_class, out_class))
-        ## do nothing, return the same
-      else
-        im = converter (im);
-      endif
-    case {"uint8", "uint16"}
-      if (indexed)
-        im = converter (im) + 1;
-      else
-        im = converter (im) / converter (intmax (im_class));
-      endif
-    case "int16"
-      im = (converter (im) + converter (intmax (im_class)) + 1) / converter (intmax ("uint16"));
-    otherwise
-      error ("unsupported image class %s", im_class);
-  endswitch
-endfunction
deleted file mode 100644
--- a/inst/private/imconversion.m
+++ /dev/null
@@ -1,34 +0,0 @@
-## Copyright (C) 2012 Carnë Draug <carandraug+dev@gmail.com>
-##
-## 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/>.
-
-## This is a private fucntion for the common code between the functions that
-## convert an input image into another class. Mostly does the input checking for
-## all of them and returns the class of the input image
-
-function im_class = imconversion (nargs, fname, ind, im1)
-  ## Input checking
-  if (nargs < 1 || nargs > 2)
-    print_usage (fname);
-  elseif (nargs == 2 && (!ischar (ind) || !strcmpi (ind, "indexed")))
-    error ("%s: second argument must be a string with the word `indexed'\n", fname);
-  endif
-
-  if (ind && !isind (im1))
-    error ("%s: input should have been an indexed image but it is not.\n", fname);
-  endif
-
-  im_class = class (im1);
-
-endfunction
deleted file mode 100644
--- a/inst/readexif.m
+++ /dev/null
@@ -1,539 +0,0 @@
-## Copyright (C) 2009 Roderick Koehle <koehle@users.sourceforge.net>
-##
-## 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} {@var{exif} =} readexif(@var{filename}, @var{thumbnail})
-## Read EXIF information from JPEG image data.
-##
-## The exif tag information are returned in the @var{exif} data structure.
-## Integer ratios are expressed as column vector.
-## For example, a focal number of 2.8 is expressed
-## as FNumber=[28; 10]. Otherwise all data are returned by the type
-## as specified in the IFD structures.
-##
-## The filename for the thumbnail image is optional.
-## If given, the thumbnail jpeg image will be stored to
-## file @var{thumbnail}. 
-##
-## Reference:
-## JEITA CP-3451, Exchangeable image file format for digital still cameras:
-## Exif Version 2.2
-##
-## @seealso{imwrite, imfinfo}
-## @end deftypefn
-
-function exif = readexif(file, thumbnail)
-
-  if (nargin < 1 || nargin > 2)
-    print_usage;
-  endif
-  persistent warned = false;
-  if (! warned)
-    warned = true;
-    warning ("Octave:deprecated-function",
-             "`readexif' has been deprecated in favor of `imfinfo'. This function will be removed from future versions of the `image' package");
-  endif
-
-
-  % Enable the debug flag to see more of the JPG sections.
-
-  debug = false;
-
-  [in, msg] = fopen(file);
-  if (in<0)
-    error ("readexif: could not open `%s': %s", file, msg);
-  end
-
-  s = fread(in, 1, 'uint16', 'ieee-be');
-
-  JPEG.SOI = 0xffd8;
-  JPEG.APP0 = 0xffe0;
-  JPEG.APP1 = 0xffe1;
-  JPEG.APP2 = 0xffe2;
-  JPEG.DQT = 0xffdb;
-  JPEG.DHT = 0xffc4;
-  JPEG.DRI = 0xffdd;
-  JPEG.SOF = 0xffc0;
-  JPEG.SOS = 0xffda;
-  JPEG.EOI = 0xffd9;
-
-  % Stop if no Start of Image found
-
-  if s~=JPEG.SOI
-    error('JPEG Format error - missing start of image tag.');
-  end
-
-  exif = [];
-
-  while ~feof(in)
-    s = fread(in, 1, 'uint16', 'ieee-be');
-
-    switch s
-      case JPEG.SOI
-      case JPEG.APP0
-        l = fread(in, 1, 'uint16', 'ieee-be');
-        if debug, printf('APP0: %i\n', l); end
-        fseek(in, l-2, 'cof');
-      case JPEG.APP1
-        l = fread(in, 1, 'uint16', 'ieee-be');
-        if debug, printf('APP1: %i\n', l); end
-        app1 = fread(in, l-2, 'uchar');
-        if nargin==1
-          exif = parseexif(app1);
-        else
-          exif = parseexif(app1, thumbnail);
-        end
-        % stop reading further, remove following break
-        % if you want to extend this parser.
-        break; 
-      case JPEG.APP2
-        l = fread(in, 1, 'uint16', 'ieee-be');
-        if debug, printf('APP2: %i\n', l); end
-        fseek(in, l-2, 'cof');
-      case JPEG.DQT % define quantization table
-        l = fread(in, 1, 'uint16', 'ieee-be');
-        if debug, printf('DQT: %i\n', l); end;
-        fseek(in, l-2, 'cof');
-      case JPEG.DHT % define huffmann table
-        l = fread(in, 1, 'uint16', 'ieee-be');
-        fseek(in, l-2, 'cof');
-        if debug, printf('DHT: %i\n', l); end
-      case JPEG.DRI % define restart interoperability
-        l = fread(in, 1, 'uint16', 'ieee-be');
-        fseek(in, l-2, 'cof');
-        if debug, printf('DRI: %i\n', l); end
-      case JPEG.SOF % start of frame
-        l = fread(in, 1, 'uint16', 'ieee-be');
-        fseek(in, l-2, 'cof');
-        if debug, printf('SOF: %i\n', l); end
-      case JPEG.SOS % start of scan
-        l = fread(in, 1, 'uint16', 'ieee-be');
-        fseek(in, l-2, 'cof');
-        if debug, printf('SOS: %i\n', l); end
-
-        % JPEG compressed data comes here ...
-        break;
-
-      case JPEG.EOI % end of image
-        printf('EOI');
-      otherwise % Skip unknown tags
-        l = fread(in, 1, 'uint16', 'ieee-be');
-        if debug, printf('TAG %04X: %i\n', s, l); end
-        fseek(in, l-2, 'cof');
-    end
-  end
-
-  fclose(in);
-end
-
-%
-% Parse EXIF APP1 section
-%
-% This routine will parse the APP1 section of an jpeg image.
-% If a filename "thumb" is given, the tumbnail image data
-% will be exported to given file.
-%
-%   exif = parseexif(data, thumb)
-%
-function exif = parseexif(data, thumb)
-
-  id = char(data(1:6).');
-  if strncmp(id, ['Exif' 0 0], 6)
-
-    % TIFF header
-
-    byteorder = char(data(7:8).');
-    littleendian = strncmp(byteorder, 'II', 2);
-    bigendian    = strncmp(byteorder, 'MM', 2);
-
-    tag42     = intn(data(9:10), bigendian);
-    offset    = intn(data(11:14), bigendian);
-
-    if (~littleendian && ~bigendian) || tag42~=42
-      error('invalid TIFF header');
-    end
-
-    % IFD fields
-
-    exif = ifdparse(tifftags(), data, offset, bigendian);
-  else
-    exif = [];
-  end
-
-  % export thumbnail image
-
-  if nargin==2 && isfield(exif, 'JPEGInterchangeFormat')
-    i = exif.JPEGInterchangeFormat;
-    n = exif.JPEGInterchangeFormatLength;
-
-    jpg = data(7+i:7+i+n-1);
-    out = fopen(thumb, 'w');
-    if (out<0), 
-      error('Cannot open file "%s" for writing thumbnail image.', thumb);
-    end
-    fwrite(out, jpg, 'uint8');
-    fclose(out);
-  end
-end
-
-function ifd = ifdparse(dict, data, offset, endian)
-
-  debug = false;
-
-  ifd = [];
-
-  while offset
-    ifd_fields = intn(data(7+offset+(0:1)), endian);
-
-    if debug, printf('Tag  Type Count    Offset\n'); end
-    for i=1:ifd_fields
-      j = 9+offset+(i-1)*12;
-      ifd_tag    = intn(data(  j:j+1), endian);
-      ifd_type   = intn(data(j+2:j+3), endian);
-      ifd_count  = intn(data(j+4:j+7), endian);
-      ifd_offset = intn(data(j+8:j+11), endian);
-
-      name = ifdtagname(dict, ifd_tag);
-
-      if debug,
-        printf('%04x %04x %08x %08x %s : ', ifd_tag, ifd_type, ...
-               ifd_count, ifd_offset, name);
-      end
-      if ifd_type>0
-        n = ifdsize(ifd_type);
-
-        if n*ifd_count<=4
-          value = data(j+8:j+8+n*ifd_count-1);
-          value = reshape(value, n, ifd_count);
-        else
-          a = 7+ifd_offset;
-          b = 7+ifd_offset+n*ifd_count-1;
-          if (a>0 && b>0 && a<=length(data) && b<=length(data))
-            value = data(7+ifd_offset:7+ifd_offset+n*ifd_count-1);
-            value = reshape(value, n, ifd_count);
-          else
-            value = [];
-          end
-        end
-      end
-   
-      switch ifd_type
-        case 01 % unsigned char
-          ifd.(name) = uint8(value);
-          if debug,
-            printf('%02x ', uint8(value));
-            printf('\n');
-          end
-        case 02 % Ascii
-          ifd.(name) = char(value);
-          if debug, printf('%s\n', char(value)); end
-        case 03 % 16 bit unsigned int
-          ifd.(name) = uintn(value, endian);
-          if debug
-            printf('%i ', intn(value), endian);
-            printf('\n');
-          end
-        case 04 % 32 bit unsigned int
-          ifd.(name) = uintn(value, endian);
-          if debug, printf('%i\n', uintn(value, endian)); end
-        case 05 % 32 bit unsigned rational
-          ifd.(name) = [uintn(value(1:4,:), endian); uintn(value(5:8,:), endian)];
-          if debug, printf('%i/%i\n',uintn(value(1:4), endian),uintn(value(5:8)), endian); end
-        case 07 % unknown
-          ifd.(name) = uint8(value);
-          if debug
-            printf('%02x ', value);
-            printf('\n');
-          end
-        case 09 % 32 bit signed int
-          ifd.(name) = intn(value, endian);
-          if debug, printf('%i\n', intn(value, endian)); end
-        case 10 % 32 bit signed rational
-          ifd.(name) = [intn(value(1:4,:), endian); intn(value(5:8,:), endian)];
-          if debug, printf('%i/%i\n',intn(value(1:4), endian),intn(value(5:8)), endian); end
-        otherwise
-          printf('%02x ', value);
-          printf('\n');
-      end
-
-      switch ifd_tag
-        case 0x8769, % Exif Pointer
-          ifd.(name) = ifdparse(exiftags(), data, ifd_offset, endian);
-        case 0x8825, % GPS Pointer
-          ifd.(name) = ifdparse(gpstags(), data, ifd_offset, endian);
-        case 0xa005 % Interoperatibility Pointer
-          ifd.(name) = ifdparse(dict, data, ifd_offset, endian);
-%        case 0x927c % Makernotes
-%           ifd.(name) = ifdparse([], data, ifd_offset, endian);
-        otherwise
-      end
-    end
-    j = 9+offset+ifd_fields*12;
-    ifd_next = intn(data(j:j+3), endian);
-
-    offset = ifd_next;
-  end
-end
-
-%
-% Return bytelength for respective IFD type
-%
-function n = ifdsize(ifd_type)
-  switch ifd_type
-    case {1, 2, 7}, n = 1;
-    case 03       , n = 2;
-    case {4, 9}   , n = 4;
-    case {5, 10}  , n = 8;
-    otherwise     , n = 1;
-  end
-end
-
-
-%
-% Convert little endian character vector to integer
-%
-function y = intn(x, bigendian)
-  if bigendian
-    y = polycol(x, int32(256));
-  else
-    y = polycol(flipud(x), int32(256));
-  end
-end
-
-function y = uintn(x, bigendian)
-  if bigendian
-     y = polycol(x, uint32(256));
-  else
-     y = polycol(flipud(x), uint32(256));
-  end
-end
-
-%
-% Use own polyval that works with integers,
-% it evaluates the number polygon columnwise.
-%
-%   number = polycol(digits, base)
-%
-function y = polycol(c, x)
-  y = c(1,:);
-  for i=2:size(c, 1)
-    y = y.*x+c(i,:);
-  end
-end
-
-%
-% Query EXIF IFD tagname
-%
-% Unfortunately, neither MATLAB nor Octave provide a hash functionality,
-% so use structures as hash.
-%
-function name = ifdtagname(dict, key)
-  k = sprintf('K%04X', key);
-  if isfield(dict, k)
-    name = dict.(k);
-  else
-    name = sprintf('tag%04X', key);
-  end
-end
-
-%
-% Primary image IFD tags according to Exif 2.2
-%
-function dict = tifftags()
-  t = {
-
-    % TIFF Tags according to EXIF2.2, additional baseline TIFF tags are marked by a '%'
-
-    '0FE' 'NewSubfileType' %
-    '0FF' 'SubfileType' %
-    '100' 'ImageWidth'
-    '101' 'ImageLength'
-    '102' 'BitsPerSample'
-    '103' 'Compression'
-    '106' 'PhotometricInterpretation'
-    '108' 'CellWidth' %
-    '109' 'CellLength' %
-    '10A' 'FillOrder' %
-    '10E' 'ImageDescription'
-    '10F' 'Make'
-    '110' 'Model'
-    '111' 'StripOffsets'
-    '112' 'Orientation'
-    '115' 'SamplesPerPixel'
-    '116' 'RowsPerStrip'
-    '117' 'StripByteCounts'
-    '118' 'MinSampleValue' %
-    '119' 'MaxSampleValue' %
-    '11A' 'XResolution'
-    '11B' 'YResolution'
-    '11C' 'PlanarConfiguration'
-    '120' 'FreeOffsets' %
-    '121' 'FreeByteCounts' %
-    '122' 'GrayResponseUnit' %
-    '123' 'GrayResponseCurve' %
-    '128' 'ResolutionUnit'
-    '12D' 'TransferFunction'
-    '131' 'Software'
-    '132' 'DateTime'
-    '13B' 'Artist'
-    '13C' 'HostComputer' %
-    '13E' 'WhitePoint'
-    '13F' 'PrimaryChromaticities'
-    '140' 'ColorMap' %
-    '152' 'ExtraSamples' %
-    '201' 'JPEGInterchangeFormat'
-    '202' 'JPEGInterchangeFormatLength'
-    '211' 'YCbCrCoefficients'
-    '212' 'YCbCrSubSampling'
-    '213' 'YCbCrPositioning'
-    '214' 'ReferenceBlackWhite'
-    '8298' 'Copyright'
-    '8769' 'Exif IFD Pointer'
-    '8825' 'GPS Info IFD Pointer'
-  };
-
-  dict = [];
-  for i=1:size(t,1)
-    key   = sprintf('K%04X', hex2dec(t{i,1}));
-    value = t{i,2};
-    dict.(key) = strrep(value, ' ', '_');
-  end
-end
-
-%
-% EXIF private tags
-%
-function dict = exiftags()
-  t = {
-
-    % EXIF Tags
-
-    '829A' 'ExposureTime'
-    '829D' 'FNumber'
-    '8822' 'ExposureProgram'
-    '8824' 'SpectralSensitivity'
-    '8827' 'ISOSpeedRatings'
-    '8828' 'OECF'
-    '9000' 'ExifVersion'
-    '9003' 'DateTimeOriginal'
-    '9004' 'DateTimeDigitized'
-    '9101' 'ComponentsConfiguration'
-    '9102' 'CompressedBitsPerPixel'
-    '9201' 'ShutterSpeedValue'
-    '9202' 'ApertureValue'
-    '9203' 'BrightnessValue'
-    '9204' 'ExposureBiasValue'
-    '9205' 'MaxApertureValue'
-    '9206' 'SubjectDistance'
-    '9207' 'MeteringMode'
-    '9208' 'LightSource'
-    '9209' 'Flash'
-    '920A' 'FocalLength'
-    '9214' 'SubjectArea'
-    '927C' 'MakerNote'
-    '9286' 'UserComment'
-    '9290' 'SubsecTime'
-    '9291' 'SubsecTimeOriginal'
-    '9292' 'SubsecTimeDigitized'
-    'A000' 'FlashpixVersion'
-    'A001' 'ColorSpace'
-    'A002' 'PixelXDimension'
-    'A003' 'PixelYDimension'
-    'A004' 'RelatedSoundFile'
-    'A005' 'Interoperatibility IFD Pointer'
-    'A20B' 'FlashEnergy'
-    'A20C' 'SpatialFrequencyResponse'
-    'A20E' 'FocalPlaneXResolution'
-    'A20F' 'FocalPlaneYResolution'
-    'A210' 'FocalPlaneResolutionUnit'
-    'A214' 'SubjectLocation'
-    'A215' 'ExposureIndex'
-    'A217' 'SensingMethod'
-    'A300' 'FileSource'
-    'A301' 'SceneType'
-    'A302' 'CFAPattern'
-    'A401' 'CustomRendered'
-    'A402' 'ExposureMode'
-    'A403' 'WhiteBalance'
-    'A404' 'DigitalZoomRatio'
-    'A405' 'FocalLengthIn35mmFilm'
-    'A406' 'SceneCaptureType'
-    'A407' 'GainControl'
-    'A408' 'Contrast'
-    'A409' 'Saturation'
-    'A40A' 'Sharpness'
-    'A40B' 'DeviceSettingDescription'
-    'A40C' 'SubjectDistanceRange'
-    'A420' 'ImageUniqueID'
-
-    % Interoperatibility tags
-
-    '001' 'InteroperatibilityIndex'
-    '002' 'InteroperatibilityVersion'
-    '1000' 'RelatedImageFileFormat'
-    '1001' 'RelatedImageWidth'
-    '1002' 'RelatedImageLength'
-  };
-
-  dict = [];
-  for i=1:size(t,1)
-    key   = sprintf('K%04X', hex2dec(t{i,1}));
-    value = t{i,2};
-    dict.(key) = strrep(value, ' ', '_');
-  end
-end
-
-%
-% EXIF GPS tags
-%
-function dict = gpstags()
-  t = {
-    0 'GPSVersionID'
-    1 'GPSLatitudeRef'
-    2 'GPSLatitude'
-    3 'GPSLongitudeRef'
-    4 'GPSLongitude'
-    5 'GPSAltitudeRef'
-    6 'GPSAltitude'
-    7 'GPSTimeStamp'
-    8 'GPSSatellites'
-    9 'GPSStatus'
-    10 'GPSMeasureMode'
-    11 'GPSDOP'
-    12 'GPSSpeedRef'
-    13 'GPSSpeed'
-    14 'GPSTrackRef'
-    15 'GPSTrack'
-    16 'GPSImgDirectionRef'
-    17 'GPSImgDirection'
-    18 'GPSMapDatum'
-    19 'GPSDestLatitudeRef'
-    20 'GPSDestLatitude'
-    21 'GPSDestLongitudeRef'
-    22 'GPSDestLongitude'
-    23 'GPSDestBearingRef'
-    24 'GPSDestBearing'
-    25 'GPSDestDistanceRef'
-    26 'GPSDestDistance'
-  };
-
-  dict = [];
-  for i=1:size(t,1)
-    key   = sprintf('K%04X', t{i,1});
-    value = t{i,2};
-    dict.(key) = strrep(value, ' ', '_');
-  end
-end
-
--- a/inst/regionprops.m
+++ b/inst/regionprops.m
@@ -348,6 +348,8 @@
         error ("regionprops: unsupported property '%s'", property);
     endswitch
   endfor
+  ## Matlab returns a column vector struct array.
+  retval = retval(:);
 endfunction
 
 function retval = local_area (bw)
@@ -423,3 +425,5 @@
 %! x = sum (img(2,ix) .* (ix)) / sum (img(2,ix));
 %! assert (props(1).WeightedCentroid(1), x, 10*eps)
 %! assert (props(1).WeightedCentroid(2), 2, 10*eps)
+
+%!assert (size (regionprops ([1 0 0; 0 0 2], "Area")), [2, 1])
\ No newline at end of file
deleted file mode 100644
--- a/inst/uintlut.m
+++ /dev/null
@@ -1,51 +0,0 @@
-## Copyright (C) 2004 Josep Mones i Teixidor <jmones@puntbarra.com>
-##
-## 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} {@var{B} =} uintlut (@var{A}, @var{LUT})
-## Computes matrix B by using A as an index to lookup table LUT.
-##
-## This function has been deprecated. Use @code{intlut} instead.
-##
-## B = uintlut(A, LUT) calculates a matrix B by using @var{LUT} as a
-## lookup table indexed by values in @var{A}.
-## 
-## B class is the same as @var{LUT}.
-## @end deftypefn
-
-function B = uintlut (A, LUT)
-  if (nargin != 2)
-    print_usage;
-  endif
-  persistent warned = false;
-  if (! warned)
-    warned = true;
-    warning ("Octave:deprecated-function",
-             ["`uintlut' has been deprecated in favor of `intlut'. This " ...
-              "function will be removed from future versions of the `image'" ...
-              " package"]);
-  endif
-
-  B = LUT(A);
-endfunction
-
-%!demo
-%! uintlut(uint8([1,2,3,4]),uint8([255:-1:0]));
-%! % Returns a uint8 array [255,254,253,252]
-
-%!assert(uintlut(uint8([1,2,3,4]),uint8([255:-1:0])), uint8([255:-1:252]));
-%!assert(uintlut(uint16([1,2,3,4]),uint16([255:-1:0])), uint16([255:-1:252]));
-%!assert(uintlut(uint32([1,2,3,4]),uint32([255:-1:0])), uint32([255:-1:252]));
-%!assert(uintlut(uint64([1,2,3,4]),uint64([255:-1:0])), uint64([255:-1:252]));
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ([2.67])
-AC_INIT([Octave-Forge image package], [2.2.1])
+AC_INIT([Octave-Forge image package], [2.3.0+])
 
 AH_BOTTOM([#include "undef_unordered_map.h"])
 AC_CONFIG_HEADERS([config.h])