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 
00015 // Identify and enumerate all disjoint blobs in a binary image.
00016 void vil3d_find_blobs(const vil3d_image_view<bool>& src,
00017                       vil3d_find_blob_connectivity conn,
00018                       vil3d_image_view<unsigned>& dst)
00019 {
00020   unsigned ni=src.ni();
00021   unsigned nj=src.nj();
00022   unsigned nk=src.nk();
00023   dst.set_size(ni, nj, nk);
00024   dst.fill(0);
00025 
00026   vcl_vector<unsigned> renumbering(1u, 0u); // renumber 0->0
00027   vcl_vector<unsigned> neighbouring_labels;
00028 
00029   unsigned n_neighbours;
00030   switch (conn)
00031   {
00032    case vil3d_find_blob_connectivity_6_conn:
00033     n_neighbours=3;
00034     break;
00035    case vil3d_find_blob_connectivity_26_conn:
00036     n_neighbours=13;
00037     break;
00038    default:
00039     n_neighbours=0; assert(!"unknown connectivity");
00040   }
00041 
00042   // The 3-prev-(6)-neighbourhood are the first three entries.
00043   // The 13-prev-(26)-neighbourhood are those three plus the rest.
00044   int neighbourhood_ii[] = { -1, 0, 0, -1,  0, +1, -1, +1, -1,  0, +1, -1, +1};
00045   int neighbourhood_jj[] = { 0, -1, 0, -1, -1, -1,  0,  0, +1, +1, +1, -1, -1};
00046   int neighbourhood_kk[] = { 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1,  0,  0};
00047 
00048   typedef vcl_vector<unsigned>::iterator ITER;
00049 
00050   for (unsigned k=0; k<nk; ++k)
00051     for (unsigned j=0; j<nj; ++j)
00052       for (unsigned i=0; i<ni; ++i)
00053       {
00054         if (!src(i,j,k)) continue;
00055         neighbouring_labels.clear();
00056         for (unsigned l=0; l<n_neighbours; ++l)
00057         {
00058           unsigned ii = i + neighbourhood_ii[l];
00059           if (ii >= ni) continue; // rely on wraparound to find -ne overruns.
00060           unsigned jj = j + neighbourhood_jj[l];
00061           if (jj >= ni) continue;
00062           unsigned kk = k + neighbourhood_kk[l];
00063           if (kk >= nk) continue;
00064           unsigned d = dst(ii,jj,kk);
00065           if (d==0) continue;
00066           neighbouring_labels.push_back(d);
00067         }
00068         if (neighbouring_labels.empty())
00069         {
00070           unsigned new_label = renumbering.size();
00071           dst(i,j,k) = new_label;
00072           renumbering.push_back(new_label);
00073         }
00074         else
00075         {
00076           // See how many unique labels neighbouring labels we have
00077           vcl_sort(neighbouring_labels.begin(), neighbouring_labels.end());
00078           ITER end = vcl_unique(neighbouring_labels.begin(), neighbouring_labels.end());
00079           // don't bother erasing unique suffix, just keeping the end iterator
00080           // will be enough.
00081           ITER it=neighbouring_labels.begin();
00082           unsigned label = *it++;
00083           dst(i,j,k) = label;
00084 
00085           // If we have neighbours with multiple labels.
00086           //   then record merges of the previously disjointly labelled blocks.
00087           // If there was only a single unique label, the following for loop
00088           //   will not execute.
00089           for (; it!=end; ++it)
00090             renumbering[*it] = vcl_min(renumbering[*it], label);
00091         }
00092       }
00093 
00094   // Propagate the renumbering so that when 3 points to 2 and 2 points to 1,
00095   //   3 should then point to 1.
00096   for (ITER it=renumbering.begin(), end=renumbering.end(); it!=end; ++it)
00097     *it = renumbering[*it];
00098 
00099   // Now due to the renumbering, the set of labels may not compactly occupy
00100   // the number line. So renumber the renumbering array.
00101   vcl_vector<unsigned> labels(renumbering.begin(), renumbering.end());
00102   vcl_sort(labels.begin(), labels.end());
00103   labels.erase(vcl_unique(labels.begin(), labels.end()), labels.end());
00104   const unsigned dodgy = static_cast<unsigned>(-1);
00105   vcl_vector<unsigned> renum_renum(renumbering.size(), dodgy);
00106   renum_renum[0]=0;
00107   for (unsigned l=0, n=labels.size(); l<n; ++l)
00108     renum_renum[labels[l]] = l;
00109 
00110   for (ITER it=renumbering.begin(), end=renumbering.end(); it!=end; ++it)
00111     *it=renum_renum[*it];
00112 
00113   // Check than no DODGY values got into the renumbering.
00114   assert(vcl_find(renumbering.begin(), renumbering.end(), dodgy)
00115     == renumbering.end() );
00116 
00117   // Renumber the labels, to merge connected blobs, with a compact set of labels.
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         dst(i,j,k) = renumbering[dst(i,j,k)];
00123 }
00124