OpenVDB  2.3.0
Filter.h
Go to the documentation of this file.
1 //
3 // Copyright (c) 2012-2013 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_reduce.h>
43 #include <tbb/parallel_for.h>
44 #include <boost/bind.hpp>
45 #include <boost/function.hpp>
46 #include <boost/type_traits/is_floating_point.hpp>
47 #include <openvdb/Types.h>
48 #include <openvdb/math/Math.h>
49 #include <openvdb/math/Stencils.h>
50 #include <openvdb/math/Transform.h>
51 #include <openvdb/tree/LeafManager.h>
52 #include <openvdb/util/NullInterrupter.h>
53 #include <openvdb/Grid.h>
54 #include "Interpolation.h"
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  typedef GridT GridType;
71  typedef MaskT MaskType;
72  typedef typename GridType::TreeType TreeType;
73  typedef typename TreeType::LeafNodeType LeafType;
74  typedef typename GridType::ValueType ValueType;
75  typedef typename MaskType::ValueType AlphaType;
79  BOOST_STATIC_ASSERT(boost::is_floating_point<AlphaType>::value);
80 
84  Filter(GridT& grid, InterruptT* interrupt = NULL)
85  : mGrid(&grid)
86  , mTask(0)
87  , mInterrupter(interrupt)
88  , mMask(NULL)
89  , mGrainSize(1)
90  , mMinMask(0)
91  , mMaxMask(1)
92  , mInvertMask(false)
93  {
94  }
95 
99  Filter(const Filter& other)
100  : mGrid(other.mGrid)
101  , mTask(other.mTask)
102  , mInterrupter(other.mInterrupter)
103  , mMask(other.mMask)
104  , mGrainSize(other.mGrainSize)
105  , mMinMask(other.mMinMask)
106  , mMaxMask(other.mMaxMask)
107  , mInvertMask(other.mInvertMask)
108  {
109  }
110 
112  int getGrainSize() const { return mGrainSize; }
115  void setGrainSize(int grainsize) { mGrainSize = grainsize; }
116 
119  AlphaType minMask() const { return mMinMask; }
122  AlphaType maxMask() const { return mMaxMask; }
130  {
131  if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)");
132  mMinMask = min;
133  mMaxMask = max;
134  }
135 
138  bool isMaskInverted() const { return mInvertMask; }
141  void invertMask(bool invert=true) { mInvertMask = invert; }
142 
147  void mean(int width = 1, int iterations = 1, const MaskType* mask = NULL);
148 
156  void gaussian(int width = 1, int iterations = 1, const MaskType* mask = NULL);
157 
164  void median(int width = 1, int iterations = 1, const MaskType* mask = NULL);
165 
169  void offset(ValueType offset, const MaskType* mask = NULL);
170 
175  void operator()(const RangeType& range) const
176  {
177  if (mTask) mTask(const_cast<Filter*>(this), range);
178  else OPENVDB_THROW(ValueError, "task is undefined - call median(), mean(), etc.");
179  }
180 
181 private:
182  typedef typename TreeType::LeafNodeType LeafT;
183  typedef typename LeafT::ValueOnIter VoxelIterT;
184  typedef typename LeafT::ValueOnCIter VoxelCIterT;
185  typedef typename tree::LeafManager<TreeType>::BufferType BufferT;
186  typedef typename RangeType::Iterator LeafIterT;
187 
188  void cook(LeafManagerType& leafs);
189 
190  // Private class to derive the normalized alpha mask
191  struct AlphaMask
192  {
193  AlphaMask(const GridType& grid, const MaskType& mask,
194  AlphaType min, AlphaType max, bool invert)
195  : mAcc(mask.tree()), mSampler(mAcc, mask.transform(), grid.transform()),
196  mMin(min), mInvNorm(1/(max-min)), mInvert(invert)
197  {
198  assert(min < max);
199  }
200  inline bool operator()(const Coord& xyz, AlphaType& a, AlphaType& b) const
201  {
202  a = mSampler(xyz);
203  const AlphaType t = (a-mMin)*mInvNorm;
204  a = t > 0 ? t < 1 ? (3-2*t)*t*t : 1 : 0;//smooth mapping to 0->1
205  b = 1 - a;
206  if (mInvert) std::swap(a,b);
207  return a>0;
208  }
209  typedef typename MaskType::ConstAccessor AccType;
210  AccType mAcc;
212  const AlphaType mMin, mInvNorm;
213  const bool mInvert;
214  };
215 
216  template <size_t Axis>
217  struct Avg {
218  Avg(const GridT* grid, Int32 w) :
219  acc(grid->tree()), width(w), frac(1/ValueType(2*w+1)) {}
220  ValueType operator()(Coord xyz) {
221  ValueType sum = zeroVal<ValueType>();
222  Int32& i = xyz[Axis], j = i + width;
223  for (i -= width; i <= j; ++i) sum += acc.getValue(xyz);
224  return sum*frac;
225  }
226  typename GridT::ConstAccessor acc;
227  const Int32 width;
228  const ValueType frac;
229  };
230 
231  // Private filter methods called by tbb::parallel_for threads
232  template <typename AvgT>
233  void doBox( const RangeType& r, Int32 w);
234  void doBoxX(const RangeType& r, Int32 w) { this->doBox<Avg<0> >(r,w); }
235  void doBoxZ(const RangeType& r, Int32 w) { this->doBox<Avg<1> >(r,w); }
236  void doBoxY(const RangeType& r, Int32 w) { this->doBox<Avg<2> >(r,w); }
237  void doMedian(const RangeType&, int);
238  void doOffset(const RangeType&, ValueType);
240  bool wasInterrupted();
241 
242  GridType* mGrid;
243  typename boost::function<void (Filter*, const RangeType&)> mTask;
244  InterruptT* mInterrupter;
245  const MaskType* mMask;
246  int mGrainSize;
247  AlphaType mMinMask, mMaxMask;
248  bool mInvertMask;
249 }; // end of Filter class
250 
252 
253 template<typename GridT, typename MaskT, typename InterruptT>
254 inline void
255 Filter<GridT, MaskT, InterruptT>::mean(int width, int iterations, const MaskType* mask)
256 {
257  mMask = mask;
258 
259  if (mInterrupter) mInterrupter->start("Applying mean filter");
260 
261  const int w = std::max(1, width);
262 
263  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
264 
265  for (int i=0; i<iterations && !this->wasInterrupted(); ++i) {
266  mTask = boost::bind(&Filter::doBoxX, _1, _2, w);
267  this->cook(leafs);
268 
269  mTask = boost::bind(&Filter::doBoxY, _1, _2, w);
270  this->cook(leafs);
271 
272  mTask = boost::bind(&Filter::doBoxZ, _1, _2, w);
273  this->cook(leafs);
274  }
275 
276  if (mInterrupter) mInterrupter->end();
277 }
278 
279 template<typename GridT, typename MaskT, typename InterruptT>
280 inline void
281 Filter<GridT, MaskT, InterruptT>::gaussian(int width, int iterations, const MaskType* mask)
282 {
283  mMask = mask;
284 
285  if (mInterrupter) mInterrupter->start("Applying gaussian filter");
286 
287  const int w = std::max(1, width);
288 
289  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
290 
291  for (int i=0; i<iterations; ++i) {
292  for (int n=0; n<4 && !this->wasInterrupted(); ++n) {
293  mTask = boost::bind(&Filter::doBoxX, _1, _2, w);
294  this->cook(leafs);
295 
296  mTask = boost::bind(&Filter::doBoxY, _1, _2, w);
297  this->cook(leafs);
298 
299  mTask = boost::bind(&Filter::doBoxZ, _1, _2, w);
300  this->cook(leafs);
301  }
302  }
303 
304  if (mInterrupter) mInterrupter->end();
305 }
306 
307 
308 template<typename GridT, typename MaskT, typename InterruptT>
309 inline void
310 Filter<GridT, MaskT, InterruptT>::median(int width, int iterations, const MaskType* mask)
311 {
312  mMask = mask;
313 
314  if (mInterrupter) mInterrupter->start("Applying median filter");
315 
316  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
317 
318  mTask = boost::bind(&Filter::doMedian, _1, _2, std::max(1, width));
319  for (int i=0; i<iterations && !this->wasInterrupted(); ++i) this->cook(leafs);
320 
321  if (mInterrupter) mInterrupter->end();
322 }
323 
324 template<typename GridT, typename MaskT, typename InterruptT>
325 inline void
327 {
328  mMask = mask;
329 
330  if (mInterrupter) mInterrupter->start("Applying offset");
331 
332  LeafManagerType leafs(mGrid->tree(), 0, mGrainSize==0);
333 
334  mTask = boost::bind(&Filter::doOffset, _1, _2, value);
335  this->cook(leafs);
336 
337  if (mInterrupter) mInterrupter->end();
338 }
339 
341 
342 
345 template<typename GridT, typename MaskT, typename InterruptT>
346 inline void
347 Filter<GridT, MaskT, InterruptT>::cook(LeafManagerType& leafs)
348 {
349  if (mGrainSize>0) {
350  tbb::parallel_for(leafs.leafRange(mGrainSize), *this);
351  } else {
352  (*this)(leafs.leafRange());
353  }
354  leafs.swapLeafBuffer(1, mGrainSize==0);
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  AlphaType a, b;
367  AlphaMask 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 
388 template<typename GridT, typename MaskT, typename InterruptT>
389 inline void
390 Filter<GridT, MaskT, InterruptT>::doMedian(const RangeType& range, int width)
391 {
392  this->wasInterrupted();
393  typename math::DenseStencil<GridType> stencil(*mGrid, width);//creates local cache!
394  if (mMask) {
395  AlphaType a, b;
396  AlphaMask alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
397  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
398  BufferT& buffer = leafIter.buffer(1);
399  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
400  if (alpha(iter.getCoord(), a, b)) {
401  stencil.moveTo(iter);
402  buffer.setValue(iter.pos(), ValueType(b*(*iter) + a*stencil.median()));
403  }
404  }
405  }
406  } else {
407  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
408  BufferT& buffer = leafIter.buffer(1);
409  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
410  stencil.moveTo(iter);
411  buffer.setValue(iter.pos(), stencil.median());
412  }
413  }
414  }
415 }
416 
418 template<typename GridT, typename MaskT, typename InterruptT>
419 inline void
420 Filter<GridT, MaskT, InterruptT>::doOffset(const RangeType& range, ValueType offset)
421 {
422  this->wasInterrupted();
423  if (mMask) {
424  AlphaType a, b;
425  AlphaMask alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
426  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
427  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
428  if (alpha(iter.getCoord(), a, b)) iter.setValue(ValueType(*iter + a*offset));
429  }
430  }
431  } else {
432  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
433  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
434  iter.setValue(*iter + offset);
435  }
436  }
437  }
438 }
439 
440 template<typename GridT, typename MaskT, typename InterruptT>
441 inline bool
443 {
444  if (util::wasInterrupted(mInterrupter)) {
445  tbb::task::self().cancel_group_execution();
446  return true;
447  }
448  return false;
449 }
450 
451 } // namespace tools
452 } // namespace OPENVDB_VERSION_NAME
453 } // namespace openvdb
454 
455 #endif // OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
456 
457 // Copyright (c) 2012-2013 DreamWorks Animation LLC
458 // All rights reserved. This software is distributed under the
459 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
OPENVDB_API Hermite min(const Hermite &, const Hermite &)
min and max operations done directly on the compressed data.
Filter(GridT &grid, InterruptT *interrupt=NULL)
Definition: Filter.h:84
Volume filtering (e.g., diffusion) with optional alpha masking.
Definition: Filter.h:67
void operator()(const RangeType &range) const
Used internally by tbb::parallel_for()
Definition: Filter.h:175
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:97
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:76
void setGrainSize(int grainsize)
Set the grain-size used for multi-threading.
Definition: Filter.h:115
Definition: Exceptions.h:88
GridType::ValueType ValueType
Definition: Filter.h:74
tree::LeafManager< TreeType > LeafManagerType
Definition: Filter.h:76
LeafManagerType::LeafRange RangeType
Definition: Filter.h:77
This is a simple convenience class that allows for sampling from a source grid into the index space o...
Definition: Interpolation.h:382
#define OPENVDB_VERSION_NAME
Definition: version.h:45
OPENVDB_API Hermite max(const Hermite &, const Hermite &)
min and max operations done directly on the compressed data.
int getGrainSize() const
Definition: Filter.h:112
GridT GridType
Definition: Filter.h:70
LeafManagerType::BufferType BufferType
Definition: Filter.h:78
int32_t Int32
Definition: Types.h:59
MaskT MaskType
Definition: Filter.h:71
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:141
Axis
Definition: Math.h:767
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:138
CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition: LeafManager.h:117
Filter(const Filter &other)
Shallow copy constructor called by tbb::parallel_for() threads during filtering.
Definition: Filter.h:99
AlphaType minMask() const
Return the minimum value of the mask to be used for the derivation of a smooth alpha value...
Definition: Filter.h:119
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:67
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:109
MaskType::ValueType AlphaType
Definition: Filter.h:75
AlphaType maxMask() const
Return the maximum value of the mask to be used for the derivation of a smooth alpha value...
Definition: Filter.h:122
TreeType::LeafNodeType LeafType
Definition: Filter.h:73
void setMaskRange(AlphaType min, AlphaType max)
Define the range for the (optional) scalar mask.
Definition: Filter.h:129
GridType::TreeType TreeType
Definition: Filter.h:72