contrib/mul/vil3d/algo/vil3d_find_blobs.cxx
Go to the documentation of this file.
00001 // This is mul/vil3d/algo/vil3d_find_blobs.cxx
00002 #include "vil3d_find_blobs.h"
00003 //:
00004 // \file
00005 // \brief Identify and enumerate all disjoint blobs in a binary image.
00006 // \author Ian Scott, Kevin de Souza
00007 
00008 #include <vcl_vector.h>
00009 #include <vcl_cassert.h>
00010 #include <vcl_algorithm.h>
00011 
00012 #include <vil3d/vil3d_image_view.h>
00013 
00014 namespace
00015 {
00016   //: Manages the relabelling structure.
00017   // Got the idea from http://en.wikipedia.org/wiki/Disjoint-set_data_structure Mar 2011.
00018   // although no code was copied.
00019   class disjoint_sets
00020   {
00021     typedef unsigned LABEL;
00022     typedef unsigned LEN;
00023     struct node
00024     {
00025       LABEL parent;
00026       LEN rank;
00027     };
00028     vcl_vector<node> store_;
00029   public:
00030     disjoint_sets(): store_(1u)
00031     { // renumber 0->0
00032       store_.front().parent=0;
00033       store_.front().rank=0;
00034     }
00035 
00036     //: Get the root label for label v;
00037     LABEL root(LABEL v)
00038     {
00039       node & n=store_[v];
00040       if (n.parent == v)
00041         return v;
00042       else
00043       {
00044         n.parent=root(n.parent); // relabel to speed up later searches
00045         return n.parent;
00046       }
00047     }
00048 
00049     //: Merge two sets containing labels left and right.
00050     void merge_labels(LABEL left_label, LABEL right_label)
00051     {
00052       LABEL left_root = root(left_label);
00053       LABEL right_root = root(right_label);
00054       if (left_root == right_root) return; // already merged.
00055       node& left_root_node = store_[left_root];
00056       node& right_root_node = store_[right_root];
00057       if (left_root_node.rank > right_root_node.rank) // Find the larger tree.
00058       { // add the right tree to the left
00059         right_root_node.parent = left_root_node.parent;
00060       }
00061       else
00062       { // add the left tree to the right
00063         left_root_node.parent = right_root_node.parent;
00064         if (left_root_node.rank == right_root_node.rank)
00065           right_root_node.rank++;
00066       }
00067     }
00068 
00069     //: Create a new label;
00070     LABEL new_label()
00071     {
00072       node n = {(LABEL)store_.size(), 0};
00073       store_.push_back(n);
00074       return n.parent;
00075     }
00076 
00077     LEN size()
00078     {
00079       return store_.size();
00080     }
00081   };
00082 }
00083 
00084 // Identify and enumerate all disjoint blobs in a binary image.
00085 void vil3d_find_blobs(const vil3d_image_view<bool>& src,
00086                       vil3d_find_blob_connectivity conn,
00087                       vil3d_image_view<unsigned>& dst)
00088 {
00089   unsigned ni=src.ni();
00090   unsigned nj=src.nj();
00091   unsigned nk=src.nk();
00092   dst.set_size(ni, nj, nk);
00093   dst.fill(0);
00094 
00095   disjoint_sets merge_list;
00096   vcl_vector<unsigned> neighbouring_labels;
00097 
00098   unsigned n_neighbours;
00099   switch (conn)
00100   {
00101    case vil3d_find_blob_connectivity_6_conn:
00102     n_neighbours=3;
00103     break;
00104    case vil3d_find_blob_connectivity_26_conn:
00105     n_neighbours=13;
00106     break;
00107    default:
00108     n_neighbours=0; assert(!"unknown connectivity");
00109   }
00110 
00111   // The 3-prev-(6)-neighbourhood are the first three entries.
00112   // The 13-prev-(26)-neighbourhood are those three plus the rest.
00113   int neighbourhood_ii[] = { -1, 0, 0, -1,  0, +1, -1, +1, -1,  0, +1, -1, +1};
00114   int neighbourhood_jj[] = { 0, -1, 0, -1, -1, -1,  0,  0, +1, +1, +1, -1, -1};
00115   int neighbourhood_kk[] = { 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1,  0,  0};
00116 
00117   typedef vcl_vector<unsigned>::iterator ITER;
00118 
00119   for (unsigned k=0; k<nk; ++k)
00120     for (unsigned j=0; j<nj; ++j)
00121       for (unsigned i=0; i<ni; ++i)
00122       {
00123         if (!src(i,j,k)) continue;
00124         neighbouring_labels.clear();
00125         for (unsigned l=0; l<n_neighbours; ++l)
00126         {
00127           unsigned ii = i + neighbourhood_ii[l];
00128           if (ii >= ni) continue; // rely on wraparound to find -ne overruns.
00129           unsigned jj = j + neighbourhood_jj[l];
00130           if (jj >= nj) continue;
00131           unsigned kk = k + neighbourhood_kk[l];
00132           if (kk >= nk) continue;
00133           unsigned d = dst(ii,jj,kk);
00134           if (d==0) continue;
00135           neighbouring_labels.push_back(d);
00136         }
00137         if (neighbouring_labels.empty())
00138         {
00139           unsigned new_label = merge_list.new_label();
00140           dst(i,j,k) = new_label;
00141         }
00142         else
00143         {
00144           // See how many unique labels neighbouring labels we have
00145           vcl_sort(neighbouring_labels.begin(), neighbouring_labels.end());
00146           ITER end = vcl_unique(neighbouring_labels.begin(), neighbouring_labels.end());
00147           // don't bother erasing unique suffix, just keeping the end iterator
00148           // will be enough.
00149           ITER it=neighbouring_labels.begin();
00150           unsigned label = *it++;
00151           dst(i,j,k) = label;
00152 
00153           // If we have neighbours with multiple labels.
00154           //   then record merges of the previously disjointly labelled blocks.
00155           // If there was only a single unique label, the following for loop
00156           //   will not execute.
00157           for (; it!=end; ++it)
00158             merge_list.merge_labels(*it, label);
00159         }
00160       }
00161 
00162   unsigned n_merge=merge_list.size();
00163   vcl_vector<unsigned> renumbering(n_merge, 0u);
00164   // Convert the merge lsit into a simple renumbering array,
00165   // and change to root of each disjoint set to its lowest member.
00166   // The reinstates label order to the original raster order.
00167   for (unsigned l=1; l<n_merge; ++l)
00168   {
00169     if (renumbering[l]!=0) continue;
00170     unsigned root_label = merge_list.root(l);
00171     unsigned root_label_renumber = renumbering[root_label];
00172     if (root_label_renumber==0)
00173     {
00174       renumbering[root_label]=l;
00175       renumbering[l]=l;
00176     }
00177     else
00178       renumbering[l]=renumbering[root_label];
00179   }
00180 
00181   // Now due to the renumbering, the set of labels may not compactly occupy
00182   // the number line. So renumber the renumbering array.
00183   vcl_vector<unsigned> labels(renumbering.begin(), renumbering.end());
00184   vcl_sort(labels.begin(), labels.end());
00185   labels.erase(vcl_unique(labels.begin(), labels.end()), labels.end());
00186   const unsigned dodgy = static_cast<unsigned>(-1);
00187   vcl_vector<unsigned> renum_renum(renumbering.size(), dodgy);
00188   renum_renum[0]=0;
00189   for (unsigned l=0, n=labels.size(); l<n; ++l)
00190     renum_renum[labels[l]] = l;
00191 
00192   for (ITER it=renumbering.begin(), end=renumbering.end(); it!=end; ++it)
00193     *it=renum_renum[*it];
00194 
00195   // Check than no DODGY values got into the renumbering.
00196   assert(vcl_find(renumbering.begin(), renumbering.end(), dodgy)
00197     == renumbering.end() );
00198 
00199   // Renumber the labels, to merge connected blobs, with a compact set of labels.
00200 
00201   for (unsigned k=0; k<nk; ++k)
00202     for (unsigned j=0; j<nj; ++j)
00203       for (unsigned i=0; i<ni; ++i)
00204         dst(i,j,k) = renumbering[dst(i,j,k)];
00205 }
00206