changeset 11376:1c186f4255de octave-forge

imperspectivewarp, imrotate, imresize, imremap changes. * do not parse interpolation method. Leave it to interp2 * create private function that lowers case and deals with converting matlab compatible names to interp2 compatible methods. This extends this functions to the methods spline, pchip and triangle. * changes on input checking using isimage, isrgb, isgray and print_usage * use cast() or eval ("im2" in_class) to return image of same class as input
author carandraug
date Sat, 12 Jan 2013 15:50:13 +0000
parents fde3acd5937a
children fff993191048
files main/image/NEWS main/image/inst/imperspectivewarp.m main/image/inst/imremap.m main/image/inst/imresize.m main/image/inst/imrotate.m main/image/inst/private/interp_method.m
diffstat 6 files changed, 117 insertions(+), 145 deletions(-) [+]
line wrap: on
line diff
--- a/main/image/NEWS	Sat Jan 12 14:55:51 2013 +0000
+++ b/main/image/NEWS	Sat Jan 12 15:50:13 2013 +0000
@@ -52,12 +52,13 @@
  ** The performance of `imresize()' has been greatly improved when using the
     nearest neighbor method for N or 1/N scale factors (e.g.: 2, 50, 1/4, 1/7).
 
- ** The `imresize' function will now accept any interpolation method from
-    `interp2()' thus extending the available methods to `spline' and `pchip'.
-     The `triangle' method has also been added (same as the `linear' method).
-
- ** The `imremap' function will now accept any interpolation method from
-    `interp2()' thus extending the available methods to `pchip'.
+ ** The imperspectivewarp, imremap, imresize, and imrotate functions will now
+    accept any interpolation method from the interp2 function thus extending
+    the available methods to "spline" and "pchip".  This in addition to the
+    "bilinear" and "bicubic" methods (same as "linear" and "cubic" respectively)
+    which are kept for matlab compatibility.  For the same reason, the
+    "triangle" method (interpolation kernel) has also been added (which is the
+    same as "linear" method).
 
 Summary of important user-visible changes for image 2.0.0:
 -------------------------------------------------------------------
--- a/main/image/inst/imperspectivewarp.m	Sat Jan 12 14:55:51 2013 +0000
+++ b/main/image/inst/imperspectivewarp.m	Sat Jan 12 15:50:13 2013 +0000
@@ -20,19 +20,13 @@
 ## The transformation matrix @var{P} must be a 3x3 homogeneous matrix, or 2x2 or 2x3
 ## affine transformation matrix.
 ## 
-## The resulting image @var{warped} is computed using an interpolation method that
-## can be selected through the @var{interp} argument. This must be one
-## of the following strings
-## @table @code
-## @item "nearest"
-## Nearest neighbor interpolation.
-## @item "linear"
-## @itemx "bilinear"
-## Bilinear interpolation. This is the default behavior.
-## @item "cubic"
-## @itemx "bicubic"
-## Bicubic interpolation.
-## @end table
+## The optional argument @var{method} defines the interpolation method to be
+## used.  All methods supported by @code{interp2} can be used.  By default, the
+## @code{linear} method is used.
+##
+## For @sc{matlab} compatibility, the methods @code{bicubic} (same as
+## @code{cubic}), @code{bilinear} and @code{triangle} (both the same as
+## @code{linear}) are also supported.
 ##
 ## By default the resulting image contains the entire warped image. In some situation
 ## you only parts of the warped image. The argument @var{bbox} controls this, and can
@@ -56,47 +50,35 @@
 ## @seealso{imremap, imrotate, imresize, imshear, interp2}
 ## @end deftypefn
 
-function [warped, valid] = imperspectivewarp(im, P, interp = "bilinear", bbox = "loose", extrapolation_value = NA)
-  ## Check input
-  if (nargin < 2)
-    print_usage();
+function [warped, valid] = imperspectivewarp(im, P, interp = "linear", bbox = "loose", extrapolation_value = NA)
+
+  if (nargin < 2 || nargin > 5)
+    print_usage ();
+  elseif (! isimage (im) || (! isrgb (im) && ! isgray (im)))
+    error ("imperspectivewarp: IM must be a grayscale or RGB image.")
+  elseif (! ischar (interp))
+    error ("imperspectivewarp: INTERP must be a string with interpolation method")
+  elseif (! ischar (bbox) || ! any (strcmpi (bbox, {"loose", "crop", "same"})))
+    error ("imperspectivewarp: BBOX must be 'loose', 'crop' or 'same'");
+  elseif (! isscalar (extrapolation_value))
+    error ("imperspectivewarp: EXTRAPVAL must be a scalar");
   endif
-  
-  [imrows, imcols, imchannels, tmp] = size(im);
-  if (tmp != 1 || (imchannels != 1 && imchannels != 3))
-    error("imperspectivewarp: first input argument must be an image");
-  endif
+  interp = interp_method (interp);
   
   if (ismatrix(P) && ndims(P) == 2)
     if (issquare(P) && rows(P) == 3) # 3x3 matrix
       if (P(3,3) != 0)
         P /= P(3,3);
       else
-        error("imperspectivewarp: P(3,3) must be non-zero");
+        error ("imperspectivewarp: P(3,3) must be non-zero");
       endif
     elseif (rows(P) == 2 && (columns(P) == 2 || columns(P) == 3)) # 2x2 or 2x3 matrix
       P(3,3) = 1;
     else # unsupported matrix size
-      error("imperspectivewarp: transformation matrix must be 2x2, 2x3, or 3x3");
+      error ("imperspectivewarp: transformation matrix must be 2x2, 2x3, or 3x3");
     endif
   else
-    error("imperspectivewarp: transformation matrix not valid");
-  endif
-  
-  if (!any(strcmpi(interp, {"nearest", "linear", "bilinear", "cubic", "bicubic"})))
-    error("imperspectivewarp: unsupported interpolation method");
-  endif
-  if (any(strcmpi(interp, {"bilinear", "bicubic"})))
-    interp = interp(3:end); # Remove "bi"
-  endif
-  interp = lower(interp);
-  
-  if (!any(strcmpi(bbox, {"loose", "crop", "same"})))
-    error("imperspectivewarp: bounding box must be either 'loose', 'crop', or 'same'");
-  endif
-  
-  if (!isscalar(extrapolation_value))
-    error("imperspective: extrapolation value must be a scalar");
+    error ("imperspectivewarp: transformation matrix not valid");
   endif
   
   ## Do the transformation
@@ -140,8 +122,7 @@
 
   clear X Y D PD;
   
-  ## Interpolate
-  [warped, valid] = imremap(im, XI, YI, interp, extrapolation_value);
+  [warped, valid] = imremap (im, XI, YI, interp, extrapolation_value);
 
 endfunction
 
--- a/main/image/inst/imremap.m	Sat Jan 12 14:55:51 2013 +0000
+++ b/main/image/inst/imremap.m	Sat Jan 12 15:50:13 2013 +0000
@@ -28,11 +28,14 @@
 ## by interpolation. Note that the image @var{im} is expressed in a (X, Y)-coordinate
 ## system and not a (row, column) system.
 ##
-## The optional argument @var{interp} defines the interpolation method to be
-## used.  All methods supported by @code{interp2} can be used.  In
-## addition, the methods @code{bicubic} (same as @code{cubic}), and
-## @code{bilinear} (same as @code{linear}) are supported for @sc{matlab}
-## compatibility.  By default, the @code{linear} method is used.
+## The optional argument @var{method} defines the interpolation method to be
+## used.  All methods supported by @code{interp2} can be used.  By default, the
+## @code{linear} method is used.
+##
+## For @sc{matlab} compatibility, the methods @code{bicubic} (same as
+## @code{cubic}), @code{bilinear} and @code{triangle} (both the same as
+## @code{linear}) are also supported.
+
 ##
 ## 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}
@@ -45,35 +48,22 @@
 ## @end deftypefn
 
 function [warped, valid] = imremap(im, XI, YI, interp = "linear", extrapval = NA)
-  ## Check input
-  if (nargin < 3)
-    print_usage();
-  endif
-  
-  [imrows, imcols, imchannels, tmp] = size(im);
-  if (tmp != 1 || (imchannels != 1 && imchannels != 3))
-    error("imremap: first input argument must be an image");
-  endif
-  
-  if (!size_equal(XI, YI) || !ismatrix(XI) || ndims(XI) != 2)
-    error("imremap: XI and YI must be matrices of the same size");
+
+  if (nargin < 3 || nargin > 5)
+    print_usage ();
+  elseif (! isimage (im) || (! isrgb (im) && ! isgray (im)))
+    error ("imremap: IM must be a grayscale or RGB image.")
+  elseif (! size_equal (XI, YI) || ! ismatrix (XI) || ndims (XI) != 2)
+    error ("imremap: XI and YI must be matrices of the same size");
+  elseif (! ischar (interp))
+    error ("imremap: INTERP must be a string with interpolation method")
+  elseif (! isscalar (extrapval))
+    error ("imremap: EXTRAPVAL must be a scalar");
   endif
-  
-  ## Handle the interp argument. We do not check for the actual value. We leave
-  ## that to interp2 so we don't have to keep updating this when interp2
-  ## gets new methods
-  interp = tolower (interp);
-  switch interp
-    case "bicubic",   interp = "cubic";
-    case "bilinear",  interp = "linear";
-  endswitch
-  
-  if (!isscalar(extrapval))
-    error("imremap: extrapolation value must be a scalar");
-  endif
+  interp = interp_method (interp);
   
   ## Interpolate
-  if (imchannels == 1) # Gray
+  if (size (im, 3) == 1) # Gray
     warped = grayinterp(im, XI, YI, interp, NA);
   else # rgb image
     for i = 3:-1:1
@@ -83,13 +73,8 @@
   valid = !isna(warped);
   warped(!valid) = extrapval;
 
-  ## Change the class of the results according to the class of the image
-  c = class(im);
-  if (strcmpi(c, "uint8"))
-    warped = uint8(warped);
-  elseif (strcmpi(c, "uint16"))
-    warped = uint16(warped);
-  endif
+  ## we return image on same class as input
+  warped = cast (warped, class (im));
 
 endfunction
 
--- a/main/image/inst/imresize.m	Sat Jan 12 14:55:51 2013 +0000
+++ b/main/image/inst/imresize.m	Sat Jan 12 15:50:13 2013 +0000
@@ -34,10 +34,12 @@
 ## @end example
 ##
 ## The optional argument @var{method} defines the interpolation method to be
-## used.  All methods supported by @code{interp2} can be used.  In
-## addition, the methods @code{bicubic} (same as @code{cubic}), @code{bilinear}
-## and @code{triangle} (both, the same as @code{linear}) are supported for
-## @sc{matlab} compatibility.  By default, the @code{cubic} method is used.
+## used.  All methods supported by @code{interp2} can be used.  By default, the
+## @code{cubic} method is used.
+##
+## For @sc{matlab} compatibility, the methods @code{bicubic} (same as
+## @code{cubic}), @code{bilinear} and @code{triangle} (both the same as
+## @code{linear}) are also supported.
 ##
 ## @table @asis
 ## @item bicubic (default)
@@ -54,15 +56,17 @@
 ## @end deftypefn
 
 function im = imresize (im, scale, method = "cubic")
+
   if (nargin < 2 || nargin > 3)
-    print_usage
+    print_usage ();
   elseif (! isimage (im) || (! isrgb (im) && ! isgray (im)))
-    error ("imerode: IM must be a grayscale or RGB image.")
+    error ("imresize: IM must be a grayscale or RGB image.")
   elseif (! isnumeric (scale) || any (scale <= 0))
-    error ("imerode: SCALE or [M N] must be numeric positive values")
+    error ("imresize: SCALE or [M N] must be numeric positive values")
   elseif (! ischar (method))
-    error ("imerode: METHOD must be a string with interpolation method")
+    error ("imresize: METHOD must be a string with interpolation method")
   endif
+  method = interp_method (method);
 
   inRows = rows (im);
   inCols = columns (im);
@@ -92,16 +96,6 @@
     error ("imresize: SCALE argument must be a scalar or a 2 element vector");
   end
 
-  ## Handle the method argument. We do not check for the actual value. We leave
-  ## that to interp2 so we don't have to keep updating this when interp2
-  ## gets new methods
-  method = tolower (method);
-  switch method
-    case "bicubic",   method = "cubic";
-    case "bilinear",  method = "linear";
-    case "triangle",  method = "linear";
-  endswitch
-
   ## Perform the actual resizing
   if (inRows == outRows && inCols == outCols)
     ## no resizing to do
--- a/main/image/inst/imrotate.m	Sat Jan 12 14:55:51 2013 +0000
+++ b/main/image/inst/imrotate.m	Sat Jan 12 15:50:13 2013 +0000
@@ -24,20 +24,15 @@
 ##
 ##   @var{theta}    the rotation angle in degrees counterclockwise
 ##
-##   @var{method}
-##     @itemize @w
-##       @item "nearest" neighbor: fast, but produces aliasing effects (default).
-##       @item "bilinear" interpolation: does anti-aliasing, but is slightly slower.
-##       @item "bicubic" interpolation: does anti-aliasing, preserves edges better
-## than bilinear interpolation, but gray levels may slightly overshoot at sharp
-## edges. This is probably the best method for most purposes, but also the slowest.
-##       @item "Fourier" uses Fourier interpolation, decomposing the rotation
-## matrix into 3 shears. This method often results in different artifacts than
-## homography-based methods.  Instead of slightly blurry edges, this method can
-## result in ringing artifacts (little waves near high-contrast edges).  However,
-## Fourier interpolation is better at maintaining the image information, so that
-## unrotating will result in an image closer to the original than the other methods.
-##     @end itemize
+## The optional argument @var{method} defines the interpolation method to be
+## used.  All methods supported by @code{interp2} can be used.  In addition,
+## Fourier interpolation by decomposing the rotation matrix into 3 shears can
+## be used with the @code{fourier} method. By default, the @code{nearest} method
+## is used.
+##
+## For @sc{matlab} compatibility, the methods @code{bicubic} (same as
+## @code{cubic}), @code{bilinear} and @code{triangle} (both the same as
+## @code{linear}) are also supported.
 ##
 ##   @var{bbox}
 ##     @itemize @w
@@ -64,26 +59,20 @@
 
 function [imgPost, H, valid] = imrotate (imgPre, thetaDeg, interp = "nearest", bbox = "loose", extrapval = NA)
 
-  if (nargin < 2)
-    print_usage();
+  if (nargin < 2 || nargin > 5)
+    print_usage ();
   elseif (! isimage (imgPre) || (! isrgb (imgPre) && ! isgray (imgPre)))
     error ("imrotate: IMGPRE must be a grayscale or RGB image.")
   elseif (! isscalar (thetaDeg))
     error("imrotate: THETA must be a scalar");
   elseif (! ischar (interp))
     error("imrotate: interpolation METHOD must be a character array");
-  elseif (! any (strcmpi (interp, {"nearest", "linear", "bilinear", "cubic", "bicubic", "Fourier"})))
-    error("imrotate: unsupported METHOD interpolation method");
   elseif (! isscalar (extrapval))
     error("imrotate: EXTRAPVAL must be a scalar");
+  elseif (! ischar (bbox) || ! any (strcmpi (bbox, {"loose", "crop"})))
+    error("imrotate: BBOX must be 'loose' or 'crop'");
   endif
-
-  if (any(strcmpi(interp, {"bilinear", "bicubic"})))
-    interp = interp(3:end); # Remove "bi"
-  endif
-  if (!any(strcmpi(bbox, {"loose", "crop"})))
-    error("imrotate: bounding box must be either 'loose' or 'crop'");
-  endif
+  interp = interp_method (interp);
 
   ## Input checking done. Start working
   thetaDeg = mod(thetaDeg, 360); # some code below relies on positive angles
@@ -97,7 +86,7 @@
 
   R = [cos(theta) sin(theta); -sin(theta) cos(theta)];
 
-  if (nargin >= 4 && strcmp(bbox, "crop"))
+  if (nargin >= 4 && strcmpi(bbox, "crop"))
     sizePost = sizePre;
   else
     ## Compute new size by projecting zero-base image corner pixel
@@ -153,8 +142,8 @@
   end
 
   ## Now the actual rotations happen
-  if (strcmpi(interp, "Fourier"))
-    c = class (imgPre);
+  if (strcmpi (interp, "fourier"))
+    in_class = class (imgPre);
     imgPre = im2double (imgPre);
     if (isgray(imgPre))
       imgPost = imrotate_Fourier(imgPre, thetaDeg, interp, bbox);
@@ -165,14 +154,10 @@
     endif
     valid = NA;
     
-    switch (c)
-      case "uint8"
-        imgPost = im2uint8 (imgPost);
-      case "uint16"
-        imgPost = im2uint16 (imgPost);
-      case "single"
-        imgPost = single (imgPost);
-    endswitch
+    ## 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);
   else
     [imgPost, valid] = imperspectivewarp(imgPre, H, interp, bbox, extrapval);
   endif
@@ -181,10 +166,6 @@
 
 function fs = imrotate_Fourier (f, theta, method, bbox)
 
-    if (size (f, 3) != 1)
-      error("imrotate_Fourier: image argument must be a gray-scale image");
-    endif
-
     # Get original dimensions.
     [ydim_orig, xdim_orig] = size(f);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/image/inst/private/interp_method.m	Sat Jan 12 15:50:13 2013 +0000
@@ -0,0 +1,30 @@
+## Copyright (C) 2013 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 private function is to be common to all functions that have an option
+## for the interpolation method to use. For matlab compatibility, bicubic and
+## bilinear are accepted which are the same as cubic and linear. This does not
+## actually check if the method is valid, we leave that to interp2. The reason
+## is that if interp2 implements a new method, all this functions will
+## automatically work with it.
+
+function method = interp_method (method)
+  method = tolower (method);
+  switch method
+    case "bicubic",   method = "cubic";
+    case "bilinear",  method = "linear";
+    case "triangle",  method = "linear";  # interpolation kernel
+  endswitch
+endfunction