OpenVDB  9.0.1
LevelSetFilter.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
4 /// @author Ken Museth
5 ///
6 /// @file tools/LevelSetFilter.h
7 ///
8 /// @brief Performs various types of level set deformations with
9 /// interface tracking. These unrestricted deformations include
10 /// surface smoothing (e.g., Laplacian flow), filtering (e.g., mean
11 /// value) and morphological operations (e.g., morphological opening).
12 /// All these operations can optionally be masked with another grid that
13 /// acts as an alpha-mask.
14 
15 #ifndef OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED
16 #define OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED
17 
18 #include "LevelSetTracker.h"
19 #include "Interpolation.h"
20 #include <algorithm> // for std::max()
21 #include <functional>
22 #include <type_traits>
23 
24 
25 namespace openvdb {
27 namespace OPENVDB_VERSION_NAME {
28 namespace tools {
29 
30 /// @brief Filtering (e.g. diffusion) of narrow-band level sets. An
31 /// optional scalar field can be used to produce a (smooth) alpha mask
32 /// for the filtering.
33 ///
34 /// @note This class performs proper interface tracking which allows
35 /// for unrestricted surface deformations
36 template<typename GridT,
37  typename MaskT = typename GridT::template ValueConverter<float>::Type,
38  typename InterruptT = util::NullInterrupter>
39 class LevelSetFilter : public LevelSetTracker<GridT, InterruptT>
40 {
41 public:
43  using GridType = GridT;
44  using MaskType = MaskT;
45  using TreeType = typename GridType::TreeType;
46  using ValueType = typename TreeType::ValueType;
47  using AlphaType = typename MaskType::ValueType;
49  "LevelSetFilter requires a mask grid with floating-point values");
50 
51  /// @brief Main constructor from a grid
52  /// @param grid The level set to be filtered.
53  /// @param interrupt Optional interrupter.
54  LevelSetFilter(GridType& grid, InterruptT* interrupt = nullptr)
55  : BaseType(grid, interrupt)
56  , mMinMask(0)
57  , mMaxMask(1)
58  , mInvertMask(false)
59  {
60  }
61  /// @brief Default destructor
62  ~LevelSetFilter() override {}
63 
64  /// @brief Return the minimum value of the mask to be used for the
65  /// derivation of a smooth alpha value.
66  AlphaType minMask() const { return mMinMask; }
67  /// @brief Return the maximum value of the mask to be used for the
68  /// derivation of a smooth alpha value.
69  AlphaType maxMask() const { return mMaxMask; }
70  /// @brief Define the range for the (optional) scalar mask.
71  /// @param min Minimum value of the range.
72  /// @param max Maximum value of the range.
73  /// @details Mask values outside the range maps to alpha values of
74  /// respectfully zero and one, and values inside the range maps
75  /// smoothly to 0->1 (unless of course the mask is inverted).
76  /// @throw ValueError if @a min is not smaller than @a max.
78  {
79  if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)");
80  mMinMask = min;
81  mMaxMask = max;
82  }
83 
84  /// @brief Return true if the mask is inverted, i.e. min->max in the
85  /// original mask maps to 1->0 in the inverted alpha mask.
86  bool isMaskInverted() const { return mInvertMask; }
87  /// @brief Invert the optional mask, i.e. min->max in the original
88  /// mask maps to 1->0 in the inverted alpha mask.
89  void invertMask(bool invert=true) { mInvertMask = invert; }
90 
91  /// @brief One iteration of mean-curvature flow of the level set.
92  /// @param mask Optional alpha mask.
93  void meanCurvature(const MaskType* mask = nullptr)
94  {
95  Filter f(this, mask); f.meanCurvature();
96  }
97 
98  /// @brief One iteration of Laplacian flow of the level set.
99  /// @param mask Optional alpha mask.
100  void laplacian(const MaskType* mask = nullptr)
101  {
102  Filter f(this, mask); f.laplacian();
103  }
104 
105  /// @brief One iteration of a fast separable Gaussian filter.
106  /// @param width Width of the Gaussian kernel in voxel units.
107  /// @param mask Optional alpha mask.
108  ///
109  /// @note This is approximated as 4 iterations of a separable mean filter
110  /// which typically leads an approximation that's better than 95%!
111  void gaussian(int width = 1, const MaskType* mask = nullptr)
112  {
113  Filter f(this, mask); f.gaussian(width);
114  }
115 
116  /// @brief Offset the level set by the specified (world) distance.
117  /// @param offset Value of the offset.
118  /// @param mask Optional alpha mask.
119  void offset(ValueType offset, const MaskType* mask = nullptr)
120  {
121  Filter f(this, mask); f.offset(offset);
122  }
123 
124  /// @brief One iteration of median-value flow of the level set.
125  /// @param width Width of the median-value kernel in voxel units.
126  /// @param mask Optional alpha mask.
127  ///
128  /// @warning This filter is not separable and is hence relatively
129  /// slow!
130  void median(int width = 1, const MaskType* mask = nullptr)
131  {
132  Filter f(this, mask); f.median(width);
133  }
134 
135  /// @brief One iteration of mean-value flow of the level set.
136  /// @param width Width of the mean-value kernel in voxel units.
137  /// @param mask Optional alpha mask.
138  ///
139  /// @note This filter is separable so it's fast!
140  void mean(int width = 1, const MaskType* mask = nullptr)
141  {
142  Filter f(this, mask); f.mean(width);
143  }
144 
145 private:
146  // disallow copy construction and copy by assignment!
147  LevelSetFilter(const LevelSetFilter&);// not implemented
148  LevelSetFilter& operator=(const LevelSetFilter&);// not implemented
149 
150  // Private struct that implements all the filtering.
151  struct Filter
152  {
153  using LeafT = typename TreeType::LeafNodeType;
154  using VoxelIterT = typename LeafT::ValueOnIter;
155  using VoxelCIterT = typename LeafT::ValueOnCIter;
156  using BufferT = typename tree::LeafManager<TreeType>::BufferType;
158  using LeafIterT = typename LeafRange::Iterator;
159  using AlphaMaskT = tools::AlphaMask<GridT, MaskT>;
160 
161  Filter(LevelSetFilter* parent, const MaskType* mask) : mParent(parent), mMask(mask) {}
162  Filter(const Filter&) = default;
163  virtual ~Filter() {}
164 
165  void box(int width);
166  void median(int width);
167  void mean(int width);
168  void gaussian(int width);
169  void laplacian();
170  void meanCurvature();
171  void offset(ValueType value);
172  void operator()(const LeafRange& r) const
173  {
174  if (mTask) mTask(const_cast<Filter*>(this), r);
175  else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly");
176  }
177  void cook(bool swap)
178  {
179  const int n = mParent->getGrainSize();
180  if (n>0) {
181  tbb::parallel_for(mParent->leafs().leafRange(n), *this);
182  } else {
183  (*this)(mParent->leafs().leafRange());
184  }
185  if (swap) mParent->leafs().swapLeafBuffer(1, n==0);
186  }
187 
188  template <size_t Axis>
189  struct Avg {
190  Avg(const GridT& grid, Int32 w) :
191  acc(grid.tree()), width(w), frac(1/ValueType(2*w+1)) {}
192  inline ValueType operator()(Coord xyz)
193  {
194  ValueType sum = zeroVal<ValueType>();
195  Int32& i = xyz[Axis], j = i + width;
196  for (i -= width; i <= j; ++i) sum += acc.getValue(xyz);
197  return sum*frac;
198  }
199  typename GridT::ConstAccessor acc;
200  const Int32 width;
202  };
203 
204  template<typename AvgT>
205  void boxImpl(const LeafRange& r, Int32 w);
206 
207  void boxXImpl(const LeafRange& r, Int32 w) { this->boxImpl<Avg<0> >(r,w); }
208  void boxZImpl(const LeafRange& r, Int32 w) { this->boxImpl<Avg<1> >(r,w); }
209  void boxYImpl(const LeafRange& r, Int32 w) { this->boxImpl<Avg<2> >(r,w); }
210 
211  void medianImpl(const LeafRange&, int);
212  void meanCurvatureImpl(const LeafRange&);
213  void laplacianImpl(const LeafRange&);
214  void offsetImpl(const LeafRange&, ValueType);
215 
216  LevelSetFilter* mParent;
217  const MaskType* mMask;
218  typename std::function<void (Filter*, const LeafRange&)> mTask;
219  }; // end of private Filter struct
220 
221  AlphaType mMinMask, mMaxMask;
222  bool mInvertMask;
223 
224 }; // end of LevelSetFilter class
225 
226 
227 ////////////////////////////////////////
228 
229 template<typename GridT, typename MaskT, typename InterruptT>
230 inline void
232 {
233  mParent->startInterrupter("Median-value flow of level set");
234 
235  mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0);
236 
237  mTask = std::bind(&Filter::medianImpl,
238  std::placeholders::_1, std::placeholders::_2, std::max(1, width));
239  this->cook(true);
240 
241  mParent->track();
242 
243  mParent->endInterrupter();
244 }
245 
246 template<typename GridT, typename MaskT, typename InterruptT>
247 inline void
249 {
250  mParent->startInterrupter("Mean-value flow of level set");
251 
252  this->box(width);
253 
254  mParent->endInterrupter();
255 }
256 
257 template<typename GridT, typename MaskT, typename InterruptT>
258 inline void
260 {
261  mParent->startInterrupter("Gaussian flow of level set");
262 
263  for (int n=0; n<4; ++n) this->box(width);
264 
265  mParent->endInterrupter();
266 }
267 
268 template<typename GridT, typename MaskT, typename InterruptT>
269 inline void
271 {
272  mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0);
273 
274  width = std::max(1, width);
275 
276  mTask = std::bind(&Filter::boxXImpl, std::placeholders::_1, std::placeholders::_2, width);
277  this->cook(true);
278 
279  mTask = std::bind(&Filter::boxYImpl, std::placeholders::_1, std::placeholders::_2, width);
280  this->cook(true);
281 
282  mTask = std::bind(&Filter::boxZImpl, std::placeholders::_1, std::placeholders::_2, width);
283  this->cook(true);
284 
285  mParent->track();
286 }
287 
288 template<typename GridT, typename MaskT, typename InterruptT>
289 inline void
291 {
292  mParent->startInterrupter("Mean-curvature flow of level set");
293 
294  mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0);
295 
296  mTask = std::bind(&Filter::meanCurvatureImpl, std::placeholders::_1, std::placeholders::_2);
297  this->cook(true);
298 
299  mParent->track();
300 
301  mParent->endInterrupter();
302 }
303 
304 template<typename GridT, typename MaskT, typename InterruptT>
305 inline void
307 {
308  mParent->startInterrupter("Laplacian flow of level set");
309 
310  mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0);
311 
312  mTask = std::bind(&Filter::laplacianImpl, std::placeholders::_1, std::placeholders::_2);
313  this->cook(true);
314 
315  mParent->track();
316 
317  mParent->endInterrupter();
318 }
319 
320 template<typename GridT, typename MaskT, typename InterruptT>
321 inline void
323 {
324  mParent->startInterrupter("Offsetting level set");
325 
326  mParent->leafs().removeAuxBuffers();// no auxiliary buffers required
327 
328  const ValueType CFL = ValueType(0.5) * mParent->voxelSize(), offset = openvdb::math::Abs(value);
329  ValueType dist = 0.0;
330  while (offset-dist > ValueType(0.001)*CFL && mParent->checkInterrupter()) {
331  const ValueType delta = openvdb::math::Min(offset-dist, CFL);
332  dist += delta;
333 
334  mTask = std::bind(&Filter::offsetImpl,
335  std::placeholders::_1, std::placeholders::_2, copysign(delta, value));
336  this->cook(false);
337 
338  mParent->track();
339  }
340 
341  mParent->endInterrupter();
342 }
343 
344 
345 ///////////////////////// PRIVATE METHODS //////////////////////
346 
347 /// Performs parabolic mean-curvature diffusion
348 template<typename GridT, typename MaskT, typename InterruptT>
349 inline void
351 {
352  mParent->checkInterrupter();
353  //const float CFL = 0.9f, dt = CFL * mDx * mDx / 6.0f;
354  const ValueType dx = mParent->voxelSize(), dt = math::Pow2(dx) / ValueType(3.0);
355  math::CurvatureStencil<GridType> stencil(mParent->grid(), dx);
356  if (mMask) {
357  typename AlphaMaskT::FloatType a, b;
358  AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(),
359  mParent->maxMask(), mParent->isMaskInverted());
360  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
361  ValueType* buffer = leafIter.buffer(1).data();
362  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
363  if (alpha(iter.getCoord(), a, b)) {
364  stencil.moveTo(iter);
365  const ValueType phi0 = *iter, phi1 = phi0 + dt*stencil.meanCurvatureNormGrad();
366  buffer[iter.pos()] = b * phi0 + a * phi1;
367  }
368  }
369  }
370  } else {
371  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
372  ValueType* buffer = leafIter.buffer(1).data();
373  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
374  stencil.moveTo(iter);
375  buffer[iter.pos()] = *iter + dt*stencil.meanCurvatureNormGrad();
376  }
377  }
378  }
379 }
380 
381 /// Performs Laplacian diffusion. Note if the grids contains a true
382 /// signed distance field (e.g. a solution to the Eikonal equation)
383 /// Laplacian diffusions (e.g. geometric heat equation) is actually
384 /// identical to mean curvature diffusion, yet less computationally
385 /// expensive! In other words if you're performing renormalization
386 /// anyway (e.g. rebuilding the narrow-band) you should consider
387 /// performing Laplacian diffusion over mean curvature flow!
388 template<typename GridT, typename MaskT, typename InterruptT>
389 inline void
391 {
392  mParent->checkInterrupter();
393  //const float CFL = 0.9f, half_dt = CFL * mDx * mDx / 12.0f;
394  const ValueType dx = mParent->voxelSize(), dt = math::Pow2(dx) / ValueType(6.0);
395  math::GradStencil<GridType> stencil(mParent->grid(), dx);
396  if (mMask) {
397  typename AlphaMaskT::FloatType a, b;
398  AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(),
399  mParent->maxMask(), mParent->isMaskInverted());
400  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
401  ValueType* buffer = leafIter.buffer(1).data();
402  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
403  if (alpha(iter.getCoord(), a, b)) {
404  stencil.moveTo(iter);
405  const ValueType phi0 = *iter, phi1 = phi0 + dt*stencil.laplacian();
406  buffer[iter.pos()] = b * phi0 + a * phi1;
407  }
408  }
409  }
410  } else {
411  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
412  ValueType* buffer = leafIter.buffer(1).data();
413  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
414  stencil.moveTo(iter);
415  buffer[iter.pos()] = *iter + dt*stencil.laplacian();
416  }
417  }
418  }
419 }
420 
421 /// Offsets the values by a constant
422 template<typename GridT, typename MaskT, typename InterruptT>
423 inline void
425  const LeafRange& range, ValueType offset)
426 {
427  mParent->checkInterrupter();
428  if (mMask) {
429  typename AlphaMaskT::FloatType a, b;
430  AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(),
431  mParent->maxMask(), mParent->isMaskInverted());
432  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
433  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
434  if (alpha(iter.getCoord(), a, b)) iter.setValue(*iter + a*offset);
435  }
436  }
437  } else {
438  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
439  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
440  iter.setValue(*iter + offset);
441  }
442  }
443  }
444 }
445 
446 /// Performs simple but slow median-value diffusion
447 template<typename GridT, typename MaskT, typename InterruptT>
448 inline void
449 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::medianImpl(const LeafRange& range, int width)
450 {
451  mParent->checkInterrupter();
452  typename math::DenseStencil<GridType> stencil(mParent->grid(), width);//creates local cache!
453  if (mMask) {
454  typename AlphaMaskT::FloatType a, b;
455  AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(),
456  mParent->maxMask(), mParent->isMaskInverted());
457  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
458  ValueType* buffer = leafIter.buffer(1).data();
459  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
460  if (alpha(iter.getCoord(), a, b)) {
461  stencil.moveTo(iter);
462  buffer[iter.pos()] = b * (*iter) + a * stencil.median();
463  }
464  }
465  }
466  } else {
467  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
468  ValueType* buffer = leafIter.buffer(1).data();
469  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
470  stencil.moveTo(iter);
471  buffer[iter.pos()] = stencil.median();
472  }
473  }
474  }
475 }
476 
477 /// One dimensional convolution of a separable box filter
478 template<typename GridT, typename MaskT, typename InterruptT>
479 template <typename AvgT>
480 inline void
482 {
483  mParent->checkInterrupter();
484  AvgT avg(mParent->grid(), w);
485  if (mMask) {
486  typename AlphaMaskT::FloatType a, b;
487  AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(),
488  mParent->maxMask(), mParent->isMaskInverted());
489  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
490  ValueType* buffer = leafIter.buffer(1).data();
491  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
492  const Coord xyz = iter.getCoord();
493  if (alpha(xyz, a, b)) buffer[iter.pos()] = b * (*iter)+ a * avg(xyz);
494  }
495  }
496  } else {
497  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
498  ValueType* buffer = leafIter.buffer(1).data();
499  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
500  buffer[iter.pos()] = avg(iter.getCoord());
501  }
502  }
503  }
504 }
505 
506 
507 ////////////////////////////////////////
508 
509 
510 // Explicit Template Instantiation
511 
512 #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
513 
514 #ifdef OPENVDB_INSTANTIATE_LEVELSETFILTER
516 #endif
517 
520 
521 #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
522 
523 
524 } // namespace tools
525 } // namespace OPENVDB_VERSION_NAME
526 } // namespace openvdb
527 
528 #endif // OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED
Performs multi-threaded interface tracking of narrow band level sets. This is the building-block for ...
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
void invertMask(bool invert=true)
Invert the optional mask, i.e. min->max in the original mask maps to 1->0 in the inverted alpha mask...
Definition: LevelSetFilter.h:89
void setMaskRange(AlphaType min, AlphaType max)
Define the range for the (optional) scalar mask.
Definition: LevelSetFilter.h:77
void startInterrupter(const char *msg)
Definition: LevelSetTracker.h:369
Definition: Interpolation.h:543
typename TreeType::ValueType ValueType
Definition: LevelSetFilter.h:46
void laplacian(const MaskType *mask=nullptr)
One iteration of Laplacian flow of the level set.
Definition: LevelSetFilter.h:100
void meanCurvature(const MaskType *mask=nullptr)
One iteration of mean-curvature flow of the level set.
Definition: LevelSetFilter.h:93
Definition: Exceptions.h:65
void mean(int width=1, const MaskType *mask=nullptr)
One iteration of mean-value flow of the level set.
Definition: LevelSetFilter.h:140
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:107
~LevelSetFilter() override
Default destructor.
Definition: LevelSetFilter.h:62
bool isMaskInverted() const
Return true if the mask is inverted, i.e. min->max in the original mask maps to 1->0 in the inverted ...
Definition: LevelSetFilter.h:86
Axis
Definition: Math.h:904
Dense stencil of a given width.
Definition: Stencils.h:1764
Definition: LeafManager.h:101
AlphaType minMask() const
Return the minimum value of the mask to be used for the derivation of a smooth alpha value...
Definition: LevelSetFilter.h:66
MaskT MaskType
Definition: LevelSetFilter.h:44
LeafManagerType & leafs()
Definition: LevelSetTracker.h:189
Performs multi-threaded interface tracking of narrow band level sets.
Definition: LevelSetTracker.h:57
bool checkInterrupter()
Definition: LevelSetTracker.h:385
GridType::Ptr laplacian(const GridType &grid, bool threaded, InterruptT *interrupt)
Compute the Laplacian of the given scalar grid.
Definition: GridOperators.h:1017
void gaussian(int width=1, const MaskType *mask=nullptr)
One iteration of a fast separable Gaussian filter.
Definition: LevelSetFilter.h:111
typename MaskType::ValueType AlphaType
Definition: LevelSetFilter.h:47
Definition: Exceptions.h:13
ValueT value
Definition: GridBuilder.h:1287
Avg(const GridT &grid, Int32 w)
Definition: LevelSetFilter.h:190
Definition: Stencils.h:1515
const Type & Min(const Type &a, const Type &b)
Return the minimum of two values.
Definition: Math.h:659
#define OPENVDB_INSTANTIATE_CLASS
Definition: version.h.in:143
void offset(ValueType offset, const MaskType *mask=nullptr)
Offset the level set by the specified (world) distance.
Definition: LevelSetFilter.h:119
GridT::ConstAccessor acc
Definition: LevelSetFilter.h:199
const ValueType frac
Definition: LevelSetFilter.h:201
typename LeafManagerType::LeafRange LeafRange
Definition: LevelSetTracker.h:67
int32_t Int32
Definition: Types.h:56
const Int32 width
Definition: LevelSetFilter.h:200
typename CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition: LeafManager.h:95
typename GridType::TreeType TreeType
Definition: LevelSetFilter.h:45
FloatT FloatType
Definition: Interpolation.h:551
Definition: Stencils.h:1231
GridT GridType
Definition: LevelSetFilter.h:43
Coord Abs(const Coord &xyz)
Definition: Coord.h:514
Type Pow2(Type x)
Return x2.
Definition: Math.h:551
LevelSetFilter(GridType &grid, InterruptT *interrupt=nullptr)
Main constructor from a grid.
Definition: LevelSetFilter.h:54
void median(int width=1, const MaskType *mask=nullptr)
One iteration of median-value flow of the level set.
Definition: LevelSetFilter.h:130
AlphaType maxMask() const
Return the maximum value of the mask to be used for the derivation of a smooth alpha value...
Definition: LevelSetFilter.h:69
GridType::Ptr meanCurvature(const GridType &grid, bool threaded, InterruptT *interrupt)
Compute the mean curvature of the given grid.
Definition: GridOperators.h:1034
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:103
ValueType operator()(Coord xyz)
Definition: LevelSetFilter.h:192
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:202
Filtering (e.g. diffusion) of narrow-band level sets. An optional scalar field can be used to produce...
Definition: LevelSetFilter.h:39