14#ifndef OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
15#define OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
30#include <tbb/parallel_for.h>
31#include <tbb/concurrent_vector.h>
43template<
typename GridT,
44 typename MaskT =
typename GridT::template ValueConverter<float>::Type,
45 typename InterruptT = util::NullInterrupter>
52 using LeafType =
typename TreeType::LeafNodeType;
56 using RangeType =
typename LeafManagerType::LeafRange;
58 static_assert(std::is_floating_point<AlphaType>::value,
59 "openvdb::tools::Filter requires a mask grid with floating-point values");
64 Filter(GridT& grid, InterruptT* interrupt =
nullptr)
67 , mInterrupter(interrupt)
81 , mInterrupter(other.mInterrupter)
83 , mGrainSize(other.mGrainSize)
84 , mMinMask(other.mMinMask)
85 , mMaxMask(other.mMaxMask)
86 , mInvertMask(other.mInvertMask)
87 , mTiles(other.mTiles) {}
135 void mean(
int width = 1,
int iterations = 1,
const MaskType* mask =
nullptr);
144 void gaussian(
int width = 1,
int iterations = 1,
const MaskType* mask =
nullptr);
152 void median(
int width = 1,
int iterations = 1,
const MaskType* mask =
nullptr);
157 void offset(ValueType offset,
const MaskType* mask =
nullptr);
165 if (mTask) mTask(
const_cast<Filter*
>(
this), range);
170 using LeafT =
typename TreeType::LeafNodeType;
171 using VoxelIterT =
typename LeafT::ValueOnIter;
172 using VoxelCIterT =
typename LeafT::ValueOnCIter;
174 using LeafIterT =
typename RangeType::Iterator;
177 void cook(LeafManagerType& leafs);
179 template<
size_t Axis>
181 Avg(
const GridT* grid,
Int32 w): acc(grid->
tree()), width(w), frac(1.f/float(2*w+1)) {}
182 inline ValueType operator()(
Coord xyz);
183 typename GridT::ConstAccessor acc;
189 template <
typename AvgT>
190 void doBox(
const RangeType& r,
Int32 w);
191 void doBoxX(
const RangeType& r,
Int32 w) { this->doBox<Avg<0> >(r,w); }
192 void doBoxY(
const RangeType& r, Int32 w) { this->doBox<Avg<1> >(r,w); }
193 void doBoxZ(
const RangeType& r, Int32 w) { this->doBox<Avg<2> >(r,w); }
194 void doMedian(
const RangeType&,
int);
195 void doOffset(
const RangeType&, ValueType);
200 typename std::function<void (Filter*,
const RangeType&)> mTask;
201 InterruptT* mInterrupter;
202 const MaskType* mMask;
204 AlphaType mMinMask, mMaxMask;
214namespace filter_internal {
216template<
typename TreeT>
221 using NodeManagerT = tree::NodeManager<TreeT, TreeT::RootNodeType::LEVEL-1>;
222 using MaskT =
typename TreeT::template ValueConverter<ValueMask>::Type;
224 Voxelizer(TreeT& tree,
const bool allNeighbors,
const size_t grainSize)
227 , mGrainSize(grainSize)
228 , mOp(tree, mVoxelTopology, allNeighbors ? 26 : 6) {}
236 int run(
const int width)
238 if (!mOp.tree().hasActiveTiles())
return 0;
241 for (
int i = 0; i < width; i += int(TreeT::LeafNodeType::DIM), ++count) {
242 if (i > 0) mManager->rebuild();
243 mManager->foreachBottomUp(mOp, mGrainSize > 0, mGrainSize);
244 mOp.tree().topologyUnion(mVoxelTopology);
258 mVoxelTopology.topologyUnion(mOp.tree());
259 mManager.reset(
new NodeManagerT(mOp.tree()));
263 struct CreateVoxelMask
265 using LeafT =
typename TreeT::LeafNodeType;
266 using RootT =
typename TreeT::RootNodeType;
268 CreateVoxelMask(TreeT& tree, MaskT& mask,
const size_t NN)
269 : mTree(tree), mVoxelTopology(mask), mNeighbors(NN) {}
271 TreeT& tree() {
return mTree; }
277 void operator()(
const RootT& node)
const
279 using ChildT =
typename RootT::ChildNodeType;
280 static constexpr Int32 CHILDDIM =
Int32(ChildT::DIM);
281 static constexpr Int32 LEAFDIM =
Int32(LeafT::DIM);
282 const Tester op(mTree, mNeighbors);
285 [&](
const Coord& ijk,
291 Int32& a = offset[axis1];
292 Int32& b = offset[axis2];
293 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
294 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
295 const Coord childijk = ijk + offset;
296 if (op.test(childijk, val)) {
297 mVoxelTopology.touchLeaf(childijk);
302 offset.reset(CHILDDIM-1);
303 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
304 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
305 const Coord childijk = ijk + offset;
306 if (op.test(childijk, val)) {
307 mVoxelTopology.touchLeaf(childijk);
313 for (
auto iter = node.cbeginValueOn(); iter; ++iter) {
314 const Coord& ijk = iter.getCoord();
317 step(ijk, 0, 1, *iter);
318 step(ijk, 0, 2, *iter);
319 step(ijk, 1, 2, *iter);
323 template<
typename NodeT>
324 void operator()(
const NodeT& node)
const
326 using ChildT =
typename NodeT::ChildNodeType;
327 static constexpr Int32 CHILDDIM =
Int32(ChildT::DIM);
328 static constexpr Int32 LEAFDIM =
Int32(LeafT::DIM);
336 std::vector<Coord>& coords)
339 Int32& a = offset[axis1];
340 Int32& b = offset[axis2];
341 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
342 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
343 const Coord childijk = ijk + offset;
344 if (op.test(childijk, val)) {
345 coords.emplace_back(childijk);
350 offset.reset(CHILDDIM-1);
351 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
352 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
353 const Coord childijk = ijk + offset;
354 if (op.test(childijk, val)) {
355 coords.emplace_back(childijk);
381 if (CHILDDIM == LEAFDIM) {
389 std::vector<char> flags(NodeT::NUM_VALUES,
char(0));
390 tbb::parallel_for(tbb::blocked_range<size_t>(0, NodeT::NUM_VALUES),
391 [&](
const tbb::blocked_range<size_t>& range) {
392 const Tester op(mTree, mNeighbors);
393 for (
size_t n = range.begin(), N = range.end(); n < N; ++n) {
394 if (node.isValueMaskOn(
Index(n))) {
396 const Coord ijk = node.offsetToGlobalCoord(
Index(n));
397 flags[n] = op.test(ijk, node.getValue(ijk));
404 for (
auto iter = flags.begin(); iter != flags.end(); ++iter, ++idx) {
405 if (*iter) mVoxelTopology.touchLeaf(node.offsetToGlobalCoord(idx));
415 tbb::concurrent_vector<Coord> nodes;
416 tbb::parallel_for(tbb::blocked_range<size_t>(0, NodeT::NUM_VALUES),
417 [&](
const tbb::blocked_range<size_t>& range)
419 const Tester op(mTree, mNeighbors);
420 std::vector<Coord> coords;
422 for (
size_t n = range.begin(), N = range.end(); n < N; ++n) {
423 if (!node.isValueMaskOn(
Index(n)))
continue;
425 const Coord ijk = node.offsetToGlobalCoord(
Index(n));
426 const auto& val = node.getValue(ijk);
429 step(op, ijk, 0, 1, val, coords);
430 step(op, ijk, 0, 2, val, coords);
431 step(op, ijk, 1, 2, val, coords);
434 if (!coords.empty()) {
435 std::copy(coords.begin(), coords.end(),
436 nodes.grow_by(coords.size()));
442 for (
const auto& coord : nodes) {
443 mVoxelTopology.touchLeaf(coord);
451 Tester(
const TreeT& tree,
const size_t NN)
452 : mAcc(tree), mNeighbors(NN) {}
454 inline bool test(
const Coord& ijk,
455 const typename TreeT::ValueType& val)
const
457 static constexpr Int32 LEAFDIM =
Int32(LeafT::DIM);
458 const Coord* NN = util::COORD_OFFSETS;
459 for (
size_t i = 0; i < mNeighbors; ++i, ++NN) {
461 neighbor.x() *= LEAFDIM;
462 neighbor.y() *= LEAFDIM;
463 neighbor.z() *= LEAFDIM;
466 if (mAcc.getValue(neighbor) != val ||
467 mAcc.probeConstLeaf(neighbor)) {
474 const tree::ValueAccessor<const TreeT> mAcc;
475 const size_t mNeighbors;
480 MaskT& mVoxelTopology;
481 const size_t mNeighbors;
485 MaskT mVoxelTopology;
486 std::unique_ptr<NodeManagerT> mManager;
487 const size_t mGrainSize;
492template<
typename T>
static inline void accum(T& sum, T addend) { sum += addend; }
494inline void accum(
bool& sum,
bool addend) { sum = sum || addend; }
503template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
505inline typename GridT::ValueType
506Filter<GridT, MaskT, InterruptT>::Avg<Axis>::operator()(Coord xyz)
508 ValueType sum = zeroVal<ValueType>();
510 for (i -= width; i <= j; ++i) filter_internal::accum(sum, acc.getValue(xyz));
511 if constexpr(std::is_same<ValueType, bool>::value) {
512 return sum && frac > 0.0f;
515 ValueType value =
static_cast<ValueType
>(sum * frac);
525template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
529 if (iterations <= 0)
return;
531 const int w = std::max(1, width);
532 const bool serial = mGrainSize == 0;
534 if (mInterrupter) mInterrupter->start(
"Applying mean filter");
536 std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
540 const bool allNeighbors = iterations > 1;
543 voxelizer.reset(
new filter_internal::Voxelizer<TreeType>
544 (mGrid->tree(), allNeighbors, mGrainSize));
545 if (!voxelizer->run(w)) voxelizer.reset();
552 for (
int i=0; i<iterations && !this->wasInterrupted(); ++i, dist+=w) {
553 if (i > 0 && voxelizer) {
556 const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
558 const int searches = voxelizer->run(remain);
559 if (searches == 0) voxelizer.reset();
560 else leafs.rebuild(serial);
565 mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
569 mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
571 mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
575 if (mInterrupter) mInterrupter->end();
579template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
583 if (iterations <= 0)
return;
585 const int w = std::max(1, width);
586 const bool serial = mGrainSize == 0;
588 if (mInterrupter) mInterrupter->start(
"Applying Gaussian filter");
590 std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
594 const bool allNeighbors = iterations > 1;
598 voxelizer.reset(
new filter_internal::Voxelizer<TreeType>
599 (mGrid->tree(), allNeighbors, mGrainSize));
600 if (!voxelizer->run(w*4)) voxelizer.reset();
607 for (
int i=0; i<iterations; ++i, dist+=(w*4)) {
608 if (i > 0 && voxelizer) {
611 const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
613 const int searches = voxelizer->run(remain);
614 if (searches == 0) voxelizer.reset();
615 else leafs.rebuild(serial);
620 for (
int n=0; n<4 && !this->wasInterrupted(); ++n) {
621 mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
625 mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
627 mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
632 if (mInterrupter) mInterrupter->end();
636template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
640 if (iterations <= 0)
return;
642 const int w = std::max(1, width);
643 const bool serial = mGrainSize == 0;
645 if (mInterrupter) mInterrupter->start(
"Applying median filter");
647 std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
651 voxelizer.reset(
new filter_internal::Voxelizer<TreeType>
652 (mGrid->tree(),
true, mGrainSize));
653 if (!voxelizer->run(w)) voxelizer.reset();
658 mTask = std::bind(&Filter::doMedian, std::placeholders::_1, std::placeholders::_2, w);
662 for (
int i=0; i<iterations && !this->wasInterrupted(); ++i, dist+=w) {
663 if (i > 0 && voxelizer) {
666 const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
668 const int searches = voxelizer->run(remain);
669 if (searches == 0) voxelizer.reset();
670 else leafs.rebuild(serial);
678 if (mInterrupter) mInterrupter->end();
682template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
688 if (mInterrupter) mInterrupter->start(
"Applying offset");
694 NodeManagerT manager(mGrid->tree());
697 manager.foreachBottomUp([&](
auto& node) {
698 this->wasInterrupted();
699 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
701 for (
auto iter = node.beginValueOn(); iter; ++iter) {
702 if (!alpha(iter.getCoord(), a, b))
continue;
704 iter.modifyValue([&](
ValueType& v) { v += a*value; });
710 manager.foreachBottomUp([&](
auto& node) {
711 this->wasInterrupted();
712 for (
auto iter = node.beginValueOn(); iter; ++iter) {
713 iter.modifyValue([&](
ValueType& v) { v += value; });
720 mTask = std::bind(&Filter::doOffset, std::placeholders::_1, std::placeholders::_2, value);
723 if (mInterrupter) mInterrupter->end();
732template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
734Filter<GridT, MaskT, InterruptT>::cook(LeafManagerType& leafs)
737 tbb::parallel_for(leafs.leafRange(mGrainSize), *
this);
739 (*this)(leafs.leafRange());
741 leafs.swapLeafBuffer(1, mGrainSize==0);
746template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
747template <
typename AvgT>
749Filter<GridT, MaskT, InterruptT>::doBox(
const RangeType& range, Int32 w)
751 this->wasInterrupted();
754 typename AlphaMaskT::FloatType a, b;
755 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
756 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
757 BufferT& buffer = leafIter.buffer(1);
758 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
759 const Coord xyz = iter.getCoord();
760 if (alpha(xyz, a, b)) {
762 const ValueType value(b*(*iter) + a*avg(xyz));
764 buffer.setValue(iter.pos(), value);
769 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
770 BufferT& buffer = leafIter.buffer(1);
771 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
772 buffer.setValue(iter.pos(), avg(iter.getCoord()));
780template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
782Filter<GridT, MaskT, InterruptT>::doMedian(
const RangeType& range,
int width)
785 typename math::DenseStencil<GridType> stencil(*mGrid, width);
787 typename AlphaMaskT::FloatType a, b;
788 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
789 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
790 BufferT& buffer = leafIter.buffer(1);
791 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
792 if (alpha(iter.getCoord(), a, b)) {
793 stencil.moveTo(iter);
795 ValueType value(b*(*iter) + a*stencil.median());
797 buffer.setValue(iter.pos(), value);
802 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
803 BufferT& buffer = leafIter.buffer(1);
804 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
805 stencil.moveTo(iter);
806 buffer.setValue(iter.pos(), stencil.median());
814template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
816Filter<GridT, MaskT, InterruptT>::doOffset(
const RangeType& range, ValueType offset)
820 typename AlphaMaskT::FloatType a, b;
821 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
822 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
823 for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
824 if (alpha(iter.getCoord(), a, b)) {
826 ValueType value(*iter + a*offset);
828 iter.setValue(value);
833 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
834 for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
835 iter.setValue(*iter + offset);
842template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
844Filter<GridT, MaskT, InterruptT>::wasInterrupted()
846 if (util::wasInterrupted(mInterrupter)) {
847 thread::cancelGroupExecution();
859#ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
861#ifdef OPENVDB_INSTANTIATE_FILTER
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
Definition Exceptions.h:65
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:26
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition LeafManager.h:86
typename CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition LeafManager.h:96
To facilitate threading over the nodes of a tree, cache node pointers in linear arrays,...
Definition NodeManager.h:532
GridType
List of types that are currently supported by NanoVDB.
Definition NanoVDB.h:219
OPENVDB_AX_API void run(const char *ax, openvdb::GridBase &grid, const AttributeBindings &bindings={})
Run a full AX pipeline (parse, compile and execute) on a single OpenVDB Grid.
Axis
Definition Math.h:969
Definition PointDataGrid.h:170
bool wasInterrupted(T *i, int percent=-1)
Definition NullInterrupter.h:49
Index32 Index
Definition Types.h:34
int32_t Int32
Definition Types.h:36
Definition Exceptions.h:13
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
Defines various finite difference stencils by means of the "curiously recurring template pattern" on ...
NodeManager produces linear arrays of all tree nodes allowing for efficient threading and bottom-up p...
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:291
#define OPENVDB_INSTANTIATE_CLASS
Definition version.h.in:231