// bwlabeln.cc --- // Copyright © 2011 Jordi Gutiérrez Hermoso // 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 . #include #include #include "union-find.h++" dim_vector conn26_dim(3,3,3); boolNDArray conn26(conn26_dim,1); typedef Array 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) { 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 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 populate_neighbours(const boolNDArray& conn_mask) { set 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} address@hidden, @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 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 = args(1).bool_array_value(); set 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 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