OpenVDB  8.0.1
Filter.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
11 
12 #ifndef OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
13 #define OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
14 
15 #include <tbb/parallel_for.h>
16 #include <openvdb/Types.h>
17 #include <openvdb/math/Math.h>
18 #include <openvdb/math/Stencils.h>
19 #include <openvdb/math/Transform.h>
22 #include <openvdb/Grid.h>
23 #include "Interpolation.h"
24 #include <algorithm> // for std::max()
25 #include <functional>
26 #include <type_traits>
27 
28 
29 namespace openvdb {
31 namespace OPENVDB_VERSION_NAME {
32 namespace tools {
33 
37 template<typename GridT,
38  typename MaskT = typename GridT::template ValueConverter<float>::Type,
39  typename InterruptT = util::NullInterrupter>
40 class Filter
41 {
42 public:
43  using GridType = GridT;
44  using MaskType = MaskT;
45  using TreeType = typename GridType::TreeType;
46  using LeafType = typename TreeType::LeafNodeType;
47  using ValueType = typename GridType::ValueType;
48  using AlphaType = typename MaskType::ValueType;
50  using RangeType = typename LeafManagerType::LeafRange;
51  using BufferType = typename LeafManagerType::BufferType;
52  static_assert(std::is_floating_point<AlphaType>::value,
53  "openvdb::tools::Filter requires a mask grid with floating-point values");
54 
58  Filter(GridT& grid, InterruptT* interrupt = nullptr)
59  : mGrid(&grid)
60  , mTask(nullptr)
61  , mInterrupter(interrupt)
62  , mMask(nullptr)
63  , mGrainSize(1)
64  , mMinMask(0)
65  , mMaxMask(1)
66  , mInvertMask(false)
67  {
68  }
69 
73  Filter(const Filter& other)
74  : mGrid(other.mGrid)
75  , mTask(other.mTask)
76  , mInterrupter(other.mInterrupter)
77  , mMask(other.mMask)
78  , mGrainSize(other.mGrainSize)
79  , mMinMask(other.mMinMask)
80  , mMaxMask(other.mMaxMask)
81  , mInvertMask(other.mInvertMask)
82  {
83  }
84 
86  int getGrainSize() const { return mGrainSize; }
89  void setGrainSize(int grainsize) { mGrainSize = grainsize; }
90 
93  AlphaType minMask() const { return mMinMask; }
96  AlphaType maxMask() const { return mMaxMask; }
104  {
105  if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)");
106  mMinMask = min;
107  mMaxMask = max;
108  }
109 
112  bool isMaskInverted() const { return mInvertMask; }
115  void invertMask(bool invert=true) { mInvertMask = invert; }
116 
121  void mean(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
122 
130  void gaussian(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
131 
138  void median(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
139 
143  void offset(ValueType offset, const MaskType* mask = nullptr);
144 
149  void operator()(const RangeType& range) const
150  {
151  if (mTask) mTask(const_cast<Filter*>(this), range);
152  else OPENVDB_THROW(ValueError, "task is undefined - call median(), mean(), etc.");
153  }
154 
155 private:
156  using LeafT = typename TreeType::LeafNodeType;
157  using VoxelIterT = typename LeafT::ValueOnIter;
158  using VoxelCIterT = typename LeafT::ValueOnCIter;
159  using BufferT = typename tree::LeafManager<TreeType>::BufferType;
160  using LeafIterT = typename RangeType::Iterator;
161  using AlphaMaskT = tools::AlphaMask<GridT, MaskT>;
162 
163  void cook(LeafManagerType& leafs);
164 
165  template<size_t Axis>
166  struct Avg {
167  Avg(const GridT* grid, Int32 w): acc(grid->tree()), width(w), frac(1.f/float(2*w+1)) {}
168  inline ValueType operator()(Coord xyz);
169  typename GridT::ConstAccessor acc;
170  const Int32 width;
171  const float frac;
172  };
173 
174  // Private filter methods called by tbb::parallel_for threads
175  template <typename AvgT>
176  void doBox( const RangeType& r, Int32 w);
177  void doBoxX(const RangeType& r, Int32 w) { this->doBox<Avg<0> >(r,w); }
178  void doBoxY(const RangeType& r, Int32 w) { this->doBox<Avg<1> >(r,w); }
179  void doBoxZ(const RangeType& r, Int32 w) { this->doBox<Avg<2> >(r,w); }
180  void doMedian(const RangeType&, int);
181  void doOffset(const RangeType&, ValueType);
183  bool wasInterrupted();
184 
185  GridType* mGrid;
186  typename std::function<void (Filter*, const RangeType&)> mTask;
187  InterruptT* mInterrupter;
188  const MaskType* mMask;
189  int mGrainSize;
190  AlphaType mMinMask, mMaxMask;
191  bool mInvertMask;
192 }; // end of Filter class
193 
194 
196 
197 
198 namespace filter_internal {
199 // Helper function for Filter::Avg::operator()
200 template<typename T> static inline void accum(T& sum, T addend) { sum += addend; }
201 // Overload for bool ValueType
202 inline void accum(bool& sum, bool addend) { sum = sum || addend; }
203 }
204 
205 
206 template<typename GridT, typename MaskT, typename InterruptT>
207 template<size_t Axis>
208 inline typename GridT::ValueType
209 Filter<GridT, MaskT, InterruptT>::Avg<Axis>::operator()(Coord xyz)
210 {
211  ValueType sum = zeroVal<ValueType>();
212  Int32 &i = xyz[Axis], j = i + width;
213  for (i -= width; i <= j; ++i) filter_internal::accum(sum, acc.getValue(xyz));
215  ValueType value = static_cast<ValueType>(sum * frac);
217  return value;
218 }
219 
220 
222 
223 
224 template<typename GridT, typename MaskT, typename InterruptT>
225 inline void
226 Filter<GridT, MaskT, InterruptT>::mean(int width, int iterations, const MaskType* mask)
227 {
228  mMask = mask;
229 
230  if (mInterrupter) mInterrupter->start("Applying mean filter");
231 
232  const int w = std::max(1, width);
233 
234  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
235 
236  for (int i=0; i<iterations && !this->wasInterrupted(); ++i) {
237  mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
238  this->cook(leafs);
239 
240  // note that the order of the YZ passes are flipped to maintain backwards-compatibility
241  // with an indexing typo in the original logic
242 
243  mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
244  this->cook(leafs);
245 
246  mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
247  this->cook(leafs);
248  }
249 
250  if (mInterrupter) mInterrupter->end();
251 }
252 
253 
254 template<typename GridT, typename MaskT, typename InterruptT>
255 inline void
256 Filter<GridT, MaskT, InterruptT>::gaussian(int width, int iterations, const MaskType* mask)
257 {
258  mMask = mask;
259 
260  if (mInterrupter) mInterrupter->start("Applying Gaussian filter");
261 
262  const int w = std::max(1, width);
263 
264  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
265 
266  for (int i=0; i<iterations; ++i) {
267  for (int n=0; n<4 && !this->wasInterrupted(); ++n) {
268  mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
269  this->cook(leafs);
270 
271  // note that the order of the YZ passes are flipped to maintain backwards-compatibility
272  // with an indexing typo in the original logic
273 
274  mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
275  this->cook(leafs);
276 
277  mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
278  this->cook(leafs);
279  }
280  }
281 
282  if (mInterrupter) mInterrupter->end();
283 }
284 
285 
286 template<typename GridT, typename MaskT, typename InterruptT>
287 inline void
288 Filter<GridT, MaskT, InterruptT>::median(int width, int iterations, const MaskType* mask)
289 {
290  mMask = mask;
291 
292  if (mInterrupter) mInterrupter->start("Applying median filter");
293 
294  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
295 
296  mTask = std::bind(&Filter::doMedian,
297  std::placeholders::_1, std::placeholders::_2, std::max(1, width));
298  for (int i=0; i<iterations && !this->wasInterrupted(); ++i) this->cook(leafs);
299 
300  if (mInterrupter) mInterrupter->end();
301 }
302 
303 
304 template<typename GridT, typename MaskT, typename InterruptT>
305 inline void
307 {
308  mMask = mask;
309 
310  if (mInterrupter) mInterrupter->start("Applying offset");
311 
312  LeafManagerType leafs(mGrid->tree(), 0, mGrainSize==0);
313 
314  mTask = std::bind(&Filter::doOffset, std::placeholders::_1, std::placeholders::_2, value);
315  this->cook(leafs);
316 
317  if (mInterrupter) mInterrupter->end();
318 }
319 
320 
322 
323 
326 template<typename GridT, typename MaskT, typename InterruptT>
327 inline void
328 Filter<GridT, MaskT, InterruptT>::cook(LeafManagerType& leafs)
329 {
330  if (mGrainSize>0) {
331  tbb::parallel_for(leafs.leafRange(mGrainSize), *this);
332  } else {
333  (*this)(leafs.leafRange());
334  }
335  leafs.swapLeafBuffer(1, mGrainSize==0);
336 }
337 
338 
340 template<typename GridT, typename MaskT, typename InterruptT>
341 template <typename AvgT>
342 inline void
343 Filter<GridT, MaskT, InterruptT>::doBox(const RangeType& range, Int32 w)
344 {
345  this->wasInterrupted();
346  AvgT avg(mGrid, w);
347  if (mMask) {
348  typename AlphaMaskT::FloatType a, b;
349  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
350  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
351  BufferT& buffer = leafIter.buffer(1);
352  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
353  const Coord xyz = iter.getCoord();
354  if (alpha(xyz, a, b)) {
356  const ValueType value(b*(*iter) + a*avg(xyz));
358  buffer.setValue(iter.pos(), value);
359  }
360  }
361  }
362  } else {
363  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
364  BufferT& buffer = leafIter.buffer(1);
365  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
366  buffer.setValue(iter.pos(), avg(iter.getCoord()));
367  }
368  }
369  }
370 }
371 
372 
374 template<typename GridT, typename MaskT, typename InterruptT>
375 inline void
376 Filter<GridT, MaskT, InterruptT>::doMedian(const RangeType& range, int width)
377 {
378  this->wasInterrupted();
379  typename math::DenseStencil<GridType> stencil(*mGrid, width);//creates local cache!
380  if (mMask) {
381  typename AlphaMaskT::FloatType a, b;
382  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
383  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
384  BufferT& buffer = leafIter.buffer(1);
385  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
386  if (alpha(iter.getCoord(), a, b)) {
387  stencil.moveTo(iter);
389  ValueType value(b*(*iter) + a*stencil.median());
391  buffer.setValue(iter.pos(), value);
392  }
393  }
394  }
395  } else {
396  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
397  BufferT& buffer = leafIter.buffer(1);
398  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
399  stencil.moveTo(iter);
400  buffer.setValue(iter.pos(), stencil.median());
401  }
402  }
403  }
404 }
405 
406 
408 template<typename GridT, typename MaskT, typename InterruptT>
409 inline void
410 Filter<GridT, MaskT, InterruptT>::doOffset(const RangeType& range, ValueType offset)
411 {
412  this->wasInterrupted();
413  if (mMask) {
414  typename AlphaMaskT::FloatType a, b;
415  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
416  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
417  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
418  if (alpha(iter.getCoord(), a, b)) {
420  ValueType value(*iter + a*offset);
422  iter.setValue(value);
423  }
424  }
425  }
426  } else {
427  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
428  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
429  iter.setValue(*iter + offset);
430  }
431  }
432  }
433 }
434 
435 
436 template<typename GridT, typename MaskT, typename InterruptT>
437 inline bool
439 {
440  if (util::wasInterrupted(mInterrupter)) {
441  tbb::task::self().cancel_group_execution();
442  return true;
443  }
444  return false;
445 }
446 
447 } // namespace tools
448 } // namespace OPENVDB_VERSION_NAME
449 } // namespace openvdb
450 
451 #endif // OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
Types.h
openvdb::v8_0::util::wasInterrupted
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
OPENVDB_NO_TYPE_CONVERSION_WARNING_END
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END
Definition: Platform.h:175
openvdb::v8_0::tools::Filter::LeafManagerType
typename tree::LeafManager< TreeType > LeafManagerType
Definition: Filter.h:49
openvdb::v8_0::tools::Filter::TreeType
typename GridType::TreeType TreeType
Definition: Filter.h:45
openvdb::v8_0::tools::Filter::GridType
GridT GridType
Definition: Filter.h:43
openvdb::v8_0::tools::Filter::minMask
AlphaType minMask() const
Return the minimum value of the mask to be used for the derivation of a smooth alpha value.
Definition: Filter.h:93
openvdb::v8_0::tools::Filter::Filter
Filter(const Filter &other)
Shallow copy constructor called by tbb::parallel_for() threads during filtering.
Definition: Filter.h:73
openvdb::v8_0::math::Coord
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:26
openvdb::v8_0::tools::Filter::Filter
Filter(GridT &grid, InterruptT *interrupt=nullptr)
Definition: Filter.h:58
openvdb::v8_0::tools::Filter
Volume filtering (e.g., diffusion) with optional alpha masking.
Definition: Filter.h:41
openvdb::v8_0::tools::Filter::setMaskRange
void setMaskRange(AlphaType min, AlphaType max)
Define the range for the (optional) scalar mask.
Definition: Filter.h:103
Transform.h
openvdb::v8_0::tools::Filter::maxMask
AlphaType maxMask() const
Return the maximum value of the mask to be used for the derivation of a smooth alpha value.
Definition: Filter.h:96
openvdb::v8_0::tools::Filter::BufferType
typename LeafManagerType::BufferType BufferType
Definition: Filter.h:51
openvdb::v8_0::tools::Filter::AlphaType
typename MaskType::ValueType AlphaType
Definition: Filter.h:48
openvdb::v8_0::tree::LeafManager::BufferType
typename CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition: LeafManager.h:95
openvdb::v8_0::tools::AlphaMask
Definition: Interpolation.h:544
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition: openvdb/Exceptions.h:74
Grid.h
openvdb::v8_0::tools::Filter::ValueType
typename GridType::ValueType ValueType
Definition: Filter.h:47
Math.h
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
openvdb::v8_0::tools::Filter::isMaskInverted
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: Filter.h:112
openvdb::v8_0::tools::Filter::setGrainSize
void setGrainSize(int grainsize)
Set the grain-size used for multi-threading.
Definition: Filter.h:89
openvdb::v8_0::math::Axis
Axis
Definition: Math.h:904
openvdb::v8_0::ValueError
Definition: openvdb/Exceptions.h:65
openvdb::v8_0::tools::Filter::MaskType
MaskT MaskType
Definition: Filter.h:44
openvdb::v8_0::tree::LeafManager
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:85
NullInterrupter.h
openvdb::v8_0::tools::Filter::getGrainSize
int getGrainSize() const
Definition: Filter.h:86
Interpolation.h
openvdb::v8_0::tools::Filter::RangeType
typename LeafManagerType::LeafRange RangeType
Definition: Filter.h:50
openvdb::v8_0::Int32
int32_t Int32
Definition: openvdb/Types.h:34
openvdb::v8_0::tools::composite::max
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:107
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:153
openvdb::v8_0::tools::Filter::operator()
void operator()(const RangeType &range) const
Used internally by tbb::parallel_for()
Definition: Filter.h:149
openvdb::v8_0::tools::Filter::LeafType
typename TreeType::LeafNodeType LeafType
Definition: Filter.h:46
LeafManager.h
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
Bracket code with OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN/_END, to inhibit warnings about type conve...
Definition: Platform.h:174
openvdb::v8_0::tools::Filter::invertMask
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: Filter.h:115
Stencils.h
Defines various finite difference stencils by means of the "curiously recurring template pattern" on ...
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:101
openvdb::v8_0::tools::composite::min
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:103
openvdb
Definition: openvdb/Exceptions.h:13
openvdb::v8_0::tools::filter_internal::accum
void accum(bool &sum, bool addend)
Definition: Filter.h:202