15#ifndef NANOVDB_TOOLS_GRIDSTATS_H_HAS_BEEN_INCLUDED
16#define NANOVDB_TOOLS_GRIDSTATS_H_HAS_BEEN_INCLUDED
22#include <tbb/blocked_range.h>
23#include <tbb/parallel_reduce.h>
26#if defined(__CUDACC__)
27#include <cuda/std/limits>
52template<
typename BuildT>
55template<typename ValueT, int Rank = TensorTraits<ValueT>::Rank>
64template<
typename BuildT>
71template<
typename ValueT>
78 using ValueType = ValueT;
80#if defined(__CUDACC__)
82 : mMin(::cuda::std::numeric_limits<ValueT>::max())
83 , mMax(::cuda::std::numeric_limits<ValueT>::lowest())
85 : mMin(std::numeric_limits<ValueT>::max())
86 , mMax(std::numeric_limits<ValueT>::lowest())
95 __hostdev__ Extrema(
const ValueT& a,
const ValueT& b)
102 if (v < mMin) mMin = v;
107 if (v > mMax) mMax = v;
116 __hostdev__ Extrema& add(
const ValueT& v, uint64_t) {
return this->add(v); }
119 this->
min(other.mMin);
120 this->
max(other.mMax);
125 __hostdev__ operator bool()
const {
return mMin <= mMax; }
127 __hostdev__ static constexpr bool hasAverage() {
return false; }
128 __hostdev__ static constexpr bool hasStdDeviation() {
return false; }
130 __hostdev__ static constexpr size_t size() {
return 0; }
132 template <
typename NodeT>
135 node.setMin(this->
min());
136 node.setMax(this->
max());
141template<
typename VecT>
145 using Real =
typename VecT::ValueType;
173#if defined(__CUDACC__)
175 :
mMin(::cuda::std::numeric_limits<Real>::max())
176 ,
mMax(::cuda::std::numeric_limits<Real>::lowest())
178 :
mMin(std::numeric_limits<Real>::max())
179 ,
mMax(std::numeric_limits<Real>::lowest())
209 if (other.mMin <
mMin)
mMin = other.mMin;
210 if (
mMax < other.mMax)
mMax = other.mMax;
222 template <
typename NodeT>
225 node.setMin(this->
min());
226 node.setMax(this->
max());
232template<typename ValueT, int Rank = TensorTraits<ValueT>::Rank>
243template<
typename ValueT>
273 const double delta = double(val) -
mAvg;
275 mAux += delta * (double(val) -
mAvg);
281 const double denom = 1.0 / double(
mSize + n);
282 const double delta = double(val) -
mAvg;
283 mAvg += denom * delta * double(n);
284 mAux += denom * delta * delta * double(
mSize) * double(n);
293 if (other.mSize > 0) {
294 const double denom = 1.0 / double(
mSize + other.mSize);
295 const double delta = other.mAvg -
mAvg;
296 mAvg += denom * delta * double(other.mSize);
297 mAux += other.mAux + denom * delta * delta * double(
mSize) * double(other.mSize);
299 mSize += other.mSize;
332 template <
typename NodeT>
335 node.setMin(this->min());
336 node.setMax(this->max());
337 node.setAvg(this->
avg());
338 node.setDev(this->
std());
350template<
typename ValueT>
371 typename BaseT::Pair tmp(val);
374 const double delta = tmp.scalar -
mAvg;
376 mAux += delta * (tmp.scalar -
mAvg);
382 typename BaseT::Pair tmp(val);
383 const double denom = 1.0 / double(
mSize + n);
384 const double delta = tmp.scalar -
mAvg;
385 mAvg += denom * delta * double(n);
386 mAux += denom * delta * delta * double(
mSize) * double(n);
395 if (other.mSize > 0) {
396 const double denom = 1.0 / double(
mSize + other.mSize);
397 const double delta = other.mAvg -
mAvg;
398 mAvg += denom * delta * double(other.mSize);
399 mAux += other.mAux + denom * delta * delta * double(
mSize) * double(other.mSize);
401 mSize += other.mSize;
434 template <
typename NodeT>
437 node.setMin(this->min());
438 node.setMax(this->max());
439 node.setAvg(this->
avg());
440 node.setDev(this->
std());
445template<
typename ValueT>
459 template <
typename NodeT>
466template<
typename Gr
idT,
typename StatsT = Stats<
typename Gr
idT::ValueType>>
470 using TreeT =
typename GridT::TreeType;
471 using ValueT =
typename TreeT::ValueType;
472 using BuildT =
typename TreeT::BuildType;
473 using Node0 =
typename TreeT::Node0;
474 using Node1 =
typename TreeT::Node1;
475 using Node2 =
typename TreeT::Node2;
476 using RootT =
typename TreeT::Node3;
481 void process( GridT& );
482 void process( TreeT& );
483 void process( RootT& );
486 template<
typename NodeT>
489 template<
typename DataT,
int Rank>
491 template<
typename DataT,
int Rank>
493 template<
typename DataT>
496 template<
typename T,
typename FlagT>
497 typename std::enable_if<!std::is_floating_point<T>::value>::type
498 setFlag(
const T&,
const T&, FlagT& flag)
const { flag &= ~FlagT(1); }
500 template<
typename T,
typename FlagT>
501 typename std::enable_if<std::is_floating_point<T>::value>::type
502 setFlag(
const T& min,
const T& max, FlagT& flag)
const;
507 void update(GridT& grid, ValueT delta = ValueT(0));
511template<
typename Gr
idT,
typename StatsT>
522 bbox[0].minComponent(other.
bbox[0]);
523 bbox[1].maxComponent(other.
bbox[1]);
530template<
typename Gr
idT,
typename StatsT>
534 this->process( grid );
539template<
typename Gr
idT,
typename StatsT>
540template<
typename DataT,
int Rank>
544 data->setMin(e.min());
545 data->setMax(e.max());
548template<
typename Gr
idT,
typename StatsT>
549template<
typename DataT,
int Rank>
550inline void GridStats<GridT, StatsT>::
551 setStats(DataT* data,
const Stats<ValueT, Rank>& s)
553 data->setMin(s.min());
554 data->setMax(s.max());
555 data->setAvg(s.avg());
556 data->setDev(s.std());
561template<
typename Gr
idT,
typename StatsT>
562template<
typename T,
typename FlagT>
563inline typename std::enable_if<std::is_floating_point<T>::value>::type
564GridStats<GridT, StatsT>::
565 setFlag(
const T& min,
const T& max, FlagT& flag)
const
567 if (mDelta > 0 && (min > mDelta || max < -mDelta)) {
576template<
typename Gr
idT,
typename StatsT>
577void GridStats<GridT, StatsT>::process( GridT &grid )
579 this->process( grid.tree() );
582 auto& data = *grid.data();
583 const auto& indexBBox = grid.tree().root().bbox();
584 if (indexBBox.empty()) {
586 data.setBBoxOn(
false);
596 grid.mWorldBBox =
CoordBBox(indexBBox[0], indexBBox[1].offsetBy(1)).transform(grid.map());
597 grid.setBBoxOn(
true);
601 data.setMinMaxOn(StatsT::hasMinMax());
602 data.setAverageOn(StatsT::hasAverage());
603 data.setStdDeviationOn(StatsT::hasStdDeviation());
608template<
typename Gr
idT,
typename StatsT>
609inline void GridStats<GridT, StatsT>::process(
typename GridT::TreeType &tree )
611 this->process( tree.root() );
616template<
typename Gr
idT,
typename StatsT>
617void GridStats<GridT, StatsT>::process(RootT &root)
619 using ChildT = Node2;
620 auto &data = *root.data();
621 if (data.mTableSize == 0) {
622 data.mMinimum = data.mMaximum = data.mBackground;
623 data.mAverage = data.mStdDevi = 0;
627 for (uint32_t i = 0; i < data.mTableSize; ++i) {
628 auto* tile = data.tile(i);
629 if (tile->isChild()) {
630 total.add( this->process( *data.getChild(tile) ) );
631 }
else if (tile->state) {
632 const Coord ijk = tile->origin();
633 total.bbox[0].minComponent(ijk);
634 total.bbox[1].maxComponent(ijk + Coord(ChildT::DIM - 1));
635 if (StatsT::hasStats()) {
636 total.stats.add(tile->value, ChildT::NUM_VALUES);
640 this->setStats(&data, total.stats);
641 if (total.bbox.empty()) {
642 std::cerr <<
"\nWarning in GridStats: input tree only contained inactive root tiles!"
643 <<
"\nWhile not strictly an error it's rather suspicious!\n";
645 data.mBBox = total.bbox;
651template<
typename Gr
idT,
typename StatsT>
652template<
typename NodeT>
654GridStats<GridT, StatsT>::process(NodeT &node)
657 using ChildT =
typename NodeT::ChildNodeType;
660 auto* data = node.data();
663 if (
const auto tileCount = data->mValueMask.countOn()) {
665 for (
auto it = data->mValueMask.beginOn(); it; ++it) {
666 if (StatsT::hasStats()) {
667 total.stats.add( data->mTable[*it].value, ChildT::NUM_VALUES );
669 const Coord ijk = node.offsetToGlobalCoord(*it);
670 total.bbox[0].minComponent(ijk);
671 total.bbox[1].maxComponent(ijk + Coord(int32_t(ChildT::DIM) - 1));
676 if (
const size_t childCount = data->mChildMask.countOn()) {
677#ifndef NANOVDB_USE_TBB
678 for (
auto it = data->mChildMask.beginOn(); it; ++it) {
679 total.add( this->process( *data->getChild(*it) ) );
682 std::unique_ptr<ChildT*[]> childNodes(
new ChildT*[childCount]);
683 ChildT **ptr = childNodes.get();
684 for (
auto it = data->mChildMask.beginOn(); it; ++it) {
685 *ptr++ = data->getChild( *it );
687 using RangeT = tbb::blocked_range<size_t>;
688 total.add( tbb::parallel_reduce(RangeT(0, childCount), NodeStats(),
689 [&](
const RangeT &r, NodeStats local)->NodeStats {
690 for(
size_t i=r.begin(); i!=r.end(); ++i){
691 local.add( this->process( *childNodes[i] ) );
694 [](NodeStats a,
const NodeStats &b)->NodeStats {
return a.add( b ); }
699 data->mBBox = total.
bbox;
700 if (total.bbox.empty()) {
701 data->mFlags |= uint32_t(1);
702 data->mFlags &= ~uint32_t(2);
704 data->mFlags |= uint32_t(2);
705 if (StatsT::hasStats()) {
706 this->setStats(data, total.stats);
707 this->setFlag(data->mMinimum, data->mMaximum, data->mFlags);
715template<
typename Gr
idT,
typename StatsT>
717GridStats<GridT, StatsT>::process(Node0 &leaf)
720 if (leaf.updateBBox()) {
721 local.bbox[0] = local.bbox[1] = leaf.mBBoxMin;
722 local.bbox[1] += Coord(leaf.mBBoxDif[0], leaf.mBBoxDif[1], leaf.mBBoxDif[2]);
723 if (StatsT::hasStats()) {
724 for (
auto it = leaf.cbeginValueOn(); it; ++it) local.stats.add(*it);
725 this->setStats(&leaf, local.stats);
726 this->setFlag(leaf.getMin(), leaf.getMax(), leaf.mFlags);
734template<
typename BuildT>
739 using ValueT =
typename GridT::ValueType;
752 throw std::runtime_error(
"gridStats: Unsupported statistics mode.");
756template<
typename BuildT>
757[[deprecated(
"Use nanovdb::tools::updateGridStats(NanoGrid*, StatsMode) instead")]]
769template<
typename NodeT>
773 auto b = CoordBBox::createCube(node->origin(), node->dim());
774 assert( bbox.hasOverlap(b) );
775 if ( bbox.isInside(b) ) {
780 b.min() &= NodeT::DIM-1u;
781 b.min() >>= NodeT::ChildNodeType::TOTAL;
782 b.max() &= NodeT::DIM-1u;
783 b.max() >>= NodeT::ChildNodeType::TOTAL;
784 assert( !b.empty() );
786 for (
const Coord& ijk = *it; it; ++it) {
787 mask.
setOn(ijk[2] + (ijk[1] << NodeT::LOG2DIM) + (ijk[0] << 2*NodeT::LOG2DIM));
797template<
typename BuildT>
802 using ValueT =
typename GridT::ValueType;
810 const RootT &root = grid.
tree().root();
811 const auto &bbox3 = root.bbox();
812 if (bbox.isInside(bbox3)) {
813 extrema.
min(root.minimum());
814 extrema.
max(root.maximum());
815 extrema.
add(root.background());
816 }
else if (bbox.hasOverlap(bbox3)) {
817 const auto *data3 = root.data();
818 for (uint32_t i=0; i<data3->mTableSize; ++i) {
819 const auto *tile = data3->tile(i);
820 CoordBBox bbox2 = CoordBBox::createCube(tile->origin(), Node2::dim());
821 if (!bbox.hasOverlap(bbox2))
continue;
822 if (tile->isChild()) {
823 const Node2 *node2 = data3->getChild(tile);
824 if (bbox.isInside(bbox2)) {
825 extrema.
min(node2->minimum());
826 extrema.
max(node2->maximum());
828 auto *data2 = node2->data();
829 const auto bboxMask2 = getBBoxMask(bbox, node2);
830 for (
auto it2 = bboxMask2.beginOn(); it2; ++it2) {
831 if (data2->mChildMask.isOn(*it2)) {
832 const Node1* node1 = data2->getChild(*it2);
833 CoordBBox bbox1 = CoordBBox::createCube(node1->origin(), Node1::dim());
834 if (bbox.isInside(bbox1)) {
835 extrema.
min(node1->minimum());
836 extrema.
max(node1->maximum());
838 auto *data1 = node1->data();
839 const auto bboxMask1 = getBBoxMask(bbox, node1);
840 for (
auto it1 = bboxMask1.beginOn(); it1; ++it1) {
841 if (data1->mChildMask.isOn(*it1)) {
842 const Node0* node0 = data1->getChild(*it1);
843 CoordBBox bbox0 = CoordBBox::createCube(node0->origin(), Node0::dim());
844 if (bbox.isInside(bbox0)) {
845 extrema.
min(node0->minimum());
846 extrema.
max(node0->maximum());
848 auto *data0 = node0->data();
849 const auto bboxMask0 = getBBoxMask(bbox, node0);
850 for (
auto it0 = bboxMask0.beginOn(); it0; ++it0) {
851 extrema.
add(data0->getValue(*it0));
855 extrema.
add(data1->mTable[*it1].value);
860 extrema.
add(data2->mTable[*it2].value);
865 extrema.
add(tile->value);
869 extrema.
add(root.background());
Implements a light-weight self-contained VDB data-structure in a single file! In other words,...
const TreeT & tree() const
Return a const reference to the tree.
Definition NanoVDB.h:2236
Bit-mask to encode active states and facilitate sequential iterators and a fast codec for I/O compres...
Definition NanoVDB.h:1068
void setOn(uint32_t n)
Set the specified bit on.
Definition NanoVDB.h:1258
void add(double val)
Add a single sample.
Definition Stats.h:103
double min() const
Return the minimum value.
Definition Stats.h:122
double max() const
Return the maximum value.
Definition Stats.h:125
#define __hostdev__
Definition SampleFromVoxels.h:29
Defines a simple memory pool used to call cub functions that use dynamic temporary storage.
Definition GridHandle.h:31
Grid< NanoTree< BuildT > > NanoGrid
Definition NanoVDB.h:4742
math::BBox< Vec3d > Vec3dBBox
Definition Math.h:2242
math::BBox< Coord > CoordBBox
Definition Math.h:2241
#define NANOVDB_ASSERT(x)
Definition Util.h:53
typename GridT::TreeType type
Definition NanoVDB.h:2464
Struct to derive node type from its level in a given grid, tree or root while preserving constness.
Definition NanoVDB.h:1723
static constexpr bool value
Definition Util.h:328