HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Filter.h
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 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 tools/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 <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 
61 /// @brief Volume filtering (e.g., diffusion) with optional alpha masking
62 ///
63 /// @note Only the values in the grid are changed, not its topology!
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;
80  "openvdb::tools::Filter requires a mask grid with floating-point values");
81 
82  /// Constructor
83  /// @param grid Grid to be filtered.
84  /// @param interrupt Optional interrupter.
85  Filter(GridT& grid, InterruptT* interrupt = nullptr)
86  : mGrid(&grid)
87  , mTask(nullptr)
88  , mInterrupter(interrupt)
89  , mMask(nullptr)
90  , mGrainSize(1)
91  , mMinMask(0)
92  , mMaxMask(1)
93  , mInvertMask(false)
94  {
95  }
96 
97  /// @brief Shallow copy constructor called by tbb::parallel_for()
98  /// threads during filtering.
99  /// @param other The other Filter from which to copy.
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 
112  /// @return the grain-size used for multi-threading
113  int getGrainSize() const { return mGrainSize; }
114  /// @brief Set the grain-size used for multi-threading.
115  /// @note A grain size of 0 or less disables multi-threading!
116  void setGrainSize(int grainsize) { mGrainSize = grainsize; }
117 
118  /// @brief Return the minimum value of the mask to be used for the
119  /// derivation of a smooth alpha value.
120  AlphaType minMask() const { return mMinMask; }
121  /// @brief Return the maximum value of the mask to be used for the
122  /// derivation of a smooth alpha value.
123  AlphaType maxMask() const { return mMaxMask; }
124  /// @brief Define the range for the (optional) scalar mask.
125  /// @param min Minimum value of the range.
126  /// @param max Maximum value of the range.
127  /// @details Mask values outside the range are clamped to zero or one, and
128  /// values inside the range map smoothly to 0->1 (unless the mask is inverted).
129  /// @throw ValueError if @a min is not smaller than @a max.
131  {
132  if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)");
133  mMinMask = min;
134  mMaxMask = max;
135  }
136 
137  /// @brief Return true if the mask is inverted, i.e. min->max in the
138  /// original mask maps to 1->0 in the inverted alpha mask.
139  bool isMaskInverted() const { return mInvertMask; }
140  /// @brief Invert the optional mask, i.e. min->max in the original
141  /// mask maps to 1->0 in the inverted alpha mask.
142  void invertMask(bool invert=true) { mInvertMask = invert; }
143 
144  /// @brief One iteration of a fast separable mean-value (i.e. box) filter.
145  /// @param width The width of the mean-value filter is 2*width+1 voxels.
146  /// @param iterations Number of times the mean-value filter is applied.
147  /// @param mask Optional alpha mask.
148  void mean(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
149 
150  /// @brief One iteration of a fast separable Gaussian filter.
151  ///
152  /// @note This is approximated as 4 iterations of a separable mean filter
153  /// which typically leads an approximation that's better than 95%!
154  /// @param width The width of the mean-value filter is 2*width+1 voxels.
155  /// @param iterations Number of times the mean-value filter is applied.
156  /// @param mask Optional alpha mask.
157  void gaussian(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
158 
159  /// @brief One iteration of a median-value filter
160  ///
161  /// @note This filter is not separable and is hence relatively slow!
162  /// @param width The width of the mean-value filter is 2*width+1 voxels.
163  /// @param iterations Number of times the mean-value filter is applied.
164  /// @param mask Optional alpha mask.
165  void median(int width = 1, int iterations = 1, const MaskType* mask = nullptr);
166 
167  /// Offsets (i.e. adds) a constant value to all active voxels.
168  /// @param offset Offset in the same units as the grid.
169  /// @param mask Optional alpha mask.
170  void offset(ValueType offset, const MaskType* mask = nullptr);
171 
172  /// @brief Used internally by tbb::parallel_for()
173  /// @param range Range of LeafNodes over which to multi-thread.
174  ///
175  /// @warning Never call this method directly!
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);
209  /// @return true if the process was interrupted
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 
222 ////////////////////////////////////////
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
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));
242  ValueType value = static_cast<ValueType>(sum * frac);
244  return value;
245 }
246 
247 
248 ////////////////////////////////////////
249 
250 
251 template<typename GridT, typename MaskT, typename InterruptT>
252 inline void
253 Filter<GridT, MaskT, InterruptT>::mean(int width, int iterations, const MaskType* mask)
254 {
255  mMask = mask;
256 
257  if (mInterrupter) mInterrupter->start("Applying mean 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 && !this->wasInterrupted(); ++i) {
264  mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
265  this->cook(leafs);
266 
267  mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
268  this->cook(leafs);
269 
270  mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
271  this->cook(leafs);
272  }
273 
274  if (mInterrupter) mInterrupter->end();
275 }
276 
277 
278 template<typename GridT, typename MaskT, typename InterruptT>
279 inline void
280 Filter<GridT, MaskT, InterruptT>::gaussian(int width, int iterations, const MaskType* mask)
281 {
282  mMask = mask;
283 
284  if (mInterrupter) mInterrupter->start("Applying Gaussian filter");
285 
286  const int w = std::max(1, width);
287 
288  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
289 
290  for (int i=0; i<iterations; ++i) {
291  for (int n=0; n<4 && !this->wasInterrupted(); ++n) {
292  mTask = std::bind(&Filter::doBoxX, std::placeholders::_1, std::placeholders::_2, w);
293  this->cook(leafs);
294 
295  mTask = std::bind(&Filter::doBoxY, std::placeholders::_1, std::placeholders::_2, w);
296  this->cook(leafs);
297 
298  mTask = std::bind(&Filter::doBoxZ, std::placeholders::_1, std::placeholders::_2, w);
299  this->cook(leafs);
300  }
301  }
302 
303  if (mInterrupter) mInterrupter->end();
304 }
305 
306 
307 template<typename GridT, typename MaskT, typename InterruptT>
308 inline void
309 Filter<GridT, MaskT, InterruptT>::median(int width, int iterations, const MaskType* mask)
310 {
311  mMask = mask;
312 
313  if (mInterrupter) mInterrupter->start("Applying median filter");
314 
315  LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0);
316 
317  mTask = std::bind(&Filter::doMedian,
318  std::placeholders::_1, std::placeholders::_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 
325 template<typename GridT, typename MaskT, typename InterruptT>
326 inline void
328 {
329  mMask = mask;
330 
331  if (mInterrupter) mInterrupter->start("Applying offset");
332 
333  LeafManagerType leafs(mGrid->tree(), 0, mGrainSize==0);
334 
335  mTask = std::bind(&Filter::doOffset, std::placeholders::_1, std::placeholders::_2, value);
336  this->cook(leafs);
337 
338  if (mInterrupter) mInterrupter->end();
339 }
340 
341 
342 ////////////////////////////////////////
343 
344 
345 /// Private method to perform the task (serial or threaded) and
346 /// subsequently swap the leaf buffers.
347 template<typename GridT, typename MaskT, typename InterruptT>
348 inline void
349 Filter<GridT, MaskT, InterruptT>::cook(LeafManagerType& leafs)
350 {
351  if (mGrainSize>0) {
352  tbb::parallel_for(leafs.leafRange(mGrainSize), *this);
353  } else {
354  (*this)(leafs.leafRange());
355  }
356  leafs.swapLeafBuffer(1, mGrainSize==0);
357 }
358 
359 
360 /// One dimensional convolution of a separable box filter
361 template<typename GridT, typename MaskT, typename InterruptT>
362 template <typename AvgT>
363 inline void
364 Filter<GridT, MaskT, InterruptT>::doBox(const RangeType& range, Int32 w)
365 {
366  this->wasInterrupted();
367  AvgT avg(mGrid, w);
368  if (mMask) {
369  typename AlphaMaskT::FloatType a, b;
370  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
371  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
372  BufferT& buffer = leafIter.buffer(1);
373  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
374  const Coord xyz = iter.getCoord();
375  if (alpha(xyz, a, b)) {
377  const ValueType value(b*(*iter) + a*avg(xyz));
379  buffer.setValue(iter.pos(), value);
380  }
381  }
382  }
383  } else {
384  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
385  BufferT& buffer = leafIter.buffer(1);
386  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
387  buffer.setValue(iter.pos(), avg(iter.getCoord()));
388  }
389  }
390  }
391 }
392 
393 
394 /// Performs simple but slow median-value diffusion
395 template<typename GridT, typename MaskT, typename InterruptT>
396 inline void
397 Filter<GridT, MaskT, InterruptT>::doMedian(const RangeType& range, int width)
398 {
399  this->wasInterrupted();
400  typename math::DenseStencil<GridType> stencil(*mGrid, width);//creates local cache!
401  if (mMask) {
402  typename AlphaMaskT::FloatType a, b;
403  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
404  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
405  BufferT& buffer = leafIter.buffer(1);
406  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
407  if (alpha(iter.getCoord(), a, b)) {
408  stencil.moveTo(iter);
410  ValueType value(b*(*iter) + a*stencil.median());
412  buffer.setValue(iter.pos(), value);
413  }
414  }
415  }
416  } else {
417  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
418  BufferT& buffer = leafIter.buffer(1);
419  for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
420  stencil.moveTo(iter);
421  buffer.setValue(iter.pos(), stencil.median());
422  }
423  }
424  }
425 }
426 
427 
428 /// Offsets the values by a constant
429 template<typename GridT, typename MaskT, typename InterruptT>
430 inline void
431 Filter<GridT, MaskT, InterruptT>::doOffset(const RangeType& range, ValueType offset)
432 {
433  this->wasInterrupted();
434  if (mMask) {
435  typename AlphaMaskT::FloatType a, b;
436  AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask);
437  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
438  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
439  if (alpha(iter.getCoord(), a, b)) {
441  ValueType value(*iter + a*offset);
443  iter.setValue(value);
444  }
445  }
446  }
447  } else {
448  for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
449  for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
450  iter.setValue(*iter + offset);
451  }
452  }
453  }
454 }
455 
456 
457 template<typename GridT, typename MaskT, typename InterruptT>
458 inline bool
460 {
461  if (util::wasInterrupted(mInterrupter)) {
462  tbb::task::self().cancel_group_execution();
463  return true;
464  }
465  return false;
466 }
467 
468 } // namespace tools
469 } // namespace OPENVDB_VERSION_NAME
470 } // namespace openvdb
471 
472 #endif // OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED
473 
474 // Copyright (c) DreamWorks Animation LLC
475 // All rights reserved. This software is distributed under the
476 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
vint4 max(const vint4 &a, const vint4 &b)
Definition: simd.h:4703
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END
Definition: Platform.h:224
void parallel_for(int64_t start, int64_t end, std::function< void(int64_t index)> &&task, parallel_options opt=parallel_options(0, Split_Y, 1))
Definition: parallel.h:153
GLenum GLint * range
Definition: glew.h:3500
GLboolean invert
Definition: glew.h:1422
void mean(int width=1, int iterations=1, const MaskType *mask=nullptr)
One iteration of a fast separable mean-value (i.e. box) filter.
Definition: Filter.h:253
Volume filtering (e.g., diffusion) with optional alpha masking.
Definition: Filter.h:67
typename CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition: LeafManager.h:120
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
GLclampf GLclampf GLclampf alpha
Definition: glew.h:1520
GLint GLint GLint GLint GLint GLint GLsizei width
Definition: glew.h:1252
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
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:9477
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:200
GLint GLfloat GLint stencil
Definition: glew.h:2167
GLenum GLint GLuint mask
Definition: glew.h:1845
void offset(ValueType offset, const MaskType *mask=nullptr)
Definition: Filter.h:327
typename tree::LeafManager< TreeType > LeafManagerType
Definition: Filter.h:76
void setMaskRange(AlphaType min, AlphaType max)
Define the range for the (optional) scalar mask.
Definition: Filter.h:130
GLclampf f
Definition: glew.h:3499
typename MaskType::ValueType AlphaType
Definition: Filter.h:75
Filter(GridT &grid, InterruptT *interrupt=nullptr)
Definition: Filter.h:85
GLuint buffer
Definition: glew.h:1680
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:100
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1890
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
GLsizei n
Definition: glew.h:4040
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:109
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
void setGrainSize(int grainsize)
Set the grain-size used for multi-threading.
Definition: Filter.h:116
typename GridType::TreeType TreeType
Definition: Filter.h:72
void operator()(const RangeType &range) const
Used internally by tbb::parallel_for()
Definition: Filter.h:176
void gaussian(int width=1, int iterations=1, const MaskType *mask=nullptr)
One iteration of a fast separable Gaussian filter.
Definition: Filter.h:280
GLdouble GLdouble GLdouble b
Definition: glew.h:9122
typename TreeType::LeafNodeType LeafType
Definition: Filter.h:73
typename LeafManagerType::BufferType BufferType
Definition: Filter.h:78
#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:223
GLdouble GLdouble GLdouble r
Definition: glew.h:1406
Defines various finite difference stencils by means of the "curiously recurring template pattern" on ...
typename GridType::ValueType ValueType
Definition: Filter.h:74
void median(int width=1, int iterations=1, const MaskType *mask=nullptr)
One iteration of a median-value filter.
Definition: Filter.h:309
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
vint4 min(const vint4 &a, const vint4 &b)
Definition: simd.h:4694
bool wasInterrupted(T *i, int percent=-1)
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:146
GLsizei const GLfloat * value
Definition: glew.h:1849
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:109
typename LeafManagerType::LeafRange RangeType
Definition: Filter.h:77
GLintptr offset
Definition: glew.h:1682