changeset 689:ed7b091a5eb2

strel: major changes on code flow, many improvements and bugfixes. Mainly * many new tests * fix case insensitivity to shape * vectorized disk and diamond shapes * make getheight work instead of storing it on object
author carandraug
date Mon, 17 Dec 2012 21:54:50 +0000
parents 9c42e8039a1d
children 519e8cbcfe35
files inst/@strel/getheight.m inst/@strel/strel.m
diffstat 2 files changed, 143 insertions(+), 153 deletions(-) [+]
line wrap: on
line diff
--- a/inst/@strel/getheight.m
+++ b/inst/@strel/getheight.m
@@ -23,6 +23,6 @@
 
 function H = getheight (SE)
 
-  H = SE.height;
+  H = zeros (size (SE.nhood));
 
-endfunction
\ No newline at end of file
+endfunction
--- a/inst/@strel/strel.m
+++ b/inst/@strel/strel.m
@@ -1,4 +1,5 @@
 ## Copyright (C) 2012 Roberto Metere <roberto@metere.it>
+## Copyright (C) 2012 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
@@ -41,187 +42,176 @@
 ## @seealso{imdilate, imerode}
 ## @end deftypefn
 
-function SE = strel (varargin)
+function SE = strel (shape, varargin)
 
-  % Check minimum number of arguments
-  switch (nargin)
-    case 0
-      print_usage();
+  if (nargin < 1 || nargin > 4 || (! ischar (shape) && ! ismatrix (shape)))
+    print_usage;
+  endif
 
-    case 1
-      a = varargin{1}
-      if (isnumeric(varargin{1}))
-        SE = strel('arbitrary', varargin{1});
-      else
-        print_usage();
-      endif
+  if (! ischar (shape))
+    varargin(2:end+1) = varargin(:);
+    varargin(1) = shape;
+    shape = "arbitrary";
+  endif
 
-    otherwise
-      if (isnumeric(varargin{1}) && isnumeric(varargin{2}))
-        SE = strel('arbitrary', varargin{1}, varargin{2});
+  SE        = struct;
+  SE.shape  = tolower (shape);
+
+  switch (SE.shape)
+    case "arbitrary"
+      if (numel (varargin) == 1)
+        nhood = varargin{1};
       else
-        shape = varargin{1};
+        ## TODO implement nonflat arbitrary (will take 2 arguments)
+        error ("strel: an arbitrary shape only takes 1 argument");
       endif
-  endswitch
-
-  % Not yet implemented, sorry (a complete version won't have this switch)
-  switch (shape)
-    case {'octagon', 'line', 'periodicline', 'ball', 'arbitrary'}
-      error ("strel: shape '%s' not yet implemented", shape);
+      if (! isbw (nhood, "non-logical"))
+        error ("strel: NHOOD must be a matrix with only 0 and 1 values")
+      endif
+      SE.nhood = logical (nhood);
+      SE.flat  = false;
 
-    otherwise % Just for completeness
-  endswitch
-
+#    case "ball"
+      ## TODO implement ball shape
 
-  % Get shape parameters in arg1, arg2, etc...
-  switch (shape)
-    case {'diamond', 'octagon', 'pair', 'rectangle', 'square'}
-      if (nargin == 2)
-        arg1 = varargin{nargin};
+    case "diamond"
+      if (numel (varargin) == 1)
+        radius = varargin{1};
       else
-        error ("strel: shape '%s' needs 1 parameter", shape);
+        error ("strel: no RADIUS specified for diamond shape");
+      endif
+      if (! is_positive_integer (radius))
+        error ("strel: RADIUS must be a positive integer");
       endif
 
-    case {'line', 'periodicline'}
-      if (nargin == 3)
-        arg1 = varargin{nargin - 1};
-        arg2 = varargin{nargin};
-      else
-        error ("strel: shape '%s' needs 2 parameters", shape);
-      endif
+      [xx, yy]  = meshgrid (-radius:radius);
+      SE.nhood  = (abs (xx) + abs (yy)) <= radius;
+      SE.flat   = true;
 
-    case 'ball'
-      if (nargin == 4)
-        arg1 = varargin{nargin - 2};
-        arg2 = varargin{nargin - 1};
-        arg3 = varargin{nargin};
+    case "disk"
+      if (numel (varargin) == 1)
+        radius = varargin{1};
       else
-        error ("strel: shape '%s' needs 3 parameters", shape);
+        ## TODO implement second option for number of periodic lines
+        error ("strel: no RADIUS specified for disk shape");
+      endif
+      if (! is_positive_integer (radius))
+        error ("strel: RADIUS must be a positive integer");
       endif
 
-    case {'arbitrary', 'disk'}
-      if (nargin == 2)
-        arg1 = varargin{nargin};
-      elseif (nargin == 3)
-        arg1 = varargin{nargin - 1};
-        arg2 = varargin{nargin};
-      else
-        error ("strel: shape '%s' needs 2 or 3 parameters", shape);
-      endif
+      SE.nhood = fspecial ("disk", radius) > 0;
+      SE.flat  = true;
+
+#    case "line"
+      ## TODO implement line shape
+
+#    case "octagon"
+      ## TODO implement octagon shape
 
-    otherwise
-      error ("strel: unknown shape '%s'", shape);
-  endswitch
-
-
-  % Compute structure element
-  switch (shape)
-    case 'square'
-      if (isscalar(arg1) && isnumeric(arg1) && arg1 > 0 && fix (arg1) == arg1)
-        SE.height = zeros([arg1 arg1], 'double');
-        SE.nhood = true (arg1);
-        SE.flat = true;
-        SE = class (SE, "strel");
+    case "pair"
+      if (numel (varargin) == 1)
+        offset = varargin{1};
       else
-        error ("strel: square EDGE must be a positive integer");
+        error ("strel: no OFFSET specified for pair shape");
+      endif
+      if (! ismatrix (offset) || numel (offset) != 2 || ! isnumeric (offset))
+        error ("strel: OFFSET must be a 2 element vector");
+      elseif (any (fix (offset) != offset))
+        error ("strel: OFFSET values must be integers");
       endif
 
-    case 'rectangle'
-      if (!isscalar(arg1) && isvector(arg1) && isnumeric(arg1) && prod(size(arg1)) == 2 && arg1(1) > 0 && arg1(2) > 0 && fix (arg1) == arg1)
-        SE.height = zeros(arg1, 'double');
-        SE.nhood = true (arg1);
-        SE.flat = true;
-        SE = class (SE, "strel");
+      lengths  = abs (2*offset) + 1;
+      SE.nhood = false (lengths);
+      origin   = (lengths + 1)/2;
+      SE.nhood(origin(1), origin(2)) = true;
+      SE.nhood(origin(1) + offset(1), origin(2) + offset(2)) = true;
+
+      SE.flat = true;
+
+    case "periodicline"
+      ## TODO implement periodicline shape
+
+    case "rectangle"
+      if (numel (varargin) == 1)
+        dimensions = varargin{1};
       else
-        error("strel: rectangle DIMENSIONS must be a positive integer vector with two elements");
+        error ("strel: no DIMENSIONS specified for rectangle shape");
+      endif
+      if (! ismatrix (dimensions) || numel (dimensions) != 2 || ! isnumeric (dimensions))
+        error ("strel: DIMENSIONS must be a 2 element vector");
+      elseif (! is_positive_integer (dimensions(1)) || ! is_positive_integer (dimensions(2)))
+        error ("strel: DIMENSIONS values must be positive integers");
       endif
 
-    case 'diamond'
-      if (isscalar(arg1) && isnumeric(arg1) && arg1 > 0 && fix (arg1) == arg1)
-        n = int32(2*arg1 + 1);
-        c = (n + 1)/2;
+      SE.nhood = true (dimensions);
+      SE.flat  = true;
 
-        SE.height = zeros([arg1 arg1], 'double');
-        SE.nhood = false (arg1);
-        SE.flat = true;
-        SE = class (SE, "strel");
-
-        for i = 1:n
-          m = n - abs (2*(i - c));
-          for j = (c - m/2 + 1):(c + m/2 - 1)
-            SE.nhood(i, j) = 1;
-          endfor
-        endfor
+    case "square"
+      if (numel (varargin) == 1)
+        edge = varargin{1};
       else
-        error("strel: diamond RADIUS must be a positive integer");
+        error ("strel: no EDGE specified for square shape");
       endif
-
-    case 'pair'
-      if (!isscalar(arg1) && isvector(arg1) && isnumeric(arg1) && prod(size(arg1)) == 2 && arg1(1) > 0 && arg1(2) > 0 && fix (arg1) == arg1)
-        m = abs(2*arg1(1)) + 1;
-        n = abs(2*arg1(2)) + 1;
-
-        SE.height = zeros([m n], 'double');
-        SE.nhood = false ([m n]);
-        SE.flat = true;
-        SE = class (SE, "strel");
-
-        cy = (m + 1)/2;
-        cx = (n + 1)/2;
-        SE.nhood(cy, cx) = 1;
-        SE.nhood(cy + arg1(1), cx + arg1(2) ) = 1;
-      else
-        error("strel: pair OFFSET must be a positive integer vector with two elements");
+      if (! is_positive_integer (edge))
+        error ("strel: EDGE value must be positive integers");
       endif
 
-    case 'disk'
-      if (isscalar(arg1) && isnumeric(arg1) && arg1 > 0 && fix (arg1) == arg1)
-        % Default value of N
-        if (nargin <= 2)
-          arg2 = 4;
-        else
-          allowed_n = [0 4 6 8];
-          if (isscalar(arg2) && isnumeric(arg2) && sum(allowed_n(:) == arg2) == 1 && fix(arg2) == arg2)
-            warning("strel: disk N (number of periodic lines to approximate disk) is ignored");
-          else
-            error("strel: disk N (number of periodic lines to approximate disk) may assume only values: 0, 4, 6 or 8");
-          endif
-        endif
-
-        % Compute disk
-        switch (arg2)
-          % This should be only case 0
-          case {0, 4, 6, 8}
-            radius = arg1;
-            n = 2*radius + 1;
+      SE.nhood = true (edge);
+      SE.flat  = true;
 
-            SE.height = zeros([n n], 'double');
-            SE.nhood = false (n);
-            SE.flat = true;
-            SE = class (SE, "strel");
-
-            radius2 = radius^2;
-            for i = 1:n
-              for j = 1:n
-                pitagora = (i - radius - 1)^2 + (j - radius - 1)^2;
-                if (pitagora <= radius2)
-                  SE.nhood(i, j) = 1;
-                endif
-              endfor
-            endfor
-
-          otherwise
-            error("strel: bug - execution should never reach this line. Please report this bug");
-        endswitch
-      else
-        error("strel: disk RADIUS must be a positive integer");
-      endif
+    otherwise
+      error ("strel: unknown SHAPE `%s'", shape);
   endswitch
 
+  SE = class (SE, "strel");
+endfunction
+
+function retval = is_positive_integer (val)
+  retval = isscalar(val) && isnumeric(val) && val > 0 && fix (val) == val;
 endfunction
 
-%!demo
-%!assert(gethnood(strel('disk',3)))==[0,0,0,1,0,0,0;0,1,1,1,1,1,0;0,1,1,1,1,1,0;1,1,1,1,1,1,1;0,1,1,1,1,1,0;0,1,1,1,1,1,0;0,0,0,1,0,0,0]);
+%!shared shape
+%! shape = [0 0 0 1];
+%!assert (getnhood (strel (shape)), logical (shape));
+%!assert (getnhood (strel ("arbitrary", shape)), logical (shape));
+%! shape = [0 0 0 1 0 0 0
+%!          0 0 1 1 1 0 0
+%!          0 1 1 1 1 1 0
+%!          1 1 1 1 1 1 1
+%!          0 1 1 1 1 1 0
+%!          0 0 1 1 1 0 0
+%!          0 0 0 1 0 0 0];
+%!assert (getnhood (strel ("diamond", 3)), logical (shape));
+%! shape = [0 0 0 1 0 0 0
+%!          0 1 1 1 1 1 0
+%!          0 1 1 1 1 1 0
+%!          1 1 1 1 1 1 1
+%!          0 1 1 1 1 1 0
+%!          0 1 1 1 1 1 0
+%!          0 0 0 1 0 0 0];
+%!assert (getnhood (strel ("disk", 3)), logical (shape));
+%! shape = [1;1;0];
+%!assert (getnhood (strel ("pair", [-1 0])), logical (shape));
+%! shape = [1 0 0 0 0 0 0
+%!          0 0 0 1 0 0 0
+%!          0 0 0 0 0 0 0];
+%!assert (getnhood (strel ("pair", [-1 -3])), logical (shape));
+%! shape = [0 0 0 0 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 0 0 0 0 1];
+%!assert (getnhood (strel ("pair", [2 3])), logical (shape));
+%!assert (getnhood (strel ("rectangle", [10 5])), true (10, 5));
+%!assert (getnhood (strel ("square", 5)), true (5));
 
+## test input validation
 %!error strel()
-%!error strel('rectangle', 2)
+%!error strel("nonmethodthing", 2)
+%!error strel("arbitrary", "stuff")
+%!error strel("diamond", -3)
+%!error strel("disk", -3)
+%!error strel("pair", [45 67 90])
+%!error strel("rectangle", 2)
+%!error strel("rectangle", [2 -5])
+%!error strel("square", [34 1-2])