OpenVDB  6.0.0
Morphology.h
Go to the documentation of this file.
1 //
3 // Copyright (c) 2012-2018 DreamWorks Animation LLC
4 //
5 // All rights reserved. This software is distributed under the
6 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
7 //
8 // Redistributions of source code must retain the above copyright
9 // and license notice and the following restrictions and disclaimer.
10 //
11 // * Neither the name of DreamWorks Animation nor the names of
12 // its contributors may be used to endorse or promote products derived
13 // from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
27 // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
28 //
30 //
45 
46 #ifndef OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED
47 #define OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED
48 
49 #include <openvdb/Types.h>
50 #include <openvdb/Grid.h>
51 #include <openvdb/math/Math.h> // for isApproxEqual()
55 #include "Prune.h"// for pruneLevelSet
56 #include "ValueTransformer.h" // for foreach()
57 #include <tbb/tbb_thread.h>
58 #include <tbb/task_scheduler_init.h>
59 #include <tbb/enumerable_thread_specific.h>
60 #include <tbb/parallel_for.h>
61 #include <type_traits>
62 
63 
64 namespace openvdb {
66 namespace OPENVDB_VERSION_NAME {
67 namespace tools {
68 
86 
101 
117 template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION
118 inline void dilateActiveValues(TreeType& tree,
119  int iterations = 1,
121  TilePolicy mode = PRESERVE_TILES);
122 
147 template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION
149  int iterations = 1,
151  TilePolicy mode = PRESERVE_TILES);
152 
153 
166 template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION
167 inline void dilateVoxels(TreeType& tree,
168  int iterations = 1,
170 
185 template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION
186 inline void dilateVoxels(tree::LeafManager<TreeType>& manager,
187  int iterations = 1,
189 
190 
192 template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION
198 inline void erodeVoxels(TreeType& tree,
199  int iterations=1,
201 
202 template<typename TreeType> OPENVDB_STATIC_SPECIALIZATION
203 inline void erodeVoxels(tree::LeafManager<TreeType>& manager,
204  int iterations = 1,
207 
208 
211 template<typename GridOrTree>
212 inline void activate(
213  GridOrTree&,
214  const typename GridOrTree::ValueType& value,
215  const typename GridOrTree::ValueType& tolerance = zeroVal<typename GridOrTree::ValueType>()
216 );
217 
218 
221 template<typename GridOrTree>
222 inline void deactivate(
223  GridOrTree&,
224  const typename GridOrTree::ValueType& value,
225  const typename GridOrTree::ValueType& tolerance = zeroVal<typename GridOrTree::ValueType>()
226 );
227 
228 
230 
231 
233 template<Index Log2Dim> struct DimToWord {};
234 template<> struct DimToWord<3> { typedef uint8_t Type; };
235 template<> struct DimToWord<4> { typedef uint16_t Type; };
236 template<> struct DimToWord<5> { typedef uint32_t Type; };
237 template<> struct DimToWord<6> { typedef uint64_t Type; };
238 
239 
241 
242 
243 template<typename TreeType>
245 {
246 public:
248 
249  Morphology(TreeType& tree):
250  mOwnsManager(true), mManager(new ManagerType(tree)), mAcc(tree), mSteps(1) {}
252  mOwnsManager(false), mManager(mgr), mAcc(mgr->tree()), mSteps(1) {}
253  virtual ~Morphology() { if (mOwnsManager) delete mManager; }
254 
256  void dilateVoxels6();
258  void dilateVoxels18();
260  void dilateVoxels26();
261  void dilateVoxels(int iterations = 1, NearestNeighbors nn = NN_FACE);
262 
264  void erodeVoxels6() { mSteps = 1; this->doErosion(NN_FACE); }
266  void erodeVoxels18() { mSteps = 1; this->doErosion(NN_FACE_EDGE); }
268  void erodeVoxels26() { mSteps = 1; this->doErosion(NN_FACE_EDGE_VERTEX); }
269  void erodeVoxels(int iterations = 1, NearestNeighbors nn = NN_FACE)
270  {
271  mSteps = iterations;
272  this->doErosion(nn);
273  }
274 
275 protected:
276 
277  void doErosion(NearestNeighbors nn);
278 
279  typedef typename TreeType::LeafNodeType LeafType;
280  typedef typename LeafType::NodeMaskType MaskType;
282 
283  const bool mOwnsManager;
286  int mSteps;
287 
288  static const int LEAF_DIM = LeafType::DIM;
289  static const int LEAF_LOG2DIM = LeafType::LOG2DIM;
291 
292  struct Neighbor {
293  LeafType* leaf;//null if a tile
294  bool init;//true if initialization is required
295  bool isOn;//true if an active tile
296  Neighbor() : leaf(nullptr), init(true) {}
297  inline void clear() { leaf = nullptr; init = true; }
298  template<int DX, int DY, int DZ>
299  void scatter(AccessorType& acc, const Coord &xyz, int indx, Word mask)
300  {
301  if (init) {
302  init = false;
303  Coord orig = xyz.offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM);
304  leaf = acc.probeLeaf(orig);
305  if ((leaf == nullptr) && !acc.isValueOn(orig)) leaf = acc.touchLeaf(orig);
306  }
307 #ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics
308  static
309 #endif
310  const int N = (LEAF_DIM - 1)*(DY + DX*LEAF_DIM);
311  if (leaf) leaf->getValueMask().template getWord<Word>(indx-N) |= mask;
312  }
313 
314  template<int DX, int DY, int DZ>
315  Word gather(AccessorType& acc, const Coord &xyz, int indx)
316  {
317  if (init) {
318  init = false;
319  Coord orig = xyz.offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM);
320  leaf = acc.probeLeaf(orig);
321  isOn = leaf ? false : acc.isValueOn(orig);
322  }
323 #ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics
324  static
325 #endif
326  const int N = (LEAF_DIM -1 )*(DY + DX*LEAF_DIM);
327  return leaf ? leaf->getValueMask().template getWord<Word>(indx-N)
328  : isOn ? ~Word(0) : Word(0);
329  }
330  };// Neighbor
331 
332  struct LeafCache
333  {
334  LeafCache(size_t n, TreeType& tree) : size(n), leafs(new LeafType*[n]), acc(tree)
335  {
336  onTile.setValuesOn();
337  this->clear();
338  }
339  ~LeafCache() { delete [] leafs; }
340  LeafType*& operator[](int offset) { return leafs[offset]; }
341  inline void clear() { for (size_t i = 0; i < size; ++i) leafs[i] = nullptr; }
342  inline void setOrigin(const Coord& xyz) { origin = &xyz; }
343  inline void scatter(int n, int indx)
344  {
345  assert(leafs[n]);
346  leafs[n]->getValueMask().template getWord<Word>(indx) |= mask;
347  }
348  template<int DX, int DY, int DZ>
349  inline void scatter(int n, int indx)
350  {
351  if (!leafs[n]) {
352  const Coord xyz = origin->offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM);
353  leafs[n] = acc.probeLeaf(xyz);
354  if (!leafs[n]) leafs[n] = acc.isValueOn(xyz) ? &onTile : acc.touchLeaf(xyz);
355  }
356  this->scatter(n, indx - (LEAF_DIM - 1)*(DY + DX*LEAF_DIM));
357  }
358  inline Word gather(int n, int indx)
359  {
360  assert(leafs[n]);
361  return leafs[n]->getValueMask().template getWord<Word>(indx);
362  }
363  template<int DX, int DY, int DZ>
364  inline Word gather(int n, int indx)
365  {
366  if (!leafs[n]) {
367  const Coord xyz = origin->offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM);
368  leafs[n] = acc.probeLeaf(xyz);
369  if (!leafs[n]) leafs[n] = acc.isValueOn(xyz) ? &onTile : &offTile;
370  }
371  return this->gather(n, indx - (LEAF_DIM -1 )*(DY + DX*LEAF_DIM));
372  }
373  // Scatters in the xy face-directions relative to leaf i1
374  void scatterFacesXY(int x, int y, int i1, int n, int i2);
375 
376  // Scatters in the xy edge-directions relative to leaf i1
377  void scatterEdgesXY(int x, int y, int i1, int n, int i2);
378 
379  Word gatherFacesXY(int x, int y, int i1, int n, int i2);
380 
381  Word gatherEdgesXY(int x, int y, int i1, int n, int i2);
382 
383  const Coord* origin;
384  size_t size;
386  LeafType onTile, offTile;
389  };// LeafCache
390 
391  struct ErodeVoxelsOp {
392  typedef tbb::blocked_range<size_t> RangeT;
393  ErodeVoxelsOp(std::vector<MaskType>& masks, ManagerType& manager)
394  : mTask(0), mSavedMasks(masks) , mManager(manager) {}
395  void runParallel(NearestNeighbors nn);
396  void operator()(const RangeT& r) const {mTask(const_cast<ErodeVoxelsOp*>(this), r);}
397  void erode6( const RangeT&) const;
398  void erode18(const RangeT&) const;
399  void erode26(const RangeT&) const;
400  private:
401  using FuncT = typename std::function<void (ErodeVoxelsOp*, const RangeT&)>;
402  FuncT mTask;
403  std::vector<MaskType>& mSavedMasks;
404  ManagerType& mManager;
405  };// ErodeVoxelsOp
406 
407  struct MaskManager {
408  MaskManager(std::vector<MaskType>& masks, ManagerType& manager)
409  : mMasks(masks) , mManager(manager), mSaveMasks(true) {}
410 
411  void save() { mSaveMasks = true; tbb::parallel_for(mManager.getRange(), *this); }
412  void update() { mSaveMasks = false; tbb::parallel_for(mManager.getRange(), *this); }
413  void operator()(const tbb::blocked_range<size_t>& range) const
414  {
415  if (mSaveMasks) {
416  for (size_t i = range.begin(); i < range.end(); ++i) {
417  mMasks[i] = mManager.leaf(i).getValueMask();
418  }
419  } else {
420  for (size_t i = range.begin(); i < range.end(); ++i) {
421  mManager.leaf(i).setValueMask(mMasks[i]);
422  }
423  }
424  }
425  private:
426  std::vector<MaskType>& mMasks;
427  ManagerType& mManager;
428  bool mSaveMasks;
429  };// MaskManager
430 
431  struct UpdateMasks {
432  UpdateMasks(const std::vector<MaskType>& masks, ManagerType& manager)
433  : mMasks(masks), mManager(manager) {}
434  void update() { tbb::parallel_for(mManager.getRange(), *this); }
435  void operator()(const tbb::blocked_range<size_t>& r) const {
436  for (size_t i=r.begin(); i<r.end(); ++i) mManager.leaf(i).setValueMask(mMasks[i]);
437  }
438  const std::vector<MaskType>& mMasks;
440  };
441  struct CopyMasks {
442  CopyMasks(std::vector<MaskType>& masks, const ManagerType& manager)
443  : mMasks(masks), mManager(manager) {}
444  void copy() { tbb::parallel_for(mManager.getRange(), *this); }
445  void operator()(const tbb::blocked_range<size_t>& r) const {
446  for (size_t i=r.begin(); i<r.end(); ++i) mMasks[i]=mManager.leaf(i).getValueMask();
447  }
448  std::vector<MaskType>& mMasks;
450  };
451  void copyMasks(std::vector<MaskType>& a, const ManagerType& b) {CopyMasks c(a, b); c.copy();}
452 };// Morphology
453 
454 
455 template<typename TreeType>
456 inline void
458 {
459  for (int i=0; i<iterations; ++i) {
460  switch (nn) {
461  case NN_FACE_EDGE:
462  this->dilateVoxels18();
463  break;
464  case NN_FACE_EDGE_VERTEX:
465  this->dilateVoxels26();
466  break;
467  case NN_FACE:
468  default:
469  this->dilateVoxels6();
470  }
471  }
472 }
473 
474 
475 template<typename TreeType>
476 inline void
478 {
480  const int leafCount = static_cast<int>(mManager->leafCount());
481 
482  // Save the value masks of all leaf nodes.
483  std::vector<MaskType> savedMasks(leafCount);
484  this->copyMasks(savedMasks, *mManager);
485  LeafCache cache(7, mManager->tree());
486  for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) {
487  const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node
488  cache[0] = &mManager->leaf(leafIdx);
489  cache.setOrigin(cache[0]->origin());
490  for (int x = 0; x < LEAF_DIM; ++x ) {
491  for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) {
492  // Extract the portion of the original mask that corresponds to a row in z.
493  if (const Word w = oldMask.template getWord<Word>(n)) {
494 
495  // Dilate the current leaf in the +z and -z direction
496  cache.mask = Word(w | (w>>1) | (w<<1)); cache.scatter(0, n);
497 
498  // Dilate into neighbor leaf in the -z direction
499  if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) {
500  cache.template scatter< 0, 0,-1>(1, n);
501  }
502  // Dilate into neighbor leaf in the +z direction
503  if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) {
504  cache.template scatter< 0, 0, 1>(2, n);
505  }
506  // Dilate in the xy-face directions relative to the center leaf
507  cache.mask = w; cache.scatterFacesXY(x, y, 0, n, 3);
508  }
509  }// loop over y
510  }//loop over x
511  cache.clear();
512  }//loop over leafs
513 
514  mManager->rebuildLeafArray();
515 }//dilateVoxels6
516 
517 
518 template<typename TreeType>
519 inline void
521 {
523  const int leafCount = static_cast<int>(mManager->leafCount());
524 
525  // Save the value masks of all leaf nodes.
526  std::vector<MaskType> savedMasks(leafCount);
527  this->copyMasks(savedMasks, *mManager);
528  LeafCache cache(19, mManager->tree());
529  Coord orig_mz, orig_pz;//origins of neighbor leaf nodes in the -z and +z directions
530  for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) {
531  const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node
532  cache[0] = &mManager->leaf(leafIdx);
533  orig_mz = cache[0]->origin().offsetBy(0, 0, -LEAF_DIM);
534  orig_pz = cache[0]->origin().offsetBy(0, 0, LEAF_DIM);
535  for (int x = 0; x < LEAF_DIM; ++x ) {
536  for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) {
537  if (const Word w = oldMask.template getWord<Word>(n)) {
538  {
539  cache.mask = Word(w | (w>>1) | (w<<1));
540  cache.setOrigin(cache[0]->origin());
541  cache.scatter(0, n);
542  cache.scatterFacesXY(x, y, 0, n, 3);
543  cache.mask = w;
544  cache.scatterEdgesXY(x, y, 0, n, 3);
545  }
546  if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) {
547  cache.setOrigin(cache[0]->origin());
548  cache.template scatter< 0, 0,-1>(1, n);
549  cache.setOrigin(orig_mz);
550  cache.scatterFacesXY(x, y, 1, n, 11);
551  }
552  if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) {
553  cache.setOrigin(cache[0]->origin());
554  cache.template scatter< 0, 0, 1>(2, n);
555  cache.setOrigin(orig_pz);
556  cache.scatterFacesXY(x, y, 2, n, 15);
557  }
558  }
559  }// loop over y
560  }//loop over x
561  cache.clear();
562  }//loop over leafs
563 
564  mManager->rebuildLeafArray();
565 }// dilateVoxels18
566 
567 
568 template<typename TreeType>
569 inline void
571 {
572  const int leafCount = static_cast<int>(mManager->leafCount());
573 
574  // Save the value masks of all leaf nodes.
575  std::vector<MaskType> savedMasks(leafCount);
576  this->copyMasks(savedMasks, *mManager);
577  LeafCache cache(27, mManager->tree());
578  Coord orig_mz, orig_pz;//origins of neighbor leaf nodes in the -z and +z directions
579  for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) {
580  const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node
581  cache[0] = &mManager->leaf(leafIdx);
582  orig_mz = cache[0]->origin().offsetBy(0, 0, -LEAF_DIM);
583  orig_pz = cache[0]->origin().offsetBy(0, 0, LEAF_DIM);
584  for (int x = 0; x < LEAF_DIM; ++x ) {
585  for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) {
586  if (const Word w = oldMask.template getWord<Word>(n)) {
587  {
588  cache.mask = Word(w | (w>>1) | (w<<1));
589  cache.setOrigin(cache[0]->origin());
590  cache.scatter(0, n);
591  cache.scatterFacesXY(x, y, 0, n, 3);
592  cache.scatterEdgesXY(x, y, 0, n, 3);
593  }
594  if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) {
595  cache.setOrigin(cache[0]->origin());
596  cache.template scatter< 0, 0,-1>(1, n);
597  cache.setOrigin(orig_mz);
598  cache.scatterFacesXY(x, y, 1, n, 11);
599  cache.scatterEdgesXY(x, y, 1, n, 11);
600  }
601  if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) {
602  cache.setOrigin(cache[0]->origin());
603  cache.template scatter< 0, 0, 1>(2, n);
604  cache.setOrigin(orig_pz);
605  cache.scatterFacesXY(x, y, 2, n, 19);
606  cache.scatterEdgesXY(x, y, 2, n, 19);
607  }
608  }
609  }// loop over y
610  }//loop over x
611  cache.clear();
612  }//loop over leafs
613 
614  mManager->rebuildLeafArray();
615 }// dilateVoxels26
616 
617 
618 template<typename TreeType>
619 inline void
620 Morphology<TreeType>::LeafCache::scatterFacesXY(int x, int y, int i1, int n, int i2)
621 {
622  // dilate current leaf or neighbor in the -x direction
623  if (x > 0) {
624  this->scatter(i1, n-LEAF_DIM);
625  } else {
626  this->template scatter<-1, 0, 0>(i2, n);
627  }
628  // dilate current leaf or neighbor in the +x direction
629  if (x < LEAF_DIM-1) {
630  this->scatter(i1, n+LEAF_DIM);
631  } else {
632  this->template scatter< 1, 0, 0>(i2+1, n);
633  }
634  // dilate current leaf or neighbor in the -y direction
635  if (y > 0) {
636  this->scatter(i1, n-1);
637  } else {
638  this->template scatter< 0,-1, 0>(i2+2, n);
639  }
640  // dilate current leaf or neighbor in the +y direction
641  if (y < LEAF_DIM-1) {
642  this->scatter(i1, n+1);
643  } else {
644  this->template scatter< 0, 1, 0>(i2+3, n);
645  }
646 }
647 
648 
649 template<typename TreeType>
650 inline void
651 Morphology<TreeType>::LeafCache::scatterEdgesXY(int x, int y, int i1, int n, int i2)
652 {
653  if (x > 0) {
654  if (y > 0) {
655  this->scatter(i1, n-LEAF_DIM-1);
656  } else {
657  this->template scatter< 0,-1, 0>(i2+2, n-LEAF_DIM);
658  }
659  if (y < LEAF_DIM-1) {
660  this->scatter(i1, n-LEAF_DIM+1);
661  } else {
662  this->template scatter< 0, 1, 0>(i2+3, n-LEAF_DIM);
663  }
664  } else {
665  if (y < LEAF_DIM-1) {
666  this->template scatter<-1, 0, 0>(i2 , n+1);
667  } else {
668  this->template scatter<-1, 1, 0>(i2+7, n );
669  }
670  if (y > 0) {
671  this->template scatter<-1, 0, 0>(i2 , n-1);
672  } else {
673  this->template scatter<-1,-1, 0>(i2+4, n );
674  }
675  }
676  if (x < LEAF_DIM-1) {
677  if (y > 0) {
678  this->scatter(i1, n+LEAF_DIM-1);
679  } else {
680  this->template scatter< 0,-1, 0>(i2+2, n+LEAF_DIM);
681  }
682  if (y < LEAF_DIM-1) {
683  this->scatter(i1, n+LEAF_DIM+1);
684  } else {
685  this->template scatter< 0, 1, 0>(i2+3, n+LEAF_DIM);
686  }
687  } else {
688  if (y > 0) {
689  this->template scatter< 1, 0, 0>(i2+1, n-1);
690  } else {
691  this->template scatter< 1,-1, 0>(i2+6, n );
692  }
693  if (y < LEAF_DIM-1) {
694  this->template scatter< 1, 0, 0>(i2+1, n+1);
695  } else {
696  this->template scatter< 1, 1, 0>(i2+5, n );
697  }
698  }
699 }
700 
701 
702 template<typename TreeType>
703 inline void
705 {
706  namespace ph = std::placeholders;
707  switch (nn) {
708  case NN_FACE_EDGE:
709  mTask = std::bind(&ErodeVoxelsOp::erode18, ph::_1, ph::_2);
710  break;
711  case NN_FACE_EDGE_VERTEX:
712  mTask = std::bind(&ErodeVoxelsOp::erode26, ph::_1, ph::_2);
713  break;
714  case NN_FACE:
715  default:
716  mTask = std::bind(&ErodeVoxelsOp::erode6, ph::_1, ph::_2);
717  }
718  tbb::parallel_for(mManager.getRange(), *this);
719 }
720 
721 
722 template<typename TreeType>
723 inline typename Morphology<TreeType>::Word
724 Morphology<TreeType>::LeafCache::gatherFacesXY(int x, int y, int i1, int n, int i2)
725 {
726  // erode current leaf or neighbor in negative x-direction
727  Word w = x>0 ? this->gather(i1,n-LEAF_DIM) : this->template gather<-1,0,0>(i2, n);
728 
729  // erode current leaf or neighbor in positive x-direction
730  w = Word(w & (x<LEAF_DIM-1?this->gather(i1,n+LEAF_DIM):this->template gather<1,0,0>(i2+1,n)));
731 
732  // erode current leaf or neighbor in negative y-direction
733  w = Word(w & (y>0 ? this->gather(i1, n-1) : this->template gather<0,-1,0>(i2+2, n)));
734 
735  // erode current leaf or neighbor in positive y-direction
736  w = Word(w & (y<LEAF_DIM-1 ? this->gather(i1, n+1) : this->template gather<0,1,0>(i2+3, n)));
737 
738  return w;
739 }
740 
741 
742 template<typename TreeType>
743 inline typename Morphology<TreeType>::Word
744 Morphology<TreeType>::LeafCache::gatherEdgesXY(int x, int y, int i1, int n, int i2)
745 {
746  Word w = ~Word(0);
747 
748  if (x > 0) {
749  w &= y > 0 ? this->gather(i1, n-LEAF_DIM-1) :
750  this->template gather< 0,-1, 0>(i2+2, n-LEAF_DIM);
751  w &= y < LEAF_DIM-1 ? this->gather(i1, n-LEAF_DIM+1) :
752  this->template gather< 0, 1, 0>(i2+3, n-LEAF_DIM);
753  } else {
754  w &= y < LEAF_DIM-1 ? this->template gather<-1, 0, 0>(i2 , n+1):
755  this->template gather<-1, 1, 0>(i2+7, n );
756  w &= y > 0 ? this->template gather<-1, 0, 0>(i2 , n-1):
757  this->template gather<-1,-1, 0>(i2+4, n );
758  }
759  if (x < LEAF_DIM-1) {
760  w &= y > 0 ? this->gather(i1, n+LEAF_DIM-1) :
761  this->template gather< 0,-1, 0>(i2+2, n+LEAF_DIM);
762  w &= y < LEAF_DIM-1 ? this->gather(i1, n+LEAF_DIM+1) :
763  this->template gather< 0, 1, 0>(i2+3, n+LEAF_DIM);
764  } else {
765  w &= y > 0 ? this->template gather< 1, 0, 0>(i2+1, n-1):
766  this->template gather< 1,-1, 0>(i2+6, n );
767  w &= y < LEAF_DIM-1 ? this->template gather< 1, 0, 0>(i2+1, n+1):
768  this->template gather< 1, 1, 0>(i2+5, n );
769  }
770 
771  return w;
772 }
773 
774 
775 template <typename TreeType>
776 inline void
778 {
779  LeafCache cache(7, mManager.tree());
780  for (size_t leafIdx = range.begin(); leafIdx < range.end(); ++leafIdx) {
781  cache[0] = &mManager.leaf(leafIdx);
782  if (cache[0]->isEmpty()) continue;
783  cache.setOrigin(cache[0]->origin());
784  MaskType& newMask = mSavedMasks[leafIdx];//original bit-mask of current leaf node
785  for (int x = 0; x < LEAF_DIM; ++x ) {
786  for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) {
787  // Extract the portion of the original mask that corresponds to a row in z.
788  if (Word& w = newMask.template getWord<Word>(n)) {
789 
790  // erode in two z directions (this is first since it uses the original w)
791  w = Word(w &
792  (Word(w<<1 | (cache.template gather<0,0,-1>(1, n)>>(LEAF_DIM-1))) &
793  Word(w>>1 | (cache.template gather<0,0, 1>(2, n)<<(LEAF_DIM-1)))));
794 
795  w = Word(w & cache.gatherFacesXY(x, y, 0, n, 3));
796  }
797  }// loop over y
798  }//loop over x
799  cache.clear();
800  }//loop over leafs
801 }
802 
803 
804 template <typename TreeType>
805 inline void
807 {
808  OPENVDB_THROW(NotImplementedError, "tools::erode18 is not implemented yet!");
809 }
810 
811 
812 template <typename TreeType>
813 inline void
815 {
816  OPENVDB_THROW(NotImplementedError, "tools::erode26 is not implemented yet!");
817 }
818 
819 
820 template<typename TreeType>
821 inline void
823 {
825  const size_t leafCount = mManager->leafCount();
826 
827  // Save the value masks of all leaf nodes.
828  std::vector<MaskType> savedMasks(leafCount);
829  this->copyMasks(savedMasks, *mManager);
830  UpdateMasks a(savedMasks, *mManager);
831  ErodeVoxelsOp erode(savedMasks, *mManager);
832 
833  for (int i = 0; i < mSteps; ++i) {
834  erode.runParallel(nn);
835  a.update();
836  }
837 
838  tools::pruneLevelSet(mManager->tree());
839 }
840 
841 
843 
844 
845 template<typename TreeType>
848 {
849  if (iterations > 0 ) {
850  Morphology<TreeType> m(&manager);
851  m.dilateVoxels(iterations, nn);
852  }
853 }
854 
855 template<typename TreeType>
857 dilateVoxels(TreeType& tree, int iterations, NearestNeighbors nn)
858 {
859  if (iterations > 0 ) {
860  Morphology<TreeType> m(tree);
861  m.dilateVoxels(iterations, nn);
862  }
863 }
864 
865 template<typename TreeType>
868 {
869  if (iterations > 0 ) {
870  Morphology<TreeType> m(&manager);
871  m.erodeVoxels(iterations, nn);
872  }
873 }
874 
875 template<typename TreeType>
877 erodeVoxels(TreeType& tree, int iterations, NearestNeighbors nn)
878 {
879  if (iterations > 0 ) {
880  Morphology<TreeType> m(tree);
881  m.erodeVoxels(iterations, nn);
882  }
883 }
884 
885 
887 
888 
889 namespace activation {
890 
891 template<typename TreeType>
893 {
894 public:
895  typedef typename TreeType::ValueType ValueT;
896 
897  ActivationOp(bool state, const ValueT& val, const ValueT& tol)
898  : mActivate(state)
899  , mValue(val)
900  , mTolerance(tol)
901  {}
902 
903  void operator()(const typename TreeType::ValueOnIter& it) const
904  {
905  if (math::isApproxEqual(*it, mValue, mTolerance)) {
906  it.setValueOff();
907  }
908  }
909 
910  void operator()(const typename TreeType::ValueOffIter& it) const
911  {
912  if (math::isApproxEqual(*it, mValue, mTolerance)) {
913  it.setActiveState(/*on=*/true);
914  }
915  }
916 
917  void operator()(const typename TreeType::LeafIter& lit) const
918  {
919  typedef typename TreeType::LeafNodeType LeafT;
920  LeafT& leaf = *lit;
921  if (mActivate) {
922  for (typename LeafT::ValueOffIter it = leaf.beginValueOff(); it; ++it) {
923  if (math::isApproxEqual(*it, mValue, mTolerance)) {
924  leaf.setValueOn(it.pos());
925  }
926  }
927  } else {
928  for (typename LeafT::ValueOnIter it = leaf.beginValueOn(); it; ++it) {
929  if (math::isApproxEqual(*it, mValue, mTolerance)) {
930  leaf.setValueOff(it.pos());
931  }
932  }
933  }
934  }
935 
936 private:
937  bool mActivate;
938  const ValueT mValue, mTolerance;
939 }; // class ActivationOp
940 
941 } // namespace activation
942 
943 
944 template<typename GridOrTree>
945 inline void
946 activate(GridOrTree& gridOrTree, const typename GridOrTree::ValueType& value,
947  const typename GridOrTree::ValueType& tolerance)
948 {
949  typedef TreeAdapter<GridOrTree> Adapter;
950  typedef typename Adapter::TreeType TreeType;
951 
952  TreeType& tree = Adapter::tree(gridOrTree);
953 
954  activation::ActivationOp<TreeType> op(/*activate=*/true, value, tolerance);
955 
956  // Process all leaf nodes in parallel.
957  foreach(tree.beginLeaf(), op);
958 
959  // Process all other inactive values serially (because changing active states
960  // is not thread-safe unless no two threads modify the same node).
961  typename TreeType::ValueOffIter it = tree.beginValueOff();
962  it.setMaxDepth(tree.treeDepth() - 2);
963  foreach(it, op, /*threaded=*/false);
964 }
965 
966 
967 template<typename GridOrTree>
968 inline void
969 deactivate(GridOrTree& gridOrTree, const typename GridOrTree::ValueType& value,
970  const typename GridOrTree::ValueType& tolerance)
971 {
972  typedef TreeAdapter<GridOrTree> Adapter;
973  typedef typename Adapter::TreeType TreeType;
974 
975  TreeType& tree = Adapter::tree(gridOrTree);
976 
977  activation::ActivationOp<TreeType> op(/*activate=*/false, value, tolerance);
978 
979  // Process all leaf nodes in parallel.
980  foreach(tree.beginLeaf(), op);
981 
982  // Process all other active values serially (because changing active states
983  // is not thread-safe unless no two threads modify the same node).
984  typename TreeType::ValueOnIter it = tree.beginValueOn();
985  it.setMaxDepth(tree.treeDepth() - 2);
986  foreach(it, op, /*threaded=*/false);
987 }
988 
991 template<typename TreeT>
993 {
994  typedef typename TreeT::template ValueConverter<ValueMask>::Type MaskT;
995  typedef tbb::enumerable_thread_specific<MaskT> PoolT;
996  typedef typename MaskT::LeafNodeType LeafT;
997 
998  // Very light-weight member data
999  const int mIter;// number of iterations
1000  const tools::NearestNeighbors mNN;//enum to specify the dilation scheme
1001  PoolT *mPool;// pointer to the thread-local pool of mask trees
1002  LeafT **mLeafs;// raw array of pointers to leaf nodes
1003 
1004 public:
1005 
1006  DilationOp(TreeT &tree, int iterations, NearestNeighbors nn, TilePolicy mode)
1007  : mIter(iterations), mNN(nn), mPool(nullptr), mLeafs(nullptr)
1008  {
1009  const size_t numLeafs = this->init( tree, mode );
1010  const size_t numThreads = size_t(tbb::task_scheduler_init::default_num_threads());
1011  const size_t grainSize = math::Max(size_t(1), numLeafs/(2*numThreads));
1012 
1013  MaskT mask;
1014  PoolT pool(mask);// Scoped thread-local storage of mask trees
1015  mPool = &pool;
1016 
1017  tbb::parallel_for(tbb::blocked_range<LeafT**>(mLeafs, mLeafs+numLeafs, grainSize), *this);
1018 
1019  delete [] mLeafs;// no more need for the array of leaf node pointers
1020 
1021  typedef typename PoolT::iterator IterT;
1022  for (IterT it=pool.begin(); it!=pool.end(); ++it) mask.merge(*it);// fast stealing
1023 
1024  if (mode == PRESERVE_TILES) tools::prune(mask);//multithreaded
1025 
1026  tree.topologyUnion(mask);//multithreaded
1027  }
1028 
1029  // This is required by tbb and should never be called directly
1030  void operator()(const tbb::blocked_range<LeafT**> &r) const
1031  {
1032  MaskT mask;// thread-local temporary mask tree
1033  for (LeafT** it=r.begin(); it!=r.end(); ++it) mask.addLeaf( *it );
1034  tree::LeafManager<MaskT> manager(mask, r.begin(), r.end());
1035  tools::dilateVoxels(manager, mIter, mNN);// serial dilation of active voxels
1036  mPool->local().merge(mask, MERGE_ACTIVE_STATES);
1037  }
1038 private:
1039 
1040  // Simple wrapper of a raw double-pointer to mimic a std container
1041  struct MyArray {
1042  typedef LeafT* value_type;//required by Tree::stealNodes
1043  value_type* ptr;
1044  MyArray(value_type* array) : ptr(array) {}
1045  void push_back(value_type leaf) { *ptr++ = leaf; }//required by Tree::stealNodes
1046  };
1047 
1048  // Convert active tiles to leafs and de-construct the tree into a linear array of leafs.
1049  size_t linearize(MaskT& mask, TilePolicy mode)
1050  {
1051  if (mode != IGNORE_TILES) mask.voxelizeActiveTiles();//lightweight since this is a mask tree
1052  const size_t numLeafs = mask.leafCount();
1053  mLeafs = new LeafT*[numLeafs];// fast pre-allocation
1054  MyArray tmp(mLeafs);
1055  mask.stealNodes(tmp);// serializes the mask tree and leaves it empty
1056  return numLeafs;
1057  }
1058 
1059  template<typename T>
1060  typename std::enable_if<std::is_same<T, MaskT>::value, size_t>::type
1061  init(T& tree, TilePolicy mode)
1062  {
1063  return this->linearize(tree, mode);
1064  }
1065 
1066  template<typename T>
1067  typename std::enable_if<!std::is_same<T, MaskT>::value, size_t>::type
1068  init(const T& tree, TilePolicy mode)
1069  {
1070  MaskT mask(tree, false, true, TopologyCopy());
1071  return this->linearize(mask, mode);
1072  }
1073 
1074 };// DilationOp
1075 
1076 template<typename TreeType>
1078 dilateActiveValues(TreeType& tree, int iterations, NearestNeighbors nn, TilePolicy mode)
1079 {
1080  if (iterations > 0 ) DilationOp<TreeType> tmp(tree, iterations, nn, mode);
1081 }
1082 
1083 template<typename TreeType>
1086  int iterations,
1087  NearestNeighbors nn,
1088  TilePolicy mode)
1089 {
1090  if (iterations > 0 ) {
1091  DilationOp<TreeType> tmp(manager.tree(), iterations, nn, mode);
1092  manager.rebuildLeafArray();
1093  }
1094 }
1095 
1096 } // namespace tools
1097 } // namespace OPENVDB_VERSION_NAME
1098 } // namespace openvdb
1099 
1100 #endif // OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED
1101 
1102 // Copyright (c) 2012-2018 DreamWorks Animation LLC
1103 // All rights reserved. This software is distributed under the
1104 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
LeafNodeT * probeLeaf(const Coord &xyz)
Return a pointer to the leaf node that contains voxel (x, y, z), or nullptr if no such node exists.
Definition: ValueAccessor.h:417
This adapter allows code that is templated on a Tree type to accept either a Tree type or a Grid type...
Definition: Grid.h:944
void operator()(const typename TreeType::ValueOffIter &it) const
Definition: Morphology.h:910
void dilateActiveValues(tree::LeafManager< TreeType > &manager, int iterations=1, NearestNeighbors nn=NN_FACE, TilePolicy mode=PRESERVE_TILES)
Topologically dilate all active values (i.e. both voxels and tiles) in a tree using one of three near...
Definition: Morphology.h:1085
TreeType::LeafNodeType LeafType
Definition: Morphology.h:279
void pruneLevelSet(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing nodes whose values are all inactive with inactive ...
Definition: Prune.h:416
tbb::blocked_range< size_t > RangeT
Definition: Morphology.h:392
void rebuildLeafArray()
Remove the auxiliary buffers and rebuild the leaf array.
Definition: LeafManager.h:316
size_t size
Definition: Morphology.h:384
ActivationOp(bool state, const ValueT &val, const ValueT &tol)
Definition: Morphology.h:897
void dilateVoxels(int iterations=1, NearestNeighbors nn=NN_FACE)
Definition: Morphology.h:457
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
void operator()(const tbb::blocked_range< size_t > &range) const
Definition: Morphology.h:413
Definition: Morphology.h:100
bool init
Definition: Morphology.h:294
uint32_t Type
Definition: Morphology.h:236
bool isValueOn(const Coord &xyz) const
Return the active state of the voxel at the given coordinates.
Definition: ValueAccessor.h:264
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:109
LeafType onTile
Definition: Morphology.h:386
AccessorType acc
Definition: Morphology.h:387
void dilateVoxels(tree::LeafManager< TreeType > &manager, int iterations=1, NearestNeighbors nn=NN_FACE)
Topologically dilate all leaf-level active voxels in a tree using one of three nearest neighbor conne...
Definition: Morphology.h:847
void clear()
Definition: Morphology.h:341
ManagerType * mManager
Definition: Morphology.h:284
TreeType::ValueType ValueT
Definition: Morphology.h:895
std::vector< MaskType > & mMasks
Definition: Morphology.h:448
UpdateMasks(const std::vector< MaskType > &masks, ManagerType &manager)
Definition: Morphology.h:432
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:51
void operator()(const RangeT &r) const
Definition: Morphology.h:396
void erodeVoxels26()
Face-, edge- and vertex-adjacent erosion pattern.
Definition: Morphology.h:268
Definition: Morphology.h:292
Word gather(int n, int indx)
Definition: Morphology.h:358
void copyMasks(std::vector< MaskType > &a, const ManagerType &b)
Definition: Morphology.h:451
Defined various multi-threaded utility functions for trees.
~LeafCache()
Definition: Morphology.h:339
Definition: Morphology.h:85
void erodeVoxels(int iterations=1, NearestNeighbors nn=NN_FACE)
Definition: Morphology.h:269
ErodeVoxelsOp(std::vector< MaskType > &masks, ManagerType &manager)
Definition: Morphology.h:393
uint16_t Type
Definition: Morphology.h:235
void scatter(int n, int indx)
Definition: Morphology.h:349
uint8_t Type
Definition: Morphology.h:234
const std::vector< MaskType > & mMasks
Definition: Morphology.h:438
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:136
void operator()(const tbb::blocked_range< LeafT ** > &r) const
Definition: Morphology.h:1030
AccessorType mAcc
Definition: Morphology.h:285
void setOrigin(const Coord &xyz)
Definition: Morphology.h:342
void operator()(const typename TreeType::ValueOnIter &it) const
Definition: Morphology.h:903
NearestNeighbors
Voxel topology of nearest neighbors.
Definition: Morphology.h:85
void activate(GridOrTree &, const typename GridOrTree::ValueType &value, const typename GridOrTree::ValueType &tolerance=zeroVal< typename GridOrTree::ValueType >())
Mark as active any inactive tiles or voxels in the given grid or tree whose values are equal to value...
Definition: Morphology.h:946
Definition: Types.h:329
const ManagerType & mManager
Definition: Morphology.h:449
int mSteps
Definition: Morphology.h:286
bool isOn
Definition: Morphology.h:295
Definition: Morphology.h:244
const bool mOwnsManager
Definition: Morphology.h:283
const Coord * origin
Definition: Morphology.h:383
Definition: Exceptions.h:40
LeafCache(size_t n, TreeType &tree)
Definition: Morphology.h:334
uint64_t Type
Definition: Morphology.h:237
DilationOp(TreeT &tree, int iterations, NearestNeighbors nn, TilePolicy mode)
Definition: Morphology.h:1006
void copy()
Definition: Morphology.h:444
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:110
void scatter(int n, int indx)
Definition: Morphology.h:343
const Type & Max(const Type &a, const Type &b)
Return the maximum of two values.
Definition: Math.h:549
bool isApproxEqual(const Type &a, const Type &b)
Return true if a is equal to b to within the default floating-point comparison tolerance.
Definition: Math.h:358
Word gather(AccessorType &acc, const Coord &xyz, int indx)
Definition: Morphology.h:315
Morphology(TreeType &tree)
Definition: Morphology.h:249
Definition: Morphology.h:100
tree::ValueAccessor< TreeType > AccessorType
Definition: Morphology.h:281
LeafType & leaf(size_t leafIdx) const
Return a pointer to the leaf node at index leafIdx in the array.
Definition: LeafManager.h:359
Definition: Morphology.h:85
TilePolicy
Different policies when dilating trees with active tiles.
Definition: Morphology.h:100
void erodeVoxels(tree::LeafManager< TreeType > &manager, int iterations=1, NearestNeighbors nn=NN_FACE)
Topologically erode all leaf-level active voxels in the given tree.
Definition: Morphology.h:867
const TreeType & tree() const
Return a const reference to tree associated with this manager.
Definition: LeafManager.h:343
LeafNodeT * touchLeaf(const Coord &xyz)
Return a pointer to the leaf node that contains voxel (x, y, z). If no such node exists,...
Definition: ValueAccessor.h:386
virtual ~Morphology()
Definition: Morphology.h:253
Class that performs multi-threaded dilation with support for active tiles.
Definition: Morphology.h:992
#define OPENVDB_STATIC_SPECIALIZATION
Macro for determining if there are sufficient C++0x/C++11 features.
Definition: Platform.h:91
ManagerType & mManager
Definition: Morphology.h:439
RangeType getRange(size_t grainsize=1) const
Return a tbb::blocked_range of leaf array indices.
Definition: LeafManager.h:383
void scatter(AccessorType &acc, const Coord &xyz, int indx, Word mask)
Definition: Morphology.h:299
void runParallel(NearestNeighbors nn)
Definition: Morphology.h:704
Neighbor()
Definition: Morphology.h:296
void clear()
Definition: Morphology.h:297
tree::LeafManager< TreeType > ManagerType
Definition: Morphology.h:247
Word gather(int n, int indx)
Definition: Morphology.h:364
CopyMasks(std::vector< MaskType > &masks, const ManagerType &manager)
Definition: Morphology.h:442
LeafType::NodeMaskType MaskType
Definition: Morphology.h:280
MaskManager(std::vector< MaskType > &masks, ManagerType &manager)
Definition: Morphology.h:408
LeafType * leaf
Definition: Morphology.h:293
void update()
Definition: Morphology.h:412
void operator()(const tbb::blocked_range< size_t > &r) const
Definition: Morphology.h:435
void erodeVoxels6()
Face-adjacent erosion pattern.
Definition: Morphology.h:264
Definition: Morphology.h:100
void operator()(const tbb::blocked_range< size_t > &r) const
Definition: Morphology.h:445
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:188
DimToWord< LEAF_LOG2DIM >::Type Word
Definition: Morphology.h:290
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
LeafType *& operator[](int offset)
Definition: Morphology.h:340
Mapping from a Log2Dim to a data type of size 2^Log2Dim bits.
Definition: Morphology.h:233
Word mask
Definition: Morphology.h:388
void prune(TreeT &tree, typename TreeT::ValueType tolerance=zeroVal< typename TreeT::ValueType >(), bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing with tiles any nodes whose values are all the same...
Definition: Prune.h:361
void erodeVoxels18()
Face- and edge-adjacent erosion pattern.
Definition: Morphology.h:266
Definition: Exceptions.h:88
void save()
Definition: Morphology.h:411
void update()
Definition: Morphology.h:434
Morphology(ManagerType *mgr)
Definition: Morphology.h:251
void deactivate(GridOrTree &, const typename GridOrTree::ValueType &value, const typename GridOrTree::ValueType &tolerance=zeroVal< typename GridOrTree::ValueType >())
Mark as inactive any active tiles or voxels in the given grid or tree whose values are equal to value...
Definition: Morphology.h:969
void operator()(const typename TreeType::LeafIter &lit) const
Definition: Morphology.h:917
LeafType ** leafs
Definition: Morphology.h:385
Coord offsetBy(Int32 dx, Int32 dy, Int32 dz) const
Definition: Coord.h:118