changeset 892:a2140b980079

iptcheckconn: implement in C++ as static method for connectivity. * iptcheckconn.m: file removed; help text and tests reused for C++. * conndef.cc: implement two new connectivity::validate() methods and the iptcheckconn function for Octave as caller to those methods. * conndef.h: define the connectivity::validate() static methods. * COPYING
author Carnë Draug <carandraug@octave.org>
date Wed, 01 Oct 2014 20:22:37 +0100
parents a65390dffc5a
children f34897bc944f
files COPYING inst/iptcheckconn.m src/conndef.cc src/conndef.h
diffstat 4 files changed, 132 insertions(+), 86 deletions(-) [+]
line wrap: on
line diff
--- a/COPYING
+++ b/COPYING
@@ -97,7 +97,6 @@
 inst/imtransform.m                      GPLv3+
 inst/imtranslate.m                      GPLv3+
 inst/intlut.m                           GPLv3+
-inst/iptcheckconn.m                     GPLv3+
 inst/iptcheckmap.m                      GPLv3+
 inst/iptchecknargin.m                   GPLv3+
 inst/iptcheckstrs.m                     GPLv3+
deleted file mode 100644
--- a/inst/iptcheckconn.m
+++ /dev/null
@@ -1,81 +0,0 @@
-## Copyright (C) 2011 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} {} iptcheckconn (@var{con}, @var{func_name}, @var{var_name}, @var{pos})
-## Check if argument is valid connectivity.
-##
-## If @var{con} is not a valid connectivity argument, gives a properly formatted
-## error message. @var{func_name} is the name of the function to be used on the
-## error message, @var{var_name} the name of the argument being checked (for the
-## error message), and @var{pos} the position of the argument in the input.
-##
-## A valid connectivity argument must be either double or logical. It must also
-## be either a scalar from set [1 4 6 8 18 26], or a symmetric matrix with all
-## dimensions of size 3, with only 0 or 1 as values, and 1 at its center.
-##
-## @end deftypefn
-
-function iptcheckconn (con, func_name, var_name, pos)
-
-  ## thanks to Oldak in ##matlab for checking the validity of connectivities
-  ## with more than 2D and the error messages
-
-  if (nargin != 4)
-    print_usage;
-  elseif (!ischar (func_name))
-    error ("Argument func_name must be a string");
-  elseif (!ischar (var_name))
-    error ("Argument var_name must be a string");
-  elseif (!isnumeric (pos) || !isscalar (pos) || !isreal (pos) || pos <= 0 || rem (pos, 1) != 0)
-    error ("Argument pos must be a real positive integer");
-  endif
-
-  base_msg = sprintf ("Function %s expected input number %d, %s, to be a valid connectivity specifier.\n       ", ...
-                      func_name, pos, var_name);
-
-  ## error ends in \n so the back trace of the error is not show. This is on
-  ## purpose since the whole idea of this function is already to give a properly
-  ## formatted error message
-  if (! any (strcmp (class (con), {'logical', 'double'})) || ! isreal (con))
-    error ("%sConnectivity must be a real number of the logical or double class.\n", base_msg);
-  elseif (isscalar (con))
-    if (!any (con == [1 4 6 8 18 26]))
-      error ("%sIf connectivity is a scalar, must belong to the set [1 4 6 8 18 26].\n", base_msg);
-    endif
-  elseif (ismatrix (con))
-    center_index = ceil(numel(con)/2);
-    if (any (size (con) != 3))
-      error ("%sIf connectivity is a matrix, all dimensions must have size 3.\n", base_msg);
-    elseif (!all (con(:) == 1 | con(:) == 0))
-      error ("%sIf connectivity is a matrix, only 0 and 1 are valid.\n", base_msg);
-    elseif (con(center_index) != 1)
-      error ("%sIf connectivity is a matrix, central element must be 1.\n", base_msg);
-    elseif (!all (con(1:center_index-1) == con(end:-1:center_index+1)))
-      error ("%sIf connectivity is a matrix, it must be symmetric relative to its center.\n", base_msg);
-    endif
-  else
-    error ("%s\n", base_msg);
-  endif
-
-endfunction
-
-%!test ("iptcheckconn (4, 'func', 'var', 2)");                      # simple must work
-%!test ("iptcheckconn (ones(3,3,3,3), 'func', 'var', 2)");          # accept more than just 3D
-%!fail ("iptcheckconn (3, 'func', 'var', 2)");                      # does not belong to set
-%!fail ("iptcheckconn ([1 1 1; 1 0 1; 1 1 1], 'func', 'var', 2)");  # matrix center must be 1
-%!fail ("iptcheckconn ([1 2 1; 1 1 1; 1 1 1], 'func', 'var', 2)");  # matrix must be 1 and 0 only
-%!fail ("iptcheckconn ([0 1 1; 1 1 1; 1 1 1], 'func', 'var', 2)");  # matrix must be symmetric
-%!fail ("iptcheckconn (ones(3,3,3,4), 'func', 'var', 2)");          # matrix must have all sizes 3
--- a/src/conndef.cc
+++ b/src/conndef.cc
@@ -89,7 +89,7 @@
       mask = boolNDArray (size, false);
       bool* md = mask.fortran_vec ();
 
-      md += int (ceil (pow (3, ndims) /2) -1);  // move to center
+      md += int (floor (pow (3, ndims) /2));  // move to center
       md[0] = true;
       for (octave_idx_type dim = 0; dim < ndims; dim++)
         {
@@ -124,7 +124,7 @@
       if (mask(ind))
         {
           for (octave_idx_type i = 0; i < ndims; i++)
-            diff(i) = 1 - sub(i);
+            diff(i) = 1 - sub(i); // 1 is center since conn is 3x3x...x3
 
           octave_idx_type off = diff(0);
           for (octave_idx_type dim = 1; dim < ndims; dim++)
@@ -138,6 +138,44 @@
 }
 
 
+bool
+connectivity::validate (const double& conn)
+{
+  if (conn == 4 || conn == 8 || conn == 6 || conn == 18 || conn == 26
+      || conn == 1)
+    return true;
+
+  return false;
+}
+
+
+bool
+connectivity::validate (const boolNDArray& mask)
+{
+  // Must be 3x3x3x...x3
+  const dim_vector dims = mask.dims ();
+  const octave_idx_type ndims = mask.ndims ();
+  for (octave_idx_type i = 0; i < ndims; i++)
+    if (dims(i) != 3)
+      return false;
+
+  // Center must be true
+  const octave_idx_type numel = mask.numel ();
+  const octave_idx_type center = floor (numel /2);
+  if (! mask(center))
+    return false;
+
+  // Must be symmetric relative to its center
+  const bool* start = mask.fortran_vec ();
+  const bool* end   = mask.fortran_vec () + (numel -1);
+  for (octave_idx_type i = 0; i < center; i++)
+    if (start[i] != end[-i])
+      return false;
+
+  return true;
+}
+
+
 // The conndef() function is really really simple and could have easily
 // been a m file (actually it once was, check the hg log if it ever needs
 // to be recovered) but then it would be awkward to call it from oct
@@ -145,8 +183,8 @@
 
 DEFUN_DLD(conndef, args, , "\
 -*- texinfo -*-\n\
-@deftypefn  {Function File} {} conndef (@var{conn})\n\
-@deftypefnx {Function File} {} conndef (@var{ndims}, @var{type})\n\
+@deftypefn  {Loadable Function} {} conndef (@var{conn})\n\
+@deftypefnx {Loadable Function} {} conndef (@var{ndims}, @var{type})\n\
 Create connectivity array.\n\
 \n\
 Creates a matrix of for morphological operations, where elements with\n\
@@ -289,3 +327,90 @@
 %!                                0 1 0 1 1 1 0 1 0], [3 3 3]))
 
 */
+
+// PKG_ADD: autoload ("iptcheckconn", which ("conndef"));
+// PKG_DEL: autoload ("iptcheckconn", which ("conndef"), "remove");
+DEFUN_DLD(iptcheckconn, args, , "\
+-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} iptcheckconn (@var{con}, @var{func}, @var{var}, @var{pos})\n\
+Check if argument is valid connectivity.\n\
+\n\
+If @var{conn} is not a valid connectivity argument, gives a properly\n\
+formatted error message.  @var{func} is the name of the function to be\n\
+used on the error message, @var{var} the name of the argument being\n\
+checked (for the error message), and @var{pos} the position of the\n\
+argument in the input.\n\
+\n\
+A valid connectivity argument must be either double or logical.  It must\n\
+also be either a scalar from set [1 4 6 8 18 26], or a symmetric matrix\n\
+with all dimensions of size 3, with only 0 or 1 as values, and 1 at its\n\
+center.\n\
+\n\
+@seealso{conndef}\n\
+@end deftypefn")
+{
+  const octave_idx_type nargin = args.length ();
+//  const octave_value rv = octave_value ();
+
+  if (nargin != 4)
+    {
+      print_usage ();
+      return octave_value ();
+    }
+
+  const std::string func = args(1).string_value ();
+  if (error_state)
+    {
+      error ("iptcheckconn: FUNC must be a string");
+      return octave_value ();
+    }
+  const std::string var = args(2).string_value ();
+  if (error_state)
+    {
+      error ("iptcheckconn: VAR must be a string");
+      return octave_value ();
+    }
+  const octave_idx_type pos = args(3).idx_type_value (true);
+  if (error_state || pos < 1)
+    {
+      error ("iptcheckconn: POS must be a positive integer");
+      return octave_value ();
+    }
+
+  bool bad = true;
+
+  const double conn = args(0).double_value ();
+  // check is_scalar_type because of the warning Octave:array-to-scalar
+  if (! error_state && args(0).is_scalar_type () //
+      && connectivity::validate (conn))
+    bad = false;
+  else
+    {
+      const boolNDArray mask = args(0).bool_array_value ();
+      // bool_array_value converts anything other than 0 to true, which will
+      // then validate asconn array, hence any_element_not_one_or_zero
+      if (! error_state && connectivity::validate (mask)
+          && ! args(0).array_value ().any_element_not_one_or_zero ())
+        bad = false;
+    }
+
+  if (bad)
+    error ("%s: %s at pos %i is not a valid connectivity array",
+           func.c_str (), var.c_str (), pos);
+
+  return octave_value ();
+}
+
+/*
+// the complete error message should be "expected error <.> but got none",
+// but how to escape <> within the error message?
+
+%!error <expected error> fail ("iptcheckconn (4, 'func', 'var', 2)");
+%!error <expected error> fail ("iptcheckconn (ones (3, 3, 3, 3), 'func', 'var', 2)");
+
+%!error <not a valid connectivity array> iptcheckconn (3, "func", "var", 2);
+%!error <not a valid connectivity array> iptcheckconn ([1 1 1; 1 0 1; 1 1 1], "func", "var", 2);
+%!error <not a valid connectivity array> iptcheckconn ([1 2 1; 1 1 1; 1 1 1], "func", "var", 2);
+%!error <not a valid connectivity array> iptcheckconn ([0 1 1; 1 1 1; 1 1 1], "func", "var", 2);
+%!error <not a valid connectivity array> iptcheckconn (ones (3, 3, 3, 4), "func", "var", 2);
+*/
--- a/src/conndef.h
+++ b/src/conndef.h
@@ -35,6 +35,9 @@
         // For a matrix of size `size', what are the offsets for all of its
         // connected elements (will have negative and positive values).
         Array<octave_idx_type> offsets (const dim_vector& size) const;
+
+        static bool validate (const boolNDArray& mask);
+        static bool validate (const double& conn);
     };
   }
 }