changeset 799:08e2890c96cd

Expand functions that make use of imdilate and imerode to use strel. * private/prepare_strel.m: small private function common to all other which checks validity of a structuring element and creates a strel object if input was a binary matrix. * imbothat.m, imclose.m, imopen.m, imtophat.m, mmgradm.m: make use of new prepare_strel, uniform the documentation, add many tests for N dimensions, and weird SEs, follow Octave coding guidelines, * mmgradm.m: change default SEs for elementary cross, fix gradient when input is logical matrix (use boolean logic instead of subtraction), expand documentation with examples, and how to perform half gradients. * imerode.cc: small change on documentation for uniformity.
author Carnë Draug <carandraug@octave.org>
date Wed, 09 Oct 2013 07:57:08 +0100 (2013-10-09)
parents 088376c53e6c
children 1e708656e295
files inst/imbothat.m inst/imclose.m inst/imopen.m inst/imtophat.m inst/mmgradm.m inst/private/prepare_strel.m src/imerode.cc
diffstat 7 files changed, 540 insertions(+), 108 deletions(-) [+]
line wrap: on
line diff
--- a/inst/imbothat.m
+++ b/inst/imbothat.m
@@ -1,5 +1,5 @@
-## Copyright (C) 2005  Carvalho-Mariel
-## Copyright (C) 2010-2011 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2005 Carvalho-Mariel
+## Copyright (C) 2010-2013 Carnë Draug <carandraug@octave.org>
 ##
 ## 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,12 +15,24 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{B} = imbothat (@var{A}, @var{se})
+## @deftypefn {Function File} {} imbothat (@var{img}, @var{SE})
 ## Perform morphological bottom hat filtering.
 ##
-## The image @var{A} must be a grayscale or binary image, and @var{se} must be a
-## structuring element. Both must have the same class, e.g., if @var{A} is a
-## logical matrix, @var{se} must also be logical.
+## The matrix @var{img} must be numeric while @var{SE} can be a:
+## @itemize @bullet
+## @item
+## strel object;
+## @item
+## array of strel objects as returned by `@strel/getsequence';
+## @item
+## matrix of 0's and 1's.
+## @end itemize
+##
+## A bottom hat transform corresponds to the difference between the closing
+## of @var{img} and @var{img} itself, i.e., it is equivalent to:
+## @example
+## imclose (img, se) - img;
+## @end example
 ##
 ## A bottom-hat transform is also known as 'black', or 'closing', top-hat
 ## transform.
@@ -28,28 +40,137 @@
 ## @seealso{imerode, imdilate, imopen, imclose, imtophat, mmgradm}
 ## @end deftypefn
 
-function retval = imbothat (im, se)
+function black = imbothat (img, se)
 
-  ## Checkinput
   if (nargin != 2)
-    print_usage();
+    print_usage ();
+  elseif (! isimage (img))
+    error("imbothat: IMG must be a numeric matrix");
   endif
-  if (!ismatrix(im) || !isreal(im))
-    error("imbothat: first input argument must be a real matrix");
-  elseif (!ismatrix(se) || !isreal(se))
-    error("imbothat: second input argument must be a real matrix");
-  elseif ( !strcmp(class(im), class(se)) )
-    error("imbothat: image and structuring element must have the same class");
-  endif
+  se = prepare_strel ("imbothat", se);
 
   ## Perform filtering
   ## Note that in case that the transform is to applied to a logical image,
   ## subtraction must be handled in a different way (x & !y) instead of (x - y)
   ## or it will return a double precision matrix
-  if (islogical(im))
-    retval = imclose(im, se) & !im;
+  if (islogical (img))
+    black = imclose (img, se) & ! img;
   else
-    retval = imclose(im, se) - im;
+    black = imclose (img, se) - img;
   endif
 
 endfunction
+
+%!assert (imbothat (ones (3), [1 1; 0 1]), zeros (3));
+%!assert (imbothat (true (3), [1 1; 0 1]), false (3));
+
+%!shared in, out, se
+%! in =  [ 0   0   0   1   1   1   0   0   1   1
+%!         0   1   0   1   1   1   0   0   0   1
+%!         1   1   1   1   1   0   0   0   0   0
+%!         0   1   1   1   1   0   0   0   0   0
+%!         0   0   0   1   0   0   0   0   1   0
+%!         0   0   0   0   0   0   0   1   1   1
+%!         0   0   0   0   1   0   1   0   1   0
+%!         0   0   0   1   1   1   1   1   0   0
+%!         0   0   0   0   1   1   1   0   0   0
+%!         0   0   0   1   1   1   0   0   0   0];
+%!
+%! out = [ 1   1   1   0   0   0   1   1   0   0
+%!         1   0   1   0   0   0   0   0   0   0
+%!         0   0   0   0   0   0   0   0   0   1
+%!         1   0   0   0   0   0   0   0   0   1
+%!         0   0   0   0   1   0   0   0   0   1
+%!         0   0   0   1   1   1   1   0   0   0
+%!         0   0   0   1   0   1   0   1   0   1
+%!         0   0   0   0   0   0   0   0   0   0
+%!         0   0   0   1   0   0   0   0   0   0
+%!         0   0   0   0   0   0   1   0   0   0];
+%!assert (imbothat (logical (in), ones (3)), logical (out));
+%!
+%! out = [ 7    0   15    8    1    6    0   13    6   24
+%!         0    8    9    2    0    0   16    7    0   23
+%!        89    7    0   41   39    7   12    7    0   23
+%!         8    1   69   40   58    1    6    2    0   43
+%!         7    0   63   59   52    0    0    0   14   32
+%!        62   55    6    7    0    7    0   23   16    1
+%!        56   74    0    2    0    0   16   14    7    0
+%!         0   73   69    0    0   19   15    8    1    0
+%!         8    6    0    0    6   13    9    2    0    6
+%!         7    0    0   19    0   14    7    0   23    0];
+%!assert (imbothat (magic (10), ones (3)), out);
+%!assert (imbothat (uint8 (magic (10)), strel ("square", 3)), uint8 (out));
+%!
+%! ## using a se that will be decomposed in 2 pieces
+%! out =[ 7    0   87   66   59    7    0   19   12   30
+%!        0   13   81   60   58    1   19   13    6   29
+%!       89   12    0   54   52   20   18    7    0   23
+%!        8    6   69   53   71   14   12    2    0   43
+%!        7    0   63   73   66   14    7    0   23   41
+%!       76   69   14    7    0   30   23   46   39    7
+%!       70   88    9    2    0   24   42   40   33    6
+%!       14   87   80    0    0   43   41   34   27    0
+%!       84   82    0    0   19   37   35   28   26   19
+%!       89   82    0   20   13   36   29   22   45   13];
+%!assert (imbothat (magic (10), ones(5)), out);
+%!
+%! ## using a weird non-symmetric and even-size se
+%! out =[ 0    0   15    8    1    3    0    7    0   18
+%!        0    8   53   59    0    0   14   13    0   17
+%!       84    0    0   40   38    6   13    6    0   23
+%!        2    0   42   47   58    0    6    0    0   41
+%!        0    0   62   59   52    0    0    0   16   35
+%!        6   58   13    6    0    3   19   19   35    1
+%!        0   18    0    0    0    0   15   13    6    0
+%!        0   17   69    0    0   17   17    8    0    0
+%!        8   67    0    0    0   15    9    2    0    6
+%!        7    0    0   17   10   42    7    0   19    0];
+%!assert (imbothat (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out);
+%!
+%! ## N dimensional and weird se
+%! in = reshape (magic(16), [4 8 4 2]);
+%! se = ones (3, 3, 3);
+%! se(:,:,1) = [1 0 1; 0 1 1; 0 0 0];
+%! se(:,:,3) = [1 0 1; 0 1 1; 0 0 1];
+%! out = zeros (size (in));
+%! out(:,:,1,1) = [
+%!     0   17   81  145  237  146   64    0
+%!   205  128   64    0    0   37   83  147
+%!   175  111   47    0    0   64  117  181
+%!     0   64  128  209  173  109   45    0];
+%! out(:,:,2,1) = [
+%!   235  142   78   18    0   23   69  133
+%!     0   35  103  163  215  128   46    0
+%!     0   64  128  195  183  123   48    0
+%!   153   93   43    0   14   78  146  215];
+%! out(:,:,3,1) = [
+%!     0   25   89  153  229  142   64    0
+%!   201  128   64    0    0   41   91  155
+%!   167  103   57    0    0   64  125  189
+%!     0   64  146  217  165  101   37    0];
+%! out(:,:,4,1) = [
+%!   227  142   78   14    0   31   77  141
+%!     0   43  107  171  211  128   46    0
+%!     0   64  128  203  179  115   48    0
+%!   149   99   35    0   18   82  146  223];
+%! out(:,:,1,2) = [
+%!     0   33   97  161  221  146   64    0
+%!   189  125   61    0    0   53   99  163
+%!   159   95   31    0    0   64  128  197
+%!     0   64  128  225  157   93   29    0];
+%! out(:,:,2,2) = [
+%!   219  142   78   18    0   39   85  149
+%!     0   51  119  179  199  128   46    0
+%!     0   64  128  211  167  107   43    0
+%!   137   77   27    0   14   78  146  231];
+%! out(:,:,3,2) = [
+%!     0   41  105  169  213  142   64    0
+%!   185  121   64    0    0   57  107  171
+%!   151   87   41    0    0   64  128  205
+%!     0   64  146  233  149   85   21    0];
+%! out(:,:,4,2) = [
+%!   211  142   78   14    0   47   93  157
+%!     0   59  123  187  195  128   46    0
+%!     0   64  128  219  163   99   35    0
+%!   133   83   19    0   18   82  146  239];
+%!assert (imbothat (in, se), out);
--- a/inst/imclose.m
+++ b/inst/imclose.m
@@ -14,31 +14,101 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{B} = imclose (@var{A}, @var{se})
-## Perform morphological closing on a given image.
-## The image @var{A} must be a grayscale or binary image, and @var{se} must be a
-## structuring element.
+## @deftypefn {Function File} {} imclose (@var{img}, @var{SE})
+## Perform morphological closing.
 ##
-## The closing corresponds to a dilation followed by an erosion of the image, i.e.
+## The matrix @var{img} must be numeric while @var{SE} can be a:
+## @itemize @bullet
+## @item
+## strel object;
+## @item
+## array of strel objects as returned by `@strel/getsequence';
+## @item
+## matrix of 0's and 1's.
+## @end itemize
+##
+## The closing corresponds to a dilation followed by an erosion of @var{img},
+## using the same @var{SE}, i.e., it is equivalent to:
 ## @example
-## B = imerode(imdilate(A, se), se);
+## imerode (imdilate (img, se), se);
 ## @end example
-## @seealso{imdilate, imerode, imclose}
+##
+## @seealso{imdilate, imerode, imopen}
 ## @end deftypefn
 
-function retval = imclose (im, se)
-  ## Checkinput
+function closed = imclose (img, se)
+
   if (nargin != 2)
-    print_usage();
+    print_usage ();
+  elseif (! isimage (img))
+    error("imclose: IMG must be a numeric matrix");
   endif
-  if (!ismatrix(im) || !isreal(im))
-    error("imclose: first input argument must be a real matrix");
-  endif
-  if (!ismatrix(se) || !isreal(se))
-    error("imclose: second input argument must be a real matrix");
-  endif
-  
+  se = prepare_strel ("imclose", se);
+
   ## Perform filtering
-  retval = imerode(imdilate(im, se), se);
+  closed = imerode (imdilate (img, se), se);
 
 endfunction
+
+%!shared in, out
+%! in =  [ 0   0   0   1   1   1   0   0   1   1
+%!       0   1   0   1   1   1   0   0   0   1
+%!       1   1   1   1   1   0   0   0   0   0
+%!       0   1   1   1   1   0   0   0   0   0
+%!       0   0   0   1   0   0   0   0   1   0
+%!       0   0   0   0   0   0   0   1   1   1
+%!       0   0   0   0   1   0   1   0   1   0
+%!       0   0   0   1   1   1   1   1   0   0
+%!       0   0   0   0   1   1   1   0   0   0
+%!       0   0   0   1   1   1   0   0   0   0];
+%!
+%! out = [ 1   1   1   1   1   1   1   1   1   1
+%!       1   1   1   1   1   1   0   0   0   1
+%!       1   1   1   1   1   0   0   0   0   1
+%!       1   1   1   1   1   0   0   0   0   1
+%!       0   0   0   1   1   0   0   0   1   1
+%!       0   0   0   1   1   1   1   1   1   1
+%!       0   0   0   1   1   1   1   1   1   1
+%!       0   0   0   1   1   1   1   1   0   0
+%!       0   0   0   1   1   1   1   0   0   0
+%!       0   0   0   1   1   1   1   0   0   0];
+%!assert (imclose (logical (in), ones (3)), logical (out));
+%!
+%! out = [99   99   16   16   16   73   74   64   64   64
+%!      98   88   16   16   16   73   71   64   64   64
+%!      93   88   88   61   61   61   68   70   70   70
+%!      93   88   88   61   61   61   68   71   71   71
+%!      93   93   88   61   61   61   68   75   66   66
+%!      79   79   82   90   90   49   49   49   49   66
+%!      79   79   82   91   91   48   46   46   46   66
+%!      79   79   82   95   97   48   46   46   46   72
+%!      18   18   94   96   84   48   46   46   46   59
+%!      18   18  100   96   84   50   50   50   50   59];
+%!assert (imclose (magic (10), ones (3)), out);
+%!assert (imclose (uint8 (magic (10)), strel ("square", 3)), uint8 (out));
+%!
+%! ## using a se that will be decomposed in 2 pieces
+%! out =[ 99   99   88   74   74   74   74   70   70   70
+%!        98   93   88   74   74   74   74   70   70   70
+%!        93   93   88   74   74   74   74   70   70   70
+%!        93   93   88   74   74   74   74   71   71   71
+%!        93   93   88   75   75   75   75   75   75   75
+%!        93   93   90   90   90   72   72   72   72   72
+%!        93   93   91   91   91   72   72   72   72   72
+%!        93   93   93   95   97   72   72   72   72   72
+%!        94   94   94   96   97   72   72   72   72   72
+%!       100  100  100   97   97   72   72   72   72   72];
+%!assert (imclose (magic (10), ones(5)), out);
+%!
+%! ## using a weird non-symmetric and even-size se
+%! out =[ 92   99   16   16   16   70   74   58   58   58
+%!        98   88   60   73   16   73   69   70   64   58
+%!        88   81   88   60   60   60   69   69   70   70
+%!        87   87   61   68   61   60   68   69   71   69
+%!        86   93   87   61   61   61   68   75   68   69
+%!        23   82   89   89   90   45   68   45   68   66
+%!        23   23   82   89   91   48   45   45   45   66
+%!        79   23   82   95   97   46   48   46   45   72
+%!        18   79   94   96   78   50   46   46   46   59
+%!        18   18  100   94   94   78   50   50   46   59];
+%!assert (imclose (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out);
--- a/inst/imopen.m
+++ b/inst/imopen.m
@@ -14,31 +14,101 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{B} = imopen (@var{A}, @var{se})
-## Perform morphological opening on a given image.
-## The image @var{A} must be a grayscale or binary image, and @var{se} must be a
-## structuring element.
+## @deftypefn {Function File} {} imopen (@var{img}, @var{SE})
+## Perform morphological opening.
 ##
-## The opening corresponds to an erosion followed by a dilation of the image, i.e.
+## The matrix @var{img} must be numeric while @var{SE} can be a:
+## @itemize @bullet
+## @item
+## strel object;
+## @item
+## array of strel objects as returned by `@strel/getsequence';
+## @item
+## matrix of 0's and 1's.
+## @end itemize
+##
+## The opening corresponds to an erosion followed by a dilation of @var{img},
+## using the same @var{SE}, i.e., it is equivalent to:
 ## @example
-## B = imdilate(imerode(A, se), se);
+## imdilate (imerode (img, se), se);
 ## @end example
+##
 ## @seealso{imdilate, imerode, imclose}
 ## @end deftypefn
 
-function retval = imopen(im, se)
-  ## Checkinput
+function opened = imopen (img, se)
+
   if (nargin != 2)
-    print_usage();
+    print_usage ();
+  elseif (! isimage (img))
+    error("imopen: IMG must be a numeric matrix");
   endif
-  if (!ismatrix(im) || !isreal(im))
-    error("imopen: first input argument must be a real matrix");
-  endif
-  if (!ismatrix(se) || !isreal(se))
-    error("imopen: second input argument must be a real matrix");
-  endif
-  
+  se = prepare_strel ("imopen", se);
+
   ## Perform filtering
-  retval = imdilate(imerode(im, se), se);
+  opened = imdilate (imerode (img, se), se);
 
 endfunction
+
+%!shared in, out
+%! in =  [ 0   0   0   1   1   1   0   0   1   1
+%!         0   1   0   1   1   1   0   0   0   1
+%!         1   1   1   1   1   0   0   0   0   0
+%!         0   1   1   1   1   0   0   0   0   0
+%!         0   0   0   1   0   0   0   0   1   0
+%!         0   0   0   0   0   0   0   1   1   1
+%!         0   0   0   0   1   0   1   0   1   0
+%!         0   0   0   1   1   1   1   1   0   0
+%!         0   0   0   0   1   1   1   0   0   0
+%!         0   0   0   1   1   1   0   0   0   0];
+%!
+%! out = [ 0   0   0   1   1   1   0   0   0   0
+%!         0   0   0   1   1   1   0   0   0   0
+%!         0   0   0   0   0   0   0   0   0   0
+%!         0   0   0   0   0   0   0   0   0   0
+%!         0   0   0   0   0   0   0   0   0   0
+%!         0   0   0   0   0   0   0   0   0   0
+%!         0   0   0   0   0   0   0   0   0   0
+%!         0   0   0   0   0   0   0   0   0   0
+%!         0   0   0   0   0   0   0   0   0   0
+%!         0   0   0   0   0   0   0   0   0   0];
+%!assert (imopen (logical (in), ones (3)), logical (out));
+%!
+%! out = [80   80    1    8   15   51   51   51   51   40
+%!        80   80    7    8   15   54   55   55   55   40
+%!         4    7    7    8   15   54   55   55   55   40
+%!        17   17   17    7    3   54   55   55   55   28
+%!        17   17   17    2    9   54   54   54   52   33
+%!        17   17   17   29   29   29   29   26   33   33
+%!         5    5   13   29   29   29   30   32   39   39
+%!         6    6   13   29   29   29   30   32   39   39
+%!        10   12   77   77   77   35   35   35   39   39
+%!        10   12   77   77   77   35   35   35   27   27];
+%!assert (imopen (magic (10), ones (3)), out);
+%!assert (imopen (uint8 (magic (10)), strel ("square", 3)), uint8 (out));
+%!
+%! ## using a se that will be decomposed in 2 pieces
+%! out =[ 1    1    1    8   15   40   40   40   40   40
+%!        4    4    4    8   15   40   40   40   40   40
+%!        4    4    4    8   15   40   40   40   40   40
+%!        5    5    5    3    3   28   28   28   28   28
+%!        5    5    5    2    9   28   28   28   28   28
+%!        5    5   13   26   26   26   26   26   26   26
+%!        5    5   13   29   29   29   29   29   27   27
+%!        6    6   13   29   29   29   29   29   27   27
+%!        6    6   13   29   29   29   29   29   27   27
+%!        6    6   13   29   29   29   29   29   27   27];
+%!assert (imopen (magic (10), ones(5)), out);
+%!
+%! ## using a weird non-symmetric and even-size se
+%! out =[ 7    7    1    8   15   55   51   51   41   40
+%!        7    7    7    8   16   55   55   55   51   41
+%!        4    9    7    7   16   54   55   54   55   47
+%!       25   25    9    9    3   52   54   52   54   28
+%!       25   24   25    2    9   33   52   34   52   34
+%!       17   24   29   31   29   30   33   26   33   34
+%!       17    5   29   31   31   31   30   32   39   33
+%!       10    6   13   35   35   29   31   32   45   39
+%!       10   12   77   36   36   35   35   31   45   45
+%!       11   12   77   77   77   36   36   35   27   45];
+%!assert (imopen (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out);
--- a/inst/imtophat.m
+++ b/inst/imtophat.m
@@ -15,52 +15,162 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{B} = imtophat (@var{A}, @var{se})
+## @deftypefn {Function File} {} imtophat (@var{img}, @var{SE})
 ## Perform morphological top hat filtering.
 ##
-## The image @var{A} must be a grayscale or binary image, and @var{se} must be a
-## structuring element. Both must have the same class, e.g., if @var{A} is a
-## logical matrix, @var{se} must also be logical.
+## The matrix @var{img} must be numeric while @var{SE} can be a:
+## @itemize @bullet
+## @item
+## strel object;
+## @item
+## array of strel objects as returned by `@strel/getsequence';
+## @item
+## matrix of 0's and 1's.
+## @end itemize
 ##
-## A 'black', or 'closing', top-hat transform is also known as bottom-hat
-## transform and so that is the same @code{imbothat}.
+## A top hat transform corresponds to the difference between @var{img},
+## and the opening of @var{img}, i.e., it is equivalent to:
+## @example
+## img - imopen (img, se);
+## @end example
+##
+## Use @code{imbothat} to perform a 'black' or 'closing', top-hat transform
+## (is is also known as bottom-hat transform).
 ##
 ## @seealso{imerode, imdilate, imopen, imclose, imbothat, mmgradm}
 ## @end deftypefn
 
-function retval = imtophat (im, se)
+function white = imtophat (img, se)
 
-  ## Checkinput
   if (nargin != 2)
     print_usage ();
+  elseif (! isimage (img))
+    error("imtophat: IMG must be a numeric matrix");
   endif
-  if (!ismatrix(im) || !isreal(im))
-    error("imtophat: first input argument must be a real matrix");
-  elseif (!ismatrix(se) || !isreal(se))
-    error("imtophat: second input argument must be a real matrix");
-  elseif ( !strcmp(class(im), class(se)) )
-    error("imtophat: image and structuring element must have the same class");
-  endif
+  se = prepare_strel ("imtophat", se);
 
   ## Perform filtering
   ## Note that in case that the transform is to applied to a logical image,
   ## subtraction must be handled in a different way (x & !y) instead of (x - y)
   ## or it will return a double precision matrix
-  if (islogical(im))
-    retval = im & !imopen(im,se);
+  if (islogical (img))
+    white = img & ! imopen (img, se);
   else
-    retval = im - imopen(im, se);
+    white = img - imopen (img, se);
   endif
 
 endfunction
 
-%!test
-%! I = [1 1 1; 1 1 1; 1 1 1;];
-%! se = [1 1; 0 1;];
-%! ## class of input should be the same as the output
-%! result = imtophat(logical(I), logical(se));
-%! expected = 0.5 < [0 0 1; 0 0 1; 1 1 1];
-%! assert(expected, result);
-%! result = imtophat((I), (se));
-%! expected = [0 0 1; 0 0 1; 1 1 1];
-%! assert(expected, result);
+%!assert (imtophat (ones (3), [1 1; 0 1]), zeros (3));
+%!assert (imtophat (true (3), [1 1; 0 1]), false (3));
+
+%!shared in, out, se
+%! in =  [ 0   0   0   1   1   1   0   0   1   1
+%!     0   1   0   1   1   1   0   0   0   1
+%!     1   1   1   1   1   0   0   0   0   0
+%!     0   1   1   1   1   0   0   0   0   0
+%!     0   0   0   1   0   0   0   0   1   0
+%!     0   0   0   0   0   0   0   1   1   1
+%!     0   0   0   0   1   0   1   0   1   0
+%!     0   0   0   1   1   1   1   1   0   0
+%!     0   0   0   0   1   1   1   0   0   0
+%!     0   0   0   1   1   1   0   0   0   0];
+%!
+%! out = [ 0   0   0   0   0   0   0   0   1   1
+%!         0   1   0   0   0   0   0   0   0   1
+%!         1   1   1   1   1   0   0   0   0   0
+%!         0   1   1   1   1   0   0   0   0   0
+%!         0   0   0   1   0   0   0   0   1   0
+%!         0   0   0   0   0   0   0   1   1   1
+%!         0   0   0   0   1   0   1   0   1   0
+%!         0   0   0   1   1   1   1   1   0   0
+%!         0   0   0   0   1   1   1   0   0   0
+%!         0   0   0   1   1   1   0   0   0   0];
+%!assert (imtophat (logical (in), ones (3)), logical (out));
+%!
+%! out = [12  19   0   0   0  16  23   0   7   0
+%!        18   0   0   6   1  19   0   2   9   1
+%!         0  74  81  12   7   0   1   8  15   7
+%!        68  70   2  14   0   6   7  14  16   0
+%!        69  76   8   0   0   7  14  21   0   1
+%!         0   7  59  54  61  13  20   0   0  32
+%!        18   0  69  60  62  19   0   0   0  27
+%!        73   0   0  66  68   0   1   6   6  33
+%!         0   0  17  19   1   0   2   9   7  14
+%!         1   6  23   0   7   1   8  15   0  32];
+%!assert (imtophat (magic (10), ones (3)), out);
+%!assert (imtophat (uint8 (magic (10)), strel ("square", 3)), uint8 (out));
+%!
+%! ## using a se that will be decomposed in 2 pieces
+%! out =[91  98   0   0   0  27  34  11  18   0
+%!       94  76   3   6   1  33  15  17  24   1
+%!        0  77  84  12   7  14  16  23  30   7
+%!       80  82  14  18   0  32  34  41  43   0
+%!       81  88  20   0   0  33  40  47  24   6
+%!       12  19  63  57  64  16  23   0   7  39
+%!       18   0  69  60  62  19   1   3  12  39
+%!       73   0   0  66  68   0   2   9  18  45
+%!        4   6  81  67  49   6   8  15  19  26
+%!        5  12  87  48  55   7  14  21   0  32];
+%!assert (imtophat (magic (10), ones(5)), out);
+%!
+%! ## using a weird non-symmetric and even-size se
+%! out =[85  92   0   0   0  12  23   0  17   0
+%!       91  73   0   6   0  18   0   2  13   0
+%!        0  72  81  13   6   0   1   9  15   0
+%!       60  62  10  12   0   8   8  17  17   0
+%!       61  69   0   0   0  28  16  41   0   0
+%!        0   0  47  52  61  12  16   0   0  31
+%!        6   0  53  58  60  17   0   0   0  33
+%!       69   0   0  60  62   0   0   6   0  33
+%!        0   0  17  60  42   0   2  13   1   8
+%!        0   6  23   0   7   0   7  15   0  14];
+%!assert (imtophat (magic (10), [1 0 0 0; 1 1 1 0; 0 1 0 1]), out);
+%!
+%! ## N dimensional and weird se
+%! in = reshape (magic(16), [4 8 4 2]);
+%! se = ones (3, 3, 3);
+%! se(:,:,1) = [1 0 1; 0 1 1; 0 0 0];
+%! se(:,:,3) = [1 0 1; 0 1 1; 0 0 1];
+%! out = zeros (size (in));
+%! out(:,:,1,1) = [
+%!   239  146   82   18    0   19   83  133
+%!     0   35   99  163  219  128   64    0
+%!     0   46  128  195  187  123   59    0
+%!   157   93   47    0   14   78  142  211];
+%! out(:,:,2,1) = [
+%!     0   21   85  149  233  146   64    0
+%!   205  128   64    0    0   41   87  151
+%!   171  107   57    0    0   64  121  185
+%!     0   64  142  213  169  105   41    0];
+%! out(:,:,3,1) = [
+%!   231  146   78   14    0   27   77  137
+%!     0   43  107  167  211  128   64    0
+%!     0   46  128  199  179  119   51    0
+%!   149   85   39    0   18   78  142  219];
+%! out(:,:,4,1) = [
+%!     0   29   93  157  225  128   64    0
+%!   197  128   64    0    0   31   95  159
+%!   163   99   53    0    0   61  125  189
+%!     0   64  146  221  161   97   33    0];
+%! out(:,:,1,2) = [
+%!   223  146   82   18    0   35   99  149
+%!     0   48  115  179  203  128   64    0
+%!     0   46  128  211  171  107   43    0
+%!   141   77   31    0   14   78  142  227];
+%! out(:,:,2,2) = [
+%!     0   37  101  165  217  146   64    0
+%!   189  125   64    0    0   57  103  167
+%!   155   91   41    0    0   64  128  201
+%!     0   64  142  229  153   89   25    0];
+%! out(:,:,3,2) = [
+%!   215  146   78   14    0   43   93  153
+%!     0   48  123  183  195  128   64    0
+%!     0   46  128  215  163  103   35    0
+%!   133   69   23    0   18   78  142  235];
+%! out(:,:,4,2) = [
+%!     0   45  109  173  209  128   64    0
+%!   181  117   64    0    0   47  111  175
+%!   147   83   37    0    0   64  128  205
+%!     0   64  146  237  145   81   17    0];
+%!assert (imtophat (in, se), out);
--- a/inst/mmgradm.m
+++ b/inst/mmgradm.m
@@ -1,4 +1,4 @@
-## Copyright (C) 2010 Carnë Draug <carandraug+dev@gmail.com>
+## Copyright (C) 2010, 2013 Carnë Draug <carandraug@octave.org>
 ##
 ## 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,37 +14,65 @@
 ## this program; if not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
-## @deftypefn {Function File} @var{grad} = mmgradm(@var{A}, @var{se})
-## @deftypefnx {Function File} @var{grad} = mmgradm(@var{A}, @var{se_dil}, @var{se_ero})
-## Calculates the morphological gradient @var{grad} of a given image @var{A}.
+## @deftypefn  {Function File} {} mmgradm (@var{img})
+## @deftypefnx {Function File} {} mmgradm (@var{img}, @var{se_dil})
+## @deftypefnx {Function File} {} mmgradm (@var{img}, @var{se_dil}, @var{se_ero})
+## Perform morphological gradient.
+##
+## The matrix @var{img} must be numeric whose gradients is calculated, while
+## @var{se_dil} and @var{se_ero} are the structuring elements for the dilation
+## and erosion respectively.  They can be a:
+## @itemize @bullet
+## @item
+## strel object;
+## @item
+## array of strel objects as returned by `@strel/getsequence';
+## @item
+## matrix of 0's and 1's.
+## @end itemize
 ##
-## In the first form, the same structuring element @var{se} is used for dilation
-## and erosion. In the second form, @var{se_dil} and @var{se_ero} are the
-## corresponding structuring elements used for dilation and erosion
+## The @var{se_dil} and @var{se_ero} default to the elementary cross, i.e.:
+## @example
+## [ 0 1 0
+##   1 1 1
+##   0 1 0];
+## @end example
 ##
-## The image @var{A} must be a grayscale or a binary image.
+## The basic morphological gradient corresponds to a matrix erosion
+## subtracted to its dilation, which is equivalent to:
+## @example
+## imdilate (img, se_dil) - imerode (img, se_ero)
+## @end example
 ##
-## The morphological gradient of a image corresponds to its erosion subtracted
-## to its dilation.
+## To perform the half-gradients by erosion or dilation, or the internal or
+## external gradients, simply pass an empty matrix as structuring element:
+## @example
+## mmgradm (img, [], se_ero) # half-gradient by erosion or internal gradient
+## mmgradm (img, se_dil, []) # half-gradient by dilation or external gradient
+## @end example
 ##
 ## @seealso{imerode, imdilate, imopen, imclose, imtophat, imbothat}
 ## @end deftypefn
 
-function grad = mmgradm (im, se_dil, se_ero)
+function grad = mmgradm (img, se_dil = strel ("diamond", 1), se_ero = strel ("diamond", 1))
+
+  ## This function does not exist in Matlab. It is meant to be compatible
+  ## with the mmgradm function from the SDC morphology toolbox
 
-  ## sanity checks
-  if (nargin == 1)
-    error ("Structuring element must be specified");
-  elseif (nargin == 2)              # if only one SE is specified, use it for both erosion and dilation
-    se_ero  = se_dil;
-  elseif (nargin == 3)
-    # all is good
+  if (nargin < 1 || nargin > 3)
+    print_usage ();
+  elseif (! isimage (img))
+    error("imtophat: IMG must be a numeric matrix");
+  endif
+  se_dil = prepare_strel ("mmgradm", se_dil);
+  se_ero = prepare_strel ("mmgradm", se_ero);
+
+  dilated   = imdilate  (img, se_dil);
+  eroded    = imerode   (img, se_ero);
+
+  if (islogical (img))
+    grad  = dilated & ! eroded;
   else
-    print_usage;
+    grad  = dilated - eroded;
   endif
-
-  dilated   = imdilate  (im, se_dil);
-  eroded    = imerode   (im, se_ero);
-
-  grad      = dilated - eroded;
 endfunction
new file mode 100644
--- /dev/null
+++ b/inst/private/prepare_strel.m
@@ -0,0 +1,33 @@
+## Copyright (C) 2013 Carnë Draug <carandraug@octave.org>
+##
+## 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 function will be common to imopen, imclose, imbothat, imtophat,
+## and any other function that can take a strel object or a matrix of 0
+## and 1. It checks the input and gets a strel object in the later case.
+
+function se = prepare_strel (func, se)
+
+  ## We could do a lot more of input checking but it'd be repeated again
+  ## in imerode and imdilate. We just do the minimum for strel as well.
+  ## Since imerode and imdilate will create a strel object out of the SE
+  ## we pass them, we can create it now ourselves.
+  if (! strcmpi (class (se), "strel"))
+    if (! ismatrix (se) || any (se(:) == 1 & se(:) == 0))
+      error ("%s: SE must be a strel object or matrix of 0 and 1", func);
+    endif
+    se = strel ("arbitrary", se);
+  endif
+
+endfunction
--- a/src/imerode.cc
+++ b/src/imerode.cc
@@ -368,7 +368,7 @@
 -*- texinfo -*-\n\
 @deftypefn  {Loadable Function} {} imerode (@var{im}, @var{SE})\n\
 @deftypefnx {Loadable Function} {} imerode (@var{im}, @var{SE}, @var{shape})\n\
-Perform morphological erosion on image.\n\
+Perform morphological erosion.\n\
 \n\
 The image @var{im} must be a numeric matrix with any number of dimensions.\n\
 The erosion is performed with the structuring element @var{se} which can\n\
@@ -666,7 +666,7 @@
 -*- texinfo -*-\n\
 @deftypefn  {Loadable Function} {} imdilate (@var{im}, @var{SE})\n\
 @deftypefnx {Loadable Function} {} imdilate (@var{im}, @var{SE}, @var{shape})\n\
-Perform morphological dilation on image.\n\
+Perform morphological dilation.\n\
 \n\
 The image @var{im} must be a numeric matrix with any number of dimensions.\n\
 The dilation is performed with the structuring element @var{se} which can\n\