OpenVDB: Filter.h Source File

OpenVDB 8.1.0

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>
22 #include <openvdb/tree/NodeManager.h>
23 #include <openvdb/tree/LeafManager.h>
24 #include <openvdb/util/NullInterrupter.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 {
34 OPENVDB_USE_VERSION_NAMESPACE
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;
51 using LeafManagerType = typename tree::LeafManager<TreeType>;
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; }
113 void setMaskRange(AlphaType min, AlphaType max)
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;
171 using AlphaMaskT = tools::AlphaMask<GridT, MaskT>;
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:
469 const tree::ValueAccessor<const TreeT> mAcc;
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
499 Filter<GridT, MaskT, InterruptT>::Avg<Axis>::operator()(Coord xyz)
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));
504 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
505 ValueType value = static_cast<ValueType>(sum * frac);
506 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
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
673 Filter<GridT, MaskT, InterruptT>::offset(ValueType value, const MaskType* mask)
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;
692 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
693 iter.modifyValue([&](ValueType& v) { v += a*value; });
694 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
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
723 Filter<GridT, MaskT, InterruptT>::cook(LeafManagerType& leafs)
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
738 Filter<GridT, MaskT, InterruptT>::doBox(const RangeType& range, Int32 w)
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)) {
750 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
751 const ValueType value(b*(*iter) + a*avg(xyz));
752 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
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
771 Filter<GridT, MaskT, InterruptT>::doMedian(const RangeType& range, int width)
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);
783 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
784 ValueType value(b*(*iter) + a*stencil.median());
785 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
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
805 Filter<GridT, MaskT, InterruptT>::doOffset(const RangeType& range, ValueType offset)
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)) {
814 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
815 ValueType value(*iter + a*offset);
816 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
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
833 Filter<GridT, MaskT, InterruptT>::wasInterrupted()
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
Grid.h
Interpolation.h
LeafManager.h
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
Math.h
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
NodeManager.h
NodeManager produces linear arrays of all tree nodes allowing for efficient threading and bottom-up p...
NullInterrupter.h
OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
#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
OPENVDB_NO_TYPE_CONVERSION_WARNING_END
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END
Definition: Platform.h:207
Stencils.h
Defines various finite difference stencils by means of the "curiously recurring template pattern" on ...
Transform.h
Util.h
openvdb::v8_1::ValueError
Definition: openvdb/Exceptions.h:65
openvdb::v8_1::math::Coord
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:26
openvdb::v8_1::tools::AlphaMask
Definition: Interpolation.h:544
openvdb::v8_1::tools::AlphaMask::FloatType
FloatT FloatType
Definition: Interpolation.h:551
openvdb::v8_1::tools::Filter
Volume filtering (e.g., diffusion) with optional alpha masking.
Definition: Filter.h:43
openvdb::v8_1::tools::Filter::getProcessTiles
bool getProcessTiles() const
Definition: Filter.h:92
openvdb::v8_1::tools::Filter::AlphaType
typename MaskType::ValueType AlphaType
Definition: Filter.h:50
openvdb::v8_1::tools::Filter::LeafManagerType
typename tree::LeafManager< TreeType > LeafManagerType
Definition: Filter.h:51
openvdb::v8_1::tools::Filter::minMask
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
openvdb::v8_1::tools::Filter::setProcessTiles
void setProcessTiles(bool flag)
Set whether active tiles should also be processed.
Definition: Filter.h:99
openvdb::v8_1::tools::Filter::setGrainSize
void setGrainSize(int grainsize)
Set the grain-size used for multi-threading.
Definition: Filter.h:89
openvdb::v8_1::tools::Filter::invertMask
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
openvdb::v8_1::tools::Filter::getGrainSize
int getGrainSize() const
Definition: Filter.h:86
openvdb::v8_1::tools::Filter::ValueType
typename GridType::ValueType ValueType
Definition: Filter.h:49
openvdb::v8_1::tools::Filter::TreeType
typename GridType::TreeType TreeType
Definition: Filter.h:47
openvdb::v8_1::tools::Filter::setMaskRange
void setMaskRange(AlphaType min, AlphaType max)
Define the range for the (optional) scalar mask.
Definition: Filter.h:113
openvdb::v8_1::tools::Filter::maxMask
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
openvdb::v8_1::tools::Filter::Filter
Filter(const Filter &other)
Shallow copy constructor called by tbb::parallel_for() threads during filtering.
Definition: Filter.h:74
openvdb::v8_1::tools::Filter::operator()
void operator()(const RangeType &range) const
Used internally by tbb::parallel_for()
Definition: Filter.h:159
openvdb::v8_1::tools::Filter::RangeType
typename LeafManagerType::LeafRange RangeType
Definition: Filter.h:52
openvdb::v8_1::tools::Filter::BufferType
typename LeafManagerType::BufferType BufferType
Definition: Filter.h:53
openvdb::v8_1::tools::Filter::MaskType
MaskT MaskType
Definition: Filter.h:46
openvdb::v8_1::tools::Filter::LeafType
typename TreeType::LeafNodeType LeafType
Definition: Filter.h:48
openvdb::v8_1::tools::Filter::isMaskInverted
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
openvdb::v8_1::tools::Filter::GridType
GridT GridType
Definition: Filter.h:45
openvdb::v8_1::tools::Filter::Filter
Filter(GridT &grid, InterruptT *interrupt=nullptr)
Definition: Filter.h:60
openvdb::v8_1::tree::LeafManager< TreeType >
openvdb::v8_1::tree::LeafManager::BufferType
typename CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition: LeafManager.h:95
openvdb::v8_1::tree::NodeManager
To facilitate threading over the nodes of a tree, cache node pointers in linear arrays,...
Definition: NodeManager.h:531
openvdb::v8_1::math::Axis
Axis
Definition: Math.h:904
openvdb::v8_1::tools::composite::max
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:107
openvdb::v8_1::tools::composite::min
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:103
openvdb::v8_1::tools::filter_internal::accum
void accum(bool &sum, bool addend)
Definition: Filter.h:489
openvdb::v8_1::util::COORD_OFFSETS
OPENVDB_API const Coord COORD_OFFSETS[26]
coordinate offset table for neighboring voxels
openvdb::v8_1::util::wasInterrupted
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
openvdb::v8_1::Index
Index32 Index
Definition: openvdb/Types.h:50
openvdb::v8_1::Int32
int32_t Int32
Definition: openvdb/Types.h:52
openvdb
Definition: openvdb/Exceptions.h:13
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition: openvdb/Exceptions.h:74
Types.h
openvdb::v8_1::tools::filter_internal::Voxelizer
Definition: Filter.h:213
openvdb::v8_1::tools::filter_internal::Voxelizer::run
int run(const int width)
Convert tiles to leaf nodes that exist at a particular voxel distance away.
Definition: Filter.h:231
openvdb::v8_1::tools::filter_internal::Voxelizer::Voxelizer
Voxelizer(TreeT &tree, const bool allNeighbours, const size_t grainSize)
Definition: Filter.h:219
openvdb::v8_1::tools::filter_internal::Voxelizer::MaskT
typename TreeT::template ValueConverter< ValueMask >::Type MaskT
Definition: Filter.h:217
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:178