view inst/imcrop.m @ 886:a0c42a32c6c4

Move conversion between each image type to imcast. * im2double.m, im2int16.m, im2single.m, im2uint16.m, im2uint8.m: move code into imcast. Fix several small bugs mainly dealing with indexed and logical images, small precision for Matlab compatibility, and increased performance. Expanded documentation. Added new tests. * imcast.m: implement the conversion between each image type instead of being a wrapper around functions for each conversion. This reduces code duplication, and places all very similar (but not equal) code together so that a bug fix in one can be easily notices that requires application in others. Expand documetation. Add more tests. * private/imconversion.m, private/im2float.m: remove no longer necessary function since this has all been moved to imcast. * COPYING: remove license for private/im2float.m and private/imconversion.m. * NEWS: make note of bug fixes for this functions.
author Carnë Draug <carandraug@octave.org>
date Mon, 24 Mar 2014 22:00:05 +0000
parents dac019dc55f3
children d316de4780ce
line wrap: on
line source

## 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} {} imcrop ()
## @deftypefnx {Function File} {} imcrop (@var{img})
## @deftypefnx {Function File} {} imcrop (@var{ind}, @var{cmap})
## @deftypefnx {Function File} {} imcrop (@var{h})
## @deftypefnx {Function File} {} imcrop (@dots{}, @var{rect})
## @deftypefnx {Function File} {[@var{cropped}] =} imcrop (@dots{})
## @deftypefnx {Function File} {[@var{cropped}, @var{rect}] =} imcrop (@dots{})
## @deftypefnx {Function File} {[@var{x}, @var{y}, @var{cropped}, @var{rect}] =} imcrop (@dots{})
## Crop image.
##
## Displays the image @var{img} in a figure window and waits for the user to
## select two points defining a bounding box.  First click on the top left
## and then on the bottom right corner of the region.  For an indexed image, a
## corresponding colormap can be specified in @var{cmap}.  For multi-dimensional
## images (each 2D image is concatenated in the 4th dimension), only the
## first image is displayed.
##
## if no image data is given, uses the current figure or figure from graphics
## handle @var{h}.
##
## Non-interactive usage is supported if the last input argument is 4 element
## vector @var{rect} defining the region of interest.  The first two elements
## specify the initial @var{x_ini} and @var{y_ini} coordinates, and the last
## two the @var{width} and @var{height}, i.e.,
## @code{@var{rect} = [@var{x_ini} @var{y_ini} @var{width} @var{height}]}.
## Note how this the opposite of the majority of Octave indexing rules where
## rows come before columns.
##
## Returns the @var{cropped} image and a vector @var{rect} with the
## coordinates and size for @var{cropped}.  If more than 3 output arguments
## are requested, also returns the @var{x} and @var{y} data that define
## the coordinate system.
##
## @emph{Note}: the values in @var{rect} are not necessarily integer values
## and can't always be used directly as index values for other images.  To
## crop the same region from a multiple images of the same size, either using
## a multi-dimensional image:
##
## @example
## @group
## nd_img = cat (4, img1, img2, img3, img4);
## croped = imcrop (nd_img);
## cropped_1 = cropped(:,:,:,1);
## cropped_2 = cropped(:,:,:,2);
## cropped_3 = cropped(:,:,:,3);
## cropped_4 = cropped(:,:,:,4);
## @end group
## @end example
##
## or multiple calls to @code{imcrop}:
##
## @example
## @group
## [croped_1, rect] = imcrop (img1);
## cropped_2        = imcrop (img2, rect);
## cropped_3        = imcrop (img3, rect);
## cropped_4        = imcrop (img4, rect);
## @end group
## @end example
##
## @seealso{impixel, imshow}
## @end deftypefn

## TODO not yet implemented
## @deftypefnx {Function File} {} imcrop (@var{xData}, @var{yData}, @dots{})

function varargout = imcrop (varargin)

  ## Screw Matlab and their over complicated API's! How can we properly
  ## parse all the possible alternative calls? See
  ## http://www.youtube.com/watch?v=1oZWacjmYm8 to understand how such
  ## API's develop.

  ## There is no check for this things, anything is valid. We (Octave)
  ## are at least checking the number of elements otherwise the input
  ## parsing would be horrible.
  valid_rect   = @(x) numel (x) == 4;
  valid_system = @(x) numel (x) == 2;

  rect = [];
  interactive = true;   # is interactive usage
  alt_system  = false;  # was an alternative coordinate system requested?
  from_fig    = false;  # do we have the image data or need to fetch from figure?

  if (nargin > 5)
    print_usage ();
  endif

  rect = [];
  if (numel (varargin) > 1 && valid_rect (varargin{end}))
    interactive = false;
    rect = varargin{end};
    varargin(end) = [];
  endif

  xdata = [];
  ydata = [];
  if (numel (varargin) > 2 && valid_system (varargin{1}) && valid_system (varargin{2}))
    ## requested messy stuff
    ## we should probably reuse part of what impixel does
    alt_system = true;
    xdata = varargin{1};
    ydata = varargin{2};
    varargin([1 2]) = [];
    error ("imcrop: messing around with coordinate system is not implemented");
  endif

  ## After we remove all that extra stuff, we are left with the image
  fnargin = numel (varargin);
  if (fnargin > 2)
    print_usage ();
  elseif (fnargin == 0)
    ## use current figure
    from_fig = true;
    h = gcf ();
  elseif (fnargin == 1 && ishandle (varargin{1}))
    ## use specified figure
    from_fig = true;
    h = varargin{1};
  elseif (interactive)
    ## leave input check to imshow
    h = nd_imshow (varargin{:});
  elseif (isimage (varargin{1}))
    ## we have the image data and it's not interactive, so there is
    ## nothing to do. We only check the minimum in the image.
  else
    print_usage ();
  endif

  if (from_fig)
    cdata = get (h, "cdata");
    xdata = get (h, "xdata");
    ydata = get (h, "ydata");
  else
    cdata = varargin{1};
    if (! alt_system)
      xdata = [1 columns(cdata)];
      ydata = [1 rows(cdata)];
    endif
  endif

  ## Finally, crop the image
  if (interactive)
    [x, y] = ginput (2);
    rect = [x(1) y(1) x(2)-x(1) y(2)-y(1)];
  endif
  i_ini = round ([rect(1) rect(2)]);
  i_end = round ([rect(1)+rect(3) rect(2)+rect(4)]);
  img = cdata(i_ini(2):i_end(2), i_ini(1):i_end(1),:,:); # don't forget RGB and ND images

  ## Even the API for the output is complicated
  if (nargout == 0 && interactive)
    figure ();
    ## In case we have a colormap or something like that, use
    ## it again when displaying the cropped image.
    nd_imshow (img, varargin{2:end});
  elseif (nargout < 3)
    varargout{1} = img;
    varargout{2} = rect;
  else
    varargout{1} = xdata;
    varargout{2} = ydata;
    varargout{3} = img;
    varargout{4} = rect;
  endif

endfunction

## shadows core function to support ND image.  If we have one, use
## the first frame only
function h = nd_imshow (varargin)
  size (varargin{1});
  h = imshow (varargin{1}(:,:,:,1), varargin{2:end});
endfunction

## test typical non-interactive usage, grayscale image
%!test
%! a = randi (255, [100 100]);
%! rect = [20 30 3 5];
%! assert (nthargout ([1 2], @imcrop, a, rect), {a(30:35, 20:23) rect});
%! assert (nthargout (2, @imcrop, a, rect), rect);
%! assert (nthargout ([3 4], 4, @imcrop, a, rect), {a(30:35, 20:23) rect});

## test typical non-interactive usage, RGB image
%!test
%! rgb = randi (255, [100 100 3]);
%! rect = [20 30 3 5];
%! assert (nthargout ([1 2], @imcrop, rgb, rect), {rgb(30:35, 20:23,:) rect});
%! assert (nthargout (2, @imcrop, rgb, rect), rect);
%! assert (nthargout ([3 4], 4, @imcrop, rgb, rect), {rgb(30:35, 20:23,:) rect});

## test typical non-interactive usage, indexed image
%!test
%! a = randi (255, [100 100]);
%! rect = [20 30 3 5];
%! cmap = jet (255);
%! assert (nthargout ([1 2], @imcrop, a, cmap, rect), {a(30:35, 20:23) rect});
%! assert (nthargout (2, @imcrop, a, cmap, rect), rect);
%! assert (nthargout ([3 4], 4, @imcrop, a, cmap, rect), {a(30:35, 20:23) rect});

## test typical non-interactive usage, logical image
%!test
%! a = rand (100) > 0.5;
%! rect = [20 30 3 5];
%! assert (nthargout ([1 2], @imcrop, a, rect), {a(30:35, 20:23) rect});
%! assert (nthargout (2, @imcrop, a, rect), rect);
%! assert (nthargout ([3 4], 4, @imcrop, a, rect), {a(30:35, 20:23) rect});