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