15#ifndef NANOVDB_TOOLS_GRIDSTATS_H_HAS_BEEN_INCLUDED
16#define NANOVDB_TOOLS_GRIDSTATS_H_HAS_BEEN_INCLUDED
23#include <tbb/blocked_range.h>
24#include <tbb/parallel_reduce.h>
27#if defined(__CUDACC__)
28#include <cuda/std/limits>
53template<
typename BuildT>
56template<typename ValueT, int Rank = TensorTraits<ValueT>::Rank>
65template<
typename BuildT>
72template<
typename ValueT>
79 using ValueType = ValueT;
81#if defined(__CUDACC__)
83 : mMin(::cuda::std::numeric_limits<ValueT>::max())
84 , mMax(::cuda::std::numeric_limits<ValueT>::lowest())
86 : mMin(std::numeric_limits<ValueT>::max())
87 , mMax(std::numeric_limits<ValueT>::lowest())
96 __hostdev__ Extrema(
const ValueT& a,
const ValueT& b)
103 if (v < mMin) mMin = v;
108 if (v > mMax) mMax = v;
117 __hostdev__ Extrema& add(
const ValueT& v, uint64_t) {
return this->add(v); }
120 this->
min(other.mMin);
121 this->
max(other.mMax);
126 __hostdev__ operator bool()
const {
return mMin <= mMax; }
128 __hostdev__ static constexpr bool hasAverage() {
return false; }
129 __hostdev__ static constexpr bool hasStdDeviation() {
return false; }
131 __hostdev__ static constexpr size_t size() {
return 0; }
133 template <
typename NodeT>
136 node.setMin(this->
min());
137 node.setMax(this->
max());
142template<
typename VecT>
146 using Real =
typename VecT::ValueType;
174#if defined(__CUDACC__)
176 :
mMin(::cuda::std::numeric_limits<Real>::max())
177 ,
mMax(::cuda::std::numeric_limits<Real>::lowest())
179 :
mMin(std::numeric_limits<Real>::max())
180 ,
mMax(std::numeric_limits<Real>::lowest())
210 if (other.mMin <
mMin)
mMin = other.mMin;
211 if (
mMax < other.mMax)
mMax = other.mMax;
223 template <
typename NodeT>
226 node.setMin(this->
min());
227 node.setMax(this->
max());
275 this->
min(other.mMin);
276 this->
max(other.mMax);
288 template <
typename NodeT>
291 node.setMin(this->
min());
292 node.setMax(this->
max());
299template<typename ValueT, int Rank = TensorTraits<ValueT>::Rank>
310template<
typename ValueT>
340 const double delta = double(val) -
mAvg;
342 mAux += delta * (double(val) -
mAvg);
348 const double denom = 1.0 / double(
mSize + n);
349 const double delta = double(val) -
mAvg;
350 mAvg += denom * delta * double(n);
351 mAux += denom * delta * delta * double(
mSize) * double(n);
360 if (other.mSize > 0) {
361 const double denom = 1.0 / double(
mSize + other.mSize);
362 const double delta = other.mAvg -
mAvg;
363 mAvg += denom * delta * double(other.mSize);
364 mAux += other.mAux + denom * delta * delta * double(
mSize) * double(other.mSize);
366 mSize += other.mSize;
399 template <
typename NodeT>
402 node.setMin(this->min());
403 node.setMax(this->max());
404 node.setAvg(this->
avg());
405 node.setDev(this->
std());
417template<
typename ValueT>
438 typename BaseT::Pair tmp(val);
441 const double delta = tmp.scalar -
mAvg;
443 mAux += delta * (tmp.scalar -
mAvg);
449 typename BaseT::Pair tmp(val);
450 const double denom = 1.0 / double(
mSize + n);
451 const double delta = tmp.scalar -
mAvg;
452 mAvg += denom * delta * double(n);
453 mAux += denom * delta * delta * double(
mSize) * double(n);
462 if (other.mSize > 0) {
463 const double denom = 1.0 / double(
mSize + other.mSize);
464 const double delta = other.mAvg -
mAvg;
465 mAvg += denom * delta * double(other.mSize);
466 mAux += other.mAux + denom * delta * delta * double(
mSize) * double(other.mSize);
468 mSize += other.mSize;
501 template <
typename NodeT>
504 node.setMin(this->min());
505 node.setMax(this->max());
506 node.setAvg(this->
avg());
507 node.setDev(this->
std());
563 const double denom = 1.0 / double(
mSize + n);
565 mAvg += denom * delta * double(n);
566 mAux += denom * delta * delta * double(
mSize) * double(n);
575 if (other.mSize > 0) {
576 const double denom = 1.0 / double(
mSize + other.mSize);
577 const double delta = other.mAvg -
mAvg;
578 mAvg += denom * delta * double(other.mSize);
579 mAux += other.mAux + denom * delta * delta * double(
mSize) * double(other.mSize);
581 mSize += other.mSize;
614 template <
typename NodeT>
617 node.setMin(this->
min());
618 node.setMax(this->
max());
619 node.setAvg(this->
avg());
620 node.setDev(this->
std());
625template<
typename ValueT>
639 template <
typename NodeT>
646template<
typename Gr
idT,
typename StatsT = Stats<
typename Gr
idT::ValueType>>
650 using TreeT =
typename GridT::TreeType;
651 using ValueT =
typename TreeT::ValueType;
652 using BuildT =
typename TreeT::BuildType;
653 using Node0 =
typename TreeT::Node0;
654 using Node1 =
typename TreeT::Node1;
655 using Node2 =
typename TreeT::Node2;
656 using RootT =
typename TreeT::Node3;
661 void process( GridT& );
662 void process( TreeT& );
663 void process( RootT& );
666 template<
typename NodeT>
669 template<
typename DataT,
int Rank>
671 template<
typename DataT,
int Rank>
673 template<
typename DataT>
676 template<
typename T,
typename FlagT>
677 typename std::enable_if<!std::is_floating_point<T>::value>::type
678 setFlag(
const T&,
const T&, FlagT& flag)
const { flag &= ~FlagT(1); }
680 template<
typename T,
typename FlagT>
681 typename std::enable_if<std::is_floating_point<T>::value>::type
682 setFlag(
const T& min,
const T& max, FlagT& flag)
const;
687 void update(GridT& grid, ValueT delta = ValueT());
691template<
typename Gr
idT,
typename StatsT>
702 bbox[0].minComponent(other.
bbox[0]);
703 bbox[1].maxComponent(other.
bbox[1]);
710template<
typename Gr
idT,
typename StatsT>
714 this->process( grid );
719template<
typename Gr
idT,
typename StatsT>
720template<
typename DataT,
int Rank>
724 data->setMin(e.min());
725 data->setMax(e.max());
728template<
typename Gr
idT,
typename StatsT>
729template<
typename DataT,
int Rank>
730inline void GridStats<GridT, StatsT>::
731 setStats(DataT* data,
const Stats<ValueT, Rank>& s)
733 data->setMin(s.min());
734 data->setMax(s.max());
735 using FloatT =
typename RootT::FloatType;
736 data->setAvg(
static_cast<FloatT
>(s.avg()));
737 data->setDev(
static_cast<FloatT
>(s.std()));
742template<
typename Gr
idT,
typename StatsT>
743template<
typename T,
typename FlagT>
744inline typename std::enable_if<std::is_floating_point<T>::value>::type
745GridStats<GridT, StatsT>::
746 setFlag(
const T& min,
const T& max, FlagT& flag)
const
748 if (mDelta > 0 && (min > mDelta || max < -mDelta)) {
757template<
typename Gr
idT,
typename StatsT>
758void GridStats<GridT, StatsT>::process( GridT &grid )
760 this->process( grid.tree() );
763 auto& data = *grid.data();
764 const auto& indexBBox = grid.tree().root().bbox();
765 if (indexBBox.empty()) {
767 data.setBBoxOn(
false);
777 grid.mWorldBBox =
CoordBBox(indexBBox[0], indexBBox[1].offsetBy(1)).transform(grid.map());
778 grid.setBBoxOn(
true);
782 data.setMinMaxOn(StatsT::hasMinMax());
783 data.setAverageOn(StatsT::hasAverage());
784 data.setStdDeviationOn(StatsT::hasStdDeviation());
789template<
typename Gr
idT,
typename StatsT>
790inline void GridStats<GridT, StatsT>::process(
typename GridT::TreeType &tree )
792 this->process( tree.root() );
797template<
typename Gr
idT,
typename StatsT>
798void GridStats<GridT, StatsT>::process(RootT &root)
800 using ChildT = Node2;
801 auto &data = *root.data();
802 if (data.mTableSize == 0) {
803 data.mMinimum = data.mMaximum = data.mBackground;
804 data.mAverage = data.mStdDevi = 0;
808 for (uint32_t i = 0; i < data.mTableSize; ++i) {
809 auto* tile = data.tile(i);
810 if (tile->isChild()) {
811 total.add( this->process( *data.getChild(tile) ) );
812 }
else if (tile->state) {
813 const Coord ijk = tile->origin();
814 total.bbox[0].minComponent(ijk);
815 total.bbox[1].maxComponent(ijk + Coord(ChildT::DIM - 1));
816 if (StatsT::hasStats()) {
817 total.stats.add(tile->value, ChildT::NUM_VALUES);
821 this->setStats(&data, total.stats);
822 if (total.bbox.empty()) {
823 std::cerr <<
"\nWarning in GridStats: input tree only contained inactive root tiles!"
824 <<
"\nWhile not strictly an error it's rather suspicious!\n";
826 data.mBBox = total.bbox;
832template<
typename Gr
idT,
typename StatsT>
833template<
typename NodeT>
835GridStats<GridT, StatsT>::process(NodeT &node)
838 using ChildT =
typename NodeT::ChildNodeType;
841 auto* data = node.data();
844 if (
const auto tileCount = data->mValueMask.countOn()) {
846 for (
auto it = data->mValueMask.beginOn(); it; ++it) {
847 if (StatsT::hasStats()) {
848 total.stats.add( data->mTable[*it].value, ChildT::NUM_VALUES );
850 const Coord ijk = node.offsetToGlobalCoord(*it);
851 total.bbox[0].minComponent(ijk);
852 total.bbox[1].maxComponent(ijk + Coord(int32_t(ChildT::DIM) - 1));
857 if (
const size_t childCount = data->mChildMask.countOn()) {
858#ifndef NANOVDB_USE_TBB
859 for (
auto it = data->mChildMask.beginOn(); it; ++it) {
860 total.add( this->process( *data->getChild(*it) ) );
863 std::unique_ptr<ChildT*[]> childNodes(
new ChildT*[childCount]);
864 ChildT **ptr = childNodes.get();
865 for (
auto it = data->mChildMask.beginOn(); it; ++it) {
866 *ptr++ = data->getChild( *it );
868 using RangeT = tbb::blocked_range<size_t>;
869 total.add( tbb::parallel_reduce(RangeT(0, childCount), NodeStats(),
870 [&](
const RangeT &r, NodeStats local)->NodeStats {
871 for(
size_t i=r.begin(); i!=r.end(); ++i){
872 local.add( this->process( *childNodes[i] ) );
875 [](NodeStats a,
const NodeStats &b)->NodeStats {
return a.add( b ); }
880 data->mBBox = total.
bbox;
881 if (total.bbox.empty()) {
882 data->mFlags |= uint32_t(1);
883 data->mFlags &= ~uint32_t(2);
885 data->mFlags |= uint32_t(2);
886 if (StatsT::hasStats()) {
887 this->setStats(data, total.stats);
888 this->setFlag(data->mMinimum, data->mMaximum, data->mFlags);
896template<
typename Gr
idT,
typename StatsT>
898GridStats<GridT, StatsT>::process(Node0 &leaf)
901 if (leaf.updateBBox()) {
902 local.bbox[0] = local.bbox[1] = leaf.mBBoxMin;
903 local.bbox[1] += Coord(leaf.mBBoxDif[0], leaf.mBBoxDif[1], leaf.mBBoxDif[2]);
904 if (StatsT::hasStats()) {
905 for (
auto it = leaf.cbeginValueOn(); it; ++it) local.stats.add(*it);
906 this->setStats(&leaf, local.stats);
907 this->setFlag(leaf.getMin(), leaf.getMax(), leaf.mFlags);
915template<
typename BuildT>
920 using ValueT =
typename GridT::ValueType;
933 throw std::runtime_error(
"updateGridStats: Unsupported statistics mode.");
943template<
typename NodeT>
947 auto b = CoordBBox::createCube(node->origin(), node->dim());
948 assert( bbox.hasOverlap(b) );
949 if ( bbox.isInside(b) ) {
954 b.min() &= NodeT::DIM-1u;
955 b.min() >>= NodeT::ChildNodeType::TOTAL;
956 b.max() &= NodeT::DIM-1u;
957 b.max() >>= NodeT::ChildNodeType::TOTAL;
958 assert( !b.empty() );
960 for (
const Coord& ijk = *it; it; ++it) {
961 mask.
setOn(ijk[2] + (ijk[1] << NodeT::LOG2DIM) + (ijk[0] << 2*NodeT::LOG2DIM));
971template<
typename BuildT>
976 using ValueT =
typename GridT::ValueType;
984 const RootT &root = grid.
tree().root();
985 const auto &bbox3 = root.bbox();
986 if (bbox.isInside(bbox3)) {
987 extrema.
min(root.minimum());
988 extrema.
max(root.maximum());
989 extrema.
add(root.background());
990 }
else if (bbox.hasOverlap(bbox3)) {
991 const auto *data3 = root.data();
992 for (uint32_t i=0; i<data3->mTableSize; ++i) {
993 const auto *tile = data3->tile(i);
994 CoordBBox bbox2 = CoordBBox::createCube(tile->origin(), Node2::dim());
995 if (!bbox.hasOverlap(bbox2))
continue;
996 if (tile->isChild()) {
997 const Node2 *node2 = data3->getChild(tile);
998 if (bbox.isInside(bbox2)) {
999 extrema.
min(node2->minimum());
1000 extrema.
max(node2->maximum());
1002 auto *data2 = node2->data();
1003 const auto bboxMask2 = getBBoxMask(bbox, node2);
1004 for (
auto it2 = bboxMask2.beginOn(); it2; ++it2) {
1005 if (data2->mChildMask.isOn(*it2)) {
1006 const Node1* node1 = data2->getChild(*it2);
1007 CoordBBox bbox1 = CoordBBox::createCube(node1->origin(), Node1::dim());
1008 if (bbox.isInside(bbox1)) {
1009 extrema.
min(node1->minimum());
1010 extrema.
max(node1->maximum());
1012 auto *data1 = node1->data();
1013 const auto bboxMask1 = getBBoxMask(bbox, node1);
1014 for (
auto it1 = bboxMask1.beginOn(); it1; ++it1) {
1015 if (data1->mChildMask.isOn(*it1)) {
1016 const Node0* node0 = data1->getChild(*it1);
1017 CoordBBox bbox0 = CoordBBox::createCube(node0->origin(), Node0::dim());
1018 if (bbox.isInside(bbox0)) {
1019 extrema.
min(node0->minimum());
1020 extrema.
max(node0->maximum());
1022 auto *data0 = node0->data();
1023 const auto bboxMask0 = getBBoxMask(bbox, node0);
1024 for (
auto it0 = bboxMask0.beginOn(); it0; ++it0) {
1025 extrema.
add(data0->getValue(*it0));
1029 extrema.
add(data1->mTable[*it1].value);
1034 extrema.
add(data2->mTable[*it2].value);
1039 extrema.
add(tile->value);
1043 extrema.
add(root.background());
Implements a light-weight self-contained VDB data-structure in a single file! In other words,...
#define __hostdev__
Definition SampleFromVoxels.h:29
const TreeT & tree() const
Return a const reference to the tree.
Definition NanoVDB.h:2211
Type containing raw 16 bits for half precision floating point values (IEEE 754 Half)
Definition NanoVDB.h:187
uint16_t raw
Definition NanoVDB.h:189
Bit-mask to encode active states and facilitate sequential iterators and a fast codec for I/O compres...
Definition NanoVDB.h:1054
void setOn(uint32_t n)
Set the specified bit on.
Definition NanoVDB.h:1242
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
__hostdev__ float HalfToFloat(uint16_t h_raw)
Definition HalfLite.h:27
Defines a simple memory pool used to call cub functions that use dynamic temporary storage.
Definition GridHandle.h:34
Grid< NanoTree< BuildT > > NanoGrid
Definition NanoVDB.h:4699
math::BBox< Vec3d > Vec3dBBox
Definition Math.h:2839
math::BBox< Coord > CoordBBox
Definition Math.h:2838
#define NANOVDB_ASSERT(x)
Definition Util.h:53
typename GridT::TreeType type
Definition NanoVDB.h:2427
Struct to derive node type from its level in a given grid, tree or root while preserving constness.
Definition NanoVDB.h:1708
static constexpr bool value
Definition Util.h:359