OpenVDB  8.1.1
Filter.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
13 
14 #ifndef OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
15 #define OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
16 
17 #include <tbb/parallel_for.h>
18 #include <openvdb/Types.h>
19 #include <openvdb/math/Math.h>
20 #include <openvdb/math/Stencils.h>
21 #include <openvdb/math/Transform.h>
25 #include <openvdb/util/Util.h>
26 #include <openvdb/Grid.h>
27 #include "Interpolation.h"
28 #include <algorithm> // for std::max()
29 #include <functional>
30 #include <type_traits>
31 #include <tbb/concurrent_vector.h>
32 
33 namespace openvdb {
35 namespace OPENVDB_VERSION_NAME {
36 namespace tools {
37 
39 template<typename GridT,
40  typename MaskT = typename GridT::template ValueConverter<float>::Type,
41  typename InterruptT = util::NullInterrupter>
42 class Filter
43 {
44 public:
45  using GridType = GridT;
46  using MaskType = MaskT;
47  using TreeType = typename GridType::TreeType;
48  using LeafType = typename TreeType::LeafNodeType;
49  using ValueType = typename GridType::ValueType;
50  using AlphaType = typename MaskType::ValueType;
52  using RangeType = typename LeafManagerType::LeafRange;
53  using BufferType = typename LeafManagerType::BufferType;
54  static_assert(std::is_floating_point<AlphaType>::value,
55  "openvdb::tools::Filter requires a mask grid with floating-point values");
56 
60  Filter(GridT& grid, InterruptT* interrupt = nullptr)
61  : mGrid(&grid)
62  , mTask(nullptr)
63  , mInterrupter(interrupt)
64  , mMask(nullptr)
65  , mGrainSize(1)
66  , mMinMask(0)
67  , mMaxMask(1)
68  , mInvertMask(false)
69  , mTiles(false) {}
70 
74  Filter(const Filter& other)
75  : mGrid(other.mGrid)
76  , mTask(other.mTask)
77  , mInterrupter(other.mInterrupter)
78  , mMask(other.mMask)
79  , mGrainSize(other.mGrainSize)
80  , mMinMask(other.mMinMask)
81  , mMaxMask(other.mMaxMask)
82  , mInvertMask(other.mInvertMask)
83  , mTiles(other.mTiles) {}
84 
86  int getGrainSize() const { return mGrainSize; }
89  void setGrainSize(int grainsize) { mGrainSize = grainsize; }
90 
92  bool getProcessTiles() const { return mTiles; }
99  void setProcessTiles(bool flag) { mTiles = flag; }
100 
103  AlphaType minMask() const { return mMinMask; }
106  AlphaType maxMask() const { return mMaxMask; }
114  {
115  if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)");
116  mMinMask = min;
117  mMaxMask = max;
118  }
119 
122  bool isMaskInverted() const { return mInvertMask; }
125  void invertMask(bool invert=true) { mInvertMask = invert; }
126 
131  void mean(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
132 
140  void gaussian(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
141 
148  void median(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
149 
153  void offset(ValueType offset, const MaskType* mask = nullptr);
154 
159  void operator()(const RangeType& range) const
160  {
161  if (mTask) mTask(const_cast<Filter*>(this), range);
162  else OPENVDB_THROW(ValueError, "task is undefined - call median(), mean(), etc.");
163  }
164 
165 private:
166  using LeafT = typename TreeType::LeafNodeType;
167  using VoxelIterT = typename LeafT::ValueOnIter;
168  using VoxelCIterT = typename LeafT::ValueOnCIter;
169  using BufferT = typename tree::LeafManager<TreeType>::BufferType;
170  using LeafIterT = typename RangeType::Iterator;
172 
173  void cook(LeafManagerType& leafs);
174 
175  template<size_t Axis>
176  struct Avg {
177  Avg(const GridT* grid, Int32 w): acc(grid->tree()), width(w), frac(1.f/float(2*w+1)) {}
178  inline ValueType operator()(Coord xyz);
179  typename GridT::ConstAccessor acc;
180  const Int32 width;
181  const float frac;
182  };
183 
184  // Private filter methods called by tbb::parallel_for threads
185  template <typename AvgT>
186  void doBox(const RangeType& r, Int32 w);
187  void doBoxX(const RangeType& r, Int32 w) { this->doBox<Avg<0> >(r,w); }
188  void doBoxY(const RangeType& r, Int32 w) { this->doBox<Avg<1> >(r,w); }
189  void doBoxZ(const RangeType& r, Int32 w) { this->doBox<Avg<2> >(r,w); }
190  void doMedian(const RangeType&, int);
191  void doOffset(const RangeType&, ValueType);
193  bool wasInterrupted();
194 
195  GridType* mGrid;
196  typename std::function<void (Filter*, const RangeType&)> mTask;
197  InterruptT* mInterrupter;
198  const MaskType* mMask;
199  int mGrainSize;
200  AlphaType mMinMask, mMaxMask;
201  bool mInvertMask;
202  bool mTiles;
203 }; // end of Filter class
204 
205 
207 
208 
209 namespace filter_internal {
210 
211 template<typename TreeT>
212 struct Voxelizer
213 {
214  // NodeManager for processing internal/root node values
215  // @note Should not cache leaf nodes
216  using NodeManagerT = tree::NodeManager<TreeT, TreeT::RootNodeType::LEVEL-1>;
217  using MaskT = typename TreeT::template ValueConverter<ValueMask>::Type;
218 
219  Voxelizer(TreeT& tree, const bool allNeighbours, const size_t grainSize)
220  : mVoxelTopology()
221  , mManager(nullptr)
222  , mGrainSize(grainSize)
223  , mOp(tree, mVoxelTopology, allNeighbours ? 26 : 6) {}
224 
231  int run(const int width)
232  {
233  if (!mOp.tree().hasActiveTiles()) return 0;
234  this->init();
235  int count = 0;
236  for (int i = 0; i < width; i += int(TreeT::LeafNodeType::DIM), ++count) {
237  if (i > 0) mManager->rebuild();
238  mManager->foreachBottomUp(mOp, mGrainSize > 0, mGrainSize);
239  mOp.tree().topologyUnion(mVoxelTopology);
240  }
241  return count;
242  }
243 
244 private:
245  void init()
246  {
247  if (mManager) {
248  mManager->rebuild();
249  }
250  else {
251  // @note We don't actually need the leaf topology here, just the
252  // internal node structure so that we can generate leaf nodes in parallel
253  mVoxelTopology.topologyUnion(mOp.tree());
254  mManager.reset(new NodeManagerT(mOp.tree()));
255  }
256  }
257 
258  struct CreateVoxelMask
259  {
260  using LeafT = typename TreeT::LeafNodeType;
261  using RootT = typename TreeT::RootNodeType;
262 
263  CreateVoxelMask(TreeT& tree, MaskT& mask, const size_t NN)
264  : mTree(tree), mVoxelTopology(mask), mNeighbours(NN) {}
265 
266  TreeT& tree() { return mTree; }
267 
268  // do nothing for leaf nodes. They shouldn't even be cached as
269  // part of the NodeManager used with this method.
270  void operator()(const LeafT&) const { assert(false); }
271 
272  void operator()(const RootT& node) const
273  {
274  using ChildT = typename RootT::ChildNodeType;
275  static constexpr Int32 CHILDDIM = Int32(ChildT::DIM);
276  static constexpr Int32 LEAFDIM = Int32(LeafT::DIM);
277  const Tester op(mTree, mNeighbours);
278 
279  auto step =
280  [&](const Coord& ijk,
281  const size_t axis1,
282  const size_t axis2,
283  const auto& val)
284  {
285  Coord offset(0);
286  Int32& a = offset[axis1];
287  Int32& b = offset[axis2];
288  for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
289  for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
290  const Coord childijk = ijk + offset;
291  if (op.test(childijk, val)) {
292  mVoxelTopology.touchLeaf(childijk);
293  }
294  }
295  }
296 
297  offset.reset(CHILDDIM-1);
298  for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
299  for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
300  const Coord childijk = ijk + offset;
301  if (op.test(childijk, val)) {
302  mVoxelTopology.touchLeaf(childijk);
303  }
304  }
305  }
306  };
307 
308  for (auto iter = node.cbeginValueOn(); iter; ++iter) {
309  const Coord& ijk = iter.getCoord();
310  // @todo step only needs to search if a given direction
311  // depending on the face
312  step(ijk, 0, 1, *iter);
313  step(ijk, 0, 2, *iter);
314  step(ijk, 1, 2, *iter);
315  }
316  }
317 
318  template<typename NodeT>
319  void operator()(const NodeT& node) const
320  {
321  using ChildT = typename NodeT::ChildNodeType;
322  static constexpr Int32 CHILDDIM = Int32(ChildT::DIM);
323  static constexpr Int32 LEAFDIM = Int32(LeafT::DIM);
324 
325  static auto step =
326  [](const Tester& op,
327  const Coord& ijk,
328  const size_t axis1,
329  const size_t axis2,
330  const auto& val,
331  std::vector<Coord>& coords)
332  {
333  Coord offset(0);
334  Int32& a = offset[axis1];
335  Int32& b = offset[axis2];
336  for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
337  for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
338  const Coord childijk = ijk + offset;
339  if (op.test(childijk, val)) {
340  coords.emplace_back(childijk);
341  }
342  }
343  }
344 
345  offset.reset(CHILDDIM-1);
346  for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
347  for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
348  const Coord childijk = ijk + offset;
349  if (op.test(childijk, val)) {
350  coords.emplace_back(childijk);
351  }
352  }
353  }
354  };
355 
375 
376  if (CHILDDIM == LEAFDIM) {
377  // If the current node is the parent of leaf nodes, search each
378  // neighbour directly and use a flag buffer to test offsets in
379  // this node which need converting to leaf level topology.
380  // This is faster than the more general method which steps across
381  // faces (unecessary due to CHILDDIM == LEAFDIM) and provides
382  // a simpler way of tracking new topology
383 
384  std::vector<char> flags(NodeT::NUM_VALUES, char(0));
385  tbb::parallel_for(tbb::blocked_range<size_t>(0, NodeT::NUM_VALUES),
386  [&](const tbb::blocked_range<size_t>& range) {
387  const Tester op(mTree, mNeighbours);
388  for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
389  if (node.isValueMaskOn(Index(n))) {
390  // if index is a tile, search its neighbours
391  const Coord ijk = node.offsetToGlobalCoord(Index(n));
392  flags[n] = op.test(ijk, node.getValue(ijk));
393  }
394  }
395  });
396 
397  // create leaf level topology in this internal node
398  Index idx = 0;
399  for (auto iter = flags.begin(); iter != flags.end(); ++iter, ++idx) {
400  if (*iter) mVoxelTopology.touchLeaf(node.offsetToGlobalCoord(idx));
401  }
402  }
403  else {
404  // If this is a higher level internal node, we only need to search its
405  // face/edge/vertex neighbours for values which differ or leaf level
406  // topology. When a difference is detected, store the coordinate which
407  // needs to be voxelized.
408  // @todo investigate better threaded impl
409 
410  tbb::concurrent_vector<Coord> nodes;
411  tbb::parallel_for(tbb::blocked_range<size_t>(0, NodeT::NUM_VALUES),
412  [&](const tbb::blocked_range<size_t>& range)
413  {
414  const Tester op(mTree, mNeighbours);
415  std::vector<Coord> coords;
416 
417  for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
418  if (!node.isValueMaskOn(Index(n))) continue;
419 
420  const Coord ijk = node.offsetToGlobalCoord(Index(n));
421  const auto& val = node.getValue(ijk);
422  // @todo step only needs to search if a given direction
423  // depending on the face
424  step(op, ijk, 0, 1, val, coords);
425  step(op, ijk, 0, 2, val, coords);
426  step(op, ijk, 1, 2, val, coords);
427  }
428 
429  if (!coords.empty()) {
430  std::copy(coords.begin(), coords.end(),
431  nodes.grow_by(coords.size()));
432  }
433  });
434 
435  // create leaf level topology in this internal node
436  // @note nodes may contain duplicate coords
437  for (const auto& coord : nodes) {
438  mVoxelTopology.touchLeaf(coord);
439  }
440  }
441  }
442 
443  private:
444  struct Tester
445  {
446  Tester(const TreeT& tree, const size_t NN)
447  : mAcc(tree), mNeighbours(NN) {}
448 
449  inline bool test(const Coord& ijk,
450  const typename TreeT::ValueType& val) const
451  {
452  static constexpr Int32 LEAFDIM = Int32(LeafT::DIM);
453  const Coord* NN = util::COORD_OFFSETS;
454  for (size_t i = 0; i < mNeighbours; ++i, ++NN) {
455  Coord neighbour(*NN);
456  neighbour.x() *= LEAFDIM;
457  neighbour.y() *= LEAFDIM;
458  neighbour.z() *= LEAFDIM;
459  neighbour += ijk;
460  // if a leaf exists, assume its buffer is not constant
461  if (mAcc.getValue(neighbour) != val ||
462  mAcc.probeConstLeaf(neighbour)) {
463  return true;
464  }
465  }
466  return false;
467  }
468  private:
470  const size_t mNeighbours;
471  };
472 
473  private:
474  TreeT& mTree;
475  MaskT& mVoxelTopology;
476  const size_t mNeighbours;
477  };// CreateVoxelMask
478 
479 private:
480  MaskT mVoxelTopology;
481  std::unique_ptr<NodeManagerT> mManager;
482  const size_t mGrainSize;
483  CreateVoxelMask mOp;
484 };
485 
486 // Helper function for Filter::Avg::operator()
487 template<typename T> static inline void accum(T& sum, T addend) { sum += addend; }
488 // Overload for bool ValueType
489 inline void accum(bool& sum, bool addend) { sum = sum || addend; }
490 }
491 
492 
494 
495 
496 template<typename GridT, typename MaskT, typename InterruptT>
497 template<size_t Axis>
498 inline typename GridT::ValueType
500 {
501  ValueType sum = zeroVal<ValueType>();
502  Int32 &i = xyz[Axis], j = i + width;
503  for (i -= width; i <= j; ++i) filter_internal::accum(sum, acc.getValue(xyz));
505  ValueType value = static_cast<ValueType>(sum * frac);
507  return value;
508 }
509 
510 
512 
513 
514 template<typename GridT, typename MaskT, typename InterruptT>
515 inline void
516 Filter<GridT, MaskT, InterruptT>::mean(int width, int iterations, const MaskType* mask)
517 {
518  if (iterations <= 0) return;
519  mMask = mask;
520  const int w = std::max(1, width);
521  const bool serial = mGrainSize == 0;
522 
523  if (mInterrupter) mInterrupter->start("Applying mean filter");
524 
525  std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
526  if (this->getProcessTiles()) {
527  // if performing multiple iterations, also search edge/vertex
528  // neighbours for difference topology.
529  const bool allNeighbours = iterations > 1;
530  // If processing tiles, create a voxelizer and run a single
531  // width based search for tiles that need to be voxelized
532  voxelizer.reset(new filter_internal::Voxelizer<TreeType>
533  (mGrid->tree(), allNeighbours, mGrainSize));
534  if (!voxelizer->run(w)) voxelizer.reset();
535  }
536 
537  LeafManagerType leafs(mGrid->tree(), 1, serial);
538 
539  int iter = 1; // num of leaf level neighbour based searches performed
540  int dist = w; // kernel distance of the current iteration
541  for (int i=0; i<iterations && !this->wasInterrupted(); ++i, dist+=w) {
542  if (i > 0 && voxelizer) {
543  // the total influence distance in voxels of this iteration
544  // minus how far we've already accounted for
545  const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
546  if (remain > 0) {
547  const int searches = voxelizer->run(remain);
548  if (searches == 0) voxelizer.reset();
549  else leafs.rebuild(serial);
550  iter += searches;
551  }
552  }
553 
554  mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
555  this->cook(leafs);
556  // note that the order of the YZ passes are flipped to maintain backwards-compatibility
557  // with an indexing typo in the original logic
558  mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
559  this->cook(leafs);
560  mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
561  this->cook(leafs);
562  }
563 
564  if (mInterrupter) mInterrupter->end();
565 }
566 
567 
568 template<typename GridT, typename MaskT, typename InterruptT>
569 inline void
570 Filter<GridT, MaskT, InterruptT>::gaussian(int width, int iterations, const MaskType* mask)
571 {
572  if (iterations <= 0) return;
573  mMask = mask;
574  const int w = std::max(1, width);
575  const bool serial = mGrainSize == 0;
576 
577  if (mInterrupter) mInterrupter->start("Applying Gaussian filter");
578 
579  std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
580  if (this->getProcessTiles()) {
581  // if performing multiple iterations, also search edge/vertex
582  // neighbours for difference topology.
583  const bool allNeighbours = iterations > 1;
584  // If processing tiles, create a voxelizer and run a single
585  // width based search for tiles that need to be voxelized
586  // @note account for sub iteration due to gaussian filter
587  voxelizer.reset(new filter_internal::Voxelizer<TreeType>
588  (mGrid->tree(), allNeighbours, mGrainSize));
589  if (!voxelizer->run(w*4)) voxelizer.reset();
590  }
591 
592  LeafManagerType leafs(mGrid->tree(), 1, serial);
593 
594  int iter = 1; // num of leaf level neighbour based searches performed
595  int dist = w*4; // kernel distance of the current iteration
596  for (int i=0; i<iterations; ++i, dist+=(w*4)) {
597  if (i > 0 && voxelizer) {
598  // the total influence distance in voxels of this iteration
599  // minus how far we've already accounted for
600  const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
601  if (remain > 0) {
602  const int searches = voxelizer->run(remain);
603  if (searches == 0) voxelizer.reset();
604  else leafs.rebuild(serial);
605  iter += searches;
606  }
607  }
608 
609  for (int n=0; n<4 && !this->wasInterrupted(); ++n) {
610  mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
611  this->cook(leafs);
612  // note that the order of the YZ passes are flipped to maintain backwards-compatibility
613  // with an indexing typo in the original logic
614  mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
615  this->cook(leafs);
616  mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
617  this->cook(leafs);
618  }
619  }
620 
621  if (mInterrupter) mInterrupter->end();
622 }
623 
624 
625 template<typename GridT, typename MaskT, typename InterruptT>
626 inline void
627 Filter<GridT, MaskT, InterruptT>::median(int width, int iterations, const MaskType* mask)
628 {
629  if (iterations <= 0) return;
630  mMask = mask;
631  const int w = std::max(1, width);
632  const bool serial = mGrainSize == 0;
633 
634  if (mInterrupter) mInterrupter->start("Applying median filter");
635 
636  std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
637  if (this->getProcessTiles()) {
638  // If processing tiles, create a voxelizer and run a single
639  // width based search for tiles that need to be voxelized
640  voxelizer.reset(new filter_internal::Voxelizer<TreeType>
641  (mGrid->tree(), /*allNeighbours*/true, mGrainSize));
642  if (!voxelizer->run(w)) voxelizer.reset();
643  }
644 
645  LeafManagerType leafs(mGrid->tree(), 1, serial);
646 
647  mTask = std::bind(&Filter::doMedian, std::placeholders::_1, std::placeholders::_2, w);
648 
649  int iter = 1; // num of leaf level neighbour based searches performed
650  int dist = w; // kernel distance of the current iteration
651  for (int i=0; i<iterations && !this->wasInterrupted(); ++i, dist+=w) {
652  if (i > 0 && voxelizer) {
653  // the total influence distance in voxels of this iteration
654  // minus how far we've already accounted for
655  const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
656  if (remain > 0) {
657  const int searches = voxelizer->run(remain);
658  if (searches == 0) voxelizer.reset();
659  else leafs.rebuild(serial);
660  iter += searches;
661  }
662  }
663 
664  this->cook(leafs);
665  }
666 
667  if (mInterrupter) mInterrupter->end();
668 }
669 
670 
671 template<typename GridT, typename MaskT, typename InterruptT>
672 inline void
674 {
675  mMask = mask;
676 
677  if (mInterrupter) mInterrupter->start("Applying offset");
678 
679  if (this->getProcessTiles()) {
680  // Don't process leaf nodes with the node manager - we'll do them
681  // separately to allow for cleaner branching
682  using NodeManagerT = tree::NodeManager<TreeType, TreeType::RootNodeType::LEVEL-1>;
683  NodeManagerT manager(mGrid->tree());
684 
685  if (mask) {
686  manager.foreachBottomUp([&](auto& node) {
687  this->wasInterrupted();
688  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
689  typename AlphaMaskT::FloatType a, b;
690  for (auto iter = node.beginValueOn(); iter; ++iter) {
691  if (!alpha(iter.getCoord(), a, b)) continue;
693  iter.modifyValue([&](ValueType& v) { v += a*value; });
695  }
696  });
697  }
698  else {
699  manager.foreachBottomUp([&](auto& node) {
700  this->wasInterrupted();
701  for (auto iter = node.beginValueOn(); iter; ++iter) {
702  iter.modifyValue([&](ValueType& v) { v += value; });
703  }
704  });
705  }
706  }
707 
708  LeafManagerType leafs(mGrid->tree(), 0, mGrainSize==0);
709  mTask = std::bind(&Filter::doOffset, std::placeholders::_1, std::placeholders::_2, value);
710  this->cook(leafs);
711 
712  if (mInterrupter) mInterrupter->end();
713 }
714 
715 
717 
718 
721 template<typename GridT, typename MaskT, typename InterruptT>
722 inline void
724 {
725  if (mGrainSize>0) {
726  tbb::parallel_for(leafs.leafRange(mGrainSize), *this);
727  } else {
728  (*this)(leafs.leafRange());
729  }
730  leafs.swapLeafBuffer(1, mGrainSize==0);
731 }
732 
733 
735 template<typename GridT, typename MaskT, typename InterruptT>
736 template <typename AvgT>
737 inline void
739 {
740  this->wasInterrupted();
741  AvgT avg(mGrid, w);
742  if (mMask) {
743  typename AlphaMaskT::FloatType a, b;
744  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
745  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
746  BufferT& buffer = leafIter.buffer(1);
747  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
748  const Coord xyz = iter.getCoord();
749  if (alpha(xyz, a, b)) {
751  const ValueType value(b*(*iter) + a*avg(xyz));
753  buffer.setValue(iter.pos(), value);
754  }
755  }
756  }
757  } else {
758  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
759  BufferT& buffer = leafIter.buffer(1);
760  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
761  buffer.setValue(iter.pos(), avg(iter.getCoord()));
762  }
763  }
764  }
765 }
766 
767 
769 template<typename GridT, typename MaskT, typename InterruptT>
770 inline void
772 {
773  this->wasInterrupted();
774  typename math::DenseStencil<GridType> stencil(*mGrid, width);//creates local cache!
775  if (mMask) {
776  typename AlphaMaskT::FloatType a, b;
777  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
778  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
779  BufferT& buffer = leafIter.buffer(1);
780  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
781  if (alpha(iter.getCoord(), a, b)) {
782  stencil.moveTo(iter);
784  ValueType value(b*(*iter) + a*stencil.median());
786  buffer.setValue(iter.pos(), value);
787  }
788  }
789  }
790  } else {
791  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
792  BufferT& buffer = leafIter.buffer(1);
793  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
794  stencil.moveTo(iter);
795  buffer.setValue(iter.pos(), stencil.median());
796  }
797  }
798  }
799 }
800 
801 
803 template<typename GridT, typename MaskT, typename InterruptT>
804 inline void
806 {
807  this->wasInterrupted();
808  if (mMask) {
809  typename AlphaMaskT::FloatType a, b;
810  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
811  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
812  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
813  if (alpha(iter.getCoord(), a, b)) {
815  ValueType value(*iter + a*offset);
817  iter.setValue(value);
818  }
819  }
820  }
821  } else {
822  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
823  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
824  iter.setValue(*iter + offset);
825  }
826  }
827  }
828 }
829 
830 
831 template<typename GridT, typename MaskT, typename InterruptT>
832 inline bool
834 {
835  if (util::wasInterrupted(mInterrupter)) {
836  tbb::task::self().cancel_group_execution();
837  return true;
838  }
839  return false;
840 }
841 
842 
843 } // namespace tools
844 } // namespace OPENVDB_VERSION_NAME
845 } // namespace openvdb
846 
847 #endif // OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
typename LeafManagerType::LeafRange RangeType
Definition: Filter.h:52
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
Bracket code with OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN/_END, to inhibit warnings about type conve...
Definition: Platform.h:206
void accum(bool &sum, bool addend)
Definition: Filter.h:489
FloatT FloatType
Definition: Interpolation.h:551
void moveTo(const Coord &ijk)
Initialize the stencil buffer with the values of voxel (x, y, z) and its neighbors.
Definition: Stencils.h:1781
int getGrainSize() const
Definition: Filter.h:86
#define OPENVDB_THROW(exception, message)
Definition: openvdb/Exceptions.h:74
void setProcessTiles(bool flag)
Set whether active tiles should also be processed.
Definition: Filter.h:99
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
void setGrainSize(int grainsize)
Set the grain-size used for multi-threading.
Definition: Filter.h:89
Axis
Definition: Math.h:904
NodeManager produces linear arrays of all tree nodes allowing for efficient threading and bottom-up p...
void foreachBottomUp(const NodeOp &op, bool threaded=true, size_t grainSize=1)
Threaded method that applies a user-supplied functor to all the nodes in the tree.
Definition: NodeManager.h:624
typename GridType::ValueType ValueType
Definition: Filter.h:49
To facilitate threading over the nodes of a tree, cache node pointers in linear arrays, one for each level of the tree.
Definition: NodeManager.h:30
Voxelizer(TreeT &tree, const bool allNeighbours, const size_t grainSize)
Definition: Filter.h:219
int run(const int width)
Convert tiles to leaf nodes that exist at a particular voxel distance away.
Definition: Filter.h:231
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END
Definition: Platform.h:207
typename TreeType::LeafNodeType LeafType
Definition: Filter.h:48
Index32 Index
Definition: openvdb/Types.h:50
typename CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition: LeafManager.h:95
bool isMaskInverted() const
Return true if the mask is inverted, i.e. min->max in the original mask maps to 1->0 in the inverted ...
Definition: Filter.h:122
typename tree::LeafManager< TreeType > LeafManagerType
Definition: Filter.h:51
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:107
GridT GridType
Definition: Filter.h:45
void setMaskRange(AlphaType min, AlphaType max)
Define the range for the (optional) scalar mask.
Definition: Filter.h:113
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
void operator()(const RangeType &range) const
Used internally by tbb::parallel_for()
Definition: Filter.h:159
void invertMask(bool invert=true)
Invert the optional mask, i.e. min->max in the original mask maps to 1->0 in the inverted alpha mask...
Definition: Filter.h:125
Definition: openvdb/Exceptions.h:13
ValueType median() const
Return the median value of the current stencil.
Definition: Stencils.h:121
typename GridType::TreeType TreeType
Definition: Filter.h:47
AlphaType minMask() const
Return the minimum value of the mask to be used for the derivation of a smooth alpha value...
Definition: Filter.h:103
Definition: Interpolation.h:543
Dense stencil of a given width.
Definition: Stencils.h:1761
OPENVDB_API const Coord COORD_OFFSETS[26]
coordinate offset table for neighboring voxels
typename MaskType::ValueType AlphaType
Definition: Filter.h:50
Volume filtering (e.g., diffusion) with optional alpha masking.
Definition: Filter.h:42
Definition: openvdb/Exceptions.h:65
int32_t Int32
Definition: openvdb/Types.h:52
Defines various finite difference stencils by means of the "curiously recurring template pattern" on ...
MaskT MaskType
Definition: Filter.h:46
Filter(const Filter &other)
Shallow copy constructor called by tbb::parallel_for() threads during filtering.
Definition: Filter.h:74
A LeafManager manages a linear array of pointers to a given tree&#39;s leaf nodes, as well as optional au...
Filter(GridT &grid, InterruptT *interrupt=nullptr)
Definition: Filter.h:60
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:103
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
bool getProcessTiles() const
Definition: Filter.h:92
typename LeafManagerType::BufferType BufferType
Definition: Filter.h:53
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:178
AlphaType maxMask() const
Return the maximum value of the mask to be used for the derivation of a smooth alpha value...
Definition: Filter.h:106