core/vil/algo/vil_blob.cxx
Go to the documentation of this file.
00001 //:
00002 // \file
00003 // \brief Finds connected regions in a boolean image.
00004 // \author Ian Scott
00005 
00006 #include "vil_blob.h"
00007 #include <vcl_algorithm.h>
00008 #include <vcl_cassert.h>
00009 
00010 namespace
00011 {
00012   //: Manages the relabelling structure.
00013   // Got the idea from http://en.wikipedia.org/wiki/Disjoint-set_data_structure Mar 2011.
00014   // although no code was copied.
00015   class disjoint_sets
00016   {
00017     typedef unsigned LABEL;
00018     typedef unsigned LEN;
00019     struct node
00020     {
00021       LABEL parent;
00022       LEN rank;
00023     };
00024     vcl_vector<node> store_;
00025   public:
00026     disjoint_sets(): store_(1u)
00027     { // renumber 0->0
00028       store_.front().parent=0;
00029       store_.front().rank=0;
00030     }
00031 
00032     //: Get the root label for label v;
00033     LABEL root(LABEL v)
00034     {
00035       node & n=store_[v];
00036       if (n.parent == v)
00037         return v;
00038       else
00039       {
00040         n.parent=root(n.parent); // relabel to speed up later searches
00041         return n.parent;
00042       }
00043     }
00044 
00045     //: Merge two sets containing labels left and right.
00046     void merge_labels(LABEL left_label, LABEL right_label)
00047     {
00048       LABEL left_root = root(left_label);
00049       LABEL right_root = root(right_label);
00050       if (left_root == right_root) return; // already merged.
00051       node& left_root_node = store_[left_root];
00052       node& right_root_node = store_[right_root];
00053       if (left_root_node.rank > right_root_node.rank) // Find the larger tree.
00054       { // add the right tree to the left
00055         right_root_node.parent = left_root_node.parent;
00056       }
00057       else
00058       { // add the left tree to the right
00059         left_root_node.parent = right_root_node.parent;
00060         if (left_root_node.rank == right_root_node.rank)
00061           right_root_node.rank++;
00062       }
00063     }
00064 
00065     //: Create a new label;
00066     LABEL new_label()
00067     {
00068       node n = {(LABEL)store_.size(), 0};
00069       store_.push_back(n);
00070       return n.parent;
00071     }
00072 
00073     LEN size()
00074     {
00075       return store_.size();
00076     }
00077   };
00078 }
00079 
00080 // Produce a label image that enumerates all disjoint blobs in a binary image
00081 void vil_blob_labels(const vil_image_view<bool>& src_binary,
00082                      vil_blob_connectivity conn,
00083                      vil_image_view<unsigned>& dest_label)
00084 {
00085   unsigned ni=src_binary.ni();
00086   unsigned nj=src_binary.nj();
00087   dest_label.set_size(ni, nj);
00088   dest_label.fill(0);
00089 
00090   disjoint_sets merge_list;
00091   vcl_vector<unsigned> neighbouring_labels;
00092 
00093   unsigned n_prev_neighbours;
00094   switch (conn)
00095   {
00096    case vil_blob_4_conn:
00097     n_prev_neighbours=2;
00098     break;
00099    case vil_blob_8_conn:
00100     n_prev_neighbours=4;
00101     break;
00102    default:
00103     n_prev_neighbours=0; assert(!"unknown connectivity");
00104   }
00105 
00106   // The 2-prev-(of 6)-neighbourhood are the first two entries.
00107   // The 4-prev-(of 8)-neighbourhood are those two plus the rest.
00108   int neighbourhood_ii[] = { -1,  0, -1, +1};
00109   int neighbourhood_jj[] = {  0, -1, -1, -1};
00110 
00111   typedef vcl_vector<unsigned>::iterator ITER;
00112 
00113   for (unsigned j=0; j<nj; ++j)
00114     for (unsigned i=0; i<ni; ++i)
00115     {
00116       if (!src_binary(i,j)) continue;
00117       neighbouring_labels.clear();
00118       for (unsigned l=0; l<n_prev_neighbours; ++l)
00119       {
00120         unsigned ii = i + neighbourhood_ii[l];
00121         if (ii >= ni) continue; // rely on wraparound to find -ne overruns.
00122         unsigned jj = j + neighbourhood_jj[l];
00123         if (jj >= nj) continue;
00124         unsigned d = dest_label(ii,jj);
00125         if (d!=0) neighbouring_labels.push_back(d);
00126       }
00127       if (neighbouring_labels.empty())
00128       {
00129         unsigned new_label = merge_list.new_label();
00130         dest_label(i,j) = new_label;
00131       }
00132       else
00133       {
00134         // See how many unique labels neighbouring labels we have
00135         vcl_sort(neighbouring_labels.begin(), neighbouring_labels.end());
00136         ITER end = vcl_unique(neighbouring_labels.begin(), neighbouring_labels.end());
00137         // don't bother erasing unique's suffix, just keeping the end iterator
00138         // will be enough.
00139         ITER it=neighbouring_labels.begin();
00140         unsigned label = *it++;
00141         dest_label(i,j) = label;
00142 
00143         // If we have neighbours with multiple labels.
00144         //   then record merges of the previously disjointly labelled blocks.
00145         // If there was only a single unique label, the following for loop
00146         //   will not execute.
00147         for (; it!=end; ++it)
00148           merge_list.merge_labels(*it, label);
00149       }
00150     }
00151   unsigned n_merge=merge_list.size();
00152   vcl_vector<unsigned> renumbering(n_merge, 0u);
00153   // Convert the merge lsit into a simple renumbering array,
00154   // and change to root of each disjoint set to its lowest member.
00155   // The reinstates label order to the original raster order.
00156   for (unsigned l=1; l<n_merge; ++l)
00157   {
00158     if (renumbering[l]!=0) continue;
00159     unsigned root_label = merge_list.root(l);
00160     unsigned root_label_renumber = renumbering[root_label];
00161     if (root_label_renumber==0)
00162     {
00163       renumbering[root_label]=l;
00164       renumbering[l]=l;
00165     }
00166     else
00167       renumbering[l]=renumbering[root_label];
00168   }
00169 
00170   // Now due to the renumbering, the set of labels may not compactly occupy
00171   // the number line. So renumber the renumbering array.
00172   vcl_vector<unsigned> labels(renumbering.begin(), renumbering.end());
00173   vcl_sort(labels.begin(), labels.end());
00174   labels.erase(vcl_unique(labels.begin(), labels.end()), labels.end());
00175   const unsigned dodgy = static_cast<unsigned>(-1);
00176   vcl_vector<unsigned> renum_renum(renumbering.size(), dodgy);
00177   renum_renum[0]=0;
00178   for (unsigned l=0, n=labels.size(); l<n; ++l)
00179     renum_renum[labels[l]] = l;
00180 
00181   for (ITER it=renumbering.begin(), end=renumbering.end(); it!=end; ++it)
00182     *it=renum_renum[*it];
00183 
00184   // Check than no DODGY values got into the renumbering.
00185   assert(vcl_find(renumbering.begin(), renumbering.end(), dodgy)
00186          == renumbering.end() );
00187 
00188   // Renumber the labels, to merge connected blobs, with a compact set of labels.
00189 
00190   for (unsigned j=0; j<nj; ++j)
00191     for (unsigned i=0; i<ni; ++i)
00192       dest_label(i,j) = renumbering[dest_label(i,j)];
00193 }
00194 
00195 
00196 //: Set all non-blob-edge pixels in a blob label image to zero.
00197 void vil_blob_labels_to_edge_labels(const vil_image_view<unsigned>& src_label,
00198                                     vil_blob_connectivity conn,
00199                                     vil_image_view<unsigned>& dest_label)
00200 {
00201   unsigned ni=src_label.ni();
00202   unsigned nj=src_label.nj();
00203   dest_label.set_size(ni, nj);
00204   dest_label.fill(0);
00205 
00206   unsigned n_edge_neighbours;
00207   switch (conn)
00208   {
00209    case vil_blob_4_conn:
00210     n_edge_neighbours=8;
00211     break;
00212    case vil_blob_8_conn:
00213     n_edge_neighbours=4;
00214     break;
00215    default:
00216     n_edge_neighbours=0; assert(!"unknown connectivity");
00217   }
00218 
00219   // A  4-conn blob pixel is on the edge if any of its 8-conn neighbours has different value.
00220   // An 8-conn blob pixel is on the edge if any of its 4-conn neighbours has different value.
00221   int neighbourhood_ii[] = {  0, -1,  1,  0,  -1,  1, -1,  1};
00222   int neighbourhood_jj[] = { -1,  0,  0,  1,  -1, -1,  1,  1};
00223 
00224   for (unsigned j=0; j<nj; ++j)
00225     for (unsigned i=0; i<ni; ++i)
00226     {
00227       unsigned v = src_label(i,j);
00228       if (!v) continue;
00229       for (unsigned l=0; l<n_edge_neighbours; ++l)
00230       {
00231         unsigned ii = i + neighbourhood_ii[l];
00232         if (ii >= ni) continue; // rely on wraparound to find -ne overruns.
00233         unsigned jj = j + neighbourhood_jj[l];
00234         if (jj >= nj) continue;
00235         if (v!=src_label(ii,jj)) // Only pixels that have neighbours with different values are edge pixels.
00236         {
00237           dest_label(i,j) = v;
00238           break;
00239         }
00240       }
00241     }
00242 }
00243 
00244 
00245 //: Convert a label image into a list of chorded regions.
00246 // A blob label value of n will be returned in dest_regions[n-1].
00247 void vil_blob_labels_to_regions(const vil_image_view<unsigned>& src_label,
00248                                 vcl_vector<vil_blob_region>& dest_regions)
00249 {
00250   dest_regions.clear();
00251   unsigned ni=src_label.ni();
00252   unsigned nj=src_label.nj();
00253 
00254   for (unsigned j=0; j<nj; ++j)
00255     for (unsigned i=0; i<ni;) // don't auto increment i, since the loop body does it.
00256     {
00257       unsigned label = src_label(i,j);
00258       if (!label)
00259       { // if not a label - go to next pixel.
00260         ++i;
00261         continue;
00262       }
00263       // Make sure there is a region for this label.
00264       if (label > dest_regions.size()) dest_regions.resize(label);
00265       unsigned i_start=i;
00266       // Find end of chord.
00267       while (++i < ni && src_label(i,j)==label);
00268       dest_regions[label-1].push_back(vil_chord(i_start, i-1, j));
00269     }
00270 }
00271 
00272 //: Convert a label image (or an edge label image) into a set of pixel lists.
00273 // A blob label value of n will be returned in dest_pixels_lists[n-1].
00274 // Note that pixel lists are not ordered.
00275 void vil_blob_labels_to_pixel_lists(const vil_image_view<unsigned>& src_label,
00276                                     vcl_vector<vil_blob_pixel_list>& dest_pixel_lists)
00277 {
00278   dest_pixel_lists.clear();
00279   unsigned ni=src_label.ni();
00280   unsigned nj=src_label.nj();
00281 
00282   for (unsigned j=0; j<nj; ++j)
00283     for (unsigned i=0; i<ni; ++i)
00284     {
00285       unsigned label = src_label(i,j);
00286       if (!label) continue;
00287       // Make sure there is a pixel list for this label.
00288       if (label > dest_pixel_lists.size()) dest_pixel_lists.resize(label);
00289       dest_pixel_lists[label-1].push_back(vcl_make_pair(i,j));
00290     }
00291 }