changeset 496:fa4f6291d232

Add preliminary implementation of bwlabeln
author jordigh
date Fri, 18 Nov 2011 07:08:09 +0000
parents 21a408d3da04
children b3f47893f43e
files src/bwlabel.cc src/bwlabeln.cc
diffstat 2 files changed, 322 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/src/bwlabel.cc
+++ b/src/bwlabel.cc
@@ -5,7 +5,7 @@
     copyright 2002 Jeffrey E. Boyd
 
     - uses 4, 6, or 8 connectedness
-    - See BKP Horn, Robot Vision, MIT Press, 1986, p 66 - 71 
+    - See BKP Horn, Robot Vision, MIT Press, 1986, p 66 - 71
 
     labeling scheme
 
@@ -16,15 +16,15 @@
         +-+-+-+
         | | | |
         +-+-+-+
-                
+
     A is the center pixel of a neighborhood.  In the 3 versions of
     connectedness:
-    
+
     4:  A connects to B and C
     6:  A connects to B, C, and D
     8:  A connects to B, C, D, and E
-    
-    
+
+
 --------------------------------------------------------------------- */
 
 
@@ -50,13 +50,13 @@
       print_usage ();
       return true;
     }
-    
+
   if (!args (0).is_bool_matrix ())
     {
       error ("bwlabel: first input argument must be a 'logical' matrix");
       return true;
     }
-    
+
   if (nargin == 2)
     {
       if (!args (1).is_real_scalar ())
@@ -71,8 +71,8 @@
           return true;
         }
     }
-    
-  return false;    
+
+  return false;
 }
 
 DEFUN_DLD(bwlabel, args, , "\
@@ -96,30 +96,30 @@
   octave_value_list rval;
   if (any_bad_argument (args))
     return rval;
-    
+
   // input arguments
   boolMatrix BW = args (0).bool_matrix_value ();     // the input binary image
   int nr = BW.rows ();
   int nc = BW.columns ();
-  
+
   // n-hood connectivity
   int n;
   if (args.length () < 2)
     n = 8;
   else
     n = args (1).int_value ();
-    
+
   // results
   Matrix L (nr, nc);     // the label image
   int nobj;              // number of objects found in image
-    
+
   // other variables
   int ntable;            // number of elements in the component table/tree
-    
+
   OCTAVE_LOCAL_BUFFER (int, lset, nr * nc);   // label table/tree
   ntable = 0;
   lset [0] = 0;
-    
+
   for (int r = 0; r < nr; r++)
     {
       for (int c = 0; c < nc; c++)
@@ -132,22 +132,22 @@
                 B = 0;
               else
                 B = find (lset, (int)L.elem (r, c-1));
-              
+
               if (r == 0)
                 C = 0;
               else
                 C = find (lset, (int)L.elem (r-1, c));
-              
+
               if (r == 0 || c == 0)
                 D = 0;
               else
                 D = find (lset, (int)L.elem (r-1, c-1));
-              
+
               if (r == 0 || c == nc - 1)
                 E = 0;
               else
                 E = find (lset, (int)L.elem(r-1, c+1));
-                    
+
               if (n == 4)
                 {
                   // apply 4 connectedness
@@ -236,9 +236,9 @@
                         {
                           tlabel = E;
                         }
-                        
+
                       L.elem (r, c) = tlabel;
-                      
+
                       if (B && B != tlabel)
                         lset [B] = tlabel;
                       if (C && C != tlabel)
@@ -262,7 +262,7 @@
             }
         }
     }
-    
+
   // consolidate component table
   for (int i = 0; i <= ntable; i++)
     lset [i] = find (lset, i);
@@ -271,7 +271,7 @@
   for (int r = 0; r < nr; r++)
     for (int c = 0; c < nc; c++)
       L.elem (r, c) = lset [(int)L.elem (r, c)];
-    
+
   // count up the objects in the image
   for (int i = 0; i <= ntable; i++)
     lset [i] = 0;
@@ -300,5 +300,3 @@
 /*
 %!assert(bwlabel(logical([0 1 0; 0 0 0; 1 0 1])),[0 1 0; 0 0 0; 2 0 3]);
 */
-
-
new file mode 100644
--- /dev/null
+++ b/src/bwlabeln.cc
@@ -0,0 +1,299 @@
+// bwlabeln.cc ---
+
+// Copyright  © 2011 Jordi Gutiérrez Hermoso <jordigh@octave.org>
+
+// Author: Jordi Gutiérrez Hermoso
+
+// 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/>.
+
+#include <oct.h>
+#include <set>
+#include "union-find.h++"
+
+
+dim_vector conn26_dim(3,3,3);
+boolNDArray conn26(conn26_dim,1);
+typedef Array<octave_idx_type> coord;
+
+bool operator== (const coord& a, const coord& b)
+{
+  if (a.nelem () != b.nelem())
+    return false;
+  for (octave_idx_type i = 0; i < a.nelem (); i++)
+    if (  a(i) !=  b(i) )
+      return false;
+
+  return true;
+}
+
+bool in_range (const coord& a, const dim_vector& size_vec)
+{
+  for(octave_idx_type i = 0; i < a.nelem (); i++)
+    if (a(i) < 0 or a(i) >= size_vec(i))
+      return false;
+  return true;
+}
+
+//Lexicographic order for coords
+bool operator< (const coord& a, const coord& b)
+{
+  octave_idx_type na = a.nelem (), nb = b.nelem ();
+  if (na < nb)
+    return true;
+  if (na > nb)
+    return false;
+  octave_idx_type i = 0;
+  while (a(i) == b(i) and i < na)
+    {
+      i++;
+    }
+
+  if (i == na          //They're equal, but this is strict order
+      or a(i) > b(i) )
+    return false;
+
+  return true;
+}
+
+struct coord_hash
+{
+  inline size_t operator() (const coord& c) const
+  {
+    size_t seed = 0;
+    for(octave_idx_type i = 0; i < c.nelem (); i++)
+    {
+      //Boost's hash
+      seed ^= c(i) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+    }
+    return seed;
+  }
+
+  inline size_t operator()(size_t s) const
+  {
+    return s;
+  }
+};
+
+namespace {
+
+// A few basic utility functions
+//{
+inline
+coord
+to_coord(const dim_vector& dv,
+         octave_idx_type k)
+{
+  octave_idx_type n = dv.length ();
+  coord retval ( dim_vector (n, 1));
+  for (octave_idx_type j = 0; j < n; j++)
+    {
+      retval(j) = k % dv(j);
+      k /= dv(j);
+    }
+  return retval;
+}
+
+inline
+coord
+operator+ (const coord& a, const coord& b)
+{
+  octave_idx_type na = a.nelem ();
+  coord retval( dim_vector(na,1) );
+  for (octave_idx_type i = 0; i < na; i++)
+    {
+      retval(i) = a(i) + b(i);
+    }
+  return retval;
+}
+
+
+inline
+coord
+operator- (const coord& a, const coord& b)
+{
+  octave_idx_type na = a.nelem ();
+  coord retval( dim_vector(na,1) );
+  for (octave_idx_type i = 0; i < na; i++)
+    {
+      retval(i) = a(i) - b(i);
+    }
+  return retval;
+}
+
+
+inline
+coord
+operator- (const coord& a)
+{
+  octave_idx_type na = a.nelem ();
+  coord retval( dim_vector(na,1) );
+  for (octave_idx_type i = 0; i < na; i++)
+    {
+      retval(i) = -a(i);
+    }
+  return retval;
+}
+//}
+
+bool any_bad_argument (const octave_value_list& args)
+{
+  return false;
+
+  const int nargin = args.length ();
+  if (nargin < 1 || nargin > 2)
+    {
+      print_usage ();
+      return true;
+    }
+
+  if (!args (0).is_bool_type ())
+    {
+      error ("bwlabeln: first input argument must be a 'logical' ND-array");
+      return true;
+    }
+
+  if (nargin == 2)
+    {
+      if (!args (1).is_real_scalar () && ! args(1).is_bool_type())
+        {
+          error ("bwlabeln: second input argument must be a real scalar "
+                 "or a 'logical' connectivity array");
+            return true;
+        }
+    }
+
+  return false;
+}
+
+//debug
+#include <iostream>
+using namespace std;
+
+ostream&
+operator<< (ostream& os, const coord& aidx)
+{
+  for (octave_idx_type i = 0; i < aidx.nelem (); i++)
+    os << aidx(i) + 1 << " ";
+  return os;
+}
+
+set<coord>
+populate_neighbours(const boolNDArray& conn_mask)
+{
+  set<coord> neighbours;
+
+  dim_vector conn_size = conn_mask.dims ();
+  coord centre(dim_vector(conn_size.length (), 1), 1);
+  coord zero(dim_vector(conn_size.length (), 1), 0);
+  for (octave_idx_type idx = 0; idx < conn_mask.nelem (); idx++)
+    {
+      if (conn_mask(idx))
+        {
+          coord aidx = to_coord(conn_size, idx) - centre;
+          //The zero coordinates are the centre, and the negative ones
+          //are the ones reflected about the centre, and we don't need
+          //to consider those.
+          if( aidx == zero or neighbours.find(-aidx) != neighbours.end() )
+            continue;
+          neighbours.insert (aidx);
+         }
+    }
+  return neighbours;
+}
+
+DEFUN_DLD(bwlabeln, args, , "\
+-*- texinfo -*-\n\
+@deftypefn {Function File} {[@var{l}, @var{num}] =} bwlabeln(@var{bw}, @var{n})\n\
+\n\
+Labels foreground objects in the n-dimensional binary image @var{bw}.\n\
+The output @var{l} is an Nd-array where 0 indicates a background\n\
+pixel, 1 indicates that the pixel belong to object number 1, 2 that\n\
+the pixel belong to object number 2, etc. The total number of objects\n\
+is @var{num}.\n\
+\n\
+@end deftypefn\n\
+")
+{
+  octave_value_list rval;
+
+  union_find<coord, coord_hash> u_f;
+
+  if (any_bad_argument (args))
+    return rval;
+
+  boolNDArray BW = args (0).bool_array_value ();
+
+  dim_vector size_vec = BW.dims ();
+
+  int nargin = args.length ();
+
+  //Connectivity mask
+  boolNDArray conn_mask;
+  if( nargin == 1)
+    conn_mask = conn26; //Implement this properly later
+  else
+    conn_mask = conn26;
+
+  set<coord> neighbours = populate_neighbours(conn_mask);
+
+  for (octave_idx_type idx = 0; idx < BW.nelem (); idx++)
+    {
+      if (BW(idx))
+        {
+          coord aidx = to_coord (size_vec, idx);
+
+          //Insert this one into its group
+          u_f.find_id(aidx);
+
+          //Replace this with C++0x range-based for loop later
+          //(implemented in gcc 4.6)
+          for (auto nbr = neighbours.begin (); nbr!=  neighbours.end (); nbr++)
+            {
+              coord n = *nbr + aidx;
+              if (in_range (n,size_vec) and BW(n) )
+                u_f.unite (n,aidx);
+            }
+        }
+    }
+
+  NDArray L (size_vec, 0);
+  unordered_map<octave_idx_type, octave_idx_type> ids_to_label;
+  octave_idx_type next_label = 1;
+
+  auto idxs  = u_f.get_objects ();
+
+  //C++0x foreach later
+  for (auto idx = idxs.begin (); idx != idxs.end (); idx++)
+    {
+      octave_idx_type label;
+      octave_idx_type id = u_f.find_id (idx->first);
+      auto try_label = ids_to_label.find (id);
+      if( try_label == ids_to_label.end ())
+        {
+          label = next_label++;
+          ids_to_label[id] = label;
+        }
+      else
+        {
+          label = try_label -> second;
+        }
+
+      L(idx->first) = label;
+    }
+
+  rval(0) = L;
+  rval(1) = ids_to_label.size ();
+  return rval;
+}
+}//anonymous namespace