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>
20 #include <openvdb/tree/LeafManager.h>
21 #include <openvdb/util/NullInterrupter.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 {
30 OPENVDB_USE_VERSION_NAMESPACE
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;
49 using LeafManagerType = typename tree::LeafManager<TreeType>;
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; }
103 void setMaskRange(AlphaType min, AlphaType max)
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;
161 using AlphaMaskT = tools::AlphaMask<GridT, MaskT>;
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
209 Filter<GridT, MaskT, InterruptT>::Avg<Axis>::operator()(Coord xyz)
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));
214 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
215 ValueType value = static_cast<ValueType>(sum * frac);
216 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
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
300 Filter<GridT, MaskT, InterruptT>::offset(ValueType value, const MaskType* mask)
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
322 Filter<GridT, MaskT, InterruptT>::cook(LeafManagerType& leafs)
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
337 Filter<GridT, MaskT, InterruptT>::doBox(const RangeType& range, Int32 w)
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)) {
349 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
350 const ValueType value(b*(*iter) + a*avg(xyz));
351 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
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
370 Filter<GridT, MaskT, InterruptT>::doMedian(const RangeType& range, int width)
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);
382 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
383 ValueType value(b*(*iter) + a*stencil.median());
384 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
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
404 Filter<GridT, MaskT, InterruptT>::doOffset(const RangeType& range, ValueType offset)
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)) {
413 OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
414 ValueType value(*iter + a*offset);
415 OPENVDB_NO_TYPE_CONVERSION_WARNING_END
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
432 Filter<GridT, MaskT, InterruptT>::wasInterrupted()
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
OPENVDB_NO_TYPE_CONVERSION_WARNING_END
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END
Definition: Platform.h:197
openvdb::v7_0::tools::AlphaMask
Definition: Interpolation.h:543
openvdb::v7_0::tools::Filter::LeafManagerType
typename tree::LeafManager< TreeType > LeafManagerType
Definition: Filter.h:49
openvdb::v7_0::tools::Filter::TreeType
typename GridType::TreeType TreeType
Definition: Filter.h:45
Types.h
openvdb::v7_0::tools::Filter::GridType
GridT GridType
Definition: Filter.h:43
openvdb::v7_0::tools::Filter::minMask
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
openvdb::v7_0::tools::Filter::Filter
Filter(const Filter &other)
Shallow copy constructor called by tbb::parallel_for() threads during filtering.
Definition: Filter.h:73
openvdb::v7_0::math::Coord
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:25
openvdb::v7_0::tools::Filter::Filter
Filter(GridT &grid, InterruptT *interrupt=nullptr)
Definition: Filter.h:58
openvdb::v7_0::tree::LeafManager::BufferType
typename CopyConstness< TreeType, NonConstBufferType >::Type BufferType
Definition: LeafManager.h:93
openvdb::v7_0::tools::Filter::setMaskRange
void setMaskRange(AlphaType min, AlphaType max)
Define the range for the (optional) scalar mask.
Definition: Filter.h:103
Transform.h
openvdb::v7_0::tools::Filter
Volume filtering (e.g., diffusion) with optional alpha masking.
Definition: Filter.h:40
openvdb::v7_0::tools::Filter::BufferType
typename LeafManagerType::BufferType BufferType
Definition: Filter.h:51
openvdb::v7_0::tools::Filter::maxMask
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
openvdb::v7_0::tools::Filter::AlphaType
typename MaskType::ValueType AlphaType
Definition: Filter.h:48
Grid.h
openvdb::v7_0::math::Axis
Axis
Definition: Math.h:849
openvdb::v7_0::tools::Filter::ValueType
typename GridType::ValueType ValueType
Definition: Filter.h:47
openvdb::v7_0::tools::Filter::isMaskInverted
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
Math.h
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
openvdb::v7_0::tools::Filter::setGrainSize
void setGrainSize(int grainsize)
Set the grain-size used for multi-threading.
Definition: Filter.h:89
openvdb::v7_0::tools::Filter::MaskType
MaskT MaskType
Definition: Filter.h:44
openvdb::v7_0::tree::LeafManager
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:82
openvdb::v7_0::tools::composite::max
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:106
NullInterrupter.h
openvdb::v7_0::tools::Filter::getGrainSize
int getGrainSize() const
Definition: Filter.h:86
Interpolation.h
openvdb::v7_0::tools::Filter::RangeType
typename LeafManagerType::LeafRange RangeType
Definition: Filter.h:50
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:154
openvdb::v7_0::tools::Filter::operator()
void operator()(const RangeType &range) const
Used internally by tbb::parallel_for()
Definition: Filter.h:149
openvdb::v7_0::tools::Filter::LeafType
typename TreeType::LeafNodeType LeafType
Definition: Filter.h:46
openvdb::v7_0::tools::composite::min
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:102
LeafManager.h
A LeafManager manages a linear array of pointers to a given tree's leaf nodes, as well as optional au...
OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
#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
openvdb::v7_0::tools::Filter::invertMask
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
Stencils.h
Defines various finite difference stencils by means of the "curiously recurring template pattern" on ...
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:102
openvdb::v7_0::tools::filter_internal::accum
void accum(bool &sum, bool addend)
Definition: Filter.h:202
openvdb::v7_0::ValueError
Definition: Exceptions.h:65
openvdb::v7_0::util::wasInterrupted
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
openvdb
Definition: Exceptions.h:13
openvdb::v7_0::Int32
int32_t Int32
Definition: Types.h:33
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:82