14#ifndef OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
15#define OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
27#include <openvdb/thread/Threading.h>
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...
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
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:901
Definition PointDataGrid.h:170
bool wasInterrupted(T *i, int percent=-1)
Definition NullInterrupter.h:49
Index32 Index
Definition Types.h:54
int32_t Int32
Definition Types.h:56
Definition Exceptions.h:13
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
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:218
#define OPENVDB_INSTANTIATE_CLASS
Definition version.h.in:158