OpenVDB 13.1.0
Loading...
Searching...
No Matches
GridStats.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3
4/*!
5 \file nanovdb/tools/GridStats.h
6
7 \author Ken Museth
8
9 \date August 29, 2020
10
11 \brief Re-computes min/max/avg/var/bbox information for each node in a
12 pre-existing NanoVDB grid.
13*/
14
15#ifndef NANOVDB_TOOLS_GRIDSTATS_H_HAS_BEEN_INCLUDED
16#define NANOVDB_TOOLS_GRIDSTATS_H_HAS_BEEN_INCLUDED
17
18#include <nanovdb/NanoVDB.h>
19
20#ifdef NANOVDB_USE_TBB
21#include <memory>
22#include <tbb/blocked_range.h>
23#include <tbb/parallel_reduce.h>
24#endif
25
26#if defined(__CUDACC__)
27#include <cuda/std/limits>// for cuda::std::numeric_limits
28#else
29#include <limits.h>// for std::numeric_limits
30#endif
31
32#include <atomic>
33#include <iostream>
34
35namespace nanovdb {
36
37namespace tools {//=======================================================================
38
39/// @brief Grid flags which indicate what extra information is present in the grid buffer
40enum class StatsMode : uint32_t {
41 Disable = 0,// disable the computation of any type of statistics (obviously the FASTEST!)
42 BBox = 1,// only compute the bbox of active values per node and total activeVoxelCount
43 MinMax = 2,// additionally compute extrema values
44 All = 3,// compute all of the statics, i.e. bbox, min/max, average and standard deviation
45 Default = 3,// default computational mode for statistics
46 End = 4,
47};
48
49/// @brief Re-computes the min/max, stats and bbox information for an existing NanoVDB Grid
50/// @param grid Grid whose stats to update
51/// @param mode Mode of computation for the statistics.
52template<typename BuildT>
54
55template<typename ValueT, int Rank = TensorTraits<ValueT>::Rank>
56class Extrema;
57
58/// @brief Determine the extrema of all the values in a grid that
59/// intersects the specified bounding box.
60/// @tparam BuildT Build type of the input grid
61/// @param grid typed grid
62/// @param bbox index bounding box in which min/max are computed
63/// @return Extream of values insixe @c bbox
64template<typename BuildT>
66getExtrema(const NanoGrid<BuildT>& grid, const CoordBBox &bbox);
67
68//================================================================================================
69
70/// @brief Template specialization of Extrema on scalar value types, i.e. rank = 0
71template<typename ValueT>
72class Extrema<ValueT, 0>
73{
74protected:
75 ValueT mMin, mMax;
76
77public:
78 using ValueType = ValueT;
80#if defined(__CUDACC__)
81 // note "::cuda" is needed since we also define a cuda namespace
82 : mMin(::cuda::std::numeric_limits<ValueT>::max())
83 , mMax(::cuda::std::numeric_limits<ValueT>::lowest())
84#else
85 : mMin(std::numeric_limits<ValueT>::max())
86 , mMax(std::numeric_limits<ValueT>::lowest())
87#endif
88 {
89 }
90 __hostdev__ Extrema(const ValueT& v)
91 : mMin(v)
92 , mMax(v)
93 {
94 }
95 __hostdev__ Extrema(const ValueT& a, const ValueT& b)
96 : mMin(a)
97 , mMax(b)
98 {
99 }
100 __hostdev__ Extrema& min(const ValueT& v)
101 {
102 if (v < mMin) mMin = v;
103 return *this;
104 }
105 __hostdev__ Extrema& max(const ValueT& v)
106 {
107 if (v > mMax) mMax = v;
108 return *this;
109 }
110 __hostdev__ Extrema& add(const ValueT& v)
111 {
112 this->min(v);
113 this->max(v);
114 return *this;
115 }
116 __hostdev__ Extrema& add(const ValueT& v, uint64_t) { return this->add(v); }
117 __hostdev__ Extrema& add(const Extrema& other)
118 {
119 this->min(other.mMin);
120 this->max(other.mMax);
121 return *this;
122 }
123 __hostdev__ const ValueT& min() const { return mMin; }
124 __hostdev__ const ValueT& max() const { return mMax; }
125 __hostdev__ operator bool() const { return mMin <= mMax; }
126 __hostdev__ static constexpr bool hasMinMax() { return !util::is_same<bool, ValueT>::value; }
127 __hostdev__ static constexpr bool hasAverage() { return false; }
128 __hostdev__ static constexpr bool hasStdDeviation() { return false; }
129 __hostdev__ static constexpr bool hasStats() { return !util::is_same<bool, ValueT>::value; }
130 __hostdev__ static constexpr size_t size() { return 0; }
131
132 template <typename NodeT>
133 __hostdev__ void setStats(NodeT &node) const
134 {
135 node.setMin(this->min());
136 node.setMax(this->max());
137 }
138}; // Extrema<T, 0>
139
140/// @brief Template specialization of Extrema on vector value types, i.e. rank = 1
141template<typename VecT>
142class Extrema<VecT, 1>
143{
144protected:
145 using Real = typename VecT::ValueType; // this works with both nanovdb and openvdb vectors
146 struct Pair
147 {
149 VecT vector;
150
151 __hostdev__ Pair(Real s)// is only used by Extrema() default c-tor
152 : scalar(s)
153 , vector(s)
154 {
155 }
156 __hostdev__ Pair(const VecT& v)
157 : scalar(v.lengthSqr())
158 , vector(v)
159 {
160 }
161 __hostdev__ bool operator<(const Pair& rhs) const { return scalar < rhs.scalar; }
162 } mMin, mMax;
164 {
165 if (p < mMin) mMin = p;
166 if (mMax < p) mMax = p;
167 return *this;
168 }
169
170public:
171 using ValueType = VecT;
173#if defined(__CUDACC__)
174 // note "::cuda" is needed since we also define a cuda namespace
175 : mMin(::cuda::std::numeric_limits<Real>::max())
176 , mMax(::cuda::std::numeric_limits<Real>::lowest())
177#else
178 : mMin(std::numeric_limits<Real>::max())
179 , mMax(std::numeric_limits<Real>::lowest())
180#endif
181 {
182 }
183 __hostdev__ Extrema(const VecT& v)
184 : mMin(v)
185 , mMax(v)
186 {
187 }
188 __hostdev__ Extrema(const VecT& a, const VecT& b)
189 : mMin(a)
190 , mMax(b)
191 {
192 }
193 __hostdev__ Extrema& min(const VecT& v)
194 {
195 Pair tmp(v);
196 if (tmp < mMin) mMin = tmp;
197 return *this;
198 }
199 __hostdev__ Extrema& max(const VecT& v)
200 {
201 Pair tmp(v);
202 if (mMax < tmp) mMax = tmp;
203 return *this;
204 }
205 __hostdev__ Extrema& add(const VecT& v) { return this->add(Pair(v)); }
206 __hostdev__ Extrema& add(const VecT& v, uint64_t) { return this->add(Pair(v)); }
208 {
209 if (other.mMin < mMin) mMin = other.mMin;
210 if (mMax < other.mMax) mMax = other.mMax;
211 return *this;
212 }
213 __hostdev__ const VecT& min() const { return mMin.vector; }
214 __hostdev__ const VecT& max() const { return mMax.vector; }
215 __hostdev__ operator bool() const { return !(mMax < mMin); }
216 __hostdev__ static constexpr bool hasMinMax() { return !util::is_same<bool, Real>::value; }
217 __hostdev__ static constexpr bool hasAverage() { return false; }
218 __hostdev__ static constexpr bool hasStdDeviation() { return false; }
219 __hostdev__ static constexpr bool hasStats() { return !util::is_same<bool, Real>::value; }
220 __hostdev__ static constexpr size_t size() { return 0; }
221
222 template <typename NodeT>
223 __hostdev__ void setStats(NodeT &node) const
224 {
225 node.setMin(this->min());
226 node.setMax(this->max());
227 }
228}; // Extrema<T, 1>
229
230//================================================================================================
231
232template<typename ValueT, int Rank = TensorTraits<ValueT>::Rank>
233class Stats;
234
235/// @brief This class computes statistics (minimum value, maximum
236/// value, mean, variance and standard deviation) of a population
237/// of floating-point values.
238///
239/// @details variance = Mean[ (X-Mean[X])^2 ] = Mean[X^2] - Mean[X]^2,
240/// standard deviation = sqrt(variance)
241///
242/// @note This class employs incremental computation and double precision.
243template<typename ValueT>
244class Stats<ValueT, 0> : public Extrema<ValueT, 0>
245{
246protected:
248 using RealT = double; // for accuracy the internal precission must be 64 bit floats
249 size_t mSize;
250 double mAvg, mAux;
251
252public:
253 using ValueType = ValueT;
255 : BaseT()
256 , mSize(0)
257 , mAvg(0.0)
258 , mAux(0.0)
259 {
260 }
261 __hostdev__ Stats(const ValueT& val)
262 : BaseT(val)
263 , mSize(1)
264 , mAvg(RealT(val))
265 , mAux(0.0)
266 {
267 }
268 /// @brief Add a single sample
269 __hostdev__ Stats& add(const ValueT& val)
270 {
271 BaseT::add(val);
272 mSize += 1;
273 const double delta = double(val) - mAvg;
274 mAvg += delta / double(mSize);
275 mAux += delta * (double(val) - mAvg);
276 return *this;
277 }
278 /// @brief Add @a n samples with constant value @a val.
279 __hostdev__ Stats& add(const ValueT& val, uint64_t n)
280 {
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);
285 BaseT::add(val);
286 mSize += n;
287 return *this;
288 }
289
290 /// Add the samples from the other Stats instance.
291 __hostdev__ Stats& add(const Stats& other)
292 {
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);
298 BaseT::add(other);
299 mSize += other.mSize;
300 }
301 return *this;
302 }
303
304 __hostdev__ static constexpr bool hasMinMax() { return !util::is_same<bool, ValueT>::value; }
305 __hostdev__ static constexpr bool hasAverage() { return !util::is_same<bool, ValueT>::value; }
307 __hostdev__ static constexpr bool hasStats() { return !util::is_same<bool, ValueT>::value; }
308
309 __hostdev__ size_t size() const { return mSize; }
310
311 //@{
312 /// Return the arithmetic mean, i.e. average, value.
313 __hostdev__ double avg() const { return mAvg; }
314 __hostdev__ double mean() const { return mAvg; }
315 //@}
316
317 //@{
318 /// @brief Return the population variance.
319 ///
320 /// @note The unbiased sample variance = population variance * num/(num-1)
321 __hostdev__ double var() const { return mSize < 2 ? 0.0 : mAux / double(mSize); }
322 __hostdev__ double variance() const { return this->var(); }
323 //@}
324
325 //@{
326 /// @brief Return the standard deviation (=Sqrt(variance)) as
327 /// defined from the (biased) population variance.
328 __hostdev__ double std() const { return sqrt(this->var()); }
329 __hostdev__ double stdDev() const { return this->std(); }
330 //@}
331
332 template <typename NodeT>
333 __hostdev__ void setStats(NodeT &node) const
334 {
335 node.setMin(this->min());
336 node.setMax(this->max());
337 node.setAvg(this->avg());
338 node.setDev(this->std());
339 }
340}; // end Stats<T, 0>
341
342/// @brief This class computes statistics (minimum value, maximum
343/// value, mean, variance and standard deviation) of a population
344/// of floating-point values.
345///
346/// @details variance = Mean[ (X-Mean[X])^2 ] = Mean[X^2] - Mean[X]^2,
347/// standard deviation = sqrt(variance)
348///
349/// @note This class employs incremental computation and double precision.
350template<typename ValueT>
351class Stats<ValueT, 1> : public Extrema<ValueT, 1>
352{
353protected:
355 using RealT = double; // for accuracy the internal precision must be 64 bit floats
356 size_t mSize;
357 double mAvg, mAux;
358
359public:
360 using ValueType = ValueT;
362 : BaseT()
363 , mSize(0)
364 , mAvg(0.0)
365 , mAux(0.0)
366 {
367 }
368 /// @brief Add a single sample
369 __hostdev__ Stats& add(const ValueT& val)
370 {
371 typename BaseT::Pair tmp(val);
372 BaseT::add(tmp);
373 mSize += 1;
374 const double delta = tmp.scalar - mAvg;
375 mAvg += delta / double(mSize);
376 mAux += delta * (tmp.scalar - mAvg);
377 return *this;
378 }
379 /// @brief Add @a n samples with constant value @a val.
380 __hostdev__ Stats& add(const ValueT& val, uint64_t n)
381 {
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);
387 BaseT::add(tmp);
388 mSize += n;
389 return *this;
390 }
391
392 /// Add the samples from the other Stats instance.
393 __hostdev__ Stats& add(const Stats& other)
394 {
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);
400 BaseT::add(other);
401 mSize += other.mSize;
402 }
403 return *this;
404 }
405
406 __hostdev__ static constexpr bool hasMinMax() { return !util::is_same<bool, ValueT>::value; }
407 __hostdev__ static constexpr bool hasAverage() { return !util::is_same<bool, ValueT>::value; }
409 __hostdev__ static constexpr bool hasStats() { return !util::is_same<bool, ValueT>::value; }
410
411 __hostdev__ size_t size() const { return mSize; }
412
413 //@{
414 /// Return the arithmetic mean, i.e. average, value.
415 __hostdev__ double avg() const { return mAvg; }
416 __hostdev__ double mean() const { return mAvg; }
417 //@}
418
419 //@{
420 /// @brief Return the population variance.
421 ///
422 /// @note The unbiased sample variance = population variance * num/(num-1)
423 __hostdev__ double var() const { return mSize < 2 ? 0.0 : mAux / double(mSize); }
424 __hostdev__ double variance() const { return this->var(); }
425 //@}
426
427 //@{
428 /// @brief Return the standard deviation (=Sqrt(variance)) as
429 /// defined from the (biased) population variance.
430 __hostdev__ double std() const { return sqrt(this->var()); }
431 __hostdev__ double stdDev() const { return this->std(); }
432 //@}
433
434 template <typename NodeT>
435 __hostdev__ void setStats(NodeT &node) const
436 {
437 node.setMin(this->min());
438 node.setMax(this->max());
439 node.setAvg(this->avg());
440 node.setDev(this->std());
441 }
442}; // end Stats<T, 1>
443
444/// @brief No-op Stats class
445template<typename ValueT>
447{
448 using ValueType = ValueT;
450 __hostdev__ NoopStats(const ValueT&) {}
451 __hostdev__ NoopStats& add(const ValueT&) { return *this; }
452 __hostdev__ NoopStats& add(const ValueT&, uint64_t) { return *this; }
453 __hostdev__ NoopStats& add(const NoopStats&) { return *this; }
454 __hostdev__ static constexpr size_t size() { return 0; }
455 __hostdev__ static constexpr bool hasMinMax() { return false; }
456 __hostdev__ static constexpr bool hasAverage() { return false; }
457 __hostdev__ static constexpr bool hasStdDeviation() { return false; }
458 __hostdev__ static constexpr bool hasStats() { return false; }
459 template <typename NodeT>
460 __hostdev__ void setStats(NodeT&) const{}
461}; // end NoopStats<T>
462
463//================================================================================================
464
465/// @brief Allows for the construction of NanoVDB grids without any dependency
466template<typename GridT, typename StatsT = Stats<typename GridT::ValueType>>
468{
469 struct NodeStats;
470 using TreeT = typename GridT::TreeType;
471 using ValueT = typename TreeT::ValueType;
472 using BuildT = typename TreeT::BuildType;
473 using Node0 = typename TreeT::Node0; // leaf
474 using Node1 = typename TreeT::Node1; // lower
475 using Node2 = typename TreeT::Node2; // upper
476 using RootT = typename TreeT::Node3; // root
477 static_assert(util::is_same<ValueT, typename StatsT::ValueType>::value, "Mismatching type");
478
479 ValueT mDelta; // skip rendering of node if: node.max < -mDelta || node.min > mDelta
480
481 void process( GridT& );// process grid and all tree nodes
482 void process( TreeT& );// process Tree, root node and child nodes
483 void process( RootT& );// process root node and child nodes
484 NodeStats process( Node0& );// process leaf node
485
486 template<typename NodeT>
487 NodeStats process( NodeT& );// process internal node and child nodes
488
489 template<typename DataT, int Rank>
490 void setStats(DataT*, const Extrema<ValueT, Rank>&);
491 template<typename DataT, int Rank>
492 void setStats(DataT*, const Stats<ValueT, Rank>&);
493 template<typename DataT>
494 void setStats(DataT*, const NoopStats<ValueT>&) {}
495
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); } // unset 1st bit to enable rendering
499
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;
503
504public:
505 GridStats() = default;
506
507 void update(GridT& grid, ValueT delta = ValueT(0));
508
509}; // GridStats
510
511template<typename GridT, typename StatsT>
512struct GridStats<GridT, StatsT>::NodeStats
513{
514 StatsT stats;
516
517 NodeStats(): stats(), bbox() {}//activeCount(0), bbox() {};
518
519 NodeStats& add(const NodeStats &other)
520 {
521 stats.add( other.stats );// no-op for NoopStats?!
522 bbox[0].minComponent(other.bbox[0]);
523 bbox[1].maxComponent(other.bbox[1]);
524 return *this;
525 }
526};// GridStats::NodeStats
527
528//================================================================================================
529
530template<typename GridT, typename StatsT>
531void GridStats<GridT, StatsT>::update(GridT& grid, ValueT delta)
532{
533 mDelta = delta; // delta = voxel size for level sets, else 0
534 this->process( grid );
535}
536
537//================================================================================================
538
539template<typename GridT, typename StatsT>
540template<typename DataT, int Rank>
542 setStats(DataT* data, const Extrema<ValueT, Rank>& e)
543{
544 data->setMin(e.min());
545 data->setMax(e.max());
546}
547
548template<typename GridT, typename StatsT>
549template<typename DataT, int Rank>
550inline void GridStats<GridT, StatsT>::
551 setStats(DataT* data, const Stats<ValueT, Rank>& s)
552{
553 data->setMin(s.min());
554 data->setMax(s.max());
555 data->setAvg(s.avg());
556 data->setDev(s.std());
557}
558
559//================================================================================================
560
561template<typename GridT, 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
566{
567 if (mDelta > 0 && (min > mDelta || max < -mDelta)) {// LS: min > dx || max < -dx
568 flag |= FlagT(1u);// set 1st bit to disable rendering
569 } else {
570 flag &= ~FlagT(1u);// unset 1st bit to enable rendering
571 }
572}
573
574//================================================================================================
575
576template<typename GridT, typename StatsT>
577void GridStats<GridT, StatsT>::process( GridT &grid )
578{
579 this->process( grid.tree() );// this processes tree, root and all nodes
580
581 // set world space AABB
582 auto& data = *grid.data();
583 const auto& indexBBox = grid.tree().root().bbox();
584 if (indexBBox.empty()) {
585 data.mWorldBBox = Vec3dBBox();
586 data.setBBoxOn(false);
587 } else {
588 // Note that below max is offset by one since CoordBBox.max is inclusive
589 // while bbox<Vec3d>.max is exclusive. However, min is inclusive in both
590 // CoordBBox and Vec3dBBox. This also guarantees that a grid with a single
591 // active voxel, does not have an empty world bbox! E.g. if a grid with a
592 // unit index-to-world transformation only contains the active voxel (0,0,0)
593 // then indeBBox = (0,0,0) -> (0,0,0) and then worldBBox = (0.0, 0.0, 0.0)
594 // -> (1.0, 1.0, 1.0). This is a consequence of the different definitions
595 // of index and world bounding boxes inherited from OpenVDB!
596 grid.mWorldBBox = CoordBBox(indexBBox[0], indexBBox[1].offsetBy(1)).transform(grid.map());
597 grid.setBBoxOn(true);
598 }
599
600 // set bit flags
601 data.setMinMaxOn(StatsT::hasMinMax());
602 data.setAverageOn(StatsT::hasAverage());
603 data.setStdDeviationOn(StatsT::hasStdDeviation());
604} // GridStats::process( Grid )
605
606//================================================================================================
607
608template<typename GridT, typename StatsT>
609inline void GridStats<GridT, StatsT>::process( typename GridT::TreeType &tree )
610{
611 this->process( tree.root() );
612}
613
614//================================================================================================
615
616template<typename GridT, typename StatsT>
617void GridStats<GridT, StatsT>::process(RootT &root)
618{
619 using ChildT = Node2;
620 auto &data = *root.data();
621 if (data.mTableSize == 0) { // empty root node
622 data.mMinimum = data.mMaximum = data.mBackground;
623 data.mAverage = data.mStdDevi = 0;
624 data.mBBox = CoordBBox();
625 } else {
626 NodeStats total;
627 for (uint32_t i = 0; i < data.mTableSize; ++i) {
628 auto* tile = data.tile(i);
629 if (tile->isChild()) { // process child node
630 total.add( this->process( *data.getChild(tile) ) );
631 } else if (tile->state) { // active tile
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()) { // resolved at compile time
636 total.stats.add(tile->value, ChildT::NUM_VALUES);
637 }
638 }
639 }
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";
644 }
645 data.mBBox = total.bbox;
646 }
647} // GridStats::process( RootNode )
648
649//================================================================================================
650
651template<typename GridT, typename StatsT>
652template<typename NodeT>
654GridStats<GridT, StatsT>::process(NodeT &node)
655{
656 static_assert(util::is_same<NodeT,Node1>::value || util::is_same<NodeT,Node2>::value, "Incorrect node type");
657 using ChildT = typename NodeT::ChildNodeType;
658
659 NodeStats total;
660 auto* data = node.data();
661
662 // Serial processing of active tiles
663 if (const auto tileCount = data->mValueMask.countOn()) {
664 //total.activeCount = tileCount * ChildT::NUM_VALUES; // active tiles
665 for (auto it = data->mValueMask.beginOn(); it; ++it) {
666 if (StatsT::hasStats()) { // resolved at compile time
667 total.stats.add( data->mTable[*it].value, ChildT::NUM_VALUES );
668 }
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));
672 }
673 }
674
675 // Serial or parallel processing of child nodes
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) ) );
680 }
681#else
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 );
686 }
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] ) );
692 }
693 return local;},
694 [](NodeStats a, const NodeStats &b)->NodeStats { return a.add( b ); }
695 ));
696#endif
697 }
698
699 data->mBBox = total.bbox;
700 if (total.bbox.empty()) {
701 data->mFlags |= uint32_t(1); // set 1st bit on to disable rendering of node
702 data->mFlags &= ~uint32_t(2); // set 2nd bit off since node does not contain active values
703 } else {
704 data->mFlags |= uint32_t(2); // set 2nd bit on since node contains active values
705 if (StatsT::hasStats()) { // resolved at compile time
706 this->setStats(data, total.stats);
707 this->setFlag(data->mMinimum, data->mMaximum, data->mFlags);
708 }
709 }
710 return total;
711} // GridStats::process( InternalNode )
712
713//================================================================================================
714
715template<typename GridT, typename StatsT>
717GridStats<GridT, StatsT>::process(Node0 &leaf)
718{
719 NodeStats local;
720 if (leaf.updateBBox()) {// optionally update active bounding box (updates data->mFlags)
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()) {// resolved at compile time
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);
727 }
728 }
729 return local;
730} // GridStats::process( LeafNode )
731
732//================================================================================================
733
734template<typename BuildT>
736{
737 NANOVDB_ASSERT(grid);
738 using GridT = NanoGrid<BuildT>;
739 using ValueT = typename GridT::ValueType;
740 if (mode == StatsMode::Disable) {
741 return;
744 stats.update(*grid);
745 } else if (mode == StatsMode::MinMax) {
747 stats.update(*grid);
748 } else if (mode == StatsMode::All) {
750 stats.update(*grid);
751 } else {
752 throw std::runtime_error("gridStats: Unsupported statistics mode.");
753 }
754}// updateGridStats
755
756template<typename BuildT>
757[[deprecated("Use nanovdb::tools::updateGridStats(NanoGrid*, StatsMode) instead")]]
759{
760 updateGridStats<BuildT>(&grid, mode);
761}
762
763//================================================================================================
764
765namespace {
766
767// returns a bitmask (of size 32^3 or 16^3) that marks all the entries
768// in a node table that intersects with the specified bounding box.
769template<typename NodeT>
770Mask<NodeT::LOG2DIM> getBBoxMask(const CoordBBox &bbox, const NodeT* node)
771{
772 Mask<NodeT::LOG2DIM> mask;// typically 32^3 or 16^3 bit mask
773 auto b = CoordBBox::createCube(node->origin(), node->dim());
774 assert( bbox.hasOverlap(b) );
775 if ( bbox.isInside(b) ) {
776 mask.setOn();//node is completely inside the bbox so early out
777 } else {
778 b.intersect(bbox);// trim bounding box
779 // transform bounding box from global to local coordinates
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() );
785 auto it = b.begin();// iterates over all the child nodes or tiles that intersects bbox
786 for (const Coord& ijk = *it; it; ++it) {
787 mask.setOn(ijk[2] + (ijk[1] << NodeT::LOG2DIM) + (ijk[0] << 2*NodeT::LOG2DIM));
788 }
789 }
790 return mask;
791}// getBBoxMask
792
793}// end of unnamed namespace
794
795/// @brief return the extrema of all the values in a grid that
796/// intersects the specified bounding box.
797template<typename BuildT>
799getExtrema(const NanoGrid<BuildT>& grid, const CoordBBox &bbox)
800{
801 using GridT = NanoGrid<BuildT>;
802 using ValueT = typename GridT::ValueType;
803 using TreeT = typename GridTree<GridT>::type;
804 using RootT = typename NodeTrait<TreeT, 3>::type;// root node
805 using Node2 = typename NodeTrait<TreeT, 2>::type;// upper internal node
806 using Node1 = typename NodeTrait<TreeT, 1>::type;// lower internal node
807 using Node0 = typename NodeTrait<TreeT, 0>::type;// leaf node
808
809 Extrema<ValueT> extrema;
810 const RootT &root = grid.tree().root();
811 const auto &bbox3 = root.bbox();
812 if (bbox.isInside(bbox3)) {// bbox3 is contained inside bbox
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());
827 } else {// partial intersections at level 2
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());
837 } else {// partial intersection at level 1
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());
847 } else {// partial intersection at level 0
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));
852 }
853 }// end partial intersection at level 0
854 } else {// tile at level 1
855 extrema.add(data1->mTable[*it1].value);
856 }
857 }
858 }// end of partial intersection at level 1
859 } else {// tile at level 2
860 extrema.add(data2->mTable[*it2].value);
861 }
862 }// loop over tiles and nodes at level 2
863 }// end of partial intersection at level 1
864 } else {// tile at root level
865 extrema.add(tile->value);
866 }
867 }// loop over root table
868 } else {// bbox does not overlap the grid
869 extrema.add(root.background());
870 }
871 return extrema;
872}// getExtrema
873
874}// namespace tools
875
876} // namespace nanovdb
877
878#endif // NANOVDB_TOOLS_GRIDSTATS_H_HAS_BEEN_INCLUDED
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
__hostdev__ void setStats(NodeT &node) const
Definition GridStats.h:223
static __hostdev__ constexpr bool hasAverage()
Definition GridStats.h:217
static __hostdev__ constexpr size_t size()
Definition GridStats.h:220
__hostdev__ Extrema & min(const VecT &v)
Definition GridStats.h:193
struct nanovdb::tools::Extrema< VecT, 1 >::Pair mMin
__hostdev__ Extrema & add(const Pair &p)
Definition GridStats.h:163
static __hostdev__ constexpr bool hasMinMax()
Definition GridStats.h:216
typename VecT::ValueType Real
Definition GridStats.h:145
__hostdev__ Extrema()
Definition GridStats.h:172
struct nanovdb::tools::Extrema< VecT, 1 >::Pair mMax
__hostdev__ Extrema & add(const VecT &v, uint64_t)
Definition GridStats.h:206
__hostdev__ const VecT & max() const
Definition GridStats.h:214
__hostdev__ Extrema & max(const VecT &v)
Definition GridStats.h:199
__hostdev__ Extrema(const VecT &v)
Definition GridStats.h:183
__hostdev__ Extrema & add(const Extrema &other)
Definition GridStats.h:207
__hostdev__ Extrema(const VecT &a, const VecT &b)
Definition GridStats.h:188
__hostdev__ const VecT & min() const
Definition GridStats.h:213
static __hostdev__ constexpr bool hasStdDeviation()
Definition GridStats.h:218
__hostdev__ Extrema & add(const VecT &v)
Definition GridStats.h:205
static __hostdev__ constexpr bool hasStats()
Definition GridStats.h:219
VecT ValueType
Definition GridStats.h:171
Definition GridStats.h:56
Allows for the construction of NanoVDB grids without any dependency.
Definition GridStats.h:468
void update(GridT &grid, ValueT delta=ValueT(0))
Definition GridStats.h:531
__hostdev__ Stats & add(const Stats &other)
Add the samples from the other Stats instance.
Definition GridStats.h:291
__hostdev__ void setStats(NodeT &node) const
Definition GridStats.h:333
double mAvg
Definition GridStats.h:250
static __hostdev__ constexpr bool hasAverage()
Definition GridStats.h:305
__hostdev__ double stdDev() const
Definition GridStats.h:329
__hostdev__ Stats()
Definition GridStats.h:254
ValueT ValueType
Definition GridStats.h:253
__hostdev__ double mean() const
Definition GridStats.h:314
static __hostdev__ constexpr bool hasMinMax()
Definition GridStats.h:304
size_t mSize
Definition GridStats.h:249
double RealT
Definition GridStats.h:248
__hostdev__ Stats & add(const ValueT &val)
Add a single sample.
Definition GridStats.h:269
__hostdev__ double var() const
Return the population variance.
Definition GridStats.h:321
__hostdev__ Stats & add(const ValueT &val, uint64_t n)
Add n samples with constant value val.
Definition GridStats.h:279
__hostdev__ double avg() const
Return the arithmetic mean, i.e. average, value.
Definition GridStats.h:313
__hostdev__ double std() const
Return the standard deviation (=Sqrt(variance)) as defined from the (biased) population variance.
Definition GridStats.h:328
__hostdev__ Stats(const ValueT &val)
Definition GridStats.h:261
__hostdev__ size_t size() const
Definition GridStats.h:309
static __hostdev__ constexpr bool hasStdDeviation()
Definition GridStats.h:306
double mAux
Definition GridStats.h:250
Extrema< ValueT, 0 > BaseT
Definition GridStats.h:247
__hostdev__ double variance() const
Definition GridStats.h:322
static __hostdev__ constexpr bool hasStats()
Definition GridStats.h:307
__hostdev__ Stats & add(const Stats &other)
Add the samples from the other Stats instance.
Definition GridStats.h:393
__hostdev__ void setStats(NodeT &node) const
Definition GridStats.h:435
double mAvg
Definition GridStats.h:357
static __hostdev__ constexpr bool hasAverage()
Definition GridStats.h:407
__hostdev__ double stdDev() const
Definition GridStats.h:431
__hostdev__ Stats()
Definition GridStats.h:361
ValueT ValueType
Definition GridStats.h:360
__hostdev__ double mean() const
Definition GridStats.h:416
static __hostdev__ constexpr bool hasMinMax()
Definition GridStats.h:406
size_t mSize
Definition GridStats.h:356
double RealT
Definition GridStats.h:355
__hostdev__ Stats & add(const ValueT &val)
Add a single sample.
Definition GridStats.h:369
__hostdev__ double var() const
Return the population variance.
Definition GridStats.h:423
__hostdev__ Stats & add(const ValueT &val, uint64_t n)
Add n samples with constant value val.
Definition GridStats.h:380
__hostdev__ double avg() const
Return the arithmetic mean, i.e. average, value.
Definition GridStats.h:415
__hostdev__ double std() const
Return the standard deviation (=Sqrt(variance)) as defined from the (biased) population variance.
Definition GridStats.h:430
__hostdev__ size_t size() const
Definition GridStats.h:411
static __hostdev__ constexpr bool hasStdDeviation()
Definition GridStats.h:408
double mAux
Definition GridStats.h:357
__hostdev__ double variance() const
Definition GridStats.h:424
Extrema< ValueT, 1 > BaseT
Definition GridStats.h:354
static __hostdev__ constexpr bool hasStats()
Definition GridStats.h:409
Definition GridStats.h:233
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
Definition CreateNanoGrid.h:104
Extrema< typename NanoGrid< BuildT >::ValueType > getExtrema(const NanoGrid< BuildT > &grid, const CoordBBox &bbox)
Determine the extrema of all the values in a grid that intersects the specified bounding box.
Definition GridStats.h:799
StatsMode
Grid flags which indicate what extra information is present in the grid buffer.
Definition GridStats.h:40
@ BBox
Definition GridStats.h:42
@ Default
Definition GridStats.h:45
@ End
Definition GridStats.h:46
@ All
Definition GridStats.h:44
@ MinMax
Definition GridStats.h:43
@ Disable
Definition GridStats.h:41
void gridStats(NanoGrid< BuildT > &grid, StatsMode mode=StatsMode::Default)
Definition GridStats.h:758
void updateGridStats(NanoGrid< BuildT > *grid, StatsMode mode=StatsMode::Default)
Re-computes the min/max, stats and bbox information for an existing NanoVDB Grid.
Definition GridStats.h:735
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
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition Composite.h:110
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition Composite.h:106
Definition Coord.h:590
#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
VecT vector
Definition GridStats.h:149
__hostdev__ Pair(Real s)
Definition GridStats.h:151
__hostdev__ bool operator<(const Pair &rhs) const
Definition GridStats.h:161
Real scalar
Definition GridStats.h:148
__hostdev__ Pair(const VecT &v)
Definition GridStats.h:156
Definition GridStats.h:513
NodeStats()
Definition GridStats.h:517
StatsT stats
Definition GridStats.h:514
CoordBBox bbox
Definition GridStats.h:515
NodeStats & add(const NodeStats &other)
Definition GridStats.h:519
No-op Stats class.
Definition GridStats.h:447
static __hostdev__ constexpr bool hasAverage()
Definition GridStats.h:456
__hostdev__ NoopStats & add(const ValueT &, uint64_t)
Definition GridStats.h:452
static __hostdev__ constexpr size_t size()
Definition GridStats.h:454
__hostdev__ void setStats(NodeT &) const
Definition GridStats.h:460
ValueT ValueType
Definition GridStats.h:448
static __hostdev__ constexpr bool hasMinMax()
Definition GridStats.h:455
__hostdev__ NoopStats & add(const NoopStats &)
Definition GridStats.h:453
__hostdev__ NoopStats & add(const ValueT &)
Definition GridStats.h:451
static __hostdev__ constexpr bool hasStdDeviation()
Definition GridStats.h:457
__hostdev__ NoopStats(const ValueT &)
Definition GridStats.h:450
__hostdev__ NoopStats()
Definition GridStats.h:449
static __hostdev__ constexpr bool hasStats()
Definition GridStats.h:458
static constexpr bool value
Definition Util.h:328