HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Filter.h
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2012-2017 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 //
29 ///////////////////////////////////////////////////////////////////////////
30 //
31 /// @author Ken Museth
32 ///
33 /// @file Filter.h
34 ///
35 /// @brief Filtering of VDB volumes. Note that only the values in the
36 /// grid are changed, not its topology! All operations can optionally
37 /// be masked with another grid that acts as an alpha-mask.
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 <hboost/bind.hpp>
44 #include <hboost/function.hpp>
45 #include <hboost/type_traits/is_floating_point.hpp>
46 #include <openvdb/Types.h>
47 #include <openvdb/math/Math.h>
48 #include <openvdb/math/Stencils.h>
49 #include <openvdb/math/Transform.h>
52 #include <openvdb/Grid.h>
53 #include "Interpolation.h"
54 
55 namespace openvdb {
57 namespace OPENVDB_VERSION_NAME {
58 namespace tools {
59 
60 /// @brief Volume filtering (e.g., diffusion) with optional alpha masking
61 ///
62 /// @note Only the values in the grid are changed, not its topology!
63 template<typename GridT,
64  typename MaskT = typename GridT::template ValueConverter<float>::Type,
65  typename InterruptT = util::NullInterrupter>
66 class Filter
67 {
68 public:
69  typedef GridT GridType;
70  typedef MaskT MaskType;
71  typedef typename GridType::TreeType TreeType;
72  typedef typename TreeType::LeafNodeType LeafType;
73  typedef typename GridType::ValueType ValueType;
74  typedef typename MaskType::ValueType AlphaType;
79 
80  /// Constructor
81  /// @param grid Grid to be filtered.
82  /// @param interrupt Optional interrupter.
83  Filter(GridT& grid, InterruptT* interrupt = NULL)
84  : mGrid(&grid)
85  , mTask(0)
86  , mInterrupter(interrupt)
87  , mMask(NULL)
88  , mGrainSize(1)
89  , mMinMask(0)
90  , mMaxMask(1)
91  , mInvertMask(false)
92  {
93  }
94 
95  /// @brief Shallow copy constructor called by tbb::parallel_for()
96  /// threads during filtering.
97  /// @param other The other Filter from which to copy.
98  Filter(const Filter& other)
99  : mGrid(other.mGrid)
100  , mTask(other.mTask)
101  , mInterrupter(other.mInterrupter)
102  , mMask(other.mMask)
103  , mGrainSize(other.mGrainSize)
104  , mMinMask(other.mMinMask)
105  , mMaxMask(other.mMaxMask)
106  , mInvertMask(other.mInvertMask)
107  {
108  }
109 
110  /// @return the grain-size used for multi-threading
111  int getGrainSize() const { return mGrainSize; }
112  /// @brief Set the grain-size used for multi-threading.
113  /// @note A grain size of 0 or less disables multi-threading!
114  void setGrainSize(int grainsize) { mGrainSize = grainsize; }
115 
116  /// @brief Return the minimum value of the mask to be used for the
117  /// derivation of a smooth alpha value.
118  AlphaType minMask() const { return mMinMask; }
119  /// @brief Return the maximum value of the mask to be used for the
120  /// derivation of a smooth alpha value.
121  AlphaType maxMask() const { return mMaxMask; }
122  /// @brief Define the range for the (optional) scalar mask.
123  /// @param min Minimum value of the range.
124  /// @param max Maximum value of the range.
125  /// @details Mask values outside the range are clamped to zero or one, and
126  /// values inside the range map smoothly to 0->1 (unless the mask is inverted).
127  /// @throw ValueError if @a min is not smaller than @a max.
129  {
130  if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)");
131  mMinMask = min;
132  mMaxMask = max;
133  }
134 
135  /// @brief Return true if the mask is inverted, i.e. min->max in the
136  /// original mask maps to 1->0 in the inverted alpha mask.
137  bool isMaskInverted() const { return mInvertMask; }
138  /// @brief Invert the optional mask, i.e. min->max in the original
139  /// mask maps to 1->0 in the inverted alpha mask.
140  void invertMask(bool invert=true) { mInvertMask = invert; }
141 
142  /// @brief One iteration of a fast separable mean-value (i.e. box) filter.
143  /// @param width The width of the mean-value filter is 2*width+1 voxels.
144  /// @param iterations Number of times the mean-value filter is applied.
145  /// @param mask Optional alpha mask.
146  void mean(int width = 1, int iterations = 1, const MaskType* mask = NULL);
147 
148  /// @brief One iteration of a fast separable Gaussian filter.
149  ///
150  /// @note This is approximated as 4 iterations of a separable mean filter
151  /// which typically leads an approximation that's better than 95%!
152  /// @param width The width of the mean-value filter is 2*width+1 voxels.
153  /// @param iterations Number of times the mean-value filter is applied.
154  /// @param mask Optional alpha mask.
155  void gaussian(int width = 1, int iterations = 1, const MaskType* mask = NULL);
156 
157  /// @brief One iteration of a median-value filter
158  ///
159  /// @note This filter is not separable and is hence relatively slow!
160  /// @param width The width of the mean-value filter is 2*width+1 voxels.
161  /// @param iterations Number of times the mean-value filter is applied.
162  /// @param mask Optional alpha mask.
163  void median(int width = 1, int iterations = 1, const MaskType* mask = NULL);
164 
165  /// Offsets (i.e. adds) a constant value to all active voxels.
166  /// @param offset Offset in the same units as the grid.
167  /// @param mask Optional alpha mask.
168  void offset(ValueType offset, const MaskType* mask = NULL);
169 
170  /// @brief Used internally by tbb::parallel_for()
171  /// @param range Range of LeafNodes over which to multi-thread.
172  ///
173  /// @warning Never call this method directly!
174  void operator()(const RangeType& range) const
175  {
176  if (mTask) mTask(const_cast<Filter*>(this), range);
177  else OPENVDB_THROW(ValueError, "task is undefined - call median(), mean(), etc.");
178  }
179 
180 private:
181  typedef typename TreeType::LeafNodeType LeafT;
182  typedef typename LeafT::ValueOnIter VoxelIterT;
183  typedef typename LeafT::ValueOnCIter VoxelCIterT;
184  typedef typename tree::LeafManager<TreeType>::BufferType BufferT;
185  typedef typename RangeType::Iterator LeafIterT;
186  typedef tools::AlphaMask<GridT, MaskT> AlphaMaskT;
187 
188  void cook(LeafManagerType& leafs);
189 
190  template<size_t Axis>
191  struct Avg {
192  Avg(const GridT* grid, Int32 w): acc(grid->tree()), width(w), frac(1.f/float(2*w+1)) {}
193  inline ValueType operator()(Coord xyz);
194  typename GridT::ConstAccessor acc;
195  const Int32 width;
196  const float frac;
197  };
198 
199  // Private filter methods called by tbb::parallel_for threads
200  template <typename AvgT>
201  void doBox( const RangeType& r, Int32 w);
202  void doBoxX(const RangeType& r, Int32 w) { this->doBox<Avg<0> >(r,w); }
203  void doBoxZ(const RangeType& r, Int32 w) { this->doBox<Avg<1> >(r,w); }
204  void doBoxY(const RangeType& r, Int32 w) { this->doBox<Avg<2> >(r,w); }
205  void doMedian(const RangeType&, int);
206  void doOffset(const RangeType&, ValueType);
207  /// @return true if the process was interrupted
208  bool wasInterrupted();
209 
210  GridType* mGrid;
211  typename hboost::function<void (Filter*, const RangeType&)> mTask;
212  InterruptT* mInterrupter;
213  const MaskType* mMask;
214  int mGrainSize;
215  AlphaType mMinMask, mMaxMask;
216  bool mInvertMask;
217 }; // end of Filter class
218 
219 
220 ////////////////////////////////////////
221 
222 
223 namespace filter_internal {
224 // Helper function for Filter::Avg::operator()
225 template<typename T> static inline void accum(T& sum, T addend) { sum += addend; }
226 // Overload for bool ValueType
227 inline void accum(bool& sum, bool addend) { sum = sum || addend; }
228 }
229 
230 
231 template<typename GridT, typename MaskT, typename InterruptT>
232 template<size_t Axis>
233 inline typename GridT::ValueType
235 {
236  ValueType sum = zeroVal<ValueType>();
237  Int32 &i = xyz[Axis], j = i + width;
238  for (i -= width; i <= j; ++i) filter_internal::accum(sum, acc.getValue(xyz));
239  return static_cast<ValueType>(sum * frac);
240 }
241 
242 
243 ////////////////////////////////////////
244 
245 
246 template<typename GridT, typename MaskT, typename InterruptT>
247 inline void
248 Filter<GridT, MaskT, InterruptT>::mean(int width, int iterations, const MaskType* mask)
249 {
250  mMask = mask;
251 
252  if (mInterrupter) mInterrupter->start("Applying mean filter");
253 
254  const int w = std::max(1, width);
255 
256  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
257 
258  for (int i=0; i<iterations && !this->wasInterrupted(); ++i) {
259  mTask = hboost::bind(&Filter::doBoxX, hboost::placeholders::_1, hboost::placeholders::_2, w);
260  this->cook(leafs);
261 
262  mTask = hboost::bind(&Filter::doBoxY, hboost::placeholders::_1, hboost::placeholders::_2, w);
263  this->cook(leafs);
264 
265  mTask = hboost::bind(&Filter::doBoxZ, hboost::placeholders::_1, hboost::placeholders::_2, w);
266  this->cook(leafs);
267  }
268 
269  if (mInterrupter) mInterrupter->end();
270 }
271 
272 
273 template<typename GridT, typename MaskT, typename InterruptT>
274 inline void
275 Filter<GridT, MaskT, InterruptT>::gaussian(int width, int iterations, const MaskType* mask)
276 {
277  mMask = mask;
278 
279  if (mInterrupter) mInterrupter->start("Applying Gaussian filter");
280 
281  const int w = std::max(1, width);
282 
283  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
284 
285  for (int i=0; i<iterations; ++i) {
286  for (int n=0; n<4 && !this->wasInterrupted(); ++n) {
287  mTask = hboost::bind(&Filter::doBoxX, hboost::placeholders::_1, hboost::placeholders::_2, w);
288  this->cook(leafs);
289 
290  mTask = hboost::bind(&Filter::doBoxY, hboost::placeholders::_1, hboost::placeholders::_2, w);
291  this->cook(leafs);
292 
293  mTask = hboost::bind(&Filter::doBoxZ, hboost::placeholders::_1, hboost::placeholders::_2, w);
294  this->cook(leafs);
295  }
296  }
297 
298  if (mInterrupter) mInterrupter->end();
299 }
300 
301 
302 template<typename GridT, typename MaskT, typename InterruptT>
303 inline void
304 Filter<GridT, MaskT, InterruptT>::median(int width, int iterations, const MaskType* mask)
305 {
306  mMask = mask;
307 
308  if (mInterrupter) mInterrupter->start("Applying median filter");
309 
310  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
311 
312  mTask = hboost::bind(&Filter::doMedian, hboost::placeholders::_1, hboost::placeholders::_2, std::max(1, width));
313  for (int i=0; i<iterations && !this->wasInterrupted(); ++i) this->cook(leafs);
314 
315  if (mInterrupter) mInterrupter->end();
316 }
317 
318 
319 template<typename GridT, typename MaskT, typename InterruptT>
320 inline void
322 {
323  mMask = mask;
324 
325  if (mInterrupter) mInterrupter->start("Applying offset");
326 
327  LeafManagerType leafs(mGrid->tree(), 0, mGrainSize==0);
328 
329  mTask = hboost::bind(&Filter::doOffset, hboost::placeholders::_1, hboost::placeholders::_2, value);
330  this->cook(leafs);
331 
332  if (mInterrupter) mInterrupter->end();
333 }
334 
335 
336 ////////////////////////////////////////
337 
338 
339 /// Private method to perform the task (serial or threaded) and
340 /// subsequently swap the leaf buffers.
341 template<typename GridT, typename MaskT, typename InterruptT>
342 inline void
343 Filter<GridT, MaskT, InterruptT>::cook(LeafManagerType& leafs)
344 {
345  if (mGrainSize>0) {
346  tbb::parallel_for(leafs.leafRange(mGrainSize), *this);
347  } else {
348  (*this)(leafs.leafRange());
349  }
350  leafs.swapLeafBuffer(1, mGrainSize==0);
351 }
352 
353 
354 /// One dimensional convolution of a separable box filter
355 template<typename GridT, typename MaskT, typename InterruptT>
356 template <typename AvgT>
357 inline void
358 Filter<GridT, MaskT, InterruptT>::doBox(const RangeType& range, Int32 w)
359 {
360  this->wasInterrupted();
361  AvgT avg(mGrid, w);
362  if (mMask) {
363  typename AlphaMaskT::FloatType a, b;
364  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
365  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
366  BufferT& buffer = leafIter.buffer(1);
367  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
368  const Coord xyz = iter.getCoord();
369  if (alpha(xyz, a, b)) {
370  buffer.setValue(iter.pos(), ValueType(b*(*iter) + a*avg(xyz)));
371  }
372  }
373  }
374  } else {
375  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
376  BufferT& buffer = leafIter.buffer(1);
377  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
378  buffer.setValue(iter.pos(), avg(iter.getCoord()));
379  }
380  }
381  }
382 }
383 
384 
385 /// Performs simple but slow median-value diffusion
386 template<typename GridT, typename MaskT, typename InterruptT>
387 inline void
388 Filter<GridT, MaskT, InterruptT>::doMedian(const RangeType& range, int width)
389 {
390  this->wasInterrupted();
391  typename math::DenseStencil<GridType> stencil(*mGrid, width);//creates local cache!
392  if (mMask) {
393  typename AlphaMaskT::FloatType a, b;
394  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
395  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
396  BufferT& buffer = leafIter.buffer(1);
397  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
398  if (alpha(iter.getCoord(), a, b)) {
399  stencil.moveTo(iter);
400  buffer.setValue(iter.pos(), ValueType(b*(*iter) + a*stencil.median()));
401  }
402  }
403  }
404  } else {
405  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
406  BufferT& buffer = leafIter.buffer(1);
407  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
408  stencil.moveTo(iter);
409  buffer.setValue(iter.pos(), stencil.median());
410  }
411  }
412  }
413 }
414 
415 
416 /// Offsets the values by a constant
417 template<typename GridT, typename MaskT, typename InterruptT>
418 inline void
419 Filter<GridT, MaskT, InterruptT>::doOffset(const RangeType& range, ValueType offset)
420 {
421  this->wasInterrupted();
422  if (mMask) {
423  typename AlphaMaskT::FloatType a, b;
424  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
425  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
426  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
427  if (alpha(iter.getCoord(), a, b)) iter.setValue(ValueType(*iter + a*offset));
428  }
429  }
430  } else {
431  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
432  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
433  iter.setValue(*iter + offset);
434  }
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-2017 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/ )
GLenum GLint * range
Definition: glcorearb.h:1924
Volume filtering (e.g., diffusion) with optional alpha masking.
Definition: Filter.h:66
AlphaType minMask() const
Return the minimum value of the mask to be used for the derivation of a smooth alpha value...
Definition: Filter.h:118
const hboost::disable_if_c< VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:128
GLboolean invert
Definition: glcorearb.h:548
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:140
Filter(GridT &grid, InterruptT *interrupt=NULL)
Definition: Filter.h:83
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1221
void median(int width=1, int iterations=1, const MaskType *mask=NULL)
One iteration of a median-value filter.
Definition: Filter.h:304
GLint GLuint mask
Definition: glcorearb.h:123
void offset(ValueType offset, const MaskType *mask=NULL)
Definition: Filter.h:321
LeafManagerType::BufferType BufferType
Definition: Filter.h:77
void gaussian(int width=1, int iterations=1, const MaskType *mask=NULL)
One iteration of a fast separable Gaussian filter.
Definition: Filter.h:275
GLuint buffer
Definition: glcorearb.h:659
png_uint_32 i
Definition: png.h:2877
GLint GLsizei width
Definition: glcorearb.h:102
const hboost::disable_if_c< VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:132
GLdouble n
Definition: glcorearb.h:2007
GLfloat f
Definition: glcorearb.h:1925
void setMaskRange(AlphaType min, AlphaType max)
Define the range for the (optional) scalar mask.
Definition: Filter.h:128
LeafManagerType::LeafRange RangeType
Definition: Filter.h:76
#define OPENVDB_VERSION_NAME
Definition: version.h:43
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
Filter(const Filter &other)
Shallow copy constructor called by tbb::parallel_for() threads during filtering.
Definition: Filter.h:98
tree::LeafManager< TreeType > LeafManagerType
Definition: Filter.h:75
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:137
GLintptr offset
Definition: glcorearb.h:664
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:115
GLfloat GLfloat GLfloat alpha
Definition: glcorearb.h:111
AlphaType maxMask() const
Return the maximum value of the mask to be used for the derivation of a smooth alpha value...
Definition: Filter.h:121
void setGrainSize(int grainsize)
Set the grain-size used for multi-threading.
Definition: Filter.h:114
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1221
GLint GLfloat GLint stencil
Definition: glcorearb.h:1277
void operator()(const RangeType &range) const
Used internally by tbb::parallel_for()
Definition: Filter.h:174
void mean(int width=1, int iterations=1, const MaskType *mask=NULL)
One iteration of a fast separable mean-value (i.e. box) filter.
Definition: Filter.h:248
GLsizei const GLfloat * value
Definition: glcorearb.h:823
HBOOST_STATIC_ASSERT(hboost::is_floating_point< AlphaType >::value)
Defines various finite difference stencils by means of the "curiously recurring template pattern" on ...
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:856
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
GLboolean r
Definition: glcorearb.h:1221
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:71
CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition: LeafManager.h:126
bool wasInterrupted(T *i, int percent=-1)
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:101