OpenVDB  7.0.0
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;
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 doBoxZ(const RangeType& r, Int32 w) { this->doBox<Avg<1> >(r,w); }
179  void doBoxY(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
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  mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
241  this->cook(leafs);
242 
243  mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
244  this->cook(leafs);
245  }
246 
247  if (mInterrupter) mInterrupter->end();
248 }
249 
250 
251 template<typename GridT, typename MaskT, typename InterruptT>
252 inline void
253 Filter<GridT, MaskT, InterruptT>::gaussian(int width, int iterations, const MaskType* mask)
254 {
255  mMask = mask;
256 
257  if (mInterrupter) mInterrupter->start("Applying Gaussian filter");
258 
259  const int w = std::max(1, width);
260 
261  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
262 
263  for (int i=0; i<iterations; ++i) {
264  for (int n=0; n<4 && !this->wasInterrupted(); ++n) {
265  mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
266  this->cook(leafs);
267 
268  mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
269  this->cook(leafs);
270 
271  mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
272  this->cook(leafs);
273  }
274  }
275 
276  if (mInterrupter) mInterrupter->end();
277 }
278 
279 
280 template<typename GridT, typename MaskT, typename InterruptT>
281 inline void
282 Filter<GridT, MaskT, InterruptT>::median(int width, int iterations, const MaskType* mask)
283 {
284  mMask = mask;
285 
286  if (mInterrupter) mInterrupter->start("Applying median filter");
287 
288  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
289 
290  mTask = std::bind(&Filter::doMedian,
291  std::placeholders::_1, std::placeholders::_2, std::max(1, width));
292  for (int i=0; i<iterations && !this->wasInterrupted(); ++i) this->cook(leafs);
293 
294  if (mInterrupter) mInterrupter->end();
295 }
296 
297 
298 template<typename GridT, typename MaskT, typename InterruptT>
299 inline void
301 {
302  mMask = mask;
303 
304  if (mInterrupter) mInterrupter->start("Applying offset");
305 
306  LeafManagerType leafs(mGrid->tree(), 0, mGrainSize==0);
307 
308  mTask = std::bind(&Filter::doOffset, std::placeholders::_1, std::placeholders::_2, value);
309  this->cook(leafs);
310 
311  if (mInterrupter) mInterrupter->end();
312 }
313 
314 
316 
317 
320 template<typename GridT, typename MaskT, typename InterruptT>
321 inline void
323 {
324  if (mGrainSize>0) {
325  tbb::parallel_for(leafs.leafRange(mGrainSize), *this);
326  } else {
327  (*this)(leafs.leafRange());
328  }
329  leafs.swapLeafBuffer(1, mGrainSize==0);
330 }
331 
332 
334 template<typename GridT, typename MaskT, typename InterruptT>
335 template <typename AvgT>
336 inline void
338 {
339  this->wasInterrupted();
340  AvgT avg(mGrid, w);
341  if (mMask) {
342  typename AlphaMaskT::FloatType a, b;
343  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
344  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
345  BufferT& buffer = leafIter.buffer(1);
346  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
347  const Coord xyz = iter.getCoord();
348  if (alpha(xyz, a, b)) {
350  const ValueType value(b*(*iter) + a*avg(xyz));
352  buffer.setValue(iter.pos(), value);
353  }
354  }
355  }
356  } else {
357  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
358  BufferT& buffer = leafIter.buffer(1);
359  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
360  buffer.setValue(iter.pos(), avg(iter.getCoord()));
361  }
362  }
363  }
364 }
365 
366 
368 template<typename GridT, typename MaskT, typename InterruptT>
369 inline void
371 {
372  this->wasInterrupted();
373  typename math::DenseStencil<GridType> stencil(*mGrid, width);//creates local cache!
374  if (mMask) {
375  typename AlphaMaskT::FloatType a, b;
376  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
377  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
378  BufferT& buffer = leafIter.buffer(1);
379  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
380  if (alpha(iter.getCoord(), a, b)) {
381  stencil.moveTo(iter);
383  ValueType value(b*(*iter) + a*stencil.median());
385  buffer.setValue(iter.pos(), value);
386  }
387  }
388  }
389  } else {
390  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
391  BufferT& buffer = leafIter.buffer(1);
392  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
393  stencil.moveTo(iter);
394  buffer.setValue(iter.pos(), stencil.median());
395  }
396  }
397  }
398 }
399 
400 
402 template<typename GridT, typename MaskT, typename InterruptT>
403 inline void
405 {
406  this->wasInterrupted();
407  if (mMask) {
408  typename AlphaMaskT::FloatType a, b;
409  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
410  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
411  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
412  if (alpha(iter.getCoord(), a, b)) {
414  ValueType value(*iter + a*offset);
416  iter.setValue(value);
417  }
418  }
419  }
420  } else {
421  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
422  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
423  iter.setValue(*iter + offset);
424  }
425  }
426  }
427 }
428 
429 
430 template<typename GridT, typename MaskT, typename InterruptT>
431 inline bool
433 {
434  if (util::wasInterrupted(mInterrupter)) {
435  tbb::task::self().cancel_group_execution();
436  return true;
437  }
438  return false;
439 }
440 
441 } // namespace tools
442 } // namespace OPENVDB_VERSION_NAME
443 } // namespace openvdb
444 
445 #endif // OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
#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:196
typename tree::LeafManager< TreeType > LeafManagerType
Definition: Filter.h:49
GridT GridType
Definition: Filter.h:43
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
typename GridType::TreeType TreeType
Definition: Filter.h:45
Filter(const Filter &other)
Shallow copy constructor called by tbb::parallel_for() threads during filtering.
Definition: Filter.h:73
Filter(GridT &grid, InterruptT *interrupt=nullptr)
Definition: Filter.h:58
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:82
typename CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition: LeafManager.h:93
void setMaskRange(AlphaType min, AlphaType max)
Define the range for the (optional) scalar mask.
Definition: Filter.h:103
typename LeafManagerType::BufferType BufferType
Definition: Filter.h:51
void accum(bool &sum, bool addend)
Definition: Filter.h:202
typename MaskType::ValueType AlphaType
Definition: Filter.h:48
FloatT FloatType
Definition: Interpolation.h:551
Axis
Definition: Math.h:849
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END
Definition: Platform.h:197
Definition: Exceptions.h:65
typename GridType::ValueType ValueType
Definition: Filter.h:47
void moveTo(const Coord &ijk)
Initialize the stencil buffer with the values of voxel (x, y, z) and its neighbors.
Definition: Stencils.h:1775
Definition: Interpolation.h:543
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:102
int32_t Int32
Definition: Types.h:33
Dense stencil of a given width.
Definition: Stencils.h:1755
void setGrainSize(int grainsize)
Set the grain-size used for multi-threading.
Definition: Filter.h:89
MaskT MaskType
Definition: Filter.h:44
Definition: Exceptions.h:13
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
This class manages a linear array of pointers to a given tree&#39;s leaf nodes, as well as optional auxil...
Definition: LeafManager.h:82
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
typename LeafManagerType::LeafRange RangeType
Definition: Filter.h:50
typename TreeType::LeafNodeType LeafType
Definition: Filter.h:46
ValueType median() const
Return the median value of the current stencil.
Definition: Stencils.h:121
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
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:102
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
Defines various finite difference stencils by means of the "curiously recurring template pattern" on ...
void operator()(const RangeType &range) const
Used internally by tbb::parallel_for()
Definition: Filter.h:149
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:154
A LeafManager manages a linear array of pointers to a given tree&#39;s leaf nodes, as well as optional au...
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
int getGrainSize() const
Definition: Filter.h:86
Volume filtering (e.g., diffusion) with optional alpha masking.
Definition: Filter.h:40
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:106