Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | // | ||
4 | /// @file Morphology.h | ||
5 | /// | ||
6 | /// @authors Ken Museth, Nick Avramoussis | ||
7 | /// | ||
8 | /// @brief Implementation of morphological dilation and erosion. | ||
9 | /// | ||
10 | /// @note By design the morphological operations only change the | ||
11 | /// state of voxels, not their values. If one desires to | ||
12 | /// change the values of voxels that change state an efficient | ||
13 | /// technique is to construct a boolean mask by performing a | ||
14 | /// topology difference between the original and final grids. | ||
15 | |||
16 | #ifndef OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED | ||
17 | #define OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED | ||
18 | |||
19 | #include "Activate.h" // backwards compatibility | ||
20 | #include "Prune.h" | ||
21 | #include "ValueTransformer.h" | ||
22 | #include "openvdb/Types.h" | ||
23 | #include "openvdb/Grid.h" | ||
24 | #include "openvdb/tree/ValueAccessor.h" | ||
25 | #include "openvdb/tree/LeafManager.h" | ||
26 | #include <openvdb/openvdb.h> | ||
27 | #include <openvdb/points/PointDataGrid.h> | ||
28 | |||
29 | #include <tbb/task_arena.h> | ||
30 | #include <tbb/enumerable_thread_specific.h> | ||
31 | #include <tbb/parallel_for.h> | ||
32 | |||
33 | #include <type_traits> | ||
34 | #include <vector> | ||
35 | |||
36 | |||
37 | namespace openvdb { | ||
38 | OPENVDB_USE_VERSION_NAMESPACE | ||
39 | namespace OPENVDB_VERSION_NAME { | ||
40 | namespace tools { | ||
41 | |||
42 | /// @brief Voxel topology of nearest neighbors | ||
43 | /// @details | ||
44 | /// <dl> | ||
45 | /// <dt><b>NN_FACE</b> | ||
46 | /// <dd>face adjacency (6 nearest neighbors, defined as all neighbor | ||
47 | /// voxels connected along one of the primary axes) | ||
48 | /// | ||
49 | /// <dt><b>NN_FACE_EDGE</b> | ||
50 | /// <dd>face and edge adjacency (18 nearest neighbors, defined as all | ||
51 | /// neighbor voxels connected along either one or two of the primary axes) | ||
52 | /// | ||
53 | /// <dt><b>NN_FACE_EDGE_VERTEX</b> | ||
54 | /// <dd>face, edge and vertex adjacency (26 nearest neighbors, defined | ||
55 | /// as all neighbor voxels connected along either one, two or all | ||
56 | /// three of the primary axes) | ||
57 | /// </dl> | ||
58 | enum NearestNeighbors { NN_FACE = 6, NN_FACE_EDGE = 18, NN_FACE_EDGE_VERTEX = 26 }; | ||
59 | |||
60 | /// @brief Different policies when dilating trees with active tiles | ||
61 | /// @details | ||
62 | /// <dl> | ||
63 | /// <dt><b>IGNORE_TILES</b> | ||
64 | /// <dd>Active tiles are ignores. For dilation, only active voxels are | ||
65 | /// dilated. For erosion, active tiles still appear as neighboring | ||
66 | /// activity however will themselves not be eroded. | ||
67 | /// | ||
68 | /// <dt><b>EXPAND_TILES</b> | ||
69 | /// <dd>For dilation and erosion, active tiles are voxelized (expanded), | ||
70 | /// dilated or eroded and left in their voxelized state irrespective of | ||
71 | /// their final state. | ||
72 | /// | ||
73 | /// <dt><b>PRESERVE_TILES</b> | ||
74 | /// <dd>For dilation, active tiles remain unchanged but they still | ||
75 | /// contribute to the dilation as if they were active voxels. For | ||
76 | /// erosion, active tiles are only eroded should the erosion wavefront | ||
77 | /// reach them, otherwise they are left unchanged. Additionally, dense | ||
78 | /// or empty nodes with constant values are pruned. | ||
79 | /// </dl> | ||
80 | enum TilePolicy { IGNORE_TILES, EXPAND_TILES, PRESERVE_TILES }; | ||
81 | |||
82 | /// @brief Topologically dilate all active values (i.e. both voxels | ||
83 | /// and tiles) in a tree using one of three nearest neighbor | ||
84 | /// connectivity patterns. | ||
85 | /// @details If the input is *not* a MaskTree OR if tiles are being | ||
86 | /// preserved, this algorithm will copy the input tree topology onto a | ||
87 | /// MaskTree, performs the dilation on the mask and copies the resulting | ||
88 | /// topology back. This algorithm guarantees topology preservation | ||
89 | /// (non-pruned leaf nodes will persists) EXCEPT for direct MaskTree | ||
90 | /// dilation. MaskTree dilation is optimised for performance and may | ||
91 | /// replace existing leaf nodes i.e. any held leaf node pointers may | ||
92 | /// become invalid. See the Morphology class for more granular control. | ||
93 | /// @note This method is fully multi-threaded and support active tiles, | ||
94 | /// however only the PRESERVE_TILES policy ensures a pruned topology. | ||
95 | /// The values of any voxels are unchanged. | ||
96 | /// | ||
97 | /// @param tree tree or leaf manager to be dilated. The leaf | ||
98 | /// manager will be synchronized with the result. | ||
99 | /// @param iterations number of iterations to apply the dilation | ||
100 | /// @param nn connectivity pattern of the dilation: either | ||
101 | /// face-adjacent (6 nearest neighbors), face- and edge-adjacent | ||
102 | /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 | ||
103 | /// nearest neighbors). | ||
104 | /// @param mode Defined the policy for handling active tiles | ||
105 | /// (see above for details) | ||
106 | /// @param threaded Whether to multi-thread execution | ||
107 | template<typename TreeOrLeafManagerT> | ||
108 | void dilateActiveValues(TreeOrLeafManagerT& tree, | ||
109 | const int iterations = 1, | ||
110 | const NearestNeighbors nn = NN_FACE, | ||
111 | const TilePolicy mode = PRESERVE_TILES, | ||
112 | const bool threaded = true); | ||
113 | |||
114 | /// @brief Topologically erode all active values (i.e. both voxels | ||
115 | /// and tiles) in a tree using one of three nearest neighbor | ||
116 | /// connectivity patterns. | ||
117 | /// @details If tiles are being preserve, this algorithm will copy the input | ||
118 | /// tree topology onto a MaskTree, performs the erosion on the mask and | ||
119 | /// intersects the resulting topology back. This algorithm guarantees | ||
120 | /// topology preservation (non-pruned leaf nodes will persists). See the | ||
121 | /// Morphology class for more granular control. | ||
122 | /// @note This method is fully multi-threaded and support active tiles, | ||
123 | /// however only the PRESERVE_TILES policy ensures a pruned topology. | ||
124 | /// The values of any voxels are unchanged. Erosion by NN_FACE neighbors | ||
125 | /// is usually faster than other neighbor schemes. NN_FACE_EDGE and | ||
126 | /// NN_FACE_EDGE_VERTEX operate at comparable dilation speeds. | ||
127 | /// | ||
128 | /// @param tree tree or leaf manager to be eroded. The leaf | ||
129 | /// manager will be synchronized with the result. | ||
130 | /// @param iterations number of iterations to apply the erosion | ||
131 | /// @param nn connectivity pattern of the erosion: either | ||
132 | /// face-adjacent (6 nearest neighbors), face- and edge-adjacent | ||
133 | /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 | ||
134 | /// nearest neighbors). | ||
135 | /// @param mode Defined the policy for handling active tiles | ||
136 | /// (see above for details) | ||
137 | /// @param threaded Whether to multi-thread execution | ||
138 | template<typename TreeOrLeafManagerT> | ||
139 | void erodeActiveValues(TreeOrLeafManagerT& tree, | ||
140 | const int iterations = 1, | ||
141 | const NearestNeighbors nn = NN_FACE, | ||
142 | const TilePolicy mode = PRESERVE_TILES, | ||
143 | const bool threaded = true); | ||
144 | |||
145 | |||
146 | //////////////////////////////////////// | ||
147 | |||
148 | |||
149 | namespace morphology { | ||
150 | |||
151 | /// @brief Dilation/Erosion operations over a Trees leaf level voxel topology. | ||
152 | template<typename TreeType> | ||
153 |
1/2✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
|
28210 | class Morphology |
154 | { | ||
155 | public: | ||
156 | using LeafType = typename TreeType::LeafNodeType; | ||
157 | using MaskType = typename LeafType::NodeMaskType; | ||
158 | using ValueType = typename TreeType::ValueType; | ||
159 | using MaskTreeT = typename TreeType::template ValueConverter<ValueMask>::Type; | ||
160 | using MaskLeafT = typename MaskTreeT::LeafNodeType; | ||
161 | using AccessorType = tree::ValueAccessor<TreeType>; | ||
162 | |||
163 | 15018 | Morphology(TreeType& tree) | |
164 |
1/2✓ Branch 2 taken 7509 times.
✗ Branch 3 not taken.
|
15018 | : mManagerPtr(new tree::LeafManager<TreeType>(tree)) |
165 | , mManager(*mManagerPtr) | ||
166 | 15018 | , mThreaded(true) {} | |
167 | |||
168 |
5/80✓ Branch 1 taken 1104 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✓ Branch 28 taken 1116 times.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 66 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✓ Branch 81 taken 35 times.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 84 not taken.
✗ Branch 86 not taken.
✗ Branch 87 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
✗ Branch 96 not taken.
✗ Branch 97 not taken.
✗ Branch 98 not taken.
✗ Branch 99 not taken.
✗ Branch 101 not taken.
✗ Branch 102 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 108 not taken.
✗ Branch 109 not taken.
|
2257 | Morphology(tree::LeafManager<TreeType>& tree) |
169 | : mManagerPtr(nullptr) | ||
170 | , mManager(tree) | ||
171 | 20701 | , mThreaded(true) {} | |
172 | |||
173 | /// @brief Return whether this class is using multi-threading. | ||
174 | 38744 | bool getThreaded() const { return mThreaded; } | |
175 | /// @brief Set whether to use multi-threading. | ||
176 | /// @note The grain size is not exposed | ||
177 |
7/38✓ Branch 1 taken 4616 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 13820 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✓ Branch 43 taken 2 times.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
|
18445 | inline void setThreaded(const bool threaded) { mThreaded = threaded; } |
178 | |||
179 | /// @brief Return a const reference to the leaf manager | ||
180 | inline const tree::LeafManager<TreeType>& leafManager() const { return mManager; } | ||
181 | |||
182 | /// @brief Topologically erode all voxels by the provided nearest neighbor | ||
183 | /// scheme and optionally collapse constant leaf nodes | ||
184 | /// @details Inactive Tiles contribute to the erosion but active tiles are | ||
185 | /// not modified. | ||
186 | /// @param iter Number of erosion iterations | ||
187 | /// @param nn Connectivity pattern of the erosion | ||
188 | /// @param prune Whether to collapse constant leaf nodes after the erosion | ||
189 | void erodeVoxels(const size_t iter, | ||
190 | const NearestNeighbors nn, | ||
191 | const bool prune = false); | ||
192 | |||
193 | /// @brief Topologically dilate all voxels by the provided nearest neighbor | ||
194 | /// scheme and optionally collapse constant leaf nodes | ||
195 | /// @details Voxel values are unchanged and only leaf nodes are used to | ||
196 | /// propagate the dilation. | ||
197 | /// @param iter Number of dilation iterations | ||
198 | /// @param nn Connectivity pattern of the dilation | ||
199 | /// @param prune Whether to collapse constant leaf nodes after the dilation | ||
200 | /// @param preserveMaskLeafNodes When dilating mask trees, the default behaviour | ||
201 | /// chooses to steal the mask nodes rather than copy them. Although faster, | ||
202 | /// this means that leaf nodes may be re-allocated. Set this to true if you | ||
203 | /// need the original topology pointers to be preserved. | ||
204 | void dilateVoxels(const size_t iter, | ||
205 | const NearestNeighbors nn, | ||
206 | const bool prune = false, | ||
207 | const bool preserveMaskLeafNodes = false); | ||
208 | |||
209 | |||
210 | /// @brief Copy the current node masks onto the provided vector. The vector | ||
211 | /// is resized if necessary. | ||
212 | /// @param masks The vector of NodeMasks to copy onto | ||
213 |
2/2✓ Branch 0 taken 20980 times.
✓ Branch 1 taken 1347 times.
|
44654 | void copyMasks(std::vector<MaskType>& masks) const |
214 | { | ||
215 |
2/2✓ Branch 0 taken 20980 times.
✓ Branch 1 taken 1347 times.
|
44654 | if (masks.size() < mManager.leafCount()) { |
216 | 41960 | masks.resize(mManager.leafCount()); | |
217 | } | ||
218 | |||
219 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 22327 times.
|
44654 | if (this->getThreaded()) { |
220 | // @note this is marginally faster than using leafRange or foreach | ||
221 | 44654 | tbb::parallel_for(mManager.getRange(), | |
222 | 363768 | [&](const tbb::blocked_range<size_t>& r){ | |
223 |
6/20✓ Branch 0 taken 1518 times.
✓ Branch 1 taken 1485 times.
✓ Branch 2 taken 213694 times.
✓ Branch 3 taken 133977 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 11692 times.
✓ Branch 15 taken 1402 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
363768 | for (size_t idx = r.begin(); idx < r.end(); ++idx) |
224 |
3/20✗ Branch 0 not taken.
✓ Branch 1 taken 1518 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 213694 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 11692 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
226904 | masks[idx] = mManager.leaf(idx).getValueMask(); |
225 | }); | ||
226 | } | ||
227 | else { | ||
228 | ✗ | for (size_t idx = 0; idx < mManager.leafCount(); ++idx) { | |
229 | masks[idx] = mManager.leaf(idx).getValueMask(); | ||
230 | } | ||
231 | } | ||
232 | 44654 | } | |
233 | |||
234 | public: | ||
235 | /// @brief Node Mask dilation/erosion operations for individual leaf nodes on | ||
236 | /// a given tree. The leaf node may optionally belong to a different tree | ||
237 | /// than the provided accessor, which will have the effect of dilating the | ||
238 | /// leaf node mask into a different tree, or eroding the node mask based | ||
239 | /// on corresponding neighbors in a different tree. | ||
240 |
12/116✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 2422 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 44 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4572 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 167 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 13646 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✓ Branch 30 taken 1670 times.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✓ Branch 38 taken 3141 times.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✓ Branch 50 taken 7 times.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✓ Branch 58 taken 3 times.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✓ Branch 66 taken 1 times.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 81 not taken.
✗ Branch 82 not taken.
✗ Branch 83 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 87 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✗ Branch 92 not taken.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
✗ Branch 95 not taken.
✗ Branch 96 not taken.
✗ Branch 97 not taken.
✓ Branch 98 taken 2 times.
✗ Branch 99 not taken.
✗ Branch 100 not taken.
✗ Branch 101 not taken.
✗ Branch 102 not taken.
✗ Branch 103 not taken.
✗ Branch 104 not taken.
✗ Branch 105 not taken.
✗ Branch 106 not taken.
✗ Branch 107 not taken.
✗ Branch 108 not taken.
✗ Branch 109 not taken.
✗ Branch 110 not taken.
✗ Branch 111 not taken.
✗ Branch 112 not taken.
✗ Branch 113 not taken.
✗ Branch 114 not taken.
✗ Branch 115 not taken.
|
25677 | struct NodeMaskOp |
241 | { | ||
242 | static const Int32 DIM = static_cast<Int32>(LeafType::DIM); | ||
243 | static const Int32 LOG2DIM = static_cast<Int32>(LeafType::LOG2DIM); | ||
244 | |||
245 | // Select the storage size based off the dimensions of the leaf node | ||
246 | using Word = typename std::conditional<LOG2DIM == 3, uint8_t, | ||
247 | typename std::conditional<LOG2DIM == 4, uint16_t, | ||
248 | typename std::conditional<LOG2DIM == 5, uint32_t, | ||
249 | typename std::conditional<LOG2DIM == 6, uint64_t, | ||
250 | void>::type>::type>::type>::type; | ||
251 | |||
252 | static_assert(!std::is_same<Word, void>::value, | ||
253 | "Unsupported Node Dimension for node mask dilation/erosion"); | ||
254 | |||
255 | 51354 | NodeMaskOp(AccessorType& accessor, | |
256 | const NearestNeighbors op) | ||
257 | : mOrigin(nullptr) | ||
258 | , mNeighbors(NodeMaskOp::ksize(op), nullptr) | ||
259 | , mAccessor(&accessor) | ||
260 | , mOnTile(true) | ||
261 | , mOffTile(false) | ||
262 | 51354 | , mOp(op) {} | |
263 | |||
264 | /// @brief Dilate a single leaf node by the current spatial scheme | ||
265 | /// stored on the instance of this NodeMaskOp. Neighbor leaf | ||
266 | /// nodes are also updated. | ||
267 | /// @details Unlike erode, dilate is expected to be called in a | ||
268 | /// single threaded context as it will update the node masks | ||
269 | /// of neighboring leaf nodes as well as the provided leaf. | ||
270 | /// @param leaf The leaf to dilate. The leaf's origin and value mask | ||
271 | /// are used to calculate the result of the dilation. | ||
272 | inline void dilate(LeafType& leaf) | ||
273 | { | ||
274 | // copy the mask | ||
275 | const MaskType mask = leaf.getValueMask(); | ||
276 | this->dilate(leaf, mask); | ||
277 | } | ||
278 | |||
279 | /// @brief Dilate a single leaf node by the current spatial scheme | ||
280 | /// stored on the instance of this NodeMaskOp. The provided | ||
281 | /// mask is used in place of the actual leaf's node mask and | ||
282 | /// applied to the leaf afterwards. Neighbor leaf nodes are | ||
283 | /// also updated. | ||
284 | /// @details Unlike erode, dilate is expected to be called in a | ||
285 | /// single threaded context as it will update the node masks | ||
286 | /// of neighboring leaf nodes as well as the provided leaf. | ||
287 | /// @param leaf The leaf to dilate. The leaf's origin is used to | ||
288 | /// calculate the result of the dilation. | ||
289 | /// @param mask The node mask to use in place of the current leaf | ||
290 | /// node mask. | ||
291 | 403940 | inline void dilate(LeafType& leaf, const MaskType& mask) | |
292 | { | ||
293 | 403940 | this->clear(); | |
294 |
3/4✓ Branch 0 taken 117411 times.
✓ Branch 1 taken 54257 times.
✓ Branch 2 taken 30302 times.
✗ Branch 3 not taken.
|
403940 | mNeighbors[0] = &(leaf.getValueMask()); |
295 | this->setOrigin(leaf.origin()); | ||
296 | 403940 | switch (mOp) { | |
297 | 234822 | case NN_FACE_EDGE : { this->dilate18(mask); return; } | |
298 | 108514 | case NN_FACE_EDGE_VERTEX : { this->dilate26(mask); return; } | |
299 | 60604 | case NN_FACE : { this->dilate6(mask); return; } | |
300 | ✗ | default : { | |
301 | ✗ | assert(false && "Unknown op during dilation."); return; | |
302 | } | ||
303 | } | ||
304 | } | ||
305 | |||
306 | /// @brief Erode a single leaf node by the current spatial scheme | ||
307 | /// stored on the instance of this NodeMaskOp. | ||
308 | /// @details Unlike dilate, this method updates the provided mask | ||
309 | /// and does not apply the result to the leaf node. The | ||
310 | /// leaf node is simply used to infer the position in the | ||
311 | /// tree to find it's neighbors. This allows erode to be | ||
312 | /// called from multiple threads | ||
313 | /// @param leaf The leaf to erode. The leaf's origin is used to | ||
314 | /// calculate the result of the erosion. | ||
315 | /// @return The eroded mask | ||
316 | inline MaskType erode(const LeafType& leaf) | ||
317 | { | ||
318 | // copy the mask | ||
319 | MaskType mask = leaf.getValueMask(); | ||
320 | this->erode(leaf, mask); | ||
321 | return mask; | ||
322 | } | ||
323 | |||
324 | /// @brief Erode a single leaf node by the current spatial scheme | ||
325 | /// stored on the instance of this NodeMaskOp. The provided | ||
326 | /// mask is used in place of the actual leaf's node mask and | ||
327 | /// stores the erosion result. | ||
328 | /// @details Unlike dilate, this method updates the provided mask | ||
329 | /// and does not apply the result to the leaf node. The | ||
330 | /// leaf node is simply used to infer the position in the | ||
331 | /// tree to find it's neighbors. | ||
332 | /// @param leaf The leaf to erode. The leaf's origin is used to | ||
333 | /// calculate the result of the erosion. | ||
334 | /// @param mask The node mask to use in place of the current leaf | ||
335 | /// node mask. | ||
336 | 54278 | inline void erode(const LeafType& leaf, MaskType& mask) | |
337 | { | ||
338 | 54278 | this->clear(); | |
339 | // @note leaf mask will not be modified through gather methods | ||
340 |
1/4✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 28086 times.
✗ Branch 3 not taken.
|
54278 | mNeighbors[0] = const_cast<MaskType*>(&leaf.getValueMask()); |
341 | this->setOrigin(leaf.origin()); | ||
342 | 54278 | switch (mOp) { | |
343 | ✗ | case NN_FACE_EDGE : { this->erode18(mask); return; } | |
344 | ✗ | case NN_FACE_EDGE_VERTEX : { this->erode26(mask); return; } | |
345 | 54278 | case NN_FACE : { this->erode6(mask); return; } | |
346 | ✗ | default : { | |
347 | ✗ | assert(false && "Unknown op during erosion."); return; | |
348 | } | ||
349 | } | ||
350 | } | ||
351 | |||
352 | private: | ||
353 | static size_t ksize(const NearestNeighbors op) { | ||
354 |
7/40✗ Branch 0 not taken.
✓ Branch 1 taken 2424 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 7059 times.
✓ Branch 5 taken 4036 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 7334 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 1670 times.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✓ Branch 36 taken 11 times.
✓ Branch 37 taken 3143 times.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
|
25677 | switch (op) { |
355 | case NN_FACE_EDGE : return 19; | ||
356 | 7070 | case NN_FACE_EDGE_VERTEX : return 27; | |
357 | 11273 | case NN_FACE : return 7; | |
358 | ✗ | default : return 7; | |
359 | } | ||
360 | } | ||
361 | |||
362 | void dilate6(const MaskType& mask); | ||
363 | void dilate18(const MaskType& mask); | ||
364 | void dilate26(const MaskType& mask); | ||
365 | void erode6(MaskType& mask); | ||
366 | |||
367 | /// @note Forward API for erosion of 18/26 trees is to use erodeActiveValues | ||
368 | /// which falls back to an inverse dilation | ||
369 | /// @todo It may still be worth investigating more optimal gathering | ||
370 | /// techniques | ||
371 | ✗ | inline void erode18(MaskType&) { OPENVDB_THROW(NotImplementedError, "erode18 is not implemented yet!"); } | |
372 | ✗ | inline void erode26(MaskType&) { OPENVDB_THROW(NotImplementedError, "erode26 is not implemented yet!"); } | |
373 | |||
374 |
6/80✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1894 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 117411 times.
✓ Branch 9 taken 54257 times.
✓ Branch 10 taken 30302 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✓ Branch 30 taken 14088 times.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✓ Branch 38 taken 12104 times.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 77 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
|
230056 | inline void setOrigin(const Coord& origin) { mOrigin = &origin; } |
375 | 171668 | inline const Coord& getOrigin() const { return *mOrigin; } | |
376 | 460112 | inline void clear() { std::fill(mNeighbors.begin(), mNeighbors.end(), nullptr); } | |
377 | |||
378 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 103638127 times.
|
207276254 | inline void scatter(size_t n, int indx) |
379 | { | ||
380 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 103638127 times.
|
207276254 | assert(n < mNeighbors.size()); |
381 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 103638127 times.
|
207276254 | assert(mNeighbors[n]); |
382 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 103638127 times.
|
207276254 | mNeighbors[n]->template getWord<Word>(indx) |= mWord; |
383 | |||
384 | } | ||
385 | template<int DX, int DY, int DZ> | ||
386 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23686879 times.
|
47373758 | inline void scatter(size_t n, int indx) |
387 | { | ||
388 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23686879 times.
|
47373758 | assert(n < mNeighbors.size()); |
389 |
2/2✓ Branch 0 taken 2078239 times.
✓ Branch 1 taken 21608640 times.
|
47373758 | if (!mNeighbors[n]) { |
390 | 4156478 | mNeighbors[n] = this->getNeighbor<DX,DY,DZ,true>(); | |
391 | } | ||
392 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23686879 times.
|
47373758 | assert(mNeighbors[n]); |
393 | 47373758 | this->scatter(n, indx - (DIM - 1)*(DY + DX*DIM)); | |
394 | } | ||
395 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8592216 times.
|
17030478 | inline Word gather(size_t n, int indx) |
396 | { | ||
397 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8592216 times.
|
17030478 | assert(n < mNeighbors.size()); |
398 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8592216 times.
|
17030478 | return mNeighbors[n]->template getWord<Word>(indx); |
399 | } | ||
400 | template<int DX, int DY, int DZ> | ||
401 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3573406 times.
|
7146812 | inline Word gather(size_t n, int indx) |
402 | { | ||
403 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3573406 times.
|
7146812 | assert(n < mNeighbors.size()); |
404 |
2/2✓ Branch 0 taken 153042 times.
✓ Branch 1 taken 3420364 times.
|
7146812 | if (!mNeighbors[n]) { |
405 | 306084 | mNeighbors[n] = this->getNeighbor<DX,DY,DZ,false>(); | |
406 | } | ||
407 | 7146812 | return this->gather(n, indx - (DIM -1)*(DY + DX*DIM)); | |
408 | } | ||
409 | |||
410 | void scatterFacesXY(int x, int y, int i1, int n, int i2); | ||
411 | void scatterEdgesXY(int x, int y, int i1, int n, int i2); | ||
412 | Word gatherFacesXY(int x, int y, int i1, int n, int i2); | ||
413 | /// @note Currently unused | ||
414 | Word gatherEdgesXY(int x, int y, int i1, int n, int i2); | ||
415 | |||
416 | template<int DX, int DY, int DZ, bool Create> | ||
417 | 4462562 | inline MaskType* getNeighbor() | |
418 | { | ||
419 | 4462562 | const Coord xyz = mOrigin->offsetBy(DX*DIM, DY*DIM, DZ*DIM); | |
420 | 4462562 | auto* leaf = mAccessor->probeLeaf(xyz); | |
421 |
2/2✓ Branch 0 taken 1800408 times.
✓ Branch 1 taken 430873 times.
|
4462562 | if (leaf) return &(leaf->getValueMask()); |
422 |
2/2✓ Branch 1 taken 299472 times.
✓ Branch 2 taken 131401 times.
|
861746 | if (mAccessor->isValueOn(xyz)) return &mOnTile; |
423 | 23000 | if (!Create) return &mOffTile; | |
424 | 239802 | leaf = mAccessor->touchLeaf(xyz); | |
425 | 239802 | return &(leaf->getValueMask()); | |
426 | } | ||
427 | |||
428 | private: | ||
429 | const Coord* mOrigin; | ||
430 | std::vector<MaskType*> mNeighbors; | ||
431 | AccessorType* const mAccessor; | ||
432 | Word mWord; | ||
433 | MaskType mOnTile, mOffTile; | ||
434 | const NearestNeighbors mOp; | ||
435 | };// NodeMaskOp | ||
436 | |||
437 | private: | ||
438 | std::unique_ptr<tree::LeafManager<TreeType>> mManagerPtr; | ||
439 | tree::LeafManager<TreeType>& mManager; | ||
440 | bool mThreaded; | ||
441 | };// Morphology | ||
442 | |||
443 | |||
444 | template <typename TreeT> | ||
445 | typename std::enable_if<std::is_same<TreeT, typename TreeT::template ValueConverter<ValueMask>::Type>::value, | ||
446 | typename TreeT::template ValueConverter<ValueMask>::Type*>::type | ||
447 | 6166 | getMaskTree(TreeT& tree) { return &tree; } | |
448 | |||
449 | template <typename TreeT> | ||
450 | typename std::enable_if<!std::is_same<TreeT, typename TreeT::template ValueConverter<ValueMask>::Type>::value, | ||
451 | typename TreeT::template ValueConverter<ValueMask>::Type*>::type | ||
452 | 2464 | getMaskTree(TreeT&) { return nullptr; } | |
453 | |||
454 | |||
455 | template <typename TreeType> | ||
456 | 5067 | void Morphology<TreeType>::erodeVoxels(const size_t iter, | |
457 | const NearestNeighbors nn, | ||
458 | const bool prune) | ||
459 | { | ||
460 |
1/2✓ Branch 0 taken 3364 times.
✗ Branch 1 not taken.
|
5067 | if (iter == 0) return; |
461 |
2/2✓ Branch 0 taken 3355 times.
✓ Branch 1 taken 9 times.
|
5067 | const size_t leafCount = mManager.leafCount(); |
462 |
2/2✓ Branch 0 taken 3355 times.
✓ Branch 1 taken 9 times.
|
5067 | if (leafCount == 0) return; |
463 | auto& tree = mManager.tree(); | ||
464 | |||
465 | // If the nearest neighbor mode is not FACE, fall back to an | ||
466 | // inverse dilation scheme which executes over a mask topology | ||
467 |
2/2✓ Branch 0 taken 2220 times.
✓ Branch 1 taken 1135 times.
|
5054 | if (nn != NN_FACE) { |
468 | // This method 1) dilates the input topology, 2) reverse the node masks, | ||
469 | // 3) performs a final dilation and 4) subtracts the result from the original | ||
470 | // topology. A cache of the original leaf pointers is required which tracks | ||
471 | // the original leaf nodes in a mask topology. These will need their | ||
472 | // masks updated in the original tree. The first dilation may create new leaf | ||
473 | // nodes in two instances. The first is where no topology existed before. The | ||
474 | // second is where an active tile overlaps with dilated topology. These | ||
475 | // tiles will be expanded to a dense leaf nodes by topologyUnion. We need | ||
476 | // to make sure these tiles are properly turned off. | ||
477 | |||
478 | 6672 | MaskTreeT mask(tree, false, TopologyCopy()); | |
479 | |||
480 | // Create a new morphology class to perform dilation over the mask | ||
481 |
1/2✓ Branch 1 taken 2220 times.
✗ Branch 2 not taken.
|
6672 | tree::LeafManager<MaskTreeT> manager(mask); |
482 | Morphology<MaskTreeT> m(manager); | ||
483 | m.setThreaded(this->getThreaded()); | ||
484 | |||
485 | // perform a single dilation using the current scheme. Necessary to | ||
486 | // create edge leaf nodes and compute the active wavefront. Note that | ||
487 | // the cached array pointers will continue to be valid | ||
488 |
1/2✓ Branch 1 taken 2220 times.
✗ Branch 2 not taken.
|
3336 | m.dilateVoxels(1, nn, /*prune=*/false); |
489 | |||
490 | // compute the wavefront. If the leaf previously existed, compute the | ||
491 | // xor activity result which is guaranteed to be equal to but slightly | ||
492 | // faster than a subtraction | ||
493 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5394 times.
|
13544 | auto computeWavefront = [&](const size_t idx) { |
494 | auto& leaf = manager.leaf(idx); | ||
495 | auto& nodemask = leaf.getValueMask(); | ||
496 |
4/20✓ Branch 0 taken 3756 times.
✓ Branch 1 taken 1638 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 4072 times.
✓ Branch 19 taken 1846 times.
|
11312 | if (const auto* original = tree.probeConstLeaf(leaf.origin())) { |
497 | nodemask ^= original->getValueMask(); | ||
498 | } | ||
499 | else { | ||
500 | // should never have a dense leaf if it didn't exist in the | ||
501 | // original tree (it was previous possible when dilateVoxels() | ||
502 | // called topologyUnion without the preservation of active | ||
503 | // tiles) | ||
504 |
2/20✗ Branch 0 not taken.
✓ Branch 1 taken 1638 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 1846 times.
|
3484 | assert(!nodemask.isOn()); |
505 | } | ||
506 | }; | ||
507 | |||
508 |
1/2✓ Branch 0 taken 2220 times.
✗ Branch 1 not taken.
|
3336 | if (this->getThreaded()) { |
509 |
1/2✓ Branch 1 taken 2220 times.
✗ Branch 2 not taken.
|
3336 | tbb::parallel_for(manager.getRange(), |
510 | 11312 | [&](const tbb::blocked_range<size_t>& r){ | |
511 |
4/40✓ Branch 0 taken 8271 times.
✓ Branch 1 taken 8244 times.
✓ Branch 2 taken 3041 times.
✓ Branch 3 taken 2774 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
|
22330 | for (size_t idx = r.begin(); idx < r.end(); ++idx) { |
512 | 11312 | computeWavefront(idx); | |
513 | } | ||
514 | }); | ||
515 | } | ||
516 | else { | ||
517 | ✗ | for (size_t idx = 0; idx < manager.leafCount(); ++idx) { | |
518 | ✗ | computeWavefront(idx); | |
519 | } | ||
520 | } | ||
521 | |||
522 | // perform the inverse dilation | ||
523 |
1/2✓ Branch 1 taken 2220 times.
✗ Branch 2 not taken.
|
3336 | m.dilateVoxels(iter, nn, /*prune=*/false); |
524 | |||
525 | // subtract the inverse dilation from the original node masks | ||
526 | 10060 | auto subtractTopology = [&](const size_t idx) { | |
527 |
2/20✗ Branch 0 not taken.
✓ Branch 1 taken 3756 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 4072 times.
|
7828 | auto& leaf = mManager.leaf(idx); |
528 | const auto* maskleaf = mask.probeConstLeaf(leaf.origin()); | ||
529 |
2/20✗ Branch 0 not taken.
✓ Branch 1 taken 3756 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 4072 times.
|
7828 | assert(maskleaf); |
530 | leaf.getValueMask() -= maskleaf->getValueMask(); | ||
531 | }; | ||
532 | |||
533 |
1/2✓ Branch 0 taken 2220 times.
✗ Branch 1 not taken.
|
3336 | if (this->getThreaded()) { |
534 |
1/2✓ Branch 1 taken 2220 times.
✗ Branch 2 not taken.
|
3336 | tbb::parallel_for(mManager.getRange(), |
535 | 7828 | [&](const tbb::blocked_range<size_t>& r){ | |
536 |
4/40✓ Branch 0 taken 5281 times.
✓ Branch 1 taken 5256 times.
✓ Branch 2 taken 2547 times.
✓ Branch 3 taken 2444 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
|
15528 | for (size_t idx = r.begin(); idx < r.end(); ++idx) { |
537 | 7828 | subtractTopology(idx); | |
538 | } | ||
539 | }); | ||
540 | } | ||
541 | else { | ||
542 | ✗ | for (size_t idx = 0; idx < leafCount; ++idx) { | |
543 | ✗ | subtractTopology(idx); | |
544 | } | ||
545 | } | ||
546 | } | ||
547 | else { | ||
548 | // NN_FACE erosion scheme | ||
549 | |||
550 | // Save the value masks of all leaf nodes. | ||
551 | std::vector<MaskType> nodeMasks; | ||
552 |
1/2✓ Branch 1 taken 1135 times.
✗ Branch 2 not taken.
|
1718 | this->copyMasks(nodeMasks); |
553 | |||
554 |
1/2✓ Branch 0 taken 1135 times.
✗ Branch 1 not taken.
|
1718 | if (this->getThreaded()) { |
555 | 1718 | const auto range = mManager.getRange(); | |
556 |
2/2✓ Branch 0 taken 1256 times.
✓ Branch 1 taken 1135 times.
|
3620 | for (size_t i = 0; i < iter; ++i) { |
557 | // For each leaf, in parallel, gather neighboring off values | ||
558 | // and update the cached value mask | ||
559 |
1/2✓ Branch 1 taken 1256 times.
✗ Branch 2 not taken.
|
1902 | tbb::parallel_for(range, |
560 |
1/2✓ Branch 2 taken 1894 times.
✗ Branch 3 not taken.
|
42552 | [&](const tbb::blocked_range<size_t>& r) { |
561 | AccessorType accessor(tree); | ||
562 |
3/20✓ Branch 1 taken 2422 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 22 taken 1670 times.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✓ Branch 28 taken 3141 times.
✗ Branch 29 not taken.
|
7233 | NodeMaskOp cache(accessor, nn); |
563 |
6/20✓ Branch 0 taken 2455 times.
✓ Branch 1 taken 2422 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 14156 times.
✓ Branch 15 taken 1670 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 12665 times.
✓ Branch 19 taken 3141 times.
|
36509 | for (size_t idx = r.begin(); idx < r.end(); ++idx) { |
564 |
3/20✗ Branch 0 not taken.
✓ Branch 1 taken 2455 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 14156 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 12665 times.
|
29276 | const auto& leaf = mManager.leaf(idx); |
565 |
6/20✓ Branch 0 taken 561 times.
✓ Branch 1 taken 1894 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 68 times.
✓ Branch 15 taken 14088 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 561 times.
✓ Branch 19 taken 12104 times.
|
29276 | if (leaf.isEmpty()) continue; |
566 | // original bit-mask of current leaf node | ||
567 | MaskType& newMask = nodeMasks[idx]; | ||
568 |
3/20✓ Branch 1 taken 1894 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 22 taken 14088 times.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✓ Branch 28 taken 12104 times.
✗ Branch 29 not taken.
|
28086 | cache.erode(leaf, newMask); |
569 | } | ||
570 | }); | ||
571 | |||
572 | // update the masks after all nodes have been eroded | ||
573 |
1/2✓ Branch 1 taken 1256 times.
✗ Branch 2 not taken.
|
1902 | tbb::parallel_for(range, |
574 | 36684 | [&](const tbb::blocked_range<size_t>& r){ | |
575 |
6/20✓ Branch 0 taken 2455 times.
✓ Branch 1 taken 2422 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 14156 times.
✓ Branch 15 taken 1826 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 12665 times.
✓ Branch 19 taken 3160 times.
|
36684 | for (size_t idx = r.begin(); idx < r.end(); ++idx) |
576 |
3/20✗ Branch 0 not taken.
✓ Branch 1 taken 2455 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 15 taken 14156 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 12665 times.
|
29276 | mManager.leaf(idx).setValueMask(nodeMasks[idx]); |
577 | }); | ||
578 | } | ||
579 | } | ||
580 | else { | ||
581 | AccessorType accessor(tree); | ||
582 | ✗ | NodeMaskOp cache(accessor, nn); | |
583 | ✗ | for (size_t i = 0; i < iter; ++i) { | |
584 | // For each leaf, in parallel, gather neighboring off values | ||
585 | // and update the cached value mask | ||
586 | ✗ | for (size_t idx = 0; idx < leafCount; ++idx) { | |
587 | ✗ | const auto& leaf = mManager.leaf(idx); | |
588 | ✗ | if (leaf.isEmpty()) continue; | |
589 | // original bit-mask of current leaf node | ||
590 | MaskType& newMask = nodeMasks[idx]; | ||
591 | ✗ | cache.erode(leaf, newMask); | |
592 | } | ||
593 | |||
594 | ✗ | for (size_t idx = 0; idx < leafCount; ++idx) { | |
595 | ✗ | mManager.leaf(idx).setValueMask(nodeMasks[idx]); | |
596 | } | ||
597 | } | ||
598 | } | ||
599 | } | ||
600 | |||
601 | // if prune, replace any inactive nodes | ||
602 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3355 times.
|
5054 | if (prune) { |
603 | ✗ | tools::prune(mManager.tree(), | |
604 | zeroVal<typename TreeType::ValueType>(), | ||
605 | this->getThreaded()); | ||
606 | ✗ | mManager.rebuild(!this->getThreaded()); | |
607 | } | ||
608 | } | ||
609 | |||
610 | template <typename TreeType> | ||
611 | 17244 | void Morphology<TreeType>::dilateVoxels(const size_t iter, | |
612 | const NearestNeighbors nn, | ||
613 | const bool prune, | ||
614 | const bool preserveMaskLeafNodes) | ||
615 | { | ||
616 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8622 times.
|
17244 | if (iter == 0) return; |
617 | |||
618 | const bool threaded = this->getThreaded(); | ||
619 | |||
620 | // Actual dilation op. main implementation is single threaded. Note that this | ||
621 | // is templated (auto-ed) as the threaded implemenation may call this with a | ||
622 | // different value type to the source morphology class | ||
623 | // @note GCC 6.4.0 crashes trying to compile this lambda with [&] capture | ||
624 | 35688 | auto dilate = [iter, nn, threaded](auto& manager, const bool collapse) { | |
625 | |||
626 | using LeafManagerT = typename std::decay<decltype(manager)>::type; | ||
627 | using TreeT = typename LeafManagerT::TreeType; | ||
628 | using ValueT = typename TreeT::ValueType; | ||
629 | using LeafT = typename TreeT::LeafNodeType; | ||
630 | |||
631 | // this is only used for the impl of copyMasks | ||
632 | Morphology<TreeT> m(manager); | ||
633 | m.setThreaded(threaded); | ||
634 | |||
635 | TreeT& tree = manager.tree(); | ||
636 | tree::ValueAccessor<TreeT> accessor(tree); | ||
637 | |||
638 | // build cache objects | ||
639 |
6/38✓ Branch 1 taken 4616 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 13820 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✓ Branch 43 taken 2 times.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
|
18444 | typename Morphology<TreeT>::NodeMaskOp cache(accessor, nn); |
640 | std::vector<MaskType> nodeMasks; | ||
641 | 211 | std::vector<std::unique_ptr<LeafT>> nodes; | |
642 | const ValueT& bg = tree.background(); | ||
643 | 18444 | const bool steal = iter > 1; | |
644 | |||
645 |
11/38✓ Branch 0 taken 5097 times.
✓ Branch 1 taken 4616 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 16089 times.
✓ Branch 5 taken 13820 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1 times.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✓ Branch 28 taken 2 times.
✓ Branch 29 taken 2 times.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
39636 | for (size_t i = 0; i < iter; ++i) { |
646 |
10/76✓ Branch 0 taken 481 times.
✓ Branch 1 taken 4616 times.
✓ Branch 3 taken 481 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 2269 times.
✓ Branch 11 taken 13820 times.
✓ Branch 13 taken 2269 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 3 times.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✓ Branch 31 taken 1 times.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✓ Branch 71 taken 2 times.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
✗ Branch 80 not taken.
✗ Branch 81 not taken.
✗ Branch 83 not taken.
✗ Branch 84 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
✗ Branch 88 not taken.
✗ Branch 89 not taken.
✗ Branch 90 not taken.
✗ Branch 91 not taken.
✗ Branch 93 not taken.
✗ Branch 94 not taken.
|
21194 | if (i > 0) manager.rebuild(!threaded); |
647 | // If the leaf count is zero, we can stop dilation | ||
648 | const size_t leafCount = manager.leafCount(); | ||
649 |
6/38✓ Branch 0 taken 5097 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 6 taken 16089 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✓ Branch 42 taken 2 times.
✗ Branch 43 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
|
39425 | if (leafCount == 0) return; |
650 | |||
651 | // Copy the masks. This only resizes if necessary. As we're stealing/replacing | ||
652 | // dense nodes, it's possible we don't need to re-allocate the cache. | ||
653 |
5/38✓ Branch 1 taken 5097 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 7 taken 16089 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✓ Branch 43 taken 2 times.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
|
21192 | m.copyMasks(nodeMasks); |
654 | |||
655 | // For each node, dilate the mask into itself and neighboring leaf nodes. | ||
656 | // If the node was originally dense (all active), steal/replace it so | ||
657 | // subsequent iterations are faster | ||
658 |
5/38✓ Branch 1 taken 5097 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 9 taken 16089 times.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✓ Branch 17 taken 3 times.
✗ Branch 18 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✓ Branch 57 taken 2 times.
✗ Branch 58 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✗ Branch 69 not taken.
✗ Branch 70 not taken.
✗ Branch 73 not taken.
✗ Branch 74 not taken.
|
223162 | manager.foreach([&](LeafT& leaf, const size_t idx) { |
659 | // original bit-mask of current leaf node | ||
660 | const MaskType& oldMask = nodeMasks[idx]; | ||
661 | const bool dense = oldMask.isOn(); | ||
662 | 201970 | cache.dilate(leaf, oldMask); | |
663 |
7/38✓ Branch 0 taken 1109 times.
✓ Branch 1 taken 32811 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8167 times.
✓ Branch 5 taken 159877 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 2 times.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
201970 | if (!dense) return; |
664 | // This node does not need to be visited again - replace or steal | ||
665 |
3/38✗ Branch 0 not taken.
✓ Branch 1 taken 1109 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 5736 times.
✓ Branch 5 taken 2431 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
9276 | if (collapse) { |
666 | // if collapse, replace this dense leaf with an active background tile | ||
667 | 5736 | accessor.addTile(1, leaf.origin(), bg, true); | |
668 | } | ||
669 |
4/38✓ Branch 0 taken 1021 times.
✓ Branch 1 taken 88 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2153 times.
✓ Branch 5 taken 278 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
3540 | else if (steal) { |
670 | // otherwise, temporarily steal this node | ||
671 | 3174 | nodes.emplace_back( | |
672 | tree.template stealNode<LeafT>(leaf.origin(), | ||
673 | 6348 | zeroVal<ValueT>(), true)); | |
674 | } | ||
675 | }, false); | ||
676 | } | ||
677 | |||
678 |
7/38✓ Branch 0 taken 44 times.
✓ Branch 1 taken 4572 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 167 times.
✓ Branch 5 taken 13653 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 2 times.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
18442 | if (nodes.empty()) return; |
679 | // Add back all dense nodes | ||
680 |
4/38✓ Branch 0 taken 1021 times.
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2153 times.
✓ Branch 5 taken 167 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
|
3385 | for (auto& node : nodes) { |
681 |
2/38✓ Branch 1 taken 1021 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 7 taken 2153 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
|
3174 | accessor.addLeaf(node.release()); |
682 | } | ||
683 | }; | ||
684 | |||
685 | // | ||
686 | |||
687 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8620 times.
|
17244 | if (!threaded) { |
688 | // single threaded dilation. If it's a mask tree we can collapse | ||
689 | // nodes during the dilation, otherwise we must call prune afterwards | ||
690 | constexpr bool isMask = std::is_same<TreeType, MaskTreeT>::value; | ||
691 | 4 | dilate(mManager, isMask && prune); | |
692 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
4 | if (!isMask && prune) { |
693 | ✗ | tools::prune(mManager.tree(), | |
694 | zeroVal<typename TreeType::ValueType>(), | ||
695 | threaded); | ||
696 | } | ||
697 | } | ||
698 | else { | ||
699 | // multi-threaded dilation | ||
700 | |||
701 | // Steal or create mask nodes that represent the current leaf nodes. | ||
702 | // If the input is a mask tree, optionally re-allocate the nodes if | ||
703 | // preserveMaskLeafNodes is true. This ensures that leaf node | ||
704 | // pointers are not changed in the source tree. Stealing the mask | ||
705 | // nodes is significantly faster as it also avoids a post union. | ||
706 | std::vector<MaskLeafT*> array; | ||
707 |
2/2✓ Branch 0 taken 2454 times.
✓ Branch 1 taken 6166 times.
|
17240 | MaskTreeT* mask = getMaskTree(mManager.tree()); |
708 | |||
709 |
2/2✓ Branch 0 taken 2454 times.
✓ Branch 1 taken 6166 times.
|
17240 | if (!mask) { |
710 | 9816 | MaskTreeT topology; | |
711 |
1/2✓ Branch 1 taken 2454 times.
✗ Branch 2 not taken.
|
4908 | topology.topologyUnion(mManager.tree()); |
712 |
1/2✓ Branch 1 taken 2454 times.
✗ Branch 2 not taken.
|
4908 | array.reserve(mManager.leafCount()); |
713 | topology.stealNodes(array); | ||
714 | } | ||
715 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6165 times.
|
12332 | else if (preserveMaskLeafNodes) { |
716 | mask = nullptr; // act as if theres no mask tree | ||
717 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | array.resize(mManager.leafCount()); |
718 |
1/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
2 | tbb::parallel_for(mManager.getRange(), |
719 | 40 | [&](const tbb::blocked_range<size_t>& r){ | |
720 |
2/20✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 20 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
40 | for (size_t idx = r.begin(); idx < r.end(); ++idx) { |
721 |
1/20✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
|
40 | array[idx] = new MaskLeafT(mManager.leaf(idx)); |
722 | } | ||
723 | }); | ||
724 | } | ||
725 | else { | ||
726 |
1/2✓ Branch 1 taken 6165 times.
✗ Branch 2 not taken.
|
12330 | array.reserve(mManager.leafCount()); |
727 | mask->stealNodes(array); | ||
728 | } | ||
729 | |||
730 | // @note this grain size is used for optimal threading | ||
731 |
2/2✓ Branch 0 taken 550 times.
✓ Branch 1 taken 8070 times.
|
17240 | const size_t numThreads = size_t(tbb::this_task_arena::max_concurrency()); |
732 |
2/2✓ Branch 0 taken 550 times.
✓ Branch 1 taken 8070 times.
|
17240 | const size_t subTreeSize = math::Max(size_t(1), array.size()/(2*numThreads)); |
733 | |||
734 | // perform recursive dilation to sub trees | ||
735 |
1/2✓ Branch 1 taken 8620 times.
✗ Branch 2 not taken.
|
34480 | tbb::enumerable_thread_specific<std::unique_ptr<MaskTreeT>> pool; |
736 | MaskLeafT** start = array.data(); | ||
737 |
1/2✓ Branch 1 taken 8620 times.
✗ Branch 2 not taken.
|
17240 | tbb::parallel_for(tbb::blocked_range<MaskLeafT**>(start, start + array.size(), subTreeSize), |
738 | 36884 | [&](const tbb::blocked_range<MaskLeafT**>& range) { | |
739 | 18442 | std::unique_ptr<MaskTreeT> mask(new MaskTreeT); | |
740 |
15/40✓ Branch 0 taken 10888 times.
✓ Branch 1 taken 4616 times.
✓ Branch 3 taken 10888 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 27368 times.
✓ Branch 6 taken 13820 times.
✓ Branch 8 taken 27368 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 3 times.
✓ Branch 11 taken 3 times.
✓ Branch 13 taken 3 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 1 times.
✓ Branch 16 taken 1 times.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✓ Branch 35 taken 2 times.
✓ Branch 36 taken 2 times.
✓ Branch 38 taken 2 times.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
|
56704 | for (MaskLeafT** it = range.begin(); it != range.end(); ++it) mask->addLeaf(*it); |
741 |
5/20✓ Branch 1 taken 4616 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 13820 times.
✗ Branch 7 not taken.
✓ Branch 11 taken 3 times.
✗ Branch 12 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✓ Branch 36 taken 2 times.
✗ Branch 37 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
|
36884 | tree::LeafManager<MaskTreeT> manager(*mask, range.begin(), range.end()); |
742 |
5/20✓ Branch 1 taken 4616 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13820 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 22 taken 2 times.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
|
18442 | dilate(manager, prune); |
743 | auto& subtree = pool.local(); | ||
744 |
7/20✓ Branch 0 taken 3189 times.
✓ Branch 1 taken 1427 times.
✓ Branch 2 taken 8924 times.
✓ Branch 3 taken 4896 times.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 2 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
18442 | if (!subtree) subtree = std::move(mask); |
745 |
2/20✓ Branch 1 taken 1427 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4896 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
|
6323 | else subtree->merge(*mask, MERGE_ACTIVE_STATES); |
746 | }); | ||
747 | |||
748 |
2/2✓ Branch 0 taken 8614 times.
✓ Branch 1 taken 6 times.
|
17240 | if (!pool.empty()) { |
749 | auto piter = pool.begin(); | ||
750 |
2/2✓ Branch 0 taken 2452 times.
✓ Branch 1 taken 6162 times.
|
17228 | MaskTreeT& subtree = mask ? *mask : **piter++; |
751 |
3/4✓ Branch 0 taken 9667 times.
✓ Branch 1 taken 8614 times.
✓ Branch 3 taken 9667 times.
✗ Branch 4 not taken.
|
55896 | for (; piter != pool.end(); ++piter) subtree.merge(**piter); |
752 | // prune, ensures partially merged nodes that may have become | ||
753 | // dense are converted to tiles | ||
754 |
3/4✓ Branch 0 taken 36 times.
✓ Branch 1 taken 8578 times.
✓ Branch 3 taken 36 times.
✗ Branch 4 not taken.
|
17228 | if (prune) tools::prune(subtree, zeroVal<typename MaskTreeT::ValueType>(), threaded); |
755 | // copy final topology onto dest. If mask exists, then this | ||
756 | // has already been handled by the above subtree merges | ||
757 |
3/4✓ Branch 0 taken 2452 times.
✓ Branch 1 taken 6162 times.
✓ Branch 3 taken 2452 times.
✗ Branch 4 not taken.
|
17228 | if (!mask) mManager.tree().topologyUnion(subtree, /*preserve-active-tiles*/true); |
758 | } | ||
759 | } | ||
760 | |||
761 | // sync | ||
762 | 17244 | mManager.rebuild(!threaded); | |
763 | } | ||
764 | |||
765 | |||
766 | template <typename TreeType> | ||
767 | inline void | ||
768 | 54278 | Morphology<TreeType>::NodeMaskOp::erode6(MaskType& mask) | |
769 | { | ||
770 |
2/2✓ Branch 0 taken 224688 times.
✓ Branch 1 taken 28086 times.
|
488502 | for (int x = 0; x < DIM; ++x) { |
771 |
2/2✓ Branch 0 taken 1797504 times.
✓ Branch 1 taken 224688 times.
|
3908016 | for (int y = 0, n = (x << LOG2DIM); y < DIM; ++y, ++n) { |
772 | // Extract the portion of the original mask that corresponds to a row in z. | ||
773 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 1797504 times.
✓ Branch 2 taken 1432036 times.
✓ Branch 3 taken 365468 times.
|
3473792 | if (Word& w = mask.template getWord<Word>(n)) { |
774 | // erode in two z directions (this is first since it uses the original w) | ||
775 | 2838413 | w = Word(w & | |
776 | 2838413 | (Word(w<<1 | (this->template gather<0,0,-1>(1, n)>>(DIM-1))) & | |
777 | 2838413 | Word(w>>1 | (this->template gather<0,0, 1>(2, n)<<(DIM-1))))); | |
778 | 2838413 | w = Word(w & this->gatherFacesXY(x, y, 0, n, 3)); | |
779 | } | ||
780 | }// loop over y | ||
781 | }//loop over x | ||
782 | 54278 | } | |
783 | |||
784 | template <typename TreeType> | ||
785 | inline void | ||
786 | 60604 | Morphology<TreeType>::NodeMaskOp::dilate6(const MaskType& mask) | |
787 | { | ||
788 |
2/2✓ Branch 0 taken 242416 times.
✓ Branch 1 taken 30302 times.
|
545436 | for (int x = 0; x < DIM; ++x ) { |
789 | 4363488 | for (int y = 0, n = (x << LOG2DIM); | |
790 |
2/2✓ Branch 0 taken 1939328 times.
✓ Branch 1 taken 242416 times.
|
4363488 | y < DIM; ++y, ++n) { |
791 | // Extract the portion of the original mask that corresponds to a row in z. | ||
792 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 1939328 times.
✓ Branch 2 taken 1040830 times.
✓ Branch 3 taken 898498 times.
|
3878656 | if (const Word w = mask.template getWord<Word>(n)) { |
793 | // Dilate the current leaf in the +z and -z direction | ||
794 | 2081660 | this->mWord = Word(w | (w>>1) | (w<<1)); | |
795 | 2081660 | this->scatter(0, n); | |
796 | // Dilate into neighbor leaf in the -z direction | ||
797 |
2/2✓ Branch 0 taken 783723 times.
✓ Branch 1 taken 257107 times.
|
2081660 | if ( (this->mWord = Word(w<<(DIM-1))) ) { |
798 | 1567446 | this->template scatter< 0, 0,-1>(1, n); | |
799 | } | ||
800 | // Dilate into neighbor leaf in the +z direction | ||
801 |
2/2✓ Branch 0 taken 663917 times.
✓ Branch 1 taken 376913 times.
|
2081660 | if ( (this->mWord = Word(w>>(DIM-1))) ) { |
802 | 1327834 | this->template scatter< 0, 0, 1>(2, n); | |
803 | } | ||
804 | // Dilate in the xy-face directions relative to the center leaf | ||
805 | 2081660 | this->mWord = w; | |
806 | 2081660 | this->scatterFacesXY(x, y, 0, n, 3); | |
807 | } | ||
808 | }// loop over y | ||
809 | }//loop over x | ||
810 | } | ||
811 | |||
812 | template <typename TreeType> | ||
813 | inline void | ||
814 | 234822 | Morphology<TreeType>::NodeMaskOp::dilate18(const MaskType& mask) | |
815 | { | ||
816 | //origins of neighbor leaf nodes in the -z and +z directions | ||
817 | 234822 | const Coord origin = this->getOrigin(); | |
818 | 234822 | const Coord orig_mz = origin.offsetBy(0, 0, -DIM); | |
819 | 234822 | const Coord orig_pz = origin.offsetBy(0, 0, DIM); | |
820 |
2/2✓ Branch 0 taken 939288 times.
✓ Branch 1 taken 117411 times.
|
2113398 | for (int x = 0; x < DIM; ++x ) { |
821 |
2/2✓ Branch 0 taken 7514304 times.
✓ Branch 1 taken 939288 times.
|
16907184 | for (int y = 0, n = (x << LOG2DIM); y < DIM; ++y, ++n) { |
822 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 7514304 times.
✓ Branch 2 taken 4096229 times.
✓ Branch 3 taken 3418075 times.
|
15028608 | if (const Word w = mask.template getWord<Word>(n)) { |
823 | { | ||
824 | 8192458 | this->mWord = Word(w | (w>>1) | (w<<1)); | |
825 | this->setOrigin(origin); | ||
826 | 8192458 | this->scatter(0, n); | |
827 | 8192458 | this->scatterFacesXY(x, y, 0, n, 3); | |
828 | 8192458 | this->mWord = w; | |
829 | 8192458 | this->scatterEdgesXY(x, y, 0, n, 3); | |
830 | } | ||
831 |
2/2✓ Branch 0 taken 3011004 times.
✓ Branch 1 taken 1085225 times.
|
8192458 | if ( (this->mWord = Word(w<<(DIM-1))) ) { |
832 | this->setOrigin(origin); | ||
833 | 6022008 | this->template scatter< 0, 0,-1>(1, n); | |
834 | this->setOrigin(orig_mz); | ||
835 | 6022008 | this->scatterFacesXY(x, y, 1, n, 11); | |
836 | } | ||
837 |
2/2✓ Branch 0 taken 3011682 times.
✓ Branch 1 taken 1084547 times.
|
8192458 | if ( (this->mWord = Word(w>>(DIM-1))) ) { |
838 | this->setOrigin(origin); | ||
839 | 6023364 | this->template scatter< 0, 0, 1>(2, n); | |
840 | this->setOrigin(orig_pz); | ||
841 | 6023364 | this->scatterFacesXY(x, y, 2, n, 15); | |
842 | } | ||
843 | } | ||
844 | }// loop over y | ||
845 | }//loop over x | ||
846 | } | ||
847 | |||
848 | |||
849 | template <typename TreeType> | ||
850 | inline void | ||
851 | 108514 | Morphology<TreeType>::NodeMaskOp::dilate26(const MaskType& mask) | |
852 | { | ||
853 | //origins of neighbor leaf nodes in the -z and +z directions | ||
854 | 108514 | const Coord origin = this->getOrigin(); | |
855 | 108514 | const Coord orig_mz = origin.offsetBy(0, 0, -DIM); | |
856 | 108514 | const Coord orig_pz = origin.offsetBy(0, 0, DIM); | |
857 |
2/2✓ Branch 0 taken 434056 times.
✓ Branch 1 taken 54257 times.
|
976626 | for (int x = 0; x < DIM; ++x) { |
858 |
2/2✓ Branch 0 taken 3472448 times.
✓ Branch 1 taken 434056 times.
|
7813008 | for (int y = 0, n = (x << LOG2DIM); y < DIM; ++y, ++n) { |
859 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 3472448 times.
✓ Branch 2 taken 1437729 times.
✓ Branch 3 taken 2034719 times.
|
6944896 | if (const Word w = mask.template getWord<Word>(n)) { |
860 | { | ||
861 | 2875458 | this->mWord = Word(w | (w>>1) | (w<<1)); | |
862 | this->setOrigin(origin); | ||
863 | 2875458 | this->scatter(0, n); | |
864 | 2875458 | this->scatterFacesXY(x, y, 0, n, 3); | |
865 | 2875458 | this->scatterEdgesXY(x, y, 0, n, 3); | |
866 | } | ||
867 |
2/2✓ Branch 0 taken 946918 times.
✓ Branch 1 taken 490811 times.
|
2875458 | if ( (this->mWord = Word(w<<(DIM-1))) ) { |
868 | this->setOrigin(origin); | ||
869 | 1893836 | this->template scatter< 0, 0,-1>(1, n); | |
870 | this->setOrigin(orig_mz); | ||
871 | 1893836 | this->scatterFacesXY(x, y, 1, n, 11); | |
872 | 1893836 | this->scatterEdgesXY(x, y, 1, n, 11); | |
873 | } | ||
874 |
2/2✓ Branch 0 taken 949447 times.
✓ Branch 1 taken 488282 times.
|
2875458 | if ( (this->mWord = Word(w>>(DIM-1))) ) { |
875 | this->setOrigin(origin); | ||
876 | 1898894 | this->template scatter< 0, 0, 1>(2, n); | |
877 | this->setOrigin(orig_pz); | ||
878 | 1898894 | this->scatterFacesXY(x, y, 2, n, 19); | |
879 | 1898894 | this->scatterEdgesXY(x, y, 2, n, 19); | |
880 | } | ||
881 | } | ||
882 | }// loop over y | ||
883 | }//loop over x | ||
884 | } | ||
885 | |||
886 | template<typename TreeType> | ||
887 | inline void | ||
888 | 28987678 | Morphology<TreeType>::NodeMaskOp::scatterFacesXY(int x, int y, int i1, int n, int i2) | |
889 | { | ||
890 | // dilate current leaf or neighbor in the -x direction | ||
891 |
2/2✓ Branch 0 taken 12663324 times.
✓ Branch 1 taken 1830515 times.
|
28987678 | if (x > 0) { |
892 | 25326648 | this->scatter(i1, n-DIM); | |
893 | } else { | ||
894 | 3661030 | this->template scatter<-1, 0, 0>(i2, n); | |
895 | } | ||
896 | // dilate current leaf or neighbor in the +x direction | ||
897 |
2/2✓ Branch 0 taken 12667677 times.
✓ Branch 1 taken 1826162 times.
|
28987678 | if (x < DIM-1) { |
898 | 25335354 | this->scatter(i1, n+DIM); | |
899 | } else { | ||
900 | 3652324 | this->template scatter< 1, 0, 0>(i2+1, n); | |
901 | } | ||
902 | // dilate current leaf or neighbor in the -y direction | ||
903 |
2/2✓ Branch 0 taken 12668253 times.
✓ Branch 1 taken 1825586 times.
|
28987678 | if (y > 0) { |
904 | 25336506 | this->scatter(i1, n-1); | |
905 | } else { | ||
906 | 3651172 | this->template scatter< 0,-1, 0>(i2+2, n); | |
907 | } | ||
908 | // dilate current leaf or neighbor in the +y direction | ||
909 |
2/2✓ Branch 0 taken 12686608 times.
✓ Branch 1 taken 1807231 times.
|
28987678 | if (y < DIM-1) { |
910 | 25373216 | this->scatter(i1, n+1); | |
911 | } else { | ||
912 | 3614462 | this->template scatter< 0, 1, 0>(i2+3, n); | |
913 | } | ||
914 | } | ||
915 | |||
916 | |||
917 | template<typename TreeType> | ||
918 | inline void | ||
919 | 14860646 | Morphology<TreeType>::NodeMaskOp::scatterEdgesXY(int x, int y, int i1, int n, int i2) | |
920 | { | ||
921 |
2/2✓ Branch 0 taken 6488095 times.
✓ Branch 1 taken 942228 times.
|
14860646 | if (x > 0) { |
922 |
2/2✓ Branch 0 taken 5671482 times.
✓ Branch 1 taken 816613 times.
|
12976190 | if (y > 0) { |
923 | 11342964 | this->scatter(i1, n-DIM-1); | |
924 | } else { | ||
925 | 1633226 | this->template scatter< 0,-1, 0>(i2+2, n-DIM); | |
926 | } | ||
927 |
2/2✓ Branch 0 taken 5672157 times.
✓ Branch 1 taken 815938 times.
|
12976190 | if (y < DIM-1) { |
928 | 11344314 | this->scatter(i1, n-DIM+1); | |
929 | } else { | ||
930 | 1631876 | this->template scatter< 0, 1, 0>(i2+3, n-DIM); | |
931 | } | ||
932 | } else { | ||
933 |
2/2✓ Branch 0 taken 821783 times.
✓ Branch 1 taken 120445 times.
|
1884456 | if (y < DIM-1) { |
934 | 1643566 | this->template scatter<-1, 0, 0>(i2 , n+1); | |
935 | } else { | ||
936 | 240890 | this->template scatter<-1, 1, 0>(i2+7, n ); | |
937 | } | ||
938 |
2/2✓ Branch 0 taken 820484 times.
✓ Branch 1 taken 121744 times.
|
1884456 | if (y > 0) { |
939 | 1640968 | this->template scatter<-1, 0, 0>(i2 , n-1); | |
940 | } else { | ||
941 | 243488 | this->template scatter<-1,-1, 0>(i2+4, n ); | |
942 | } | ||
943 | } | ||
944 |
2/2✓ Branch 0 taken 6490437 times.
✓ Branch 1 taken 939886 times.
|
14860646 | if (x < DIM-1) { |
945 |
2/2✓ Branch 0 taken 5672719 times.
✓ Branch 1 taken 817718 times.
|
12980874 | if (y > 0) { |
946 | 11345438 | this->scatter(i1, n+DIM-1); | |
947 | } else { | ||
948 | 1635436 | this->template scatter< 0,-1, 0>(i2+2, n+DIM); | |
949 | } | ||
950 |
2/2✓ Branch 0 taken 5674240 times.
✓ Branch 1 taken 816197 times.
|
12980874 | if (y < DIM-1) { |
951 | 11348480 | this->scatter(i1, n+DIM+1); | |
952 | } else { | ||
953 | 1632394 | this->template scatter< 0, 1, 0>(i2+3, n+DIM); | |
954 | } | ||
955 | } else { | ||
956 |
2/2✓ Branch 0 taken 819247 times.
✓ Branch 1 taken 120639 times.
|
1879772 | if (y > 0) { |
957 | 1638494 | this->template scatter< 1, 0, 0>(i2+1, n-1); | |
958 | } else { | ||
959 | 241278 | this->template scatter< 1,-1, 0>(i2+6, n ); | |
960 | } | ||
961 |
2/2✓ Branch 0 taken 819700 times.
✓ Branch 1 taken 120186 times.
|
1879772 | if (y < DIM-1) { |
962 | 1639400 | this->template scatter< 1, 0, 0>(i2+1, n+1); | |
963 | } else { | ||
964 | 240372 | this->template scatter< 1, 1, 0>(i2+5, n ); | |
965 | } | ||
966 | } | ||
967 | } | ||
968 | |||
969 | |||
970 | template<typename TreeType> | ||
971 | inline typename Morphology<TreeType>::NodeMaskOp::Word | ||
972 | 2838413 | Morphology<TreeType>::NodeMaskOp::gatherFacesXY(int x, int y, int i1, int n, int i2) | |
973 | { | ||
974 | // erode current leaf or neighbor in negative x-direction | ||
975 |
2/2✓ Branch 0 taken 1257278 times.
✓ Branch 1 taken 174758 times.
|
2838413 | Word w = x > 0 ? |
976 | this->gather(i1, n - DIM) : | ||
977 | this->template gather<-1,0,0>(i2, n); | ||
978 | |||
979 | // erode current leaf or neighbor in positive x-direction | ||
980 |
2/2✓ Branch 0 taken 1252039 times.
✓ Branch 1 taken 179997 times.
|
2838413 | w = Word(w & (x < DIM - 1 ? |
981 | this->gather(i1, n + DIM) : | ||
982 | 356982 | this->template gather<1,0,0>(i2 + 1, n))); | |
983 | |||
984 | // erode current leaf or neighbor in negative y-direction | ||
985 |
2/2✓ Branch 0 taken 1257441 times.
✓ Branch 1 taken 174595 times.
|
2838413 | w = Word(w & (y > 0 ? |
986 | this->gather(i1, n - 1) : | ||
987 | 346207 | this->template gather<0,-1,0>(i2 + 2, n))); | |
988 | |||
989 | // erode current leaf or neighbor in positive y-direction | ||
990 |
2/2✓ Branch 0 taken 1252052 times.
✓ Branch 1 taken 179984 times.
|
2838413 | w = Word(w & (y < DIM - 1 ? |
991 | this->gather(i1, n + 1) : | ||
992 | 356953 | this->template gather<0,1,0>(i2+3, n))); | |
993 | |||
994 | 2838413 | return w; | |
995 | } | ||
996 | |||
997 | |||
998 | template<typename TreeType> | ||
999 | inline typename Morphology<TreeType>::NodeMaskOp::Word | ||
1000 | Morphology<TreeType>::NodeMaskOp::gatherEdgesXY(int x, int y, int i1, int n, int i2) | ||
1001 | { | ||
1002 | Word w = ~Word(0); | ||
1003 | |||
1004 | if (x > 0) { | ||
1005 | w &= y > 0 ? this->gather(i1, n-DIM-1) : | ||
1006 | this->template gather< 0,-1, 0>(i2+2, n-DIM); | ||
1007 | w &= y < DIM-1 ? this->gather(i1, n-DIM+1) : | ||
1008 | this->template gather< 0, 1, 0>(i2+3, n-DIM); | ||
1009 | } else { | ||
1010 | w &= y < DIM-1 ? this->template gather<-1, 0, 0>(i2 , n+1): | ||
1011 | this->template gather<-1, 1, 0>(i2+7, n ); | ||
1012 | w &= y > 0 ? this->template gather<-1, 0, 0>(i2 , n-1): | ||
1013 | this->template gather<-1,-1, 0>(i2+4, n ); | ||
1014 | } | ||
1015 | if (x < DIM-1) { | ||
1016 | w &= y > 0 ? this->gather(i1, n+DIM-1) : | ||
1017 | this->template gather< 0,-1, 0>(i2+2, n+DIM); | ||
1018 | w &= y < DIM-1 ? this->gather(i1, n+DIM+1) : | ||
1019 | this->template gather< 0, 1, 0>(i2+3, n+DIM); | ||
1020 | } else { | ||
1021 | w &= y > 0 ? this->template gather< 1, 0, 0>(i2+1, n-1): | ||
1022 | this->template gather< 1,-1, 0>(i2+6, n ); | ||
1023 | w &= y < DIM-1 ? this->template gather< 1, 0, 0>(i2+1, n+1): | ||
1024 | this->template gather< 1, 1, 0>(i2+5, n ); | ||
1025 | } | ||
1026 | |||
1027 | return w; | ||
1028 | } | ||
1029 | |||
1030 | } // namespace morphology | ||
1031 | |||
1032 | |||
1033 | ///////////////////////////////////////////////////////////////////// | ||
1034 | ///////////////////////////////////////////////////////////////////// | ||
1035 | |||
1036 | /// @cond OPENVDB_DOCS_INTERNAL | ||
1037 | |||
1038 | namespace morph_internal { | ||
1039 | template <typename T> struct Adapter { | ||
1040 | using TreeType = T; | ||
1041 | static TreeType& get(T& tree) { return tree; } | ||
1042 | static void sync(T&) {} // no-op | ||
1043 | }; | ||
1044 | template <typename T> | ||
1045 | struct Adapter<openvdb::tree::LeafManager<T>> { | ||
1046 | using TreeType = T; | ||
1047 | static TreeType& get(openvdb::tree::LeafManager<T>& M) { return M.tree(); } | ||
1048 | ✗ | static void sync(openvdb::tree::LeafManager<T>& M) { M.rebuild(); } | |
1049 | }; | ||
1050 | } | ||
1051 | |||
1052 | /// @endcond | ||
1053 | |||
1054 | template<typename TreeOrLeafManagerT> | ||
1055 | 8396 | void dilateActiveValues(TreeOrLeafManagerT& treeOrLeafM, | |
1056 | const int iterations, | ||
1057 | const NearestNeighbors nn, | ||
1058 | const TilePolicy mode, | ||
1059 | const bool threaded) | ||
1060 | { | ||
1061 | using AdapterT = morph_internal::Adapter<TreeOrLeafManagerT>; | ||
1062 | using TreeT = typename AdapterT::TreeType; | ||
1063 | using MaskT = typename TreeT::template ValueConverter<ValueMask>::Type; | ||
1064 | |||
1065 |
2/2✓ Branch 0 taken 4179 times.
✓ Branch 1 taken 19 times.
|
13304 | if (iterations <= 0) return; |
1066 | |||
1067 |
2/2✓ Branch 0 taken 3406 times.
✓ Branch 1 taken 773 times.
|
8358 | if (mode == IGNORE_TILES) { |
1068 | 6742 | morphology::Morphology<TreeT> morph(treeOrLeafM); | |
1069 | morph.setThreaded(threaded); | ||
1070 | // This will also sync the leaf manager | ||
1071 |
1/2✓ Branch 1 taken 3406 times.
✗ Branch 2 not taken.
|
6812 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
1072 | return; | ||
1073 | } | ||
1074 | |||
1075 | // The following branching optimises from the different tree types | ||
1076 | // and TilePolicy combinations | ||
1077 | |||
1078 | auto& tree = AdapterT::get(treeOrLeafM); | ||
1079 | |||
1080 | // If the input is a mask tree, don't copy the topology - voxelize | ||
1081 | // it directly and let the morphology class directly steal/prune | ||
1082 | // its nodes | ||
1083 | constexpr bool isMask = std::is_same<TreeT, MaskT>::value; | ||
1084 | |||
1085 |
2/2✓ Branch 0 taken 731 times.
✓ Branch 1 taken 16 times.
|
1494 | if (isMask || mode == EXPAND_TILES) { |
1086 | tree.voxelizeActiveTiles(); | ||
1087 | AdapterT::sync(treeOrLeafM); | ||
1088 | 1514 | morphology::Morphology<TreeT> morph(treeOrLeafM); | |
1089 | morph.setThreaded(threaded); | ||
1090 | |||
1091 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 737 times.
|
1514 | if (mode == PRESERVE_TILES) { |
1092 |
1/2✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
|
40 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/true); |
1093 | } | ||
1094 | else { | ||
1095 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
12 | assert(mode == EXPAND_TILES); |
1096 |
1/2✓ Branch 1 taken 737 times.
✗ Branch 2 not taken.
|
1474 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
1097 | } | ||
1098 | return; | ||
1099 | } | ||
1100 | |||
1101 | // If the tree TreeType being dilated is not a MaskTree, always copy | ||
1102 | // the topology over onto a MaskTree, perform the required dilation | ||
1103 | // and copy the final topology back. This technique avoids unnecessary | ||
1104 | // allocation with tile expansion and correctly preserves the tree | ||
1105 | // topology. | ||
1106 | // | ||
1107 | // Note that we also always use a mask if the tile policy is PRESERVE_TILES | ||
1108 | // due to the way the underlying dilation only works on voxels. | ||
1109 | // @todo Investigate tile based dilation | ||
1110 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
32 | assert(mode == PRESERVE_TILES); |
1111 | |||
1112 | 64 | MaskT topology; | |
1113 | topology.topologyUnion(tree); | ||
1114 | topology.voxelizeActiveTiles(); | ||
1115 | |||
1116 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | morphology::Morphology<MaskT> morph(topology); |
1117 | 32 | morph.setThreaded(threaded); | |
1118 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/true); |
1119 | |||
1120 | tree.topologyUnion(topology, /*preserve-tiles*/true); | ||
1121 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | topology.clear(); |
1122 | |||
1123 | // @note this is necessary to match the behaviour of mask tree dilation | ||
1124 | // where source partial leaf nodes that become dense are also | ||
1125 | // converted into tiles, not simply newly created dense nodes | ||
1126 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | tools::prune(tree, zeroVal<typename TreeT::ValueType>(), threaded); |
1127 | AdapterT::sync(treeOrLeafM); | ||
1128 | } | ||
1129 | |||
1130 | |||
1131 | template<typename TreeOrLeafManagerT> | ||
1132 | 3368 | void erodeActiveValues(TreeOrLeafManagerT& treeOrLeafM, | |
1133 | const int iterations, | ||
1134 | const NearestNeighbors nn, | ||
1135 | const TilePolicy mode, | ||
1136 | const bool threaded) | ||
1137 | { | ||
1138 | using AdapterT = morph_internal::Adapter<TreeOrLeafManagerT>; | ||
1139 | using TreeT = typename AdapterT::TreeType; | ||
1140 | using MaskT = typename TreeT::template ValueConverter<ValueMask>::Type; | ||
1141 | |||
1142 | 3390 | if (iterations <= 0) return; | |
1143 | |||
1144 | // If the tile policiy is PRESERVE_TILES, peform the erosion on a | ||
1145 | // voxelized mask grid followed by a topology intersection such that | ||
1146 | // the original uneroded topology is preserved. | ||
1147 | 3362 | if (mode == PRESERVE_TILES) { | |
1148 | auto& tree = AdapterT::get(treeOrLeafM); | ||
1149 | 44 | MaskT topology; | |
1150 | topology.topologyUnion(tree); | ||
1151 | topology.voxelizeActiveTiles(); | ||
1152 | |||
1153 | { | ||
1154 | 22 | morphology::Morphology<MaskT> morph(topology); | |
1155 | 22 | morph.setThreaded(threaded); | |
1156 | 22 | morph.erodeVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); | |
1157 | } | ||
1158 | |||
1159 | // prune to ensure topologyIntersection does not expand tiles | ||
1160 | // which have not been changed | ||
1161 | 22 | tools::prune(topology, zeroVal<typename MaskT::ValueType>(), threaded); | |
1162 | tree.topologyIntersection(topology); | ||
1163 | AdapterT::sync(treeOrLeafM); | ||
1164 | return; | ||
1165 | } | ||
1166 | |||
1167 | 3340 | if (mode == EXPAND_TILES) { | |
1168 | // if expanding, voxelize everything first if there are active tiles | ||
1169 | // @note check first to avoid any unnecessary rebuilds | ||
1170 | auto& tree = AdapterT::get(treeOrLeafM); | ||
1171 | 6 | if (tree.hasActiveTiles()) { | |
1172 | tree.voxelizeActiveTiles(); | ||
1173 | AdapterT::sync(treeOrLeafM); | ||
1174 | } | ||
1175 | } | ||
1176 | |||
1177 | // ignoring tiles. They won't be eroded | ||
1178 | 3340 | morphology::Morphology<TreeT> morph(treeOrLeafM); | |
1179 | morph.setThreaded(threaded); | ||
1180 | 3340 | morph.erodeVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); | |
1181 | } | ||
1182 | |||
1183 | |||
1184 | ///////////////////////////////////////////////////////////////////// | ||
1185 | ///////////////////////////////////////////////////////////////////// | ||
1186 | |||
1187 | |||
1188 | /// @brief Topologically dilate all leaf-level active voxels in a tree | ||
1189 | /// using one of three nearest neighbor connectivity patterns. | ||
1190 | /// @warning This method is NOT multi-threaded and ignores active tiles! | ||
1191 | /// | ||
1192 | /// @param tree tree to be dilated | ||
1193 | /// @param iterations number of iterations to apply the dilation | ||
1194 | /// @param nn connectivity pattern of the dilation: either | ||
1195 | /// face-adjacent (6 nearest neighbors), face- and edge-adjacent | ||
1196 | /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 | ||
1197 | /// nearest neighbors). | ||
1198 | /// | ||
1199 | /// @note The values of any voxels are unchanged. | ||
1200 | template<typename TreeType> | ||
1201 | OPENVDB_DEPRECATED_MESSAGE("Switch to tools::dilateActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour") | ||
1202 | 1 | inline void dilateVoxels(TreeType& tree, | |
1203 | int iterations = 1, | ||
1204 | NearestNeighbors nn = NN_FACE) | ||
1205 | { | ||
1206 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (iterations <= 0) return; |
1207 | 1 | morphology::Morphology<TreeType> morph(tree); | |
1208 | morph.setThreaded(false); // backwards compatible | ||
1209 | // This will also sync the leaf manager | ||
1210 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
1211 | } | ||
1212 | |||
1213 | /// @brief Topologically dilate all leaf-level active voxels in a tree | ||
1214 | /// using one of three nearest neighbor connectivity patterns. | ||
1215 | /// @warning This method is NOT multi-threaded and ignores active tiles! | ||
1216 | /// | ||
1217 | /// @param manager LeafManager containing the tree to be dilated. | ||
1218 | /// On exit it is updated to include all the leaf | ||
1219 | /// nodes of the dilated tree. | ||
1220 | /// @param iterations number of iterations to apply the dilation | ||
1221 | /// @param nn connectivity pattern of the dilation: either | ||
1222 | /// face-adjacent (6 nearest neighbors), face- and edge-adjacent | ||
1223 | /// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 | ||
1224 | /// nearest neighbors). | ||
1225 | /// | ||
1226 | /// @note The values of any voxels are unchanged. | ||
1227 | template<typename TreeType> | ||
1228 | OPENVDB_DEPRECATED_MESSAGE("Switch to tools::dilateActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour") | ||
1229 | 1 | inline void dilateVoxels(tree::LeafManager<TreeType>& manager, | |
1230 | int iterations = 1, | ||
1231 | NearestNeighbors nn = NN_FACE) | ||
1232 | { | ||
1233 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (iterations <= 0) return; |
1234 | morphology::Morphology<TreeType> morph(manager); | ||
1235 | morph.setThreaded(false); | ||
1236 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | morph.dilateVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
1237 | } | ||
1238 | |||
1239 | //@{ | ||
1240 | /// @brief Topologically erode all leaf-level active voxels in the given tree. | ||
1241 | /// @details That is, shrink the set of active voxels by @a iterations voxels | ||
1242 | /// in the +x, -x, +y, -y, +z and -z directions, but don't change the values | ||
1243 | /// of any voxels, only their active states. | ||
1244 | /// @todo Currently operates only on leaf voxels; need to extend to tiles. | ||
1245 | template<typename TreeType> | ||
1246 | OPENVDB_DEPRECATED_MESSAGE("Switch to tools::erodeActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour") | ||
1247 | 1 | inline void erodeVoxels(TreeType& tree, | |
1248 | int iterations=1, | ||
1249 | NearestNeighbors nn = NN_FACE) | ||
1250 | { | ||
1251 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (iterations > 0) { |
1252 | 1 | morphology::Morphology<TreeType> morph(tree); | |
1253 | morph.setThreaded(true); | ||
1254 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | morph.erodeVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
1255 | } | ||
1256 | |||
1257 | 1 | tools::pruneLevelSet(tree); // matches old behaviour | |
1258 | 1 | } | |
1259 | |||
1260 | template<typename TreeType> | ||
1261 | OPENVDB_DEPRECATED_MESSAGE("Switch to tools::erodeActiveValues. Use tools::IGNORE_TILES to maintain same (but perhaps unintended) behaviour") | ||
1262 | 1 | inline void erodeVoxels(tree::LeafManager<TreeType>& manager, | |
1263 | int iterations = 1, | ||
1264 | NearestNeighbors nn = NN_FACE) | ||
1265 | { | ||
1266 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (iterations <= 0) return; |
1267 | morphology::Morphology<TreeType> morph(manager); | ||
1268 | morph.setThreaded(true); | ||
1269 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | morph.erodeVoxels(static_cast<size_t>(iterations), nn, /*prune=*/false); |
1270 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | tools::pruneLevelSet(manager.tree()); // matches old behaviour |
1271 | } | ||
1272 | //@} | ||
1273 | |||
1274 | |||
1275 | //////////////////////////////////////// | ||
1276 | |||
1277 | |||
1278 | // Explicit Template Instantiation | ||
1279 | |||
1280 | #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
1281 | |||
1282 | #ifdef OPENVDB_INSTANTIATE_MORPHOLOGY | ||
1283 | #include <openvdb/util/ExplicitInstantiation.h> | ||
1284 | #endif | ||
1285 | |||
1286 | #define _FUNCTION(TreeT) \ | ||
1287 | void dilateActiveValues(TreeT&, \ | ||
1288 | const int, const NearestNeighbors, const TilePolicy, const bool) | ||
1289 | OPENVDB_ALL_TREE_INSTANTIATE(_FUNCTION) | ||
1290 | #undef _FUNCTION | ||
1291 | |||
1292 | #define _FUNCTION(TreeT) \ | ||
1293 | void dilateActiveValues(tree::LeafManager<TreeT>&, \ | ||
1294 | const int, const NearestNeighbors, const TilePolicy, const bool) | ||
1295 | OPENVDB_ALL_TREE_INSTANTIATE(_FUNCTION) | ||
1296 | #undef _FUNCTION | ||
1297 | |||
1298 | #define _FUNCTION(TreeT) \ | ||
1299 | void erodeActiveValues(TreeT&, \ | ||
1300 | const int, const NearestNeighbors, const TilePolicy, const bool) | ||
1301 | OPENVDB_ALL_TREE_INSTANTIATE(_FUNCTION) | ||
1302 | #undef _FUNCTION | ||
1303 | |||
1304 | #define _FUNCTION(TreeT) \ | ||
1305 | void erodeActiveValues(tree::LeafManager<TreeT>&, \ | ||
1306 | const int, const NearestNeighbors, const TilePolicy, const bool) | ||
1307 | OPENVDB_ALL_TREE_INSTANTIATE(_FUNCTION) | ||
1308 | #undef _FUNCTION | ||
1309 | |||
1310 | #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
1311 | |||
1312 | |||
1313 | } // namespace tools | ||
1314 | } // namespace OPENVDB_VERSION_NAME | ||
1315 | } // namespace openvdb | ||
1316 | |||
1317 | #endif // OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED | ||
1318 |