[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

blockwise_labeling.hxx VIGRA

1 /************************************************************************/
2 /* */
3 /* Copyright 2013-2014 by Martin Bidlingmaier and Ullrich Koethe */
4 /* */
5 /* This file is part of the VIGRA computer vision library. */
6 /* The VIGRA Website is */
7 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
8 /* Please direct questions, bug reports, and contributions to */
9 /* ullrich.koethe@iwr.uni-heidelberg.de or */
10 /* vigra@informatik.uni-hamburg.de */
11 /* */
12 /* Permission is hereby granted, free of charge, to any person */
13 /* obtaining a copy of this software and associated documentation */
14 /* files (the "Software"), to deal in the Software without */
15 /* restriction, including without limitation the rights to use, */
16 /* copy, modify, merge, publish, distribute, sublicense, and/or */
17 /* sell copies of the Software, and to permit persons to whom the */
18 /* Software is furnished to do so, subject to the following */
19 /* conditions: */
20 /* */
21 /* The above copyright notice and this permission notice shall be */
22 /* included in all copies or substantial portions of the */
23 /* Software. */
24 /* */
25 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32 /* OTHER DEALINGS IN THE SOFTWARE. */
33 /* */
34 /************************************************************************/
35 
36 #ifndef VIGRA_BLOCKWISE_LABELING_HXX
37 #define VIGRA_BLOCKWISE_LABELING_HXX
38 
39 #include <algorithm>
40 
41 #include "threadpool.hxx"
42 #include "counting_iterator.hxx"
43 #include "multi_gridgraph.hxx"
44 #include "multi_labeling.hxx"
45 #include "multi_blockwise.hxx"
46 #include "union_find.hxx"
47 #include "multi_array_chunked.hxx"
48 #include "metaprogramming.hxx"
49 
50 #include "visit_border.hxx"
51 #include "blockify.hxx"
52 
53 namespace vigra
54 {
55 
56 /** \addtogroup Labeling
57 */
58 //@{
59 
60  /** Options object for labelMultiArrayBlockwise().
61 
62  It is simply a subclass of both \ref vigra::LabelOptions
63  and \ref vigra::BlockwiseOptions. See there for
64  detailed documentation.
65  */
67 : public LabelOptions
68 , public BlockwiseOptions
69 {
70 public:
72 
73  // reimplement setter functions to allow chaining
74 
75  template <class T>
76  BlockwiseLabelOptions& ignoreBackgroundValue(const T& value)
77  {
79  return *this;
80  }
81 
82  BlockwiseLabelOptions & neighborhood(NeighborhoodType n)
83  {
85  return *this;
86  }
87 
88  BlockwiseLabelOptions & blockShape(const Shape & shape)
89  {
91  return *this;
92  }
93 
94  template <class T, int N>
95  BlockwiseLabelOptions & blockShape(const TinyVector<T, N> & shape)
96  {
98  return *this;
99  }
100 
101  BlockwiseLabelOptions & numThreads(const int n)
102  {
103  BlockwiseOptions::numThreads(n);
104  return *this;
105  }
106 };
107 
108 namespace blockwise_labeling_detail
109 {
110 
111 template <class Equal, class Label>
112 struct BorderVisitor
113 {
114  Label u_label_offset;
115  Label v_label_offset;
116  UnionFindArray<Label>* global_unions;
117  Equal* equal;
118 
119  template <class Data, class Shape>
120  void operator()(const Data& u_data, Label& u_label, const Data& v_data, Label& v_label, const Shape& diff)
121  {
122  if(labeling_equality::callEqual(*equal, u_data, v_data, diff))
123  {
124  global_unions->makeUnion(u_label + u_label_offset, v_label + v_label_offset);
125  }
126  }
127 };
128 
129 // needed by MSVC
130 template <class LabelBlocksIterator>
131 struct BlockwiseLabelingResult
132 {
133  typedef typename LabelBlocksIterator::value_type::value_type type;
134 };
135 
136 template <class DataBlocksIterator, class LabelBlocksIterator,
137  class Equal, class Mapping>
138 typename BlockwiseLabelingResult<LabelBlocksIterator>::type
139 blockwiseLabeling(DataBlocksIterator data_blocks_begin, DataBlocksIterator data_blocks_end,
140  LabelBlocksIterator label_blocks_begin, LabelBlocksIterator label_blocks_end,
141  BlockwiseLabelOptions const & options,
142  Equal equal,
143  Mapping& mapping)
144 {
145  typedef typename LabelBlocksIterator::value_type::value_type Label;
146  typedef typename DataBlocksIterator::shape_type Shape;
147 
148  Shape blocks_shape = data_blocks_begin.shape();
149  vigra_precondition(blocks_shape == label_blocks_begin.shape() &&
150  blocks_shape == mapping.shape(),
151  "shapes of blocks of blocks do not match");
152  vigra_precondition(std::distance(data_blocks_begin,data_blocks_end) == std::distance(label_blocks_begin,label_blocks_end),
153  "the sizes of input ranges are different");
154 
155  static const unsigned int Dimensions = DataBlocksIterator::dimension + 1;
156  MultiArray<Dimensions, Label> label_offsets(label_blocks_begin.shape());
157 
158  bool has_background = options.hasBackgroundValue();
159 
160  // mapping stage: label each block and save number of labels assigned in blocks before the current block in label_offsets
161  Label unmerged_label_number;
162  {
163  DataBlocksIterator data_blocks_it = data_blocks_begin;
164  LabelBlocksIterator label_blocks_it = label_blocks_begin;
165  typename MultiArray<Dimensions, Label>::iterator offsets_it = label_offsets.begin();
166  Label current_offset = 0;
167  // a la OPENMP_PRAGMA FOR
168 
169  auto d = std::distance(data_blocks_begin, data_blocks_end);
170 
171 
172  std::vector<Label> nSeg(d);
173  //std::vector<int> ids(d);
174  //std::iota(ids.begin(), ids.end(), 0 );
175 
176  parallel_foreach(options.getNumThreads(), d,
177  [&](const int /*threadId*/, const uint64_t i){
178  Label resVal = labelMultiArray(data_blocks_it[i], label_blocks_it[i],
179  options, equal);
180  if(has_background) // FIXME: reversed condition?
181  ++resVal;
182  nSeg[i] = resVal;
183  }
184  );
185 
186  for(int i=0; i<d;++i){
187  offsets_it[i] = current_offset;
188  current_offset+=nSeg[i];
189  }
190 
191 
192  unmerged_label_number = current_offset;
193  if(!has_background)
194  ++unmerged_label_number;
195  }
196 
197  // reduce stage: merge adjacent labels if the region overlaps
198  UnionFindArray<Label> global_unions(unmerged_label_number);
199  if(has_background)
200  {
201  // merge all labels that refer to background
202  for(typename MultiArray<Dimensions, Label>::iterator offsets_it = label_offsets.begin();
203  offsets_it != label_offsets.end();
204  ++offsets_it)
205  {
206  global_unions.makeUnion(0, *offsets_it);
207  }
208  }
209 
210  typedef GridGraph<Dimensions, undirected_tag> Graph;
211  typedef typename Graph::edge_iterator EdgeIterator;
212  Graph blocks_graph(blocks_shape, options.getNeighborhood());
213  for(EdgeIterator it = blocks_graph.get_edge_iterator(); it != blocks_graph.get_edge_end_iterator(); ++it)
214  {
215  Shape u = blocks_graph.u(*it);
216  Shape v = blocks_graph.v(*it);
217  Shape difference = v - u;
218 
219  BorderVisitor<Equal, Label> border_visitor;
220  border_visitor.u_label_offset = label_offsets[u];
221  border_visitor.v_label_offset = label_offsets[v];
222  border_visitor.global_unions = &global_unions;
223  border_visitor.equal = &equal;
224  visitBorder(data_blocks_begin[u], label_blocks_begin[u],
225  data_blocks_begin[v], label_blocks_begin[v],
226  difference, options.getNeighborhood(), border_visitor);
227  }
228 
229  // fill mapping (local labels) -> (global labels)
230  Label last_label = global_unions.makeContiguous();
231  {
232  typename MultiArray<Dimensions, Label>::iterator offsets_it = label_offsets.begin();
233  Label offset = *offsets_it;
234  ++offsets_it;
235  typename Mapping::iterator mapping_it = mapping.begin();
236  for( ; offsets_it != label_offsets.end(); ++offsets_it, ++mapping_it)
237  {
238  mapping_it->clear();
239  Label next_offset = *offsets_it;
240  if(has_background)
241  {
242  for(Label current_label = offset; current_label != next_offset; ++current_label)
243  {
244  mapping_it->push_back(global_unions.findLabel(current_label));
245  }
246  }
247  else
248  {
249  mapping_it->push_back(0); // local labels start at 1
250  for(Label current_label = offset + 1; current_label != next_offset + 1; ++current_label)
251  {
252  mapping_it->push_back(global_unions.findLabel(current_label));
253  }
254  }
255 
256  offset = next_offset;
257  }
258  // last block:
259  // instead of next_offset, use last_label+1
260  mapping_it->clear();
261  if(has_background)
262  {
263  for(Label current_label = offset; current_label != unmerged_label_number; ++current_label)
264  {
265  mapping_it->push_back(global_unions.findLabel(current_label));
266  }
267  }
268  else
269  {
270  mapping_it->push_back(0);
271  for(Label current_label = offset + 1; current_label != unmerged_label_number; ++current_label)
272  {
273  mapping_it->push_back(global_unions.findLabel(current_label));
274  }
275  }
276  }
277  return last_label;
278 }
279 
280 
281 template <class LabelBlocksIterator, class MappingIterator>
282 void toGlobalLabels(LabelBlocksIterator label_blocks_begin, LabelBlocksIterator label_blocks_end,
283  MappingIterator mapping_begin, MappingIterator mapping_end)
284 {
285  typedef typename LabelBlocksIterator::value_type LabelBlock;
286  for( ; label_blocks_begin != label_blocks_end; ++label_blocks_begin, ++mapping_begin)
287  {
288  vigra_assert(mapping_begin != mapping_end, "");
289  for(typename LabelBlock::iterator labels_it = label_blocks_begin->begin();
290  labels_it != label_blocks_begin->end();
291  ++labels_it)
292  {
293  vigra_assert(*labels_it < mapping_begin->size(), "");
294  *labels_it = (*mapping_begin)[*labels_it];
295  }
296  }
297 }
298 
299 } // namespace blockwise_labeling_detail
300 
301 /*************************************************************/
302 /* */
303 /* labelMultiArrayBlockwise */
304 /* */
305 /*************************************************************/
306 
307 /** \weakgroup ParallelProcessing
308  \sa labelMultiArrayBlockwise <B>(...)</B>
309 */
310 
311 /** \brief Connected components labeling for MultiArrays and ChunkedArrays.
312 
313  <b> Declarations:</b>
314 
315  \code
316  namespace vigra {
317  // assign local labels and generate mapping (local labels) -> (global labels) for each chunk
318  template <unsigned int N, class Data, class S1,
319  class Label, class S2,
320  class Equal, class S3>
321  Label labelMultiArrayBlockwise(const MultiArrayView<N, Data, S1>& data,
322  MultiArrayView<N, Label, S2> labels,
323  const BlockwiseLabelOptions& options,
324  Equal equal,
325  MultiArrayView<N, std::vector<Label>, S3>& mapping);
326 
327  // assign local labels and generate mapping (local labels) -> (global labels) for each chunk
328  template <unsigned int N, class T, class S1,
329  class Label, class S2,
330  class EqualityFunctor>
331  Label labelMultiArrayBlockwise(const ChunkedArray<N, Data>& data,
332  ChunkedArray<N, Label>& labels,
333  const BlockwiseLabelOptions& options,
334  Equal equal,
335  MultiArrayView<N, std::vector<Label>, S3>& mapping);
336 
337  // assign global labels
338  template <unsigned int N, class Data, class S1,
339  class Label, class S2,
340  class Equal>
341  Label labelMultiArrayBlockwise(const MultiArrayView<N, Data, S1>& data,
342  MultiArrayView<N, Label, S2> labels,
343  const BlockwiseLabelOptions& options,
344  Equal equal);
345 
346  // assign global labels
347  template <unsigned int N, class T, class S1,
348  class Label, class S2,
349  class EqualityFunctor = std::equal_to<T> >
350  Label labelMultiArrayBlockwise(const ChunkedArray<N, Data>& data,
351  ChunkedArray<N, Label>& labels,
352  const BlockwiseLabelOptions& options = BlockwiseLabelOptions(),
353  Equal equal = std::equal_to<T>());
354  }
355  \endcode
356 
357  The resulting labeling is equivalent to a labeling by \ref labelMultiArray, that is, the connected components are the same but may have different ids.
358  \ref NeighborhoodType and background value (if any) can be specified with the LabelOptions object.
359  If the \a mapping parameter is provided, each chunk is labeled seperately and contiguously (starting at one, zero for background),
360  with \a mapping containing a mapping of local labels to global labels for each chunk.
361  Thus, the shape of 'mapping' has to be large enough to hold each chunk coordinate.
362 
363  Return: the number of regions found (=largest global region label)
364 
365  <b> Usage: </b>
366 
367  <b>\#include </b> <vigra/blockwise_labeling.hxx><br>
368  Namespace: vigra
369 
370  \code
371  Shape3 shape = Shape3(10);
372  Shape3 chunk_shape = Shape3(4);
373  ChunkedArrayLazy<3, int> data(shape, chunk_shape);
374  // fill data ...
375 
376  ChunkedArrayLazy<3, size_t> labels(shape, chunk_shape);
377 
378  MultiArray<3, std::vector<size_t> > mapping(Shape3(3)); // there are 3 chunks in each dimension
379 
380  labelMultiArrayBlockwise(data, labels, LabelOptions().neighborhood(DirectNeighborhood).background(0),
381  std::equal_to<int>(), mapping);
382 
383  // check out chunk in the middle
384  MultiArray<3, size_t> middle_chunk(Shape3(4));
385  labels.checkoutSubarray(Shape3(4), middle_chunk);
386 
387  // print number of non-background labels assigned in middle_chunk
388  cout << mapping[Shape3(1)].size() << endl;
389 
390  // get local label for voxel
391  // this may be the same value assigned to different component in another chunk
392  size_t local_label = middle_chunk[Shape3(2)];
393  // get global label for voxel
394  // if another voxel has the same label, they are in the same connected component albeit they may be in different chunks
395  size_t global_label = mapping[Shape3(1)][local_label
396  \endcode
397  */
398 doxygen_overloaded_function(template <...> unsigned int labelMultiArrayBlockwise)
399 
400 template <unsigned int N, class Data, class S1,
401  class Label, class S2,
402  class Equal, class S3>
403 Label labelMultiArrayBlockwise(const MultiArrayView<N, Data, S1>& data,
404  MultiArrayView<N, Label, S2> labels,
405  const BlockwiseLabelOptions& options,
406  Equal equal,
407  MultiArrayView<N, std::vector<Label>, S3>& mapping)
408 {
409  using namespace blockwise_labeling_detail;
410 
411  typedef typename MultiArrayShape<N>::type Shape;
412  Shape block_shape(options.getBlockShapeN<N>());
413 
414  MultiArray<N, MultiArrayView<N, Data, S1> > data_blocks = blockify(data, block_shape);
415  MultiArray<N, MultiArrayView<N, Label, S2> > label_blocks = blockify(labels, block_shape);
416  return blockwiseLabeling(data_blocks.begin(), data_blocks.end(),
417  label_blocks.begin(), label_blocks.end(),
418  options, equal, mapping);
419 }
420 
421 template <unsigned int N, class Data, class S1,
422  class Label, class S2,
423  class Equal>
424 Label labelMultiArrayBlockwise(const MultiArrayView<N, Data, S1>& data,
425  MultiArrayView<N, Label, S2> labels,
426  const BlockwiseLabelOptions& options,
427  Equal equal)
428 {
429  using namespace blockwise_labeling_detail;
430 
431  typedef typename MultiArrayShape<N>::type Shape;
432  Shape block_shape(options.getBlockShapeN<N>());
433 
434  MultiArray<N, MultiArrayView<N, Data, S1> > data_blocks = blockify(data, block_shape);
435  MultiArray<N, MultiArrayView<N, Label, S2> > label_blocks = blockify(labels, block_shape);
436  MultiArray<N, std::vector<Label> > mapping(data_blocks.shape());
437  Label last_label = blockwiseLabeling(data_blocks.begin(), data_blocks.end(),
438  label_blocks.begin(), label_blocks.end(),
439  options, equal, mapping);
440 
441  // replace local labels by global labels
442  toGlobalLabels(label_blocks.begin(), label_blocks.end(), mapping.begin(), mapping.end());
443  return last_label;
444 }
445 
446 template <unsigned int N, class Data, class S1,
447  class Label, class S2>
448 Label labelMultiArrayBlockwise(const MultiArrayView<N, Data, S1>& data,
449  MultiArrayView<N, Label, S2> labels,
450  const BlockwiseLabelOptions& options = BlockwiseLabelOptions())
451 {
452  return labelMultiArrayBlockwise(data, labels, options, std::equal_to<Data>());
453 }
454 
455 
456 template <unsigned int N, class Data, class Label, class Equal, class S3>
457 Label labelMultiArrayBlockwise(const ChunkedArray<N, Data>& data,
458  ChunkedArray<N, Label>& labels,
459  const BlockwiseLabelOptions& options,
460  Equal equal,
461  MultiArrayView<N, std::vector<Label>, S3> mapping)
462 {
463  using namespace blockwise_labeling_detail;
464 
465  vigra_precondition(options.getBlockShape().size() == 0,
466  "labelMultiArrayBlockwise(ChunkedArray, ...): custom block shapes not supported "
467  "(always uses the array's chunk shape).");
468 
469  typedef typename ChunkedArray<N, Data>::shape_type Shape;
470 
471  typedef typename ChunkedArray<N, Data>::chunk_const_iterator DataChunkIterator;
472  typedef typename ChunkedArray<N, Label>::chunk_iterator LabelChunkIterator;
473 
474  DataChunkIterator data_chunks_begin = data.chunk_begin(Shape(0), data.shape());
475  LabelChunkIterator label_chunks_begin = labels.chunk_begin(Shape(0), labels.shape());
476 
477  return blockwiseLabeling(data_chunks_begin, data_chunks_begin.getEndIterator(),
478  label_chunks_begin, label_chunks_begin.getEndIterator(),
479  options, equal, mapping);
480 }
481 
482 template <unsigned int N, class Data, class Label, class Equal>
483 Label labelMultiArrayBlockwise(const ChunkedArray<N, Data>& data,
484  ChunkedArray<N, Label>& labels,
485  const BlockwiseLabelOptions& options,
486  Equal equal)
487 {
488  using namespace blockwise_labeling_detail;
489  MultiArray<N, std::vector<Label> > mapping(data.chunkArrayShape());
490  Label result = labelMultiArrayBlockwise(data, labels, options, equal, mapping);
491  typedef typename ChunkedArray<N, Data>::shape_type Shape;
492  toGlobalLabels(labels.chunk_begin(Shape(0), data.shape()), labels.chunk_end(Shape(0), data.shape()), mapping.begin(), mapping.end());
493  return result;
494 }
495 
496 template <unsigned int N, class Data, class Label>
497 Label labelMultiArrayBlockwise(const ChunkedArray<N, Data>& data,
498  ChunkedArray<N, Label>& labels,
499  const BlockwiseLabelOptions& options = BlockwiseLabelOptions())
500 {
501  return labelMultiArrayBlockwise(data, labels, options, std::equal_to<Data>());
502 }
503 
504 //@}
505 
506 } // namespace vigra
507 
508 #endif // VIGRA_BLOCKWISE_LABELING_HXX
Option object for labelMultiArray().
Definition: multi_labeling.hxx:309
Definition: blockwise_labeling.hxx:66
Definition: multi_blockwise.hxx:54
unsigned int labelMultiArrayBlockwise(...)
Connected components labeling for MultiArrays and ChunkedArrays.
doxygen_overloaded_function(template<...> void separableConvolveBlockwise) template< unsigned int N
Separated convolution on ChunkedArrays.
void parallel_foreach(...)
Apply a functor to all items in a range in parallel.
LabelOptions & neighborhood(NeighborhoodType n)
Choose direct or indirect neighborhood.
Definition: multi_labeling.hxx:326
LabelOptions & ignoreBackgroundValue(T const &t)
Set background value.
Definition: multi_labeling.hxx:351
BlockwiseOptions & blockShape(const Shape &blockShape)
Definition: multi_blockwise.hxx:114
unsigned int labelMultiArray(...)
Find the connected components of a MultiArray with arbitrary many dimensions.
NeighborhoodType
Choose the neighborhood system in a dimension-independent way.
Definition: multi_fwd.hxx:186

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de)
Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany

html generated using doxygen and Python
vigra 1.11.1 (Fri May 19 2017)