changeset 564:7db61a0a2632

imhist: complete rewrite of the function to be matlab compatible. Added test cases. No old code left so removed old copyright owners.
author carandraug
date Mon, 14 May 2012 17:23:29 +0000
parents d2a6dc5e921e
children 7fcfcce0b2f9
files NEWS inst/imhist.m
diffstat 2 files changed, 118 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS
+++ b/NEWS
@@ -54,6 +54,10 @@
       isind
       mat2gray
 
+ ** `imhist' is much more compatible with matlab and among other changes,
+    it now uses the whole range of the class for the histogram rather than
+    the minimum and maximum of the input image.
+
  ** `isbw' now defines a black-and-white image as a binary non-sparse
     matrix. This is compatible with matlab. To use the old behaviour,
     use the new option for the call "isbw (img, "non-logical").
--- a/inst/imhist.m
+++ b/inst/imhist.m
@@ -1,6 +1,4 @@
-## Copyright (C) 1999,2000 Kai Habel <kai.habel@gmx.de>
-## Copyright (C) 2000 Paul Kienzle <pkienzle@users.sf.net>
-## Copyright (C) 2011 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2011, 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
@@ -20,7 +18,7 @@
 ## @deftypefnx {Function File} {} imhist (@var{I}, @var{n})
 ## @deftypefnx {Function File} {} imhist (@var{X}, @var{cmap})
 ## @deftypefnx {Function File} {[@var{counts}, @var{x}] =} imhist (@dots{})
-## Shows the histogram of an image @var{I}.
+## Produce histogram counts of image @var{I}.
 ##
 ## The second argument can either be @var{n}, a scalar that specifies the number
 ## of bins; or @var{cmap}, a colormap in which case @var{X} is expected to be
@@ -30,65 +28,142 @@
 ## If output is requested, @var{counts} is the number of counts for each bin and
 ## @var{x} is a range for the bins so that @code{stem (@var{x}, @var{counts})} will
 ## show the histogram.
+##
+## Since a colorbar is not displayed under the histogram, calling this function
+## to visualize the histogram of an indexed image may not be very helpful.
+##
 ## @seealso{hist, histc, histeq}
 ## @end deftypefn
 
-function [nn, bins] = imhist (I, b)
+function [varargout] = imhist (img, b)
+
+  ## then img can either be a "normal" or indexed image. We will need to
+  ## check the second argument to find out
+  indexed = false;
 
   if (nargin < 1 || nargin > 2)
-    print_usage();
+    print_usage;
 
   elseif (nargin == 1)
-    if (islogical(I))
-      bins = 0:1;
+    if (islogical (img))
+      b = 2;
     else
-      bins = 0:255;
+      b = 256;
     endif
 
   elseif (nargin == 2)
-    ## A matrix with 3 columns is a colormap so...
-    if (ismatrix (b) && columns (b) == 3)
-      using_colormap = true;
+    if (iscolormap (b))
       ## if using a colormap, image must be an indexed image
-      if (!isind(I))
-        error ("second argument is a colormap but image is not indexed");
+      indexed = true;
+    elseif (isnumeric (b) && isscalar (b) && fix(b) == b)
+      ## do nothing, all is good
+      if (islogical (img) && b != 2)
+        error ("there can only be 2 bins when input image is binary")
       endif
-      max_idx = max (I(:));
-      bins    = 0:rows(b)-1;
-      if (max_idx > bins(end))
-        warning ("largest index exceeds length of colormap");
-      endif
-    elseif (isnumeric (b) && isscalar (b) && fix(b) == b)
-      bins = 0:b-1;
     else
       error ("second argument should either be a positive integer scalar or a colormap");
     endif
   endif
 
-  ## matlab returns bins as one column and not one row so we transpose the range
-  bins = bins';
+  ## check if img is good
+  if (indexed)
+    if (!isind(img))
+      error ("second argument is a colormap but image is not indexed");
+    endif
+    ## an indexed image reads differently wether it's uint8/16 or double
+    ## If uint8/16, index-1 is the colormap row number (there's an offset of 1)
+    ## If double, index is the colormap row number (no offset)
+    ## isind above already checks for double/uint8/uint16 so we can use isinteger
+    ## and isfloat safely
+    if ( (isfloat   (img) && max (img(:)) > rows(b)  ) ||
+         (isinteger (img) && max (img(:)) > rows(b)-1) )
+      warning ("largest index in image exceeds length of colormap");
+    endif
+  endif
 
-  ## XXX at the moment, this function is not working at all, at least for
-  ## grayscale images. I'm assuming that the code will at least be working for
-  ## indexed images and colormaps so I'm leaving the original code for those
-  ## cases and use only the "new" code when "using_colormap" is false
-  ## Carnë Draug 10/11/2011
-  if (nargout == 0)
-    if (exist ("using_colormap", "var") && using_colormap)
-      hist (I(:), bins);
+  ## prepare bins
+  if (indexed)
+    if (isinteger (img))
+      bins = 0:rows(b)-1;
     else
-      [nn] = histc (I(:), bins);
-      stem (bins, nn);
+      bins = 1:rows(b);
     endif
   else
-    if (exist ("using_colormap", "var") && using_colormap)
-      [nn,bins] = hist (I(:), bins);
+    if (isinteger (img))
+      bins  = linspace (intmin (class (img)), intmax (class (img)), b);
+    elseif (islogical (img))
+      bins = 0:1;
     else
-      [nn] = histc (I(:), bins);
+      ## image must be single or double
+      bins = linspace (0, 1, b);
+    endif
+    ## we will use this bins with histc where they are the edges for each bin
+    ## but in imhist we want them to be the center of each bin. We can't use
+    ## hist either since values right in the middle will go to the bottom
+    ## bin (4.5 will be placed on the bin 4 instead of 5 and this is like
+    ## matlab, not an octave bug). So we still use histc but we decrease their
+    ## values by half of bin width and increase it back in the end to return
+    ## the values (if we did it on the image it would be only one step but
+    ## would be heavier on the system since images are likely to be larger
+    ## than bins)
+    if (!islogical (img))
+      bins -= ((bins(2) - bins(1))/2);
     endif
+    ## matlab returns bins as one column instead of a row but only for non
+    ## indexed images
+    bins = bins';
+  endif
 
-    vr_val_cnt = 1;
-    varargout{vr_val_cnt++} = nn;
-    varargout{vr_val_cnt++} = bins;
+  ## if not dealing with indexed image, we may need to make sure values are
+  ## between get bin values
+  if (!indexed)
+    ## while integers could in no way have a value below the minium of their
+    ## class, floats can have values below zero which need to be truncated
+    if (isfloat (img))
+      img(img < 0) = 0;
+    endif
+    ## because we adjusted the bins edge below the max value of the class, and
+    ## because histc will not count values outside the edges, we need to bring
+    ## them down (no need to worry about the min because the min edge is already
+    ## below the mininum of the class). This also adjusts floats above 1
+    if (max (img(:)) > bins(end))
+      ## in case it's a integer, if we try to assign the non integer values it 
+      ## will fail so we need to make it double. But this means it takes more
+      ## memory so let's first make sure we need to
+      if (fix(bins(end)) != bins(end))
+        img = double (img);
+      endif
+      img(img > bins(end)) = bins(end);
+    endif
+  endif
+  [nn] = histc (img(:), bins);
+  if (!indexed && !islogical(img))
+    bins += ((bins(2) - bins(1))/2);
+  endif
+
+  if (nargout != 0)
+    varargout{1} = nn;
+    varargout{2} = bins;
+  else
+    stem (bins, nn);
+    ## would be cool if we managed to have the colormap showed under the
+    ## histogram like matlab does. Should look into the code of waitfor and
+    ## colormap to check how to do it
   endif
 endfunction
+
+%!shared nn, bb, enn, ebb
+%! [nn, bb] = imhist(logical([0 1 0 0 1]));
+%!assert({nn, bb}, {[3 2]', [0 1]'})
+%! [nn, bb] = imhist([0 0.2 0.4 0.9 1], 5);
+%!assert({nn, bb}, {[1 1 1 0 2]', [0 0.25 0.5 0.75 1]'})
+%! [nn, bb] = imhist([-2 0 0.2 0.4 0.9 1 5], 5);
+%!assert({nn, bb}, {[2 1 1 0 3]', [0 0.25 0.5 0.75 1]'})
+%! [nn, bb] = imhist(uint8([0 32 255]), 256);
+%! enn = zeros(256, 1); enn([1, 33, 256]) = 1;
+%! ebb = 0:255;
+%!assert({nn, bb}, {enn, ebb'})
+%! [nn, bb] = imhist(int8([-50 0 100]), 31);
+%! enn = zeros(31, 1); enn([10, 16, 28]) = 1;
+%! ebb = -128:8.5:127;
+%!assert({nn, bb}, {enn, ebb'})