vil_blob.cxx
Go to the documentation of this file.
1 //:
2 // \file
3 // \brief Finds connected regions in a boolean image.
4 // \author Ian Scott
5 
6 #include <algorithm>
7 #include "vil_blob.h"
8 #ifdef _MSC_VER
9 # include <vcl_msvc_warnings.h>
10 #endif
11 #include <cassert>
12 
13 namespace
14 {
15  //: Manages the relabelling structure.
16  // Got the idea from http://en.wikipedia.org/wiki/Disjoint-set_data_structure Mar 2011.
17  // although no code was copied.
18  class disjoint_sets
19  {
20  typedef unsigned LABEL;
21  typedef unsigned LEN;
22  struct node
23  {
24  LABEL parent;
25  LEN rank;
26  };
27  std::vector<node> store_;
28  public:
29  disjoint_sets(): store_(1u)
30  { // renumber 0->0
31  store_.front().parent=0;
32  store_.front().rank=0;
33  }
34 
35  //: Get the root label for label v;
36  LABEL root(LABEL v)
37  {
38  node & n=store_[v];
39  if (n.parent == v)
40  return v;
41  else
42  {
43  n.parent=root(n.parent); // relabel to speed up later searches
44  return n.parent;
45  }
46  }
47 
48  //: Merge two sets containing labels left and right.
49  void merge_labels(LABEL left_label, LABEL right_label)
50  {
51  LABEL left_root = root(left_label);
52  LABEL right_root = root(right_label);
53  if (left_root == right_root) return; // already merged.
54  node& left_root_node = store_[left_root];
55  node& right_root_node = store_[right_root];
56  if (left_root_node.rank > right_root_node.rank) // Find the larger tree.
57  { // add the right tree to the left
58  right_root_node.parent = left_root_node.parent;
59  }
60  else
61  { // add the left tree to the right
62  left_root_node.parent = right_root_node.parent;
63  if (left_root_node.rank == right_root_node.rank)
64  right_root_node.rank++;
65  }
66  }
67 
68  //: Create a new label;
69  LABEL new_label()
70  {
71  node n = {(LABEL)store_.size(), 0};
72  store_.push_back(n);
73  return n.parent;
74  }
75 
76  LEN size()
77  {
78  return store_.size();
79  }
80  };
81 }
82 
83 // Produce a label image that enumerates all disjoint blobs in a binary image
84 void vil_blob_labels(const vil_image_view<bool>& src_binary,
86  vil_image_view<unsigned>& dest_label)
87 {
88  unsigned ni=src_binary.ni();
89  unsigned nj=src_binary.nj();
90  dest_label.set_size(ni, nj);
91  dest_label.fill(0);
92 
93  disjoint_sets merge_list;
94  std::vector<unsigned> neighbouring_labels;
95 
96  unsigned n_prev_neighbours;
97  switch (conn)
98  {
99  case vil_blob_4_conn:
100  n_prev_neighbours=2;
101  break;
102  case vil_blob_8_conn:
103  n_prev_neighbours=4;
104  break;
105  default:
106  n_prev_neighbours=0; assert(!"unknown connectivity");
107  }
108 
109  // The 2-prev-(of 6)-neighbourhood are the first two entries.
110  // The 4-prev-(of 8)-neighbourhood are those two plus the rest.
111  int neighbourhood_ii[] = { -1, 0, -1, +1};
112  int neighbourhood_jj[] = { 0, -1, -1, -1};
113 
114 
115  for (unsigned j=0; j<nj; ++j)
116  for (unsigned i=0; i<ni; ++i)
117  {
118  if (!src_binary(i,j)) continue;
119  neighbouring_labels.clear();
120  for (unsigned l=0; l<n_prev_neighbours; ++l)
121  {
122  unsigned ii = i + neighbourhood_ii[l];
123  if (ii >= ni) continue; // rely on wraparound to find -ne overruns.
124  unsigned jj = j + neighbourhood_jj[l];
125  if (jj >= nj) continue;
126  unsigned d = dest_label(ii,jj);
127  if (d!=0) neighbouring_labels.push_back(d);
128  }
129  if (neighbouring_labels.empty())
130  {
131  unsigned new_label = merge_list.new_label();
132  dest_label(i,j) = new_label;
133  }
134  else
135  {
136  // See how many unique labels neighbouring labels we have
137  std::sort(neighbouring_labels.begin(), neighbouring_labels.end());
138  auto end = std::unique(neighbouring_labels.begin(), neighbouring_labels.end());
139  // don't bother erasing unique's suffix, just keeping the end iterator
140  // will be enough.
141  auto it=neighbouring_labels.begin();
142  unsigned label = *it++;
143  dest_label(i,j) = label;
144 
145  // If we have neighbours with multiple labels.
146  // then record merges of the previously disjointly labelled blocks.
147  // If there was only a single unique label, the following for loop
148  // will not execute.
149  for (; it!=end; ++it)
150  merge_list.merge_labels(*it, label);
151  }
152  }
153  unsigned n_merge=merge_list.size();
154  std::vector<unsigned> renumbering(n_merge, 0u);
155  // Convert the merge lsit into a simple renumbering array,
156  // and change to root of each disjoint set to its lowest member.
157  // The reinstates label order to the original raster order.
158  for (unsigned l=1; l<n_merge; ++l)
159  {
160  if (renumbering[l]!=0) continue;
161  unsigned root_label = merge_list.root(l);
162  unsigned root_label_renumber = renumbering[root_label];
163  if (root_label_renumber==0)
164  {
165  renumbering[root_label]=l;
166  renumbering[l]=l;
167  }
168  else
169  renumbering[l]=renumbering[root_label];
170  }
171 
172  // Now due to the renumbering, the set of labels may not compactly occupy
173  // the number line. So renumber the renumbering array.
174  std::vector<unsigned> labels(renumbering.begin(), renumbering.end());
175  std::sort(labels.begin(), labels.end());
176  labels.erase(std::unique(labels.begin(), labels.end()), labels.end());
177  const auto dodgy = static_cast<unsigned>(-1);
178  std::vector<unsigned> renum_renum(renumbering.size(), dodgy);
179  renum_renum[0]=0;
180  for (unsigned l=0, n=labels.size(); l<n; ++l)
181  renum_renum[labels[l]] = l;
182 
183  for (unsigned int & it : renumbering)
184  it=renum_renum[it];
185 
186  // Check than no DODGY values got into the renumbering.
187  assert(std::find(renumbering.begin(), renumbering.end(), dodgy)
188  == renumbering.end() );
189 
190  // Renumber the labels, to merge connected blobs, with a compact set of labels.
191 
192  for (unsigned j=0; j<nj; ++j)
193  for (unsigned i=0; i<ni; ++i)
194  dest_label(i,j) = renumbering[dest_label(i,j)];
195 }
196 
197 
198 //: Set all non-blob-edge pixels in a blob label image to zero.
201  vil_image_view<unsigned>& dest_label)
202 {
203  unsigned ni=src_label.ni();
204  unsigned nj=src_label.nj();
205  dest_label.set_size(ni, nj);
206  dest_label.fill(0);
207 
208  unsigned n_edge_neighbours;
209  switch (conn)
210  {
211  case vil_blob_4_conn:
212  n_edge_neighbours=8;
213  break;
214  case vil_blob_8_conn:
215  n_edge_neighbours=4;
216  break;
217  default:
218  n_edge_neighbours=0; assert(!"unknown connectivity");
219  }
220 
221  // A 4-conn blob pixel is on the edge if any of its 8-conn neighbours has different value.
222  // An 8-conn blob pixel is on the edge if any of its 4-conn neighbours has different value.
223  int neighbourhood_ii[] = { 0, -1, 1, 0, -1, 1, -1, 1};
224  int neighbourhood_jj[] = { -1, 0, 0, 1, -1, -1, 1, 1};
225 
226  for (unsigned j=0; j<nj; ++j)
227  for (unsigned i=0; i<ni; ++i)
228  {
229  unsigned v = src_label(i,j);
230  if (!v) continue;
231  for (unsigned l=0; l<n_edge_neighbours; ++l)
232  {
233  unsigned ii = i + neighbourhood_ii[l];
234  if (ii >= ni) continue; // rely on wraparound to find -ne overruns.
235  unsigned jj = j + neighbourhood_jj[l];
236  if (jj >= nj) continue;
237  if (v!=src_label(ii,jj)) // Only pixels that have neighbours with different values are edge pixels.
238  {
239  dest_label(i,j) = v;
240  break;
241  }
242  }
243  }
244 }
245 
246 
247 //: Convert a label image into a list of chorded regions.
248 // A blob label value of n will be returned in dest_regions[n-1].
250  std::vector<vil_blob_region>& dest_regions)
251 {
252  dest_regions.clear();
253  unsigned ni=src_label.ni();
254  unsigned nj=src_label.nj();
255 
256  for (unsigned j=0; j<nj; ++j)
257  for (unsigned i=0; i<ni;) // don't auto increment i, since the loop body does it.
258  {
259  unsigned label = src_label(i,j);
260  if (!label)
261  { // if not a label - go to next pixel.
262  ++i;
263  continue;
264  }
265  // Make sure there is a region for this label.
266  if (label > dest_regions.size()) dest_regions.resize(label);
267  unsigned i_start=i;
268  // Find end of chord.
269  while (++i < ni && src_label(i,j)==label);
270  dest_regions[label-1].push_back(vil_chord(i_start, i-1, j));
271  }
272 }
273 
274 //: Convert a label image (or an edge label image) into a set of pixel lists.
275 // A blob label value of n will be returned in dest_pixels_lists[n-1].
276 // Note that pixel lists are not ordered.
278  std::vector<vil_blob_pixel_list>& dest_pixel_lists)
279 {
280  dest_pixel_lists.clear();
281  unsigned ni=src_label.ni();
282  unsigned nj=src_label.nj();
283 
284  for (unsigned j=0; j<nj; ++j)
285  for (unsigned i=0; i<ni; ++i)
286  {
287  unsigned label = src_label(i,j);
288  if (!label) continue;
289  // Make sure there is a pixel list for this label.
290  if (label > dest_pixel_lists.size()) dest_pixel_lists.resize(label);
291  dest_pixel_lists[label-1].push_back(std::make_pair(i,j));
292  }
293 }
void vil_blob_labels_to_edge_labels(const vil_image_view< unsigned > &src_label, vil_blob_connectivity conn, vil_image_view< unsigned > &dest_label)
Set all non-blob-edge pixels in a blob label image to zero.
Definition: vil_blob.cxx:199
void fill(T value)
Fill view with given value.
vil_blob_connectivity
Specify 4- or 8- neighbour connectivity.
Definition: vil_blob.h:30
void set_size(unsigned ni, unsigned nj) override
resize current planes to ni x nj.
void vil_blob_labels_to_regions(const vil_image_view< unsigned > &src_label, std::vector< vil_blob_region > &dest_regions)
Convert a label image into a list of chorded regions.
Definition: vil_blob.cxx:249
unsigned ni() const
Width.
unsigned nj() const
Height.
#define v
iterator begin()
Store information about position of a row of pixels in an image.
Definition: vil_chord.h:16
void vil_blob_labels(const vil_image_view< bool > &src_binary, vil_blob_connectivity conn, vil_image_view< unsigned > &dest_label)
Produce a label image that enumerates all disjoint blobs in a binary image.
Definition: vil_blob.cxx:84
void vil_blob_labels_to_pixel_lists(const vil_image_view< unsigned > &src_label, std::vector< vil_blob_pixel_list > &dest_pixel_lists)
Convert a label image (or an edge label image) into a set of pixel lists.
Definition: vil_blob.cxx:277
Finds connected regions in a boolean image.