OpenVDB  6.0.0
Filter.h
Go to the documentation of this file.
1 //
3 // Copyright (c) 2012-2018 DreamWorks Animation LLC
4 //
5 // All rights reserved. This software is distributed under the
6 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
7 //
8 // Redistributions of source code must retain the above copyright
9 // and license notice and the following restrictions and disclaimer.
10 //
11 // * Neither the name of DreamWorks Animation nor the names of
12 // its contributors may be used to endorse or promote products derived
13 // from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
27 // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
28 //
30 //
38 
39 #ifndef OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
40 #define OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
41 
42 #include <tbb/parallel_for.h>
43 #include <openvdb/Types.h>
44 #include <openvdb/math/Math.h>
45 #include <openvdb/math/Stencils.h>
46 #include <openvdb/math/Transform.h>
49 #include <openvdb/Grid.h>
50 #include "Interpolation.h"
51 #include <algorithm> // for std::max()
52 #include <functional>
53 #include <type_traits>
54 
55 
56 namespace openvdb {
58 namespace OPENVDB_VERSION_NAME {
59 namespace tools {
60 
64 template<typename GridT,
65  typename MaskT = typename GridT::template ValueConverter<float>::Type,
66  typename InterruptT = util::NullInterrupter>
67 class Filter
68 {
69 public:
70  using GridType = GridT;
71  using MaskType = MaskT;
72  using TreeType = typename GridType::TreeType;
73  using LeafType = typename TreeType::LeafNodeType;
74  using ValueType = typename GridType::ValueType;
75  using AlphaType = typename MaskType::ValueType;
77  using RangeType = typename LeafManagerType::LeafRange;
78  using BufferType = typename LeafManagerType::BufferType;
79  static_assert(std::is_floating_point<AlphaType>::value,
80  "openvdb::tools::Filter requires a mask grid with floating-point values");
81 
85  Filter(GridT& grid, InterruptT* interrupt = nullptr)
86  : mGrid(&grid)
87  , mTask(0)
88  , mInterrupter(interrupt)
89  , mMask(nullptr)
90  , mGrainSize(1)
91  , mMinMask(0)
92  , mMaxMask(1)
93  , mInvertMask(false)
94  {
95  }
96 
100  Filter(const Filter& other)
101  : mGrid(other.mGrid)
102  , mTask(other.mTask)
103  , mInterrupter(other.mInterrupter)
104  , mMask(other.mMask)
105  , mGrainSize(other.mGrainSize)
106  , mMinMask(other.mMinMask)
107  , mMaxMask(other.mMaxMask)
108  , mInvertMask(other.mInvertMask)
109  {
110  }
111 
113  int getGrainSize() const { return mGrainSize; }
116  void setGrainSize(int grainsize) { mGrainSize = grainsize; }
117 
120  AlphaType minMask() const { return mMinMask; }
123  AlphaType maxMask() const { return mMaxMask; }
131  {
132  if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)");
133  mMinMask = min;
134  mMaxMask = max;
135  }
136 
139  bool isMaskInverted() const { return mInvertMask; }
142  void invertMask(bool invert=true) { mInvertMask = invert; }
143 
148  void mean(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
149 
157  void gaussian(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
158 
165  void median(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
166 
170  void offset(ValueType offset, const MaskType* mask = nullptr);
171 
176  void operator()(const RangeType& range) const
177  {
178  if (mTask) mTask(const_cast<Filter*>(this), range);
179  else OPENVDB_THROW(ValueError, "task is undefined - call median(), mean(), etc.");
180  }
181 
182 private:
183  using LeafT = typename TreeType::LeafNodeType;
184  using VoxelIterT = typename LeafT::ValueOnIter;
185  using VoxelCIterT = typename LeafT::ValueOnCIter;
186  using BufferT = typename tree::LeafManager<TreeType>::BufferType;
187  using LeafIterT = typename RangeType::Iterator;
188  using AlphaMaskT = tools::AlphaMask<GridT, MaskT>;
189 
190  void cook(LeafManagerType& leafs);
191 
192  template<size_t Axis>
193  struct Avg {
194  Avg(const GridT* grid, Int32 w): acc(grid->tree()), width(w), frac(1.f/float(2*w+1)) {}
195  inline ValueType operator()(Coord xyz);
196  typename GridT::ConstAccessor acc;
197  const Int32 width;
198  const float frac;
199  };
200 
201  // Private filter methods called by tbb::parallel_for threads
202  template <typename AvgT>
203  void doBox( const RangeType& r, Int32 w);
204  void doBoxX(const RangeType& r, Int32 w) { this->doBox<Avg<0> >(r,w); }
205  void doBoxZ(const RangeType& r, Int32 w) { this->doBox<Avg<1> >(r,w); }
206  void doBoxY(const RangeType& r, Int32 w) { this->doBox<Avg<2> >(r,w); }
207  void doMedian(const RangeType&, int);
208  void doOffset(const RangeType&, ValueType);
210  bool wasInterrupted();
211 
212  GridType* mGrid;
213  typename std::function<void (Filter*, const RangeType&)> mTask;
214  InterruptT* mInterrupter;
215  const MaskType* mMask;
216  int mGrainSize;
217  AlphaType mMinMask, mMaxMask;
218  bool mInvertMask;
219 }; // end of Filter class
220 
221 
223 
224 
225 namespace filter_internal {
226 // Helper function for Filter::Avg::operator()
227 template<typename T> static inline void accum(T& sum, T addend) { sum += addend; }
228 // Overload for bool ValueType
229 inline void accum(bool& sum, bool addend) { sum = sum || addend; }
230 }
231 
232 
233 template<typename GridT, typename MaskT, typename InterruptT>
234 template<size_t Axis>
235 inline typename GridT::ValueType
236 Filter<GridT, MaskT, InterruptT>::Avg<Axis>::operator()(Coord xyz)
237 {
238  ValueType sum = zeroVal<ValueType>();
239  Int32 &i = xyz[Axis], j = i + width;
240  for (i -= width; i <= j; ++i) filter_internal::accum(sum, acc.getValue(xyz));
241  return static_cast<ValueType>(sum * frac);
242 }
243 
244 
246 
247 
248 template<typename GridT, typename MaskT, typename InterruptT>
249 inline void
250 Filter<GridT, MaskT, InterruptT>::mean(int width, int iterations, const MaskType* mask)
251 {
252  mMask = mask;
253 
254  if (mInterrupter) mInterrupter->start("Applying mean filter");
255 
256  const int w = std::max(1, width);
257 
258  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
259 
260  for (int i=0; i<iterations && !this->wasInterrupted(); ++i) {
261  mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
262  this->cook(leafs);
263 
264  mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
265  this->cook(leafs);
266 
267  mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
268  this->cook(leafs);
269  }
270 
271  if (mInterrupter) mInterrupter->end();
272 }
273 
274 
275 template<typename GridT, typename MaskT, typename InterruptT>
276 inline void
277 Filter<GridT, MaskT, InterruptT>::gaussian(int width, int iterations, const MaskType* mask)
278 {
279  mMask = mask;
280 
281  if (mInterrupter) mInterrupter->start("Applying Gaussian filter");
282 
283  const int w = std::max(1, width);
284 
285  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
286 
287  for (int i=0; i<iterations; ++i) {
288  for (int n=0; n<4 && !this->wasInterrupted(); ++n) {
289  mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
290  this->cook(leafs);
291 
292  mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
293  this->cook(leafs);
294 
295  mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
296  this->cook(leafs);
297  }
298  }
299 
300  if (mInterrupter) mInterrupter->end();
301 }
302 
303 
304 template<typename GridT, typename MaskT, typename InterruptT>
305 inline void
306 Filter<GridT, MaskT, InterruptT>::median(int width, int iterations, const MaskType* mask)
307 {
308  mMask = mask;
309 
310  if (mInterrupter) mInterrupter->start("Applying median filter");
311 
312  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
313 
314  mTask = std::bind(&Filter::doMedian,
315  std::placeholders::_1, std::placeholders::_2, std::max(1, width));
316  for (int i=0; i<iterations && !this->wasInterrupted(); ++i) this->cook(leafs);
317 
318  if (mInterrupter) mInterrupter->end();
319 }
320 
321 
322 template<typename GridT, typename MaskT, typename InterruptT>
323 inline void
325 {
326  mMask = mask;
327 
328  if (mInterrupter) mInterrupter->start("Applying offset");
329 
330  LeafManagerType leafs(mGrid->tree(), 0, mGrainSize==0);
331 
332  mTask = std::bind(&Filter::doOffset, std::placeholders::_1, std::placeholders::_2, value);
333  this->cook(leafs);
334 
335  if (mInterrupter) mInterrupter->end();
336 }
337 
338 
340 
341 
344 template<typename GridT, typename MaskT, typename InterruptT>
345 inline void
346 Filter<GridT, MaskT, InterruptT>::cook(LeafManagerType& leafs)
347 {
348  if (mGrainSize>0) {
349  tbb::parallel_for(leafs.leafRange(mGrainSize), *this);
350  } else {
351  (*this)(leafs.leafRange());
352  }
353  leafs.swapLeafBuffer(1, mGrainSize==0);
354 }
355 
356 
358 template<typename GridT, typename MaskT, typename InterruptT>
359 template <typename AvgT>
360 inline void
361 Filter<GridT, MaskT, InterruptT>::doBox(const RangeType& range, Int32 w)
362 {
363  this->wasInterrupted();
364  AvgT avg(mGrid, w);
365  if (mMask) {
366  typename AlphaMaskT::FloatType a, b;
367  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
368  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
369  BufferT& buffer = leafIter.buffer(1);
370  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
371  const Coord xyz = iter.getCoord();
372  if (alpha(xyz, a, b)) {
373  buffer.setValue(iter.pos(), ValueType(b*(*iter) + a*avg(xyz)));
374  }
375  }
376  }
377  } else {
378  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
379  BufferT& buffer = leafIter.buffer(1);
380  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
381  buffer.setValue(iter.pos(), avg(iter.getCoord()));
382  }
383  }
384  }
385 }
386 
387 
389 template<typename GridT, typename MaskT, typename InterruptT>
390 inline void
391 Filter<GridT, MaskT, InterruptT>::doMedian(const RangeType& range, int width)
392 {
393  this->wasInterrupted();
394  typename math::DenseStencil<GridType> stencil(*mGrid, width);//creates local cache!
395  if (mMask) {
396  typename AlphaMaskT::FloatType a, b;
397  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
398  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
399  BufferT& buffer = leafIter.buffer(1);
400  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
401  if (alpha(iter.getCoord(), a, b)) {
402  stencil.moveTo(iter);
403  buffer.setValue(iter.pos(), ValueType(b*(*iter) + a*stencil.median()));
404  }
405  }
406  }
407  } else {
408  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
409  BufferT& buffer = leafIter.buffer(1);
410  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
411  stencil.moveTo(iter);
412  buffer.setValue(iter.pos(), stencil.median());
413  }
414  }
415  }
416 }
417 
418 
420 template<typename GridT, typename MaskT, typename InterruptT>
421 inline void
422 Filter<GridT, MaskT, InterruptT>::doOffset(const RangeType& range, ValueType offset)
423 {
424  this->wasInterrupted();
425  if (mMask) {
426  typename AlphaMaskT::FloatType a, b;
427  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
428  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
429  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
430  if (alpha(iter.getCoord(), a, b)) iter.setValue(ValueType(*iter + a*offset));
431  }
432  }
433  } else {
434  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
435  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
436  iter.setValue(*iter + offset);
437  }
438  }
439  }
440 }
441 
442 
443 template<typename GridT, typename MaskT, typename InterruptT>
444 inline bool
446 {
447  if (util::wasInterrupted(mInterrupter)) {
448  tbb::task::self().cancel_group_execution();
449  return true;
450  }
451  return false;
452 }
453 
454 } // namespace tools
455 } // namespace OPENVDB_VERSION_NAME
456 } // namespace openvdb
457 
458 #endif // OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
459 
460 // Copyright (c) 2012-2018 DreamWorks Animation LLC
461 // All rights reserved. This software is distributed under the
462 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
GridT GridType
Definition: Filter.h:70
void accum(bool &sum, bool addend)
Definition: Filter.h:229
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
AlphaType maxMask() const
Return the maximum value of the mask to be used for the derivation of a smooth alpha value.
Definition: Filter.h:123
typename tree::LeafManager< TreeType > LeafManagerType
Definition: Filter.h:76
typename GridType::TreeType TreeType
Definition: Filter.h:72
void setMaskRange(AlphaType min, AlphaType max)
Define the range for the (optional) scalar mask.
Definition: Filter.h:130
AlphaType minMask() const
Return the minimum value of the mask to be used for the derivation of a smooth alpha value.
Definition: Filter.h:120
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:109
Filter(GridT &grid, InterruptT *interrupt=nullptr)
Definition: Filter.h:85
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:76
Filter(const Filter &other)
Shallow copy constructor called by tbb::parallel_for() threads during filtering.
Definition: Filter.h:100
typename LeafManagerType::BufferType BufferType
Definition: Filter.h:78
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:51
int32_t Int32
Definition: Types.h:63
typename MaskType::ValueType AlphaType
Definition: Filter.h:75
Volume filtering (e.g., diffusion) with optional alpha masking.
Definition: Filter.h:67
Axis
Definition: Math.h:856
void setGrainSize(int grainsize)
Set the grain-size used for multi-threading.
Definition: Filter.h:116
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:136
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:139
typename GridType::ValueType ValueType
Definition: Filter.h:74
MaskT MaskType
Definition: Filter.h:71
Definition: Exceptions.h:40
typename CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition: LeafManager.h:121
typename LeafManagerType::LeafRange RangeType
Definition: Filter.h:77
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:110
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:129
typename TreeType::LeafNodeType LeafType
Definition: Filter.h:73
Definition: Interpolation.h:570
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:133
int getGrainSize() const
Definition: Filter.h:113
Defines various finite difference stencils by means of the "curiously recurring template pattern" on ...
Definition: Exceptions.h:92
void operator()(const RangeType &range) const
Used internally by tbb::parallel_for()
Definition: Filter.h:176
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:188
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
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:142