1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
4 /// @author Ken Museth
5 ///
6 /// @file tools/LevelSetFilter.h
7 ///
8 /// @brief Performs various types of level set deformations with
9 /// interface tracking. These unrestricted deformations include
10 /// surface smoothing (e.g., Laplacian flow), filtering (e.g., mean
11 /// value) and morphological operations (e.g., morphological opening).
12 /// All these operations can optionally be masked with another grid that
13 /// acts as an alpha-mask.
14 
15 #ifndef OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED
16 #define OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED
17 
18 #include "LevelSetTracker.h"
19 #include "Interpolation.h"
20 #include <algorithm> // for std::max()
21 #include <functional>
22 #include <type_traits>
23 
24 
25 namespace openvdb {
26 OPENVDB_USE_VERSION_NAMESPACE
27 namespace OPENVDB_VERSION_NAME {
28 namespace tools {
29 
30 /// @brief Filtering (e.g. diffusion) of narrow-band level sets. An
31 /// optional scalar field can be used to produce a (smooth) alpha mask
32 /// for the filtering.
33 ///
34 /// @note This class performs proper interface tracking which allows
35 /// for unrestricted surface deformations
36 template<typename GridT,
37          typename MaskT = typename GridT::template ValueConverter<float>::Type,
38          typename InterruptT = util::NullInterrupter>
39 class LevelSetFilter : public LevelSetTracker<GridT, InterruptT>
40 {
41 public:
42     using BaseType = LevelSetTracker<GridT, InterruptT>;
43     using GridType = GridT;
44     using MaskType = MaskT;
45     using TreeType = typename GridType::TreeType;
46     using ValueType = typename TreeType::ValueType;
47     using AlphaType = typename MaskType::ValueType;
48     static_assert(std::is_floating_point<AlphaType>::value,
49         "LevelSetFilter requires a mask grid with floating-point values");
50 
51     /// @brief Main constructor from a grid
52     /// @param grid The level set to be filtered.
53     /// @param interrupt Optional interrupter.
54     LevelSetFilter(GridType& grid, InterruptT* interrupt = nullptr)
BaseType(grid,interrupt)55         : BaseType(grid, interrupt)
56         , mMinMask(0)
57         , mMaxMask(1)
58         , mInvertMask(false)
59     {
60     }
61     /// @brief Default destructor
~LevelSetFilter()62     ~LevelSetFilter() override {}
63 
64     /// @brief Return the minimum value of the mask to be used for the
65     /// derivation of a smooth alpha value.
minMask()66     AlphaType minMask() const { return mMinMask; }
67     /// @brief Return the maximum value of the mask to be used for the
68     /// derivation of a smooth alpha value.
maxMask()69     AlphaType maxMask() const { return mMaxMask; }
70     /// @brief Define the range for the (optional) scalar mask.
71     /// @param min Minimum value of the range.
72     /// @param max Maximum value of the range.
73     /// @details Mask values outside the range maps to alpha values of
74     /// respectfully zero and one, and values inside the range maps
75     /// smoothly to 0->1 (unless of course the mask is inverted).
76     /// @throw ValueError if @a min is not smaller than @a max.
setMaskRange(AlphaType min,AlphaType max)77     void setMaskRange(AlphaType min, AlphaType max)
78     {
79         if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)");
80         mMinMask = min;
81         mMaxMask = max;
82     }
83 
84     /// @brief Return true if the mask is inverted, i.e. min->max in the
85     /// original mask maps to 1->0 in the inverted alpha mask.
isMaskInverted()86     bool isMaskInverted() const { return mInvertMask; }
87     /// @brief Invert the optional mask, i.e. min->max in the original
88     /// mask maps to 1->0 in the inverted alpha mask.
89     void invertMask(bool invert=true) { mInvertMask = invert; }
90 
91     /// @brief One iteration of mean-curvature flow of the level set.
92     /// @param mask Optional alpha mask.
93     void meanCurvature(const MaskType* mask = nullptr)
94     {
95         Filter f(this, mask); f.meanCurvature();
96     }
97 
98     /// @brief One iteration of Laplacian flow of the level set.
99     /// @param mask Optional alpha mask.
100     void laplacian(const MaskType* mask = nullptr)
101     {
102         Filter f(this, mask); f.laplacian();
103     }
104 
105     /// @brief One iteration of a fast separable Gaussian filter.
106     /// @param width Width of the Gaussian kernel in voxel units.
107     /// @param mask Optional alpha mask.
108     ///
109     /// @note This is approximated as 4 iterations of a separable mean filter
110     /// which typically leads an approximation that's better than 95%!
111     void gaussian(int width = 1, const MaskType* mask = nullptr)
112     {
113         Filter f(this, mask); f.gaussian(width);
114     }
115 
116     /// @brief Offset the level set by the specified (world) distance.
117     /// @param offset Value of the offset.
118     /// @param mask Optional alpha mask.
119     void offset(ValueType offset, const MaskType* mask = nullptr)
120     {
121         Filter f(this, mask); f.offset(offset);
122     }
123 
124     /// @brief One iteration of median-value flow of the level set.
125     /// @param width Width of the median-value kernel in voxel units.
126     /// @param mask Optional alpha mask.
127     ///
128     /// @warning This filter is not separable and is hence relatively
129     /// slow!
130     void median(int width = 1, const MaskType* mask = nullptr)
131     {
132         Filter f(this, mask); f.median(width);
133     }
134 
135     /// @brief One iteration of mean-value flow of the level set.
136     /// @param width Width of the mean-value kernel in voxel units.
137     /// @param mask Optional alpha mask.
138     ///
139     /// @note This filter is separable so it's fast!
140     void mean(int width = 1, const MaskType* mask = nullptr)
141     {
142         Filter f(this, mask); f.mean(width);
143     }
144 
145 private:
146     // disallow copy construction and copy by assignment!
147     LevelSetFilter(const LevelSetFilter&);// not implemented
148     LevelSetFilter& operator=(const LevelSetFilter&);// not implemented
149 
150     // Private struct that implements all the filtering.
151     struct Filter
152     {
153         using LeafT = typename TreeType::LeafNodeType;
154         using VoxelIterT = typename LeafT::ValueOnIter;
155         using VoxelCIterT = typename LeafT::ValueOnCIter;
156         using BufferT = typename tree::LeafManager<TreeType>::BufferType;
157         using LeafRange = typename tree::LeafManager<TreeType>::LeafRange;
158         using LeafIterT = typename LeafRange::Iterator;
159         using AlphaMaskT = tools::AlphaMask<GridT, MaskT>;
160 
FilterFilter161         Filter(LevelSetFilter* parent, const MaskType* mask) : mParent(parent), mMask(mask) {}
162         Filter(const Filter&) = default;
~FilterFilter163         virtual ~Filter() {}
164 
165         void box(int width);
166         void median(int width);
167         void mean(int width);
168         void gaussian(int width);
169         void laplacian();
170         void meanCurvature();
171         void offset(ValueType value);
operatorFilter172         void operator()(const LeafRange& r) const
173         {
174             if (mTask) mTask(const_cast<Filter*>(this), r);
175             else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly");
176         }
cookFilter177         void cook(bool swap)
178         {
179             const int n = mParent->getGrainSize();
180             if (n>0) {
181                 tbb::parallel_for(mParent->leafs().leafRange(n), *this);
182             } else {
183                 (*this)(mParent->leafs().leafRange());
184             }
185             if (swap) mParent->leafs().swapLeafBuffer(1, n==0);
186         }
187 
188         template <size_t Axis>
189         struct Avg {
AvgFilter::Avg190             Avg(const GridT& grid, Int32 w) :
191                 acc(grid.tree()), width(w), frac(1/ValueType(2*w+1)) {}
operatorFilter::Avg192             inline ValueType operator()(Coord xyz)
193             {
194                 ValueType sum = zeroVal<ValueType>();
195                 Int32& i = xyz[Axis], j = i + width;
196                 for (i -= width; i <= j; ++i) sum += acc.getValue(xyz);
197                 return sum*frac;
198             }
199             typename GridT::ConstAccessor acc;
200             const Int32 width;
201             const ValueType frac;
202         };
203 
204         template<typename AvgT>
205         void boxImpl(const LeafRange& r, Int32 w);
206 
boxXImplFilter207         void boxXImpl(const LeafRange& r, Int32 w) { this->boxImpl<Avg<0> >(r,w); }
boxZImplFilter208         void boxZImpl(const LeafRange& r, Int32 w) { this->boxImpl<Avg<1> >(r,w); }
boxYImplFilter209         void boxYImpl(const LeafRange& r, Int32 w) { this->boxImpl<Avg<2> >(r,w); }
210 
211         void medianImpl(const LeafRange&, int);
212         void meanCurvatureImpl(const LeafRange&);
213         void laplacianImpl(const LeafRange&);
214         void offsetImpl(const LeafRange&, ValueType);
215 
216         LevelSetFilter* mParent;
217         const MaskType* mMask;
218         typename std::function<void (Filter*, const LeafRange&)> mTask;
219     }; // end of private Filter struct
220 
221     AlphaType mMinMask, mMaxMask;
222     bool      mInvertMask;
223 
224 }; // end of LevelSetFilter class
225 
226 
227 ////////////////////////////////////////
228 
229 template<typename GridT, typename MaskT, typename InterruptT>
230 inline void
median(int width)231 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::median(int width)
232 {
233     mParent->startInterrupter("Median-value flow of level set");
234 
235     mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0);
236 
237     mTask = std::bind(&Filter::medianImpl,
238         std::placeholders::_1, std::placeholders::_2, std::max(1, width));
239     this->cook(true);
240 
241     mParent->track();
242 
243     mParent->endInterrupter();
244 }
245 
246 template<typename GridT, typename MaskT, typename InterruptT>
247 inline void
mean(int width)248 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::mean(int width)
249 {
250     mParent->startInterrupter("Mean-value flow of level set");
251 
252     this->box(width);
253 
254     mParent->endInterrupter();
255 }
256 
257 template<typename GridT, typename MaskT, typename InterruptT>
258 inline void
gaussian(int width)259 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::gaussian(int width)
260 {
261     mParent->startInterrupter("Gaussian flow of level set");
262 
263     for (int n=0; n<4; ++n) this->box(width);
264 
265     mParent->endInterrupter();
266 }
267 
268 template<typename GridT, typename MaskT, typename InterruptT>
269 inline void
box(int width)270 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::box(int width)
271 {
272     mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0);
273 
274     width = std::max(1, width);
275 
276     mTask = std::bind(&Filter::boxXImpl, std::placeholders::_1, std::placeholders::_2, width);
277     this->cook(true);
278 
279     mTask = std::bind(&Filter::boxYImpl, std::placeholders::_1, std::placeholders::_2, width);
280     this->cook(true);
281 
282     mTask = std::bind(&Filter::boxZImpl, std::placeholders::_1, std::placeholders::_2, width);
283     this->cook(true);
284 
285     mParent->track();
286 }
287 
288 template<typename GridT, typename MaskT, typename InterruptT>
289 inline void
meanCurvature()290 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::meanCurvature()
291 {
292     mParent->startInterrupter("Mean-curvature flow of level set");
293 
294     mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0);
295 
296     mTask = std::bind(&Filter::meanCurvatureImpl, std::placeholders::_1, std::placeholders::_2);
297     this->cook(true);
298 
299     mParent->track();
300 
301     mParent->endInterrupter();
302 }
303 
304 template<typename GridT, typename MaskT, typename InterruptT>
305 inline void
laplacian()306 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::laplacian()
307 {
308     mParent->startInterrupter("Laplacian flow of level set");
309 
310     mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0);
311 
312     mTask = std::bind(&Filter::laplacianImpl, std::placeholders::_1, std::placeholders::_2);
313     this->cook(true);
314 
315     mParent->track();
316 
317     mParent->endInterrupter();
318 }
319 
320 template<typename GridT, typename MaskT, typename InterruptT>
321 inline void
offset(ValueType value)322 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::offset(ValueType value)
323 {
324     mParent->startInterrupter("Offsetting level set");
325 
326     mParent->leafs().removeAuxBuffers();// no auxiliary buffers required
327 
328     const ValueType CFL = ValueType(0.5) * mParent->voxelSize(), offset = openvdb::math::Abs(value);
329     ValueType dist = 0.0;
330     while (offset-dist > ValueType(0.001)*CFL && mParent->checkInterrupter()) {
331         const ValueType delta = openvdb::math::Min(offset-dist, CFL);
332         dist += delta;
333 
334         mTask = std::bind(&Filter::offsetImpl,
335             std::placeholders::_1, std::placeholders::_2, copysign(delta, value));
336         this->cook(false);
337 
338         mParent->track();
339     }
340 
341     mParent->endInterrupter();
342 }
343 
344 
345 ///////////////////////// PRIVATE METHODS //////////////////////
346 
347 /// Performs parabolic mean-curvature diffusion
348 template<typename GridT, typename MaskT, typename InterruptT>
349 inline void
meanCurvatureImpl(const LeafRange & range)350 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::meanCurvatureImpl(const LeafRange& range)
351 {
352     mParent->checkInterrupter();
353     //const float CFL = 0.9f, dt = CFL * mDx * mDx / 6.0f;
354     const ValueType dx = mParent->voxelSize(), dt = math::Pow2(dx) / ValueType(3.0);
355     math::CurvatureStencil<GridType> stencil(mParent->grid(), dx);
356     if (mMask) {
357         typename AlphaMaskT::FloatType a, b;
358         AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(),
359                          mParent->maxMask(), mParent->isMaskInverted());
360         for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
361             ValueType* buffer = leafIter.buffer(1).data();
362             for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
363                 if (alpha(iter.getCoord(), a, b)) {
364                     stencil.moveTo(iter);
365                     const ValueType phi0 = *iter, phi1 = phi0 + dt*stencil.meanCurvatureNormGrad();
366                     buffer[iter.pos()] = b * phi0 + a * phi1;
367                 }
368             }
369         }
370     } else {
371         for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
372             ValueType* buffer = leafIter.buffer(1).data();
373             for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
374                 stencil.moveTo(iter);
375                 buffer[iter.pos()] = *iter + dt*stencil.meanCurvatureNormGrad();
376             }
377         }
378     }
379 }
380 
381 /// Performs Laplacian diffusion. Note if the grids contains a true
382 /// signed distance field (e.g. a solution to the Eikonal equation)
383 /// Laplacian diffusions (e.g. geometric heat equation) is actually
384 /// identical to mean curvature diffusion, yet less computationally
385 /// expensive! In other words if you're performing renormalization
386 /// anyway (e.g. rebuilding the narrow-band) you should consider
387 /// performing Laplacian diffusion over mean curvature flow!
388 template<typename GridT, typename MaskT, typename InterruptT>
389 inline void
laplacianImpl(const LeafRange & range)390 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::laplacianImpl(const LeafRange& range)
391 {
392     mParent->checkInterrupter();
393     //const float CFL = 0.9f, half_dt = CFL * mDx * mDx / 12.0f;
394     const ValueType dx = mParent->voxelSize(), dt = math::Pow2(dx) / ValueType(6.0);
395     math::GradStencil<GridType> stencil(mParent->grid(), dx);
396     if (mMask) {
397         typename AlphaMaskT::FloatType a, b;
398         AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(),
399                          mParent->maxMask(), mParent->isMaskInverted());
400         for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
401             ValueType* buffer = leafIter.buffer(1).data();
402             for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
403                 if (alpha(iter.getCoord(), a, b)) {
404                     stencil.moveTo(iter);
405                     const ValueType phi0 = *iter, phi1 = phi0 + dt*stencil.laplacian();
406                     buffer[iter.pos()] = b * phi0 + a * phi1;
407                 }
408             }
409         }
410     } else {
411         for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
412             ValueType* buffer = leafIter.buffer(1).data();
413             for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
414                 stencil.moveTo(iter);
415                 buffer[iter.pos()] = *iter + dt*stencil.laplacian();
416             }
417         }
418     }
419 }
420 
421 /// Offsets the values by a constant
422 template<typename GridT, typename MaskT, typename InterruptT>
423 inline void
offsetImpl(const LeafRange & range,ValueType offset)424 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::offsetImpl(
425     const LeafRange& range, ValueType offset)
426 {
427     mParent->checkInterrupter();
428     if (mMask) {
429         typename AlphaMaskT::FloatType a, b;
430         AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(),
431                          mParent->maxMask(), mParent->isMaskInverted());
432         for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
433             for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
434                 if (alpha(iter.getCoord(), a, b)) iter.setValue(*iter + a*offset);
435             }
436         }
437     } else {
438         for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
439             for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) {
440                 iter.setValue(*iter + offset);
441             }
442         }
443     }
444 }
445 
446 /// Performs simple but slow median-value diffusion
447 template<typename GridT, typename MaskT, typename InterruptT>
448 inline void
medianImpl(const LeafRange & range,int width)449 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::medianImpl(const LeafRange& range, int width)
450 {
451     mParent->checkInterrupter();
452     typename math::DenseStencil<GridType> stencil(mParent->grid(), width);//creates local cache!
453     if (mMask) {
454         typename AlphaMaskT::FloatType a, b;
455         AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(),
456                          mParent->maxMask(), mParent->isMaskInverted());
457         for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
458             ValueType* buffer = leafIter.buffer(1).data();
459             for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
460                 if (alpha(iter.getCoord(), a, b)) {
461                     stencil.moveTo(iter);
462                     buffer[iter.pos()] = b * (*iter) + a * stencil.median();
463                 }
464             }
465         }
466     } else {
467         for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
468             ValueType* buffer = leafIter.buffer(1).data();
469             for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
470                 stencil.moveTo(iter);
471                 buffer[iter.pos()] = stencil.median();
472             }
473         }
474     }
475 }
476 
477 /// One dimensional convolution of a separable box filter
478 template<typename GridT, typename MaskT, typename InterruptT>
479 template <typename AvgT>
480 inline void
boxImpl(const LeafRange & range,Int32 w)481 LevelSetFilter<GridT, MaskT, InterruptT>::Filter::boxImpl(const LeafRange& range, Int32 w)
482 {
483     mParent->checkInterrupter();
484     AvgT avg(mParent->grid(), w);
485     if (mMask) {
486         typename AlphaMaskT::FloatType a, b;
487         AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(),
488                          mParent->maxMask(), mParent->isMaskInverted());
489         for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
490             ValueType* buffer = leafIter.buffer(1).data();
491             for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
492                 const Coord xyz = iter.getCoord();
493                 if (alpha(xyz, a, b)) buffer[iter.pos()] = b * (*iter)+ a * avg(xyz);
494             }
495         }
496     } else {
497         for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) {
498             ValueType* buffer = leafIter.buffer(1).data();
499             for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) {
500                 buffer[iter.pos()] = avg(iter.getCoord());
501             }
502         }
503     }
504 }
505 
506 
507 ////////////////////////////////////////
508 
509 
510 // Explicit Template Instantiation
511 
512 #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
513 
514 #ifdef OPENVDB_INSTANTIATE_LEVELSETFILTER
515 #include <openvdb/util/ExplicitInstantiation.h>
516 #endif
517 
518 OPENVDB_INSTANTIATE_CLASS LevelSetFilter<FloatGrid, FloatGrid, util::NullInterrupter>;
519 OPENVDB_INSTANTIATE_CLASS LevelSetFilter<DoubleGrid, FloatGrid, util::NullInterrupter>;
520 
521 #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
522 
523 
524 } // namespace tools
525 } // namespace OPENVDB_VERSION_NAME
526 } // namespace openvdb
527 
528 #endif // OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED
529