14#ifndef OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
15#define OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
26#include <openvdb/thread/Threading.h>
29#include <tbb/parallel_for.h>
30#include <tbb/concurrent_vector.h>
42template<
typename GridT,
43 typename MaskT =
typename GridT::template ValueConverter<float>::Type,
44 typename InterruptT = util::NullInterrupter>
51 using LeafType =
typename TreeType::LeafNodeType;
55 using RangeType =
typename LeafManagerType::LeafRange;
57 static_assert(std::is_floating_point<AlphaType>::value,
58 "openvdb::tools::Filter requires a mask grid with floating-point values");
63 Filter(GridT& grid, InterruptT* interrupt =
nullptr)
66 , mInterrupter(interrupt)
80 , mInterrupter(other.mInterrupter)
82 , mGrainSize(other.mGrainSize)
83 , mMinMask(other.mMinMask)
84 , mMaxMask(other.mMaxMask)
85 , mInvertMask(other.mInvertMask)
86 , mTiles(other.mTiles) {}
134 void mean(
int width = 1,
int iterations = 1,
const MaskType* mask =
nullptr);
143 void gaussian(
int width = 1,
int iterations = 1,
const MaskType* mask =
nullptr);
151 void median(
int width = 1,
int iterations = 1,
const MaskType* mask =
nullptr);
156 void offset(ValueType offset,
const MaskType* mask =
nullptr);
164 if (mTask) mTask(
const_cast<Filter*
>(
this), range);
169 using LeafT =
typename TreeType::LeafNodeType;
170 using VoxelIterT =
typename LeafT::ValueOnIter;
171 using VoxelCIterT =
typename LeafT::ValueOnCIter;
173 using LeafIterT =
typename RangeType::Iterator;
176 void cook(LeafManagerType& leafs);
178 template<
size_t Axis>
180 Avg(
const GridT* grid,
Int32 w): acc(grid->tree()), width(w), frac(1.f/float(2*w+1)) {}
181 inline ValueType operator()(
Coord xyz);
182 typename GridT::ConstAccessor acc;
188 template <
typename AvgT>
189 void doBox(
const RangeType& r,
Int32 w);
190 void doBoxX(
const RangeType& r,
Int32 w) { this->doBox<Avg<0> >(r,w); }
191 void doBoxY(
const RangeType& r, Int32 w) { this->doBox<Avg<1> >(r,w); }
192 void doBoxZ(
const RangeType& r, Int32 w) { this->doBox<Avg<2> >(r,w); }
193 void doMedian(
const RangeType&,
int);
194 void doOffset(
const RangeType&, ValueType);
199 typename std::function<void (Filter*,
const RangeType&)> mTask;
200 InterruptT* mInterrupter;
201 const MaskType* mMask;
203 AlphaType mMinMask, mMaxMask;
213namespace filter_internal {
215template<
typename TreeT>
220 using NodeManagerT = tree::NodeManager<TreeT, TreeT::RootNodeType::LEVEL-1>;
221 using MaskT =
typename TreeT::template ValueConverter<ValueMask>::Type;
223 Voxelizer(TreeT& tree,
const bool allNeighbors,
const size_t grainSize)
226 , mGrainSize(grainSize)
227 , mOp(tree, mVoxelTopology, allNeighbors ? 26 : 6) {}
235 int run(
const int width)
237 if (!mOp.tree().hasActiveTiles())
return 0;
240 for (
int i = 0; i < width; i += int(TreeT::LeafNodeType::DIM), ++count) {
241 if (i > 0) mManager->rebuild();
242 mManager->foreachBottomUp(mOp, mGrainSize > 0, mGrainSize);
243 mOp.tree().topologyUnion(mVoxelTopology);
257 mVoxelTopology.topologyUnion(mOp.tree());
258 mManager.reset(
new NodeManagerT(mOp.tree()));
262 struct CreateVoxelMask
264 using LeafT =
typename TreeT::LeafNodeType;
265 using RootT =
typename TreeT::RootNodeType;
267 CreateVoxelMask(TreeT& tree, MaskT& mask,
const size_t NN)
268 : mTree(tree), mVoxelTopology(mask), mNeighbors(NN) {}
270 TreeT& tree() {
return mTree; }
274 void operator()(
const LeafT&)
const { assert(
false); }
276 void operator()(
const RootT& node)
const
278 using ChildT =
typename RootT::ChildNodeType;
279 static constexpr Int32 CHILDDIM =
Int32(ChildT::DIM);
280 static constexpr Int32 LEAFDIM =
Int32(LeafT::DIM);
281 const Tester op(mTree, mNeighbors);
284 [&](
const Coord& ijk,
290 Int32& a = offset[axis1];
291 Int32& b = offset[axis2];
292 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
293 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
294 const Coord childijk = ijk + offset;
295 if (op.test(childijk, val)) {
296 mVoxelTopology.touchLeaf(childijk);
301 offset.reset(CHILDDIM-1);
302 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
303 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
304 const Coord childijk = ijk + offset;
305 if (op.test(childijk, val)) {
306 mVoxelTopology.touchLeaf(childijk);
312 for (
auto iter = node.cbeginValueOn(); iter; ++iter) {
313 const Coord& ijk = iter.getCoord();
316 step(ijk, 0, 1, *iter);
317 step(ijk, 0, 2, *iter);
318 step(ijk, 1, 2, *iter);
322 template<
typename NodeT>
323 void operator()(
const NodeT& node)
const
325 using ChildT =
typename NodeT::ChildNodeType;
326 static constexpr Int32 CHILDDIM =
Int32(ChildT::DIM);
327 static constexpr Int32 LEAFDIM =
Int32(LeafT::DIM);
335 std::vector<Coord>& coords)
338 Int32& a = offset[axis1];
339 Int32& b = offset[axis2];
340 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
341 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
342 const Coord childijk = ijk + offset;
343 if (op.test(childijk, val)) {
344 coords.emplace_back(childijk);
349 offset.reset(CHILDDIM-1);
350 for (a = 0; a < CHILDDIM; a+=LEAFDIM) {
351 for (b = 0; b < CHILDDIM; b+=LEAFDIM) {
352 const Coord childijk = ijk + offset;
353 if (op.test(childijk, val)) {
354 coords.emplace_back(childijk);
380 if (CHILDDIM == LEAFDIM) {
388 std::vector<char> flags(NodeT::NUM_VALUES,
char(0));
389 tbb::parallel_for(tbb::blocked_range<size_t>(0, NodeT::NUM_VALUES),
390 [&](
const tbb::blocked_range<size_t>& range) {
391 const Tester op(mTree, mNeighbors);
392 for (
size_t n = range.begin(), N = range.end(); n < N; ++n) {
393 if (node.isValueMaskOn(
Index(n))) {
395 const Coord ijk = node.offsetToGlobalCoord(
Index(n));
396 flags[n] = op.test(ijk, node.getValue(ijk));
403 for (
auto iter = flags.begin(); iter != flags.end(); ++iter, ++idx) {
404 if (*iter) mVoxelTopology.touchLeaf(node.offsetToGlobalCoord(idx));
414 tbb::concurrent_vector<Coord> nodes;
415 tbb::parallel_for(tbb::blocked_range<size_t>(0, NodeT::NUM_VALUES),
416 [&](
const tbb::blocked_range<size_t>& range)
418 const Tester op(mTree, mNeighbors);
419 std::vector<Coord> coords;
421 for (
size_t n = range.begin(), N = range.end(); n < N; ++n) {
422 if (!node.isValueMaskOn(
Index(n)))
continue;
424 const Coord ijk = node.offsetToGlobalCoord(
Index(n));
425 const auto& val = node.getValue(ijk);
428 step(op, ijk, 0, 1, val, coords);
429 step(op, ijk, 0, 2, val, coords);
430 step(op, ijk, 1, 2, val, coords);
433 if (!coords.empty()) {
434 std::copy(coords.begin(), coords.end(),
435 nodes.grow_by(coords.size()));
441 for (
const auto& coord : nodes) {
442 mVoxelTopology.touchLeaf(coord);
450 Tester(
const TreeT& tree,
const size_t NN)
451 : mAcc(tree), mNeighbors(NN) {}
453 inline bool test(
const Coord& ijk,
454 const typename TreeT::ValueType& val)
const
456 static constexpr Int32 LEAFDIM =
Int32(LeafT::DIM);
457 const Coord* NN = util::COORD_OFFSETS;
458 for (
size_t i = 0; i < mNeighbors; ++i, ++NN) {
460 neighbor.x() *= LEAFDIM;
461 neighbor.y() *= LEAFDIM;
462 neighbor.z() *= LEAFDIM;
465 if (mAcc.getValue(neighbor) != val ||
466 mAcc.probeConstLeaf(neighbor)) {
473 const tree::ValueAccessor<const TreeT> mAcc;
474 const size_t mNeighbors;
479 MaskT& mVoxelTopology;
480 const size_t mNeighbors;
484 MaskT mVoxelTopology;
485 std::unique_ptr<NodeManagerT> mManager;
486 const size_t mGrainSize;
491template<
typename T>
static inline void accum(T& sum, T addend) { sum += addend; }
493inline void accum(
bool& sum,
bool addend) { sum = sum || addend; }
502template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
504inline typename GridT::ValueType
505Filter<GridT, MaskT, InterruptT>::Avg<Axis>::operator()(Coord xyz)
507 ValueType sum = zeroVal<ValueType>();
509 for (i -= width; i <= j; ++i) filter_internal::accum(sum, acc.getValue(xyz));
511 ValueType
value =
static_cast<ValueType
>(sum * frac);
520template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
524 if (iterations <= 0)
return;
526 const int w = std::max(1, width);
527 const bool serial = mGrainSize == 0;
529 if (mInterrupter) mInterrupter->start(
"Applying mean filter");
531 std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
532 if (this->getProcessTiles()) {
535 const bool allNeighbors = iterations > 1;
538 voxelizer.reset(
new filter_internal::Voxelizer<TreeType>
539 (mGrid->tree(), allNeighbors, mGrainSize));
540 if (!voxelizer->run(w)) voxelizer.reset();
547 for (
int i=0; i<iterations && !this->wasInterrupted(); ++i, dist+=w) {
548 if (i > 0 && voxelizer) {
551 const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
553 const int searches = voxelizer->run(remain);
554 if (searches == 0) voxelizer.reset();
555 else leafs.rebuild(serial);
560 mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
564 mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
566 mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
570 if (mInterrupter) mInterrupter->end();
574template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
578 if (iterations <= 0)
return;
580 const int w = std::max(1, width);
581 const bool serial = mGrainSize == 0;
583 if (mInterrupter) mInterrupter->start(
"Applying Gaussian filter");
585 std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
586 if (this->getProcessTiles()) {
589 const bool allNeighbors = iterations > 1;
593 voxelizer.reset(
new filter_internal::Voxelizer<TreeType>
594 (mGrid->tree(), allNeighbors, mGrainSize));
595 if (!voxelizer->run(w*4)) voxelizer.reset();
602 for (
int i=0; i<iterations; ++i, dist+=(w*4)) {
603 if (i > 0 && voxelizer) {
606 const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
608 const int searches = voxelizer->run(remain);
609 if (searches == 0) voxelizer.reset();
610 else leafs.rebuild(serial);
615 for (
int n=0; n<4 && !this->wasInterrupted(); ++n) {
616 mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
620 mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
622 mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
627 if (mInterrupter) mInterrupter->end();
631template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
635 if (iterations <= 0)
return;
637 const int w = std::max(1, width);
638 const bool serial = mGrainSize == 0;
640 if (mInterrupter) mInterrupter->start(
"Applying median filter");
642 std::unique_ptr<filter_internal::Voxelizer<TreeType>> voxelizer;
643 if (this->getProcessTiles()) {
646 voxelizer.reset(
new filter_internal::Voxelizer<TreeType>
647 (mGrid->tree(),
true, mGrainSize));
648 if (!voxelizer->run(w)) voxelizer.reset();
653 mTask = std::bind(&Filter::doMedian, std::placeholders::_1, std::placeholders::_2, w);
657 for (
int i=0; i<iterations && !this->wasInterrupted(); ++i, dist+=w) {
658 if (i > 0 && voxelizer) {
661 const int remain = dist - iter * int(TreeType::LeafNodeType::DIM);
663 const int searches = voxelizer->run(remain);
664 if (searches == 0) voxelizer.reset();
665 else leafs.rebuild(serial);
673 if (mInterrupter) mInterrupter->end();
677template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
683 if (mInterrupter) mInterrupter->start(
"Applying offset");
685 if (this->getProcessTiles()) {
689 NodeManagerT manager(mGrid->tree());
692 manager.foreachBottomUp([&](
auto& node) {
693 this->wasInterrupted();
694 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
696 for (
auto iter = node.beginValueOn(); iter; ++iter) {
697 if (!alpha(iter.getCoord(), a, b))
continue;
705 manager.foreachBottomUp([&](
auto& node) {
706 this->wasInterrupted();
707 for (
auto iter = node.beginValueOn(); iter; ++iter) {
715 mTask = std::bind(&Filter::doOffset, std::placeholders::_1, std::placeholders::_2,
value);
718 if (mInterrupter) mInterrupter->end();
727template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
732 tbb::parallel_for(leafs.leafRange(mGrainSize), *
this);
734 (*this)(leafs.leafRange());
736 leafs.swapLeafBuffer(1, mGrainSize==0);
741template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
742template <
typename AvgT>
744Filter<GridT, MaskT, InterruptT>::doBox(
const RangeType& range, Int32 w)
746 this->wasInterrupted();
749 typename AlphaMaskT::FloatType a, b;
750 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
751 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
752 BufferT& buffer = leafIter.buffer(1);
753 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
754 const Coord xyz = iter.getCoord();
755 if (alpha(xyz, a, b)) {
757 const ValueType
value(b*(*iter) + a*avg(xyz));
759 buffer.setValue(iter.pos(),
value);
764 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
765 BufferT& buffer = leafIter.buffer(1);
766 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
767 buffer.setValue(iter.pos(), avg(iter.getCoord()));
775template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
777Filter<GridT, MaskT, InterruptT>::doMedian(
const RangeType& range,
int width)
780 typename math::DenseStencil<GridType> stencil(*mGrid, width);
782 typename AlphaMaskT::FloatType a, b;
783 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
784 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
785 BufferT& buffer = leafIter.buffer(1);
786 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
787 if (alpha(iter.getCoord(), a, b)) {
788 stencil.moveTo(iter);
790 ValueType
value(b*(*iter) + a*stencil.median());
792 buffer.setValue(iter.pos(),
value);
797 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
798 BufferT& buffer = leafIter.buffer(1);
799 for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
800 stencil.moveTo(iter);
801 buffer.setValue(iter.pos(), stencil.median());
809template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
811Filter<GridT, MaskT, InterruptT>::doOffset(
const RangeType& range, ValueType offset)
815 typename AlphaMaskT::FloatType a, b;
816 AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
817 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
818 for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
819 if (alpha(iter.getCoord(), a, b)) {
821 ValueType
value(*iter + a*offset);
823 iter.setValue(
value);
828 for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
829 for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
830 iter.setValue(*iter + offset);
837template<
typename Gr
idT,
typename MaskT,
typename InterruptT>
839Filter<GridT, MaskT, InterruptT>::wasInterrupted()
841 if (util::wasInterrupted(mInterrupter)) {
842 thread::cancelGroupExecution();
854#ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
856#ifdef OPENVDB_INSTANTIATE_FILTER
ValueT value
Definition GridBuilder.h:1290
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:25
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition LeafManager.h:85
typename CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition LeafManager.h:95
To facilitate threading over the nodes of a tree, cache node pointers in linear arrays,...
Definition NodeManager.h:531
GridType
List of types that are currently supported by NanoVDB.
Definition NanoVDB.h:243
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
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:212
#define OPENVDB_INSTANTIATE_CLASS
Definition version.h.in:153