vigra - vigra/blockwise_labeling.hxx Source File

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

blockwise_labeling.hxx |

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 */
66 class BlockwiseLabelOptions
67 : public LabelOptions
68 , public BlockwiseOptions
69 {
70 public:
71 typedef BlockwiseOptions::Shape Shape;
72
73 // reimplement setter functions to allow chaining
74
75 template <class T>
76 BlockwiseLabelOptions& ignoreBackgroundValue(const T& value)
77 {
78 LabelOptions::ignoreBackgroundValue(value);
79 return *this;
80 }
81
82 BlockwiseLabelOptions & neighborhood(NeighborhoodType n)
83 {
84 LabelOptions::neighborhood(n);
85 return *this;
86 }
87
88 BlockwiseLabelOptions & blockShape(const Shape & shape)
89 {
90 BlockwiseOptions::blockShape(shape);
91 return *this;
92 }
93
94 template <class T, int N>
95 BlockwiseLabelOptions & blockShape(const TinyVector<T, N> & shape)
96 {
97 BlockwiseOptions::blockShape(shape);
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
vigra::LabelOptions
Option object for labelMultiArray().
Definition: multi_labeling.hxx:309
vigra::ArrayVector< MultiArrayIndex >
vigra::BlockwiseLabelOptions
Definition: blockwise_labeling.hxx:66
vigra::BlockwiseOptions
Definition: multi_blockwise.hxx:54
vigra::labelMultiArrayBlockwise
unsigned int labelMultiArrayBlockwise(...)
Connected components labeling for MultiArrays and ChunkedArrays.
vigra::doxygen_overloaded_function
doxygen_overloaded_function(template<...> void separableConvolveBlockwise) template< unsigned int N
Separated convolution on ChunkedArrays.
vigra::parallel_foreach
void parallel_foreach(...)
Apply a functor to all items in a range in parallel.
vigra::TinyVector< T, N >
vigra::LabelOptions::neighborhood
LabelOptions & neighborhood(NeighborhoodType n)
Choose direct or indirect neighborhood.
Definition: multi_labeling.hxx:326
vigra::LabelOptions::ignoreBackgroundValue
LabelOptions & ignoreBackgroundValue(T const &t)
Set background value.
Definition: multi_labeling.hxx:351
vigra::BlockwiseOptions::blockShape
BlockwiseOptions & blockShape(const Shape &blockShape)
Definition: multi_blockwise.hxx:114
vigra::labelMultiArray
unsigned int labelMultiArray(...)
Find the connected components of a MultiArray with arbitrary many dimensions.
vigra::NeighborhoodType
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)