OpenVDB 9.0.0
TopologyToLevelSet.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: MPL-2.0
3//
4/// @file TopologyToLevelSet.h
5///
6/// @brief This tool generates a narrow-band signed distance field / level set
7/// from the interface between active and inactive voxels in a vdb grid.
8///
9/// @par Example:
10/// Combine with @c tools::PointsToVolume for fast point cloud to level set conversion.
11
12#ifndef OPENVDB_TOOLS_TOPOLOGY_TO_LEVELSET_HAS_BEEN_INCLUDED
13#define OPENVDB_TOOLS_TOPOLOGY_TO_LEVELSET_HAS_BEEN_INCLUDED
14
15#include "LevelSetFilter.h"
16#include "Morphology.h" // for erodeActiveValues and dilateActiveValues
17#include "SignedFloodFill.h"
18
19#include <openvdb/Grid.h>
20#include <openvdb/Types.h>
21#include <openvdb/math/FiniteDifference.h> // for math::BiasedGradientScheme
23#include <openvdb/openvdb.h>
25#include <tbb/task_group.h>
26#include <algorithm> // for std::min(), std::max()
27#include <vector>
28
29
30namespace openvdb {
32namespace OPENVDB_VERSION_NAME {
33namespace tools {
34
35
36/// @brief Compute the narrow-band signed distance to the interface between
37/// active and inactive voxels in the input grid.
38///
39/// @return A shared pointer to a new sdf / level set grid of type @c float
40///
41/// @param grid Input grid of arbitrary type whose active voxels are used
42/// in constructing the level set.
43/// @param halfWidth Half the width of the narrow band in voxel units.
44/// @param closingSteps Number of morphological closing steps used to fill gaps
45/// in the active voxel region.
46/// @param dilation Number of voxels to expand the active voxel region.
47/// @param smoothingSteps Number of smoothing interations.
48template<typename GridT>
49typename GridT::template ValueConverter<float>::Type::Ptr
50topologyToLevelSet(const GridT& grid, int halfWidth = 3, int closingSteps = 1, int dilation = 0,
51 int smoothingSteps = 0);
52
53
54/// @brief Compute the narrow-band signed distance to the interface between
55/// active and inactive voxels in the input grid.
56///
57/// @return A shared pointer to a new sdf / level set grid of type @c float
58///
59/// @param grid Input grid of arbitrary type whose active voxels are used
60/// in constructing the level set.
61/// @param halfWidth Half the width of the narrow band in voxel units.
62/// @param closingSteps Number of morphological closing steps used to fill gaps
63/// in the active voxel region.
64/// @param dilation Number of voxels to expand the active voxel region.
65/// @param smoothingSteps Number of smoothing interations.
66/// @param interrupt Optional object adhering to the util::NullInterrupter interface.
67template<typename GridT, typename InterrupterT>
68typename GridT::template ValueConverter<float>::Type::Ptr
69topologyToLevelSet(const GridT& grid, int halfWidth = 3, int closingSteps = 1, int dilation = 0,
70 int smoothingSteps = 0, InterrupterT* interrupt = nullptr);
71
72
73////////////////////////////////////////
74
75/// @cond OPENVDB_DOCS_INTERNAL
76
77namespace ttls_internal {
78
79
80template<typename TreeT>
81struct DilateOp
82{
83 DilateOp(TreeT& t, int n) : tree(&t), size(n) {}
84 void operator()() const {
86 }
87 TreeT* tree;
88 const int size;
89};
90
91
92template<typename TreeT>
93struct ErodeOp
94{
95 ErodeOp(TreeT& t, int n) : tree(&t), size(n) {}
96 void operator()() const {
99 }
100 TreeT* tree;
101 const int size;
102};
103
104
105template<typename TreeType>
106struct OffsetAndMinComp
107{
108 using LeafNodeType = typename TreeType::LeafNodeType;
109 using ValueType = typename TreeType::ValueType;
110
111 OffsetAndMinComp(std::vector<LeafNodeType*>& lhsNodes,
112 const TreeType& rhsTree, ValueType offset)
113 : mLhsNodes(lhsNodes.empty() ? nullptr : &lhsNodes[0]), mRhsTree(&rhsTree), mOffset(offset)
114 {
115 }
116
117 void operator()(const tbb::blocked_range<size_t>& range) const
118 {
119 using Iterator = typename LeafNodeType::ValueOnIter;
120
121 tree::ValueAccessor<const TreeType> rhsAcc(*mRhsTree);
122 const ValueType offset = mOffset;
123
124 for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
125
126 LeafNodeType& lhsNode = *mLhsNodes[n];
127 const LeafNodeType * rhsNodePt = rhsAcc.probeConstLeaf(lhsNode.origin());
128 if (!rhsNodePt) continue;
129
130 for (Iterator it = lhsNode.beginValueOn(); it; ++it) {
131 ValueType& val = const_cast<ValueType&>(it.getValue());
132 val = std::min(val, offset + rhsNodePt->getValue(it.pos()));
133 }
134 }
135 }
136
137private:
138 LeafNodeType * * const mLhsNodes;
139 TreeType const * const mRhsTree;
140 ValueType const mOffset;
141}; // struct OffsetAndMinComp
142
143
144template<typename GridType, typename InterrupterType>
145inline void
146normalizeLevelSet(GridType& grid, const int halfWidthInVoxels, InterrupterType* interrupt = nullptr)
147{
148 LevelSetFilter<GridType, GridType, InterrupterType> filter(grid, interrupt);
149 filter.setSpatialScheme(math::FIRST_BIAS);
150 filter.setNormCount(halfWidthInVoxels);
151 filter.normalize();
152 filter.prune();
153}
154
155
156template<typename GridType, typename InterrupterType>
157inline void
158smoothLevelSet(GridType& grid, int iterations, int halfBandWidthInVoxels,
159 InterrupterType* interrupt = nullptr)
160{
161 using ValueType = typename GridType::ValueType;
162 using TreeType = typename GridType::TreeType;
163 using LeafNodeType = typename TreeType::LeafNodeType;
164
165 GridType filterGrid(grid);
166
167 LevelSetFilter<GridType, GridType, InterrupterType> filter(filterGrid, interrupt);
168 filter.setSpatialScheme(math::FIRST_BIAS);
169
170 for (int n = 0; n < iterations; ++n) {
171 if (interrupt && interrupt->wasInterrupted()) break;
172 filter.mean(1);
173 }
174
175 std::vector<LeafNodeType*> nodes;
176 grid.tree().getNodes(nodes);
177
178 const ValueType offset = ValueType(double(0.5) * grid.transform().voxelSize()[0]);
179
180 tbb::parallel_for(tbb::blocked_range<size_t>(0, nodes.size()),
181 OffsetAndMinComp<TreeType>(nodes, filterGrid.tree(), -offset));
182
183 // Clean up any damanage that was done by the min operation
184 normalizeLevelSet(grid, halfBandWidthInVoxels, interrupt);
185}
186
187
188} // namespace ttls_internal
189
190/// @endcond
191
192template<typename GridT, typename InterrupterT>
193typename GridT::template ValueConverter<float>::Type::Ptr
194topologyToLevelSet(const GridT& grid, int halfWidth, int closingSteps, int dilation,
195 int smoothingSteps, InterrupterT* interrupt)
196{
197 using MaskTreeT = typename GridT::TreeType::template ValueConverter<ValueMask>::Type;
198 using FloatTreeT = typename GridT::TreeType::template ValueConverter<float>::Type;
199 using FloatGridT = Grid<FloatTreeT>;
200
201 // Check inputs
202
203 halfWidth = std::max(halfWidth, 1);
204 closingSteps = std::max(closingSteps, 0);
205 dilation = std::max(dilation, 0);
206
207 if (!grid.hasUniformVoxels()) {
208 OPENVDB_THROW(ValueError, "Non-uniform voxels are not supported!");
209 }
210
211 // Copy the topology into a MaskGrid.
212 MaskTreeT maskTree( grid.tree(), false/*background*/, openvdb::TopologyCopy() );
213
214 // Morphological closing operation.
215 tools::dilateActiveValues(maskTree, closingSteps + dilation, tools::NN_FACE, tools::IGNORE_TILES);
216 tools::erodeActiveValues(maskTree, /*iterations=*/closingSteps, tools::NN_FACE, tools::IGNORE_TILES);
217 tools::pruneInactive(maskTree);
218
219 // Generate a volume with an implicit zero crossing at the boundary
220 // between active and inactive values in the input grid.
221 const float background = float(grid.voxelSize()[0]) * float(halfWidth);
222 typename FloatTreeT::Ptr lsTree(
223 new FloatTreeT( maskTree, /*out=*/background, /*in=*/-background, openvdb::TopologyCopy() ) );
224
225 tbb::task_group pool;
226 pool.run( ttls_internal::ErodeOp< MaskTreeT >( maskTree, halfWidth ) );
227 pool.run( ttls_internal::DilateOp<FloatTreeT>( *lsTree , halfWidth ) );
228 pool.wait();// wait for both tasks to complete
229
230 lsTree->topologyDifference( maskTree );
231 tools::pruneLevelSet( *lsTree, /*threading=*/true);
232
233 // Create a level set grid from the tree
234 typename FloatGridT::Ptr lsGrid = FloatGridT::create( lsTree );
235 lsGrid->setTransform( grid.transform().copy() );
236 lsGrid->setGridClass( openvdb::GRID_LEVEL_SET );
237
238 // Use a PDE based scheme to propagate distance values from the
239 // implicit zero crossing.
240 ttls_internal::normalizeLevelSet(*lsGrid, 3*halfWidth, interrupt);
241
242 // Additional filtering
243 if (smoothingSteps > 0) {
244 ttls_internal::smoothLevelSet(*lsGrid, smoothingSteps, halfWidth, interrupt);
245 }
246
247 return lsGrid;
248}
249
250
251template<typename GridT>
252typename GridT::template ValueConverter<float>::Type::Ptr
253topologyToLevelSet(const GridT& grid, int halfWidth, int closingSteps, int dilation, int smoothingSteps)
254{
255 util::NullInterrupter interrupt;
256 return topologyToLevelSet(grid, halfWidth, closingSteps, dilation, smoothingSteps, &interrupt);
257}
258
259
260////////////////////////////////////////
261
262
263// Explicit Template Instantiation
264
265#ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
266
267#ifdef OPENVDB_INSTANTIATE_TOPOLOGYTOLEVELSET
269#endif
270
271#define _FUNCTION(TreeT) \
272 Grid<TreeT>::ValueConverter<float>::Type::Ptr topologyToLevelSet(const Grid<TreeT>&, int, int, int, int, \
273 util::NullInterrupter*)
275#undef _FUNCTION
276
277#endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
278
279
280} // namespace tools
281} // namespace OPENVDB_VERSION_NAME
282} // namespace openvdb
283
284#endif // OPENVDB_TOOLS_TOPOLOGY_TO_LEVELSET_HAS_BEEN_INCLUDED
285
Performs various types of level set deformations with interface tracking. These unrestricted deformat...
Implementation of morphological dilation and erosion.
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
Propagate the signs of distance values from the active voxels in the narrow band to the inactive valu...
Container class that associates a tree with a transform and metadata.
Definition: Grid.h:577
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition: Types.h:564
Definition: Exceptions.h:65
GridType
List of types that are currently supported by NanoVDB.
Definition: NanoVDB.h:216
@ FIRST_BIAS
Definition: FiniteDifference.h:167
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:107
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:103
GridT::template ValueConverter< float >::Type::Ptr topologyToLevelSet(const GridT &grid, int halfWidth=3, int closingSteps=1, int dilation=0, int smoothingSteps=0, InterrupterT *interrupt=nullptr)
Compute the narrow-band signed distance to the interface between active and inactive voxels in the in...
Definition: TopologyToLevelSet.h:194
@ NN_FACE
Definition: Morphology.h:58
void pruneLevelSet(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing nodes whose values are all inactive with inactive ...
Definition: Prune.h:390
void erodeActiveValues(TreeOrLeafManagerT &tree, const int iterations=1, const NearestNeighbors nn=NN_FACE, const TilePolicy mode=PRESERVE_TILES, const bool threaded=true)
Topologically erode all active values (i.e. both voxels and tiles) in a tree using one of three neare...
Definition: Morphology.h:1132
void dilateActiveValues(TreeOrLeafManagerT &tree, const int iterations=1, const NearestNeighbors nn=NN_FACE, const TilePolicy mode=PRESERVE_TILES, const bool threaded=true)
Topologically dilate all active values (i.e. both voxels and tiles) in a tree using one of three near...
Definition: Morphology.h:1055
@ IGNORE_TILES
Definition: Morphology.h:80
void pruneInactive(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing with background tiles any nodes whose values are a...
Definition: Prune.h:355
@ GRID_LEVEL_SET
Definition: Types.h:337
Definition: Exceptions.h:13
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
Base class for interrupters.
Definition: NullInterrupter.h:26
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:202
#define OPENVDB_ALL_TREE_INSTANTIATE(Function)
Definition: version.h.in:151