OpenVDB 13.0.0

Loading...
Searching...
No Matches
PrincipalComponentAnalysisImpl.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3
4#ifndef OPENVDB_POINTS_POINT_PRINCIPAL_COMPONENT_ANALYSIS_IMPL_HAS_BEEN_INCLUDED
5#define OPENVDB_POINTS_POINT_PRINCIPAL_COMPONENT_ANALYSIS_IMPL_HAS_BEEN_INCLUDED
6
7/// when enabled, prints timings for each substep of the PCA algorithm
8#ifdef OPENVDB_PROFILE_PCA
9#include <openvdb/util/CpuTimer.h>
10#endif
11
12/// @brief Experimental option to skip storing the self weight for the
13/// weighted PCA when set to 0
14/// @todo Decide what's more correct and remove this. Self contributions aren't
15/// guaranteed with maxSourcePointsPerVoxel/maxTargetPointsPerVoxel
16#define OPENVDB_PCA_SELF_CONTRIBUTION 1
17
18 namespace openvdb {
19 OPENVDB_USE_VERSION_NAMESPACE
20 namespace OPENVDB_VERSION_NAME {
21 namespace points {
22
23 namespace pca_internal {
24
25#ifdef OPENVDB_PROFILE_PCA
26 using TimerT = util::CpuTimer;
27#else
28 struct NoTimer {
29 inline void start(const char*) {}
30 inline void stop() {}
31};
32 using TimerT = NoTimer;
33#endif
34
35 struct PcaTimer final : public TimerT
36{
37 PcaTimer(util::NullInterrupter* const interrupt)
38 : TimerT()
39 , mInterrupt(interrupt) {}
40 inline void start(const char* msg)
41 {
42 TimerT::start(msg);
43 if (mInterrupt) mInterrupt->start(msg);
44 }
45 inline void stop()
46 {
47 TimerT::stop();
48 if (mInterrupt) mInterrupt->end();
49 }
50 util::NullInterrupter* const mInterrupt;
51};
52
53 using WeightSumT = double;
54 using WeightedPositionSumT = Vec3d;
55 using GroupIndexT = points::AttributeSet::Descriptor::GroupIndex;
56
57 struct AttrIndices
58{
59 size_t mPosSumIndex;
60 size_t mWeightSumIndex;
61 size_t mCovMatrixIndex;
62 size_t mPWsIndex;
63 GroupIndexT mEllipsesGroupIndex;
64};
65
66 template <typename T, typename LeafNodeT>
67 inline T* initPcaArrayAttribute(LeafNodeT& leaf, const size_t idx, const bool fill = true)
68{
69 auto& array = leaf.attributeArray(idx);
70 OPENVDB_ASSERT(array.valueType() == typeNameAsString<T>());
71 OPENVDB_ASSERT(array.codecType() == std::string(NullCodec::name()));
72 array.expand(fill); // @note does nothing if array is already not uniform
73 const char* data = array.constDataAsByteArray();
74 return reinterpret_cast<T*>(const_cast<char*>(data));
75}
76
77/// @note The PCA transfer modules are broken into two separate steps, the
78/// first which computes the weighted neighbourhoods of points and the second
79/// which computes the covariance matrices. Both these steps perform an
80/// identical loop over each points neighbourhood, with the second being
81/// necessary to re-compute the exact positions and per position weights.
82/// In theory, only a single loop is required; each point could instead
83/// create and append a large list of the exact neighbourhood positions and
84/// weights that impact it and use these to compute the covariance after the
85/// first neighbourhood loop has completed (a true gather style approach).
86///
87/// The double loop technique was chosen to better handle the computation
88/// of anisotropy for _all_ points and _all_ neighbourhoods (i.e. no limiting
89/// via the max point per voxel (MPPV) options). It makes the PCA method
90/// extremely memory efficient for _all_ MPPV and radius values. However
91/// it might be worth falling back to a gather stlye approach when the MPPV
92/// and radius values are relatively small. We should investigate this in the
93/// future.
94 template <typename PointDataTreeT>
95 struct PcaTransfer
96 : public VolumeTransfer<PointDataTreeT>
97 , public InterruptableTransfer
98{
99 using BaseT = VolumeTransfer<PointDataTreeT>;
100 using LeafNodeType = typename PointDataTreeT::LeafNodeType;
101 // We know the codec is null as this works on the world space positions
102 // that are temporarily computed for this algorithm
103 using PositionHandleT = points::AttributeHandle<Vec3d, NullCodec>;
104
105 PcaTransfer(const AttrIndices& indices,
106 const PcaSettings& settings,
107 const Real vs,
108 tree::LeafManager<PointDataTreeT>& manager,
109 util::NullInterrupter* interrupt)
110 : BaseT(manager.tree())
111 , InterruptableTransfer(interrupt)
112 , mIndices(indices)
113 , mSettings(settings)
114 , mDxInv(1.0/vs)
115 , mManager(manager)
116 , mTargetPosition()
117 , mSourcePosition() {
118 OPENVDB_ASSERT(std::isfinite(mSettings.searchRadius));
119 OPENVDB_ASSERT(std::isfinite(mDxInv));
120 }
121
122 PcaTransfer(const PcaTransfer& other)
123 : BaseT(other)
124 , InterruptableTransfer(other)
125 , mIndices(other.mIndices)
126 , mSettings(other.mSettings)
127 , mDxInv(other.mDxInv)
128 , mManager(other.mManager)
129 , mTargetPosition()
130 , mSourcePosition() {}
131
132 /*
133 // This is no longer used but kept for reference; each axis iterations of
134 // derived PCA methods uses this technique to skip voxels entirely if a
135 // point's position and search radius does not intersect it
136
137 static bool VoxelIntersectsSphere(const Coord& ijk, const Vec3d& PosIS, const Real r2)
138 {
139 const Vec3d min = ijk.asVec3d() - 0.5;
140 const Vec3d max = ijk.asVec3d() + 0.5;
141 Real dmin = 0;
142 for (int i = 0; i < 3; ++i) {
143 if (PosIS[i] < min[i]) dmin += math::Pow2(PosIS[i] - min[i]);
144 else if (PosIS[i] > max[i]) dmin += math::Pow2(PosIS[i] - max[i]);
145 }
146 return dmin <= r2;
147 }
148 */
149
150 float searchRadius() const { return mSettings.searchRadius; }
151 size_t neighbourThreshold() const { return mSettings.neighbourThreshold; }
152 size_t maxSourcePointsPerVoxel() const { return mSettings.maxSourcePointsPerVoxel; }
153 size_t maxTargetPointsPerVoxel() const { return mSettings.maxTargetPointsPerVoxel; }
154
155 Vec3i range(const Coord&, size_t) const { return this->range(); }
156 Vec3i range() const { return Vec3i(math::Round(mSettings.searchRadius * mDxInv)); }
157
158 inline LeafNodeType* initialize(const Coord& origin, const size_t idx, const CoordBBox& bounds)
159 {
160 BaseT::initialize(origin, idx, bounds);
161 auto& leaf = mManager.leaf(idx);
162 mTargetPosition = std::make_unique<PositionHandleT>(leaf.constAttributeArray(mIndices.mPWsIndex));
163 return &leaf;
164 }
165
166 inline bool startPointLeaf(const typename PointDataTreeT::LeafNodeType& leaf)
167 {
168 mSourcePosition = std::make_unique<PositionHandleT>(leaf.constAttributeArray(mIndices.mPWsIndex));
169#if OPENVDB_PCA_SELF_CONTRIBUTION == 0
170 mIsSameLeaf = this->mTargetPosition->array() == this->mSourcePosition->array();
171#endif
172 return true;
173 }
174
175 bool endPointLeaf(const typename PointDataTreeT::LeafNodeType&) { return true; }
176
177 protected:
178 const AttrIndices& mIndices;
179 const PcaSettings& mSettings;
180 const Real mDxInv;
181 const tree::LeafManager<PointDataTreeT>& mManager;
182 std::unique_ptr<PositionHandleT> mTargetPosition;
183 std::unique_ptr<PositionHandleT> mSourcePosition;
184#if OPENVDB_PCA_SELF_CONTRIBUTION == 0
185 bool mIsSameLeaf {false};
186#endif
187};
188
189 template <typename PointDataTreeT>
190 struct WeightPosSumsTransfer
191 : public PcaTransfer<PointDataTreeT>
192{
193 using BaseT = PcaTransfer<PointDataTreeT>;
194
195 static const Index DIM = PointDataTreeT::LeafNodeType::DIM;
196 static const Index LOG2DIM = PointDataTreeT::LeafNodeType::LOG2DIM;
197
198 WeightPosSumsTransfer(const AttrIndices& indices,
199 const PcaSettings& settings,
200 const Real vs,
201 tree::LeafManager<PointDataTreeT>& manager,
202 util::NullInterrupter* interrupt)
203 : BaseT(indices, settings, vs, manager, interrupt)
204 , mWeights()
205 , mWeightedPositions()
206 , mCounts() {}
207
208 WeightPosSumsTransfer(const WeightPosSumsTransfer& other)
209 : BaseT(other)
210 , mWeights()
211 , mWeightedPositions()
212 , mCounts() {}
213
214 inline void initialize(const Coord& origin, const size_t idx, const CoordBBox& bounds)
215 {
216 auto& leaf = (*BaseT::initialize(origin, idx, bounds));
217 mWeights = initPcaArrayAttribute<WeightSumT>(leaf, this->mIndices.mWeightSumIndex);
218 mWeightedPositions = initPcaArrayAttribute<WeightedPositionSumT>(leaf, this->mIndices.mPosSumIndex);
219 // track neighbours
220 mCounts.assign(this->mTargetPosition->size(), 0);
221 }
222
223 inline void rasterizePoints(const Coord&, const Index start, const Index end, const CoordBBox& bounds)
224 {
225 const Index step = std::max(Index(1), Index((end - start) / this->maxSourcePointsPerVoxel()));
226
227 const auto* const data = this->template buffer<0>();
228 const auto& mask = *(this->template mask<0>());
229
230 const float searchRadiusInv = 1.0f / this->searchRadius();
231 const Real searchRadius2 = math::Pow2(this->searchRadius());
232 const Real searchRadiusIS2 = math::Pow2(this->searchRadius() * this->mDxInv);
233
234 OPENVDB_ASSERT(std::isfinite(searchRadiusInv));
235 OPENVDB_ASSERT(std::isfinite(searchRadius2));
236 OPENVDB_ASSERT(std::isfinite(searchRadiusIS2));
237
238 const Coord& a(bounds.min());
239 const Coord& b(bounds.max());
240
241 for (Index srcid = start; srcid < end; srcid += step) {
242 const Vec3d Psrc(this->mSourcePosition->get(srcid));
243 const Vec3d PsrcIS = Psrc * this->mDxInv;
244
245 for (Coord c = a; c.x() <= b.x(); ++c.x()) {
246 const Real minx = c.x() - 0.5;
247 const Real maxx = c.x() + 0.5;
248 const Real dminx =
249 (PsrcIS[0] < minx ? math::Pow2(PsrcIS[0] - minx) :
250 (PsrcIS[0] > maxx ? math::Pow2(PsrcIS[0] - maxx) : 0));
251 if (dminx > searchRadiusIS2) continue; // next target voxel
252 const Index i = ((c.x() & (DIM-1u)) << 2*LOG2DIM); // unsigned bit shift mult
253 for (c.y() = a.y(); c.y() <= b.y(); ++c.y()) {
254 const Real miny = c.y() - 0.5;
255 const Real maxy = c.y() + 0.5;
256 const Real dminxy = dminx +
257 (PsrcIS[1] < miny ? math::Pow2(PsrcIS[1] - miny) :
258 (PsrcIS[1] > maxy ? math::Pow2(PsrcIS[1] - maxy) : 0));
259 if (dminxy > searchRadiusIS2) continue; // next target voxel
260 const Index ij = i + ((c.y() & (DIM-1u)) << LOG2DIM);
261 for (c.z() = a.z(); c.z() <= b.z(); ++c.z()) {
262 const Index offset = ij + /*k*/(c.z() & (DIM-1u));
263 if (!mask.isOn(offset)) continue; // next target voxel
264
265 const Real minz = c.z() - 0.5;
266 const Real maxz = c.z() + 0.5;
267 const Real dminxyz = dminxy +
268 (PsrcIS[2] < minz ? math::Pow2(PsrcIS[2] - minz) :
269 (PsrcIS[2] > maxz ? math::Pow2(PsrcIS[2] - maxz) : 0));
270 // Does this point's radius overlap the voxel c
271 if (dminxyz > searchRadiusIS2) continue; // next target voxel
272
273 // src point overlaps voxel c
274 const Index targetEnd = data[offset];
275 const Index targetStart = (offset == 0) ? 0 : Index(data[offset - 1]);
276 const Index targetStep =
277 std::max(Index(1), Index((targetEnd - targetStart) / this->maxTargetPointsPerVoxel()));
278
279 /// @warning stepping in this way does not guarantee
280 /// we get a self contribution, could guarantee this
281 /// by enabling the OPENVDB_PCA_SELF_CONTRIBUTION == 0
282 /// check and adding it afterwards.
283 for (Index tgtid = targetStart; tgtid < targetEnd; tgtid += targetStep) {
284#if OPENVDB_PCA_SELF_CONTRIBUTION == 0
285 if (OPENVDB_UNLIKELY(this->mIsSameLeaf && tgtid == srcid)) continue;
286#endif
287 const Vec3d Ptgt(this->mTargetPosition->get(tgtid));
288 const Real d2 = (Psrc - Ptgt).lengthSqr();
289 if (d2 > searchRadius2) continue;
290
291 // src point (srcid) reaches target point (tgtid)
292 const float weight = 1.0f - math::Pow3(float(math::Sqrt(d2)) * searchRadiusInv);
293 OPENVDB_ASSERT(weight >= 0.0f && weight <= 1.0f);
294
295 mWeights[tgtid] += weight;
296 mWeightedPositions[tgtid] += Psrc * weight; // @note: world space position is weighted
297 ++mCounts[tgtid];
298 }
299 }
300 }
301 } // outer sdf voxel
302 } // point idx
303 }
304
305 bool finalize(const Coord&, size_t idx)
306 {
307 // Add points to group with counts which are more than the neighbouring threshold
308 auto& leaf = this->mManager.leaf(idx);
309
310 {
311 // @todo add API to get the array from the group handle. The handle
312 // calls loadData but not expand.
313 auto& array = leaf.attributeArray(this->mIndices.mEllipsesGroupIndex.first);
314 array.loadData(); // so we can call setUnsafe/getUnsafe
315 array.expand();
316 }
317
318 points::GroupWriteHandle group(leaf.groupWriteHandle(this->mIndices.mEllipsesGroupIndex));
319
320 const int32_t threshold = int32_t(this->neighbourThreshold());
321 for (Index i = 0; i < this->mTargetPosition->size(); ++i) {
322 // turn points OFF if they are ON and don't meet max neighbour requirements
323 if ((threshold == 0 || (mCounts[i] < threshold)) && group.getUnsafe(i)) {
324 group.setUnsafe(i, false);
325 }
326 if (mCounts[i] <= 0) continue;
327 // Normalize weights
328 OPENVDB_ASSERT(mWeights[i] >= 0.0f);
329 mWeights[i] = 1.0 / mWeights[i];
330 mWeightedPositions[i] *= mWeights[i];
331 }
332 return true;
333 }
334
335 private:
336 WeightSumT* mWeights;
337 WeightedPositionSumT* mWeightedPositions;
338 std::vector<int32_t> mCounts;
339};
340
341 template <typename PointDataTreeT>
342 struct CovarianceTransfer
343 : public PcaTransfer<PointDataTreeT>
344{
345 using BaseT = PcaTransfer<PointDataTreeT>;
346
347 static const Index DIM = PointDataTreeT::LeafNodeType::DIM;
348 static const Index LOG2DIM = PointDataTreeT::LeafNodeType::LOG2DIM;
349
350 CovarianceTransfer(const AttrIndices& indices,
351 const PcaSettings& settings,
352 const Real vs,
353 tree::LeafManager<PointDataTreeT>& manager,
354 util::NullInterrupter* interrupt)
355 : BaseT(indices, settings, vs, manager, interrupt)
356 , mInclusionGroupHandle()
357 , mWeights()
358 , mWeightedPositions()
359 , mCovMats() {}
360
361 CovarianceTransfer(const CovarianceTransfer& other)
362 : BaseT(other)
363 , mInclusionGroupHandle()
364 , mWeights()
365 , mWeightedPositions()
366 , mCovMats() {}
367
368 inline void initialize(const Coord& origin, const size_t idx, const CoordBBox& bounds)
369 {
370 auto& leaf = (*BaseT::initialize(origin, idx, bounds));
371 mInclusionGroupHandle = std::make_unique<points::GroupHandle>(leaf.groupHandle(this->mIndices.mEllipsesGroupIndex));
372 mWeights = initPcaArrayAttribute<WeightSumT>(leaf, this->mIndices.mWeightSumIndex);
373 mWeightedPositions = initPcaArrayAttribute<WeightedPositionSumT>(leaf, this->mIndices.mPosSumIndex);
374 mCovMats = initPcaArrayAttribute<math::Mat3s>(leaf, this->mIndices.mCovMatrixIndex);
375 }
376
377 inline void rasterizePoints(const Coord&, const Index start, const Index end, const CoordBBox& bounds)
378 {
379 const Index step = std::max(Index(1), Index((end - start) / this->maxSourcePointsPerVoxel()));
380
381 const auto* const data = this->template buffer<0>();
382 const auto& mask = *(this->template mask<0>());
383
384 const float searchRadiusInv = 1.0f/this->searchRadius();
385 const Real searchRadius2 = math::Pow2(this->searchRadius());
386 const Real searchRadiusIS2 = math::Pow2(this->searchRadius() * this->mDxInv);
387
388 OPENVDB_ASSERT(std::isfinite(searchRadiusInv));
389 OPENVDB_ASSERT(std::isfinite(searchRadius2));
390 OPENVDB_ASSERT(std::isfinite(searchRadiusIS2));
391
392 const Coord& a(bounds.min());
393 const Coord& b(bounds.max());
394
395 for (Index srcid = start; srcid < end; srcid += step) {
396 const Vec3d Psrc(this->mSourcePosition->get(srcid));
397 const Vec3d PsrcIS = Psrc * this->mDxInv;
398
399 for (Coord c = a; c.x() <= b.x(); ++c.x()) {
400 const Real minx = c.x() - 0.5;
401 const Real maxx = c.x() + 0.5;
402 const Real dminx =
403 (PsrcIS[0] < minx ? math::Pow2(PsrcIS[0] - minx) :
404 (PsrcIS[0] > maxx ? math::Pow2(PsrcIS[0] - maxx) : 0));
405 if (dminx > searchRadiusIS2) continue;
406 const Index i = ((c.x() & (DIM-1u)) << 2*LOG2DIM); // unsigned bit shift mult
407 for (c.y() = a.y(); c.y() <= b.y(); ++c.y()) {
408 const Real miny = c.y() - 0.5;
409 const Real maxy = c.y() + 0.5;
410 const Real dminxy = dminx +
411 (PsrcIS[1] < miny ? math::Pow2(PsrcIS[1] - miny) :
412 (PsrcIS[1] > maxy ? math::Pow2(PsrcIS[1] - maxy) : 0));
413 if (dminxy > searchRadiusIS2) continue;
414 const Index ij = i + ((c.y() & (DIM-1u)) << LOG2DIM);
415 for (c.z() = a.z(); c.z() <= b.z(); ++c.z()) {
416 const Index offset = ij + /*k*/(c.z() & (DIM-1u));
417 if (!mask.isOn(offset)) continue;
418
419 const Real minz = c.z() - 0.5;
420 const Real maxz = c.z() + 0.5;
421 const Real dminxyz = dminxy +
422 (PsrcIS[2] < minz ? math::Pow2(PsrcIS[2] - minz) :
423 (PsrcIS[2] > maxz ? math::Pow2(PsrcIS[2] - maxz) : 0));
424 // Does this point's radius overlap the voxel c
425 if (dminxyz > searchRadiusIS2) continue;
426
427 const Index targetEnd = data[offset];
428 const Index targetStart = (offset == 0) ? 0 : Index(data[offset - 1]);
429 const Index targetStep =
430 std::max(Index(1), Index((targetEnd - targetStart) / this->maxTargetPointsPerVoxel()));
431
432 for (Index tgtid = targetStart; tgtid < targetEnd; tgtid += targetStep) {
433 if (!mInclusionGroupHandle->get(tgtid)) continue;
434#if OPENVDB_PCA_SELF_CONTRIBUTION == 0
435 if (OPENVDB_UNLIKELY(this->mIsSameLeaf && tgtid == srcid)) continue;
436#endif
437 const Vec3d Ptgt(this->mTargetPosition->get(tgtid));
438 const Real d2 = (Psrc - Ptgt).lengthSqr();
439 if (d2 > searchRadius2) continue;
440
441 // @note I've observed some performance degradation if
442 // we don't take copies of the buffers here (aliasing?)
443 const WeightSumT totalWeightInv = mWeights[tgtid];
444 const WeightedPositionSumT currWeightSum = mWeightedPositions[tgtid];
445
446 // re-compute weight
447 // @note A gather style approach might be better,
448 // where each point appends weights/positions into
449 // a container. We lose some time having to re-
450 // iterate, but this is far more memory efficient.
451 const WeightSumT weight = 1.0f - math::Pow3(float(math::Sqrt(d2)) * searchRadiusInv);
452 const WeightedPositionSumT posMeanDiff = Psrc - currWeightSum;
453 // @note Could extract the mult by totalWeightInv
454 // and put it into a loop in finalize() - except
455 // it would be mat3*float rather than vec*float,
456 // which would probably better as maxppv increases
457 const WeightedPositionSumT x = (totalWeightInv * weight) * posMeanDiff;
458
459 float* const m = mCovMats[tgtid].asPointer();
460 /// @note: equal to:
461 // mat.setCol(0, mat.col(0) + (x * posMeanDiff[0]));
462 // mat.setCol(1, mat.col(1) + (x * posMeanDiff[1]));
463 // mat.setCol(2, mat.col(2) + (x * posMeanDiff[2]));
464 // @todo formalize precision of these methods
465 m[0] += float(x[0] * posMeanDiff[0]);
466 m[1] += float(x[0] * posMeanDiff[1]);
467 m[2] += float(x[0] * posMeanDiff[2]);
468 //
469 m[3] += float(x[1] * posMeanDiff[0]);
470 m[4] += float(x[1] * posMeanDiff[1]);
471 m[5] += float(x[1] * posMeanDiff[2]);
472 //
473 m[6] += float(x[2] * posMeanDiff[0]);
474 m[7] += float(x[2] * posMeanDiff[1]);
475 m[8] += float(x[2] * posMeanDiff[2]);
476 } //point idx
477 }
478 }
479 } // outer sdf voxel
480 } // point idx
481 }
482
483 bool finalize(const Coord&, size_t) { return true; }
484
485 private:
486 points::GroupHandle::UniquePtr mInclusionGroupHandle;
487 const WeightSumT* mWeights;
488 const WeightedPositionSumT* mWeightedPositions;
489 math::Mat3s* mCovMats;
490 std::vector<int32_t> mCounts;
491};
492
493/// @brief Sort a vector into descending order and output a vector of the resulting order
494/// @param vector Vector to sort
495 template <typename Scalar>
496 inline Vec3i
497 descendingOrder(math::Vec3<Scalar>& vector)
498{
499 Vec3i order(0,1,2);
500 if (vector[0] < vector[1]) {
501 std::swap(vector[0], vector[1]);
502 std::swap(order[0], order[1]);
503 }
504 if (vector[1] < vector[2]) {
505 std::swap(vector[1], vector[2]);
506 std::swap(order[1], order[2]);
507 }
508 if (vector[0] < vector[1]) {
509 std::swap(vector[0], vector[1]);
510 std::swap(order[0], order[1]);
511 }
512 return order;
513}
514
515/// @brief Decomposes a symmetric matrix into its eigenvalues and a rotation matrix of eigenvectors.
516/// Note that if mat is positive-definite, this will be equivalent to a singular value
517/// decomposition where V = U.
518/// @param mat Matrix to decompose
519/// @param U rotation matrix. The order of its columns (which will be eigenvectors) will match
520/// the eigenvalues in sigma
521/// @param sigma vector of eigenvalues
522 template <typename Scalar>
523 inline bool
524 decomposeSymmetricMatrix(const math::Mat3<Scalar>& mat,
525 math::Mat3<Scalar>& U,
526 math::Vec3<Scalar>& sigma)
527{
528 math::Mat3<Scalar> Q;
529 const bool diagonalized = math::diagonalizeSymmetricMatrix(mat, Q, sigma);
530
531 if (!diagonalized) return false;
532
533 // need to sort eigenvalues and eigenvectors
534 Vec3i order = descendingOrder(sigma);
535
536 // we need to re-order the matrix ("Q") columns to match the new eigenvalue order
537 // to obtain the correct "U" matrix
538 U.setColumns(Q.col(order[0]), Q.col(order[1]), Q.col(order[2]));
539
540 return true;
541}
542
543} // namespace pca_internal
544
545
546 template <typename PointDataGridT>
547 inline void
548 pca(PointDataGridT& points,
549 const PcaSettings& settings,
550 const PcaAttributes& attrs)
551{
552 static_assert(IsSpecializationOf<PointDataGridT, Grid>::value);
553 static constexpr size_t INVALID_IDX = std::numeric_limits<size_t>::max();
554
555 using namespace pca_internal;
556
557 using PointDataTreeT = typename PointDataGridT::TreeType;
558 using LeafManagerT = tree::LeafManager<PointDataTreeT>;
559 using LeafNodeT = typename PointDataTreeT::LeafNodeType;
560 using Vec3T = PcaAttributes::StretchT;
561 using Mat3T = PcaAttributes::RotationT;
562
563 auto& tree = points.tree();
564 const auto leaf = tree.cbeginLeaf();
565 if (!leaf) return;
566
567 // Small lambda to init any one of the necessary ellipsoid attributes
568 // @note Various algorithms here assume that we are responsible for creating
569 // these attributes and so can optimize accordingly. If this changes we'll
570 // need to also change those optimizations (e.g. NullCodec, loadData, etc)
571 const auto initAttribute = [&](const std::string& name, const auto val)
572 {
573 using ValueT = std::decay_t<decltype(val)>;
574 if (leaf->hasAttribute(name)) {
575 OPENVDB_THROW(KeyError, "PCA attribute '" << name << "' already exists!");
576 }
577
578 points::appendAttribute<ValueT>(tree, name, val);
579 return leaf->attributeSet().find(name);
580 };
581
582 //
583
584 const size_t pvsIdx = leaf->attributeSet().find("P");
585 const auto& xform = points.constTransform();
586 const double vs = xform.voxelSize()[0];
587 LeafManagerT manager(tree);
588
589 // Configure attributes names
590 const auto& descriptor = leaf->attributeSet().descriptor();
591 std::vector<std::string> temps {
592 descriptor.uniqueName("_weightedpositionsums"),
593 descriptor.uniqueName("_inv_weightssum")
594 };
595
596 // Separate rotation (Mat3) and stretch (Vec3f) are always required in the
597 // below calculations. If we're outputting rotation as a quarternion we
598 // must create a temporary covariance matrix attribute. Otherwise we reuse
599 // output xform matrix attribute for the intermediate convariance
600 // representation. Same applies for the stretch; if we're outputting a
601 // combined transform, we must create temporary stretch storage.
602 const std::string covAttribName = [&]() {
603 if (attrs.xformOutput == PcaAttributes::XformOutput::STRETCH_AND_QUATERNION) {
604 temps.emplace_back(descriptor.uniqueName("_covariance"));
605 return temps.back();
606 }
607 return attrs.xform;
608 }();
609
610 const std::string stretchAttribName = [&]() {
611 if (attrs.xformOutput == PcaAttributes::XformOutput::COMBINED_TRANSFORM) {
612 temps.emplace_back(descriptor.uniqueName("_stretch"));
613 return temps.back();
614 }
615 return attrs.stretch;
616 }();
617
618 // 1) Create persisting attributes
619 const size_t pwsIdx = initAttribute(attrs.positionWS, zeroVal<PcaAttributes::PosWsT>());
620 const size_t rotIdx = initAttribute(covAttribName, zeroVal<Mat3T>());
621 const size_t qutIdx =
622 attrs.xformOutput == PcaAttributes::XformOutput::STRETCH_AND_QUATERNION ?
623 initAttribute(attrs.xform, zeroVal<PcaAttributes::QuatT>()) :
624 INVALID_IDX;
625 const size_t strIdx = initAttribute(stretchAttribName, PcaAttributes::StretchT(settings.nonAnisotropicStretch));
626
627 // 2) Create temporary attributes
628 const size_t posSumIndex = initAttribute(temps[0], zeroVal<WeightedPositionSumT>());
629 const size_t weightSumIndex = initAttribute(temps[1], zeroVal<WeightSumT>());
630
631 OPENVDB_NO_DEPRECATION_WARNING_BEGIN
632 // DEPRECATED, TO REMOVE
633 size_t oldRotAttr = INVALID_IDX;
634 if (!attrs.rotation.empty()) {
635 oldRotAttr = initAttribute(attrs.rotation, zeroVal<Mat3T>());
636 }
637 OPENVDB_NO_DEPRECATION_WARNING_END
638
639 // 3) Create ellipses group
640 if (!leaf->attributeSet().descriptor().hasGroup(attrs.ellipses)) {
641 points::appendGroup(tree, attrs.ellipses);
642 // Include everything by default to start with
643 points::setGroup(tree, attrs.ellipses, true);
644 }
645
646 // Re-acquire the updated descriptor and get the group idx
647 const GroupIndexT ellipsesIdx =
648 leaf->attributeSet().descriptor().groupIndex(attrs.ellipses);
649
650 PcaTimer timer(settings.interrupter);
651
652 // 3) Store the world space position on the PDG to speed up subsequent
653 // calculations.
654 timer.start("Compute position world spaces");
655 manager.foreach([&](LeafNodeT& leafnode, size_t)
656 {
657 using PvsT = Vec3f;
658 using PwsT = PcaAttributes::PosWsT;
659
660 points::AttributeHandle<PvsT> Pvs(leafnode.constAttributeArray(pvsIdx));
661 PwsT* Pws = initPcaArrayAttribute<PwsT>(leafnode, pwsIdx, /*fill=*/false);
662
663 for (auto voxel = leafnode.cbeginValueOn(); voxel; ++voxel) {
664 const Coord voxelCoord = voxel.getCoord();
665 const Vec3d coordVec = voxelCoord.asVec3d();
666 for (auto iter = leafnode.beginIndexVoxel(voxelCoord); iter; ++iter) {
667 Pws[*iter] = xform.indexToWorld(Pvs.get(*iter) + coordVec);
668 }
669 }
670 });
671
672 timer.stop();
673 if (util::wasInterrupted(settings.interrupter)) return;
674
675 AttrIndices indices;
676 indices.mPosSumIndex = posSumIndex;
677 indices.mWeightSumIndex = weightSumIndex;
678 indices.mCovMatrixIndex = rotIdx;
679 indices.mPWsIndex = pwsIdx;
680 indices.mEllipsesGroupIndex = ellipsesIdx;
681
682 // 4) Init temporary attributes and calculate:
683 // sum_j w_{i,j} * x_j / (sum_j w_j)
684 // And neighbour counts for each point.
685 // simultaneously calculates the sum of weighted vector positions (sum w_{i,j} * x_i)
686 // weighted against the inverse sum of weights (1.0 / sum w_{i,j}). Also counts number
687 // of neighours each point has and updates the ellipses group based on minimum
688 // neighbour threshold. Those points which are "included" but which lack sufficient
689 // neighbours will be marked as "not included".
690 timer.start("Compute position weights");
691 {
692 WeightPosSumsTransfer<PointDataTreeT>
693 transfer(indices, settings, float(vs), manager, settings.interrupter);
694 points::rasterize<PointDataGridT, decltype(transfer)> (points, transfer);
695 }
696
697 timer.stop();
698 if (util::wasInterrupted(settings.interrupter)) return;
699
700 // 5) Principal axes define the rotation matrix of the ellipsoid.
701 // Calculates covariance matrices given weighted sums of positions and
702 // sums of weights per-particle
703 timer.start("Compute covariance matrices");
704 {
705 CovarianceTransfer<PointDataTreeT>
706 transfer(indices, settings, float(vs), manager, settings.interrupter);
707 points::rasterize<PointDataGridT, decltype(transfer)>(points, transfer);
708 }
709
710 timer.stop();
711 if (util::wasInterrupted(settings.interrupter)) return;
712
713 // 6) radii stretches are given by the scaled singular values. Decompose
714 // the covariance matrix into its principal axes and their lengths
715 timer.start("Decompose covariance matrices");
716 manager.foreach([&](LeafNodeT& leafnode, size_t)
717 {
718 AttributeWriteHandle<Vec3T, NullCodec> stretchHandle(leafnode.attributeArray(strIdx));
719 AttributeWriteHandle<Mat3T, NullCodec> rotHandle(leafnode.attributeArray(rotIdx));
720
721 // we don't use a group filter here since we need to set the rotation
722 // matrix for excluded points - the handle handles the potential array
723 // expansion
724 GroupHandle ellipsesGroupHandle(leafnode.groupHandle(ellipsesIdx));
725
726 for (Index idx = 0; idx < stretchHandle.size(); ++idx) {
727 if (!ellipsesGroupHandle.get(idx)) {
728 rotHandle.set(idx, Mat3T::identity());
729 continue;
730 }
731
732 // get singular values of the covariance matrix
733 Mat3T u;
734 Vec3s sigma;
735 decomposeSymmetricMatrix(rotHandle.get(idx), u, sigma);
736
737 // fix sigma values, the principal lengths
738 auto maxs = sigma[0] * settings.allowedAnisotropyRatio;
739 sigma[1] = std::max(sigma[1], maxs);
740 sigma[2] = std::max(sigma[2], maxs);
741
742 // should only happen if all neighbours are coincident
743 // @note The specific tolerance here relates to the normalization
744 // of the stetch values in step (7) e.g. s*(1.0/cbrt(s.product())).
745 // math::Tolerance<float>::value is 1e-7f, but we have a bit more
746 // flexibility here, we can deal with smaller values, common for
747 // the case where a point only has one neighbour
748 // @todo have to manually construct the tolerance because
749 // math::Tolerance<Vec3T> resolves to 0.0. fix this in the math lib
750 if (math::isApproxZero(sigma, Vec3T(1e-11f))) {
751 sigma = Vec3T::ones();
752 }
753
754 stretchHandle.set(idx, sigma);
755 rotHandle.set(idx, u);
756 }
757 });
758 timer.stop();
759
760 // 7) normalise the principal lengths such that the transformation they
761 // describe 'preserves volume' thus becoming the stretch of the ellipsoids
762
763 // Calculates the average volume change that would occur if applying the
764 // transformations defined by the calculate principal axes and their
765 // lengths using the determinant of the stretch component of the
766 // transformation (given by the sum of the diagonal values) and uses this
767 // value to normalise these lengths such that they are relative to the
768 // identity. This ensures that the transformation preserves volume.
769 timer.start("Normalise the principal lengths");
770 manager.foreach([&](LeafNodeT& leafnode, size_t)
771 {
772 points::GroupFilter filter(ellipsesIdx);
773 filter.reset(leafnode);
774 AttributeWriteHandle<Vec3T, NullCodec> stretchHandle(leafnode.attributeArray(strIdx));
775
776 for (Index i = 0; i < stretchHandle.size(); ++i)
777 {
778 if (!filter.valid(&i)) continue;
779 const Vec3T stretch = stretchHandle.get(i);
780 OPENVDB_ASSERT(stretch != Vec3T::zero());
781 const float stretchScale = 1.0f / std::cbrt(stretch.product());
782 stretchHandle.set(i, stretchScale * stretch);
783 }
784 });
785
786 // DEPRECATED, TO REMOVE
787 if (oldRotAttr != INVALID_IDX) {
788 // Copy xform to "rotation"
789 manager.foreach([&](LeafNodeT& leafnode, size_t) {
790 // pretty inefficient way to do this, but its deprecated behaviour
791 AttributeWriteHandle<Mat3T, NullCodec> oldRotHandle(leafnode.attributeArray(oldRotAttr));
792 Mat3T* R = initPcaArrayAttribute<Mat3T>(leafnode, rotIdx, /*fill=*/false);
793 for (Index idx = 0; idx < oldRotHandle.size(); ++idx) {
794 R[idx] = oldRotHandle.get(idx);
795 }
796 });
797 }
798
799 /// 8) Covert attributes to desired format
800 /// @note We could consolidate this method into the parallel operators above,
801 /// but in the future we may want to do more post-processing with these
802 /// attributes (such as monitor and manipulate volume preservation, etc)
803 /// which will require a separate step. For now, this step is relatively
804 /// cheap.
805 timer.start("Coverting attributes");
806 if (attrs.xformOutput == PcaAttributes::XformOutput::STRETCH_AND_QUATERNION)
807 {
808 manager.foreach([&](LeafNodeT& leafnode, size_t) {
809 AttributeWriteHandle<Mat3T, NullCodec> rotHandle(leafnode.attributeArray(rotIdx));
810 PcaAttributes::QuatT* Q = initPcaArrayAttribute<PcaAttributes::QuatT>(leafnode, qutIdx, /*fill=*/false);
811 for (Index idx = 0; idx < rotHandle.size(); ++idx) {
812 Mat3T rot = rotHandle.get(idx);
813 // Tolerance for quaternion construction - can be pretty low as
814 // we assume our mats to be fully unitary (det of +/-1) at this
815 // point, but may have precision issues when checking it - also
816 // the quat class will try to perform a (mat * mat^t) to check
817 // unitary which is suseptible to small fp values causing
818 // drifts, so construct with UnsafeConstruct{}
819 constexpr float tolerance = 1e-3f;
820 // If we're a pure reflection (partial or global, det of -1),
821 // we need to convert to a rotation for quat support. Just flip
822 // the sign of one of the columns (axis) to change the
823 // "handedness" of the basis
824 // @todo Consider never outputing reflection/always preserve
825 // orientation? Handle this in the svd result?
826 if (math::isApproxEqual(rot.det(), -1.0f, tolerance))
827 {
828 // Choosing to apply to last column
829 rot(0,2) *= -1.0f;
830 rot(1,2) *= -1.0f;
831 rot(2,2) *= -1.0f;
832 // Quat construtor throws so guard with our own check
833 // @todo Re-write quat class...
834 if (math::isApproxEqual(rot.det(), 1.0f, tolerance)) {
835 Q[idx] = PcaAttributes::QuatT(rot, PcaAttributes::QuatT::UnsafeConstruct{});
836 continue;
837 }
838 OPENVDB_ASSERT_MESSAGE(false,
839 "Unable to convert reflection to valid rotation matrix");
840 // shouldn't be possible but fall back to ident
841 Q[idx].setIdentity();
842 }
843 else {
844 Q[idx] = PcaAttributes::QuatT(rot, PcaAttributes::QuatT::UnsafeConstruct{});
845 }
846 }
847 // remove matrix representation as we've covered it to a quaternion
848 rotHandle.collapse();
849 });
850 }
851 else if (attrs.xformOutput == PcaAttributes::XformOutput::COMBINED_TRANSFORM)
852 {
853 manager.foreach([&](LeafNodeT& leafnode, size_t) {
854 AttributeWriteHandle<Vec3T, NullCodec> stretchHandle(leafnode.attributeArray(strIdx));
855 Mat3T* R = initPcaArrayAttribute<Mat3T>(leafnode, rotIdx, /*fill=*/false);
856 for (Index idx = 0; idx < stretchHandle.size(); ++idx) {
857 R[idx] = R[idx].timesDiagonal(stretchHandle.get(idx));
858 }
859 // Remove individual scale components which have now been combined
860 stretchHandle.collapse();
861 });
862 }
863
864 timer.stop();
865 if (util::wasInterrupted(settings.interrupter)) return;
866
867 /// 9) do laplacian position smoothing here as we have the weights already
868 /// calculated (calculates the smoothed kernel origins as described in
869 /// the paper). averagePositions value biases the smoothed positions
870 /// towards the weighted mean positions. 1.0 will use the weighted means
871 /// while 0.0 will use the original world-space positions
872 timer.start("Laplacian smooth positions");
873 if (settings.averagePositions > 0.0f)
874 {
875 manager.foreach([&](LeafNodeT& leafnode, size_t) {
876 AttributeWriteHandle<Vec3d, NullCodec> Pws(leafnode.attributeArray(pwsIdx));
877 AttributeHandle<WeightedPositionSumT, NullCodec> weightedPosSumHandle(leafnode.constAttributeArray(posSumIndex));
878
879 for (Index i = 0; i < Pws.size(); ++i)
880 {
881 // @note Technically possible for the weights to be valid
882 // _and_ zero.
883 if (math::isApproxZero(weightedPosSumHandle.get(i),
884 Vec3d(math::Tolerance<double>::value()))) {
885 continue;
886 }
887 const Vec3d smoothedPosition = (1.0f - settings.averagePositions) *
888 Pws.get(i) + settings.averagePositions * weightedPosSumHandle.get(i);
889 Pws.set(i, smoothedPosition);
890 }
891 });
892 }
893
894 timer.stop();
895
896 // Remove temporary attributes
897 points::dropAttributes(tree, temps);
898}
899
900}
901}
902}
903
904#endif // OPENVDB_POINTS_POINT_PRINCIPAL_COMPONENT_ANALYSIS_IMPL_HAS_BEEN_INCLUDED
OPENVDB_ASSERT_MESSAGE
#define OPENVDB_ASSERT_MESSAGE(X, MSG)
Definition Assert.h:42
OPENVDB_ASSERT
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
OPENVDB_NO_DEPRECATION_WARNING_END
#define OPENVDB_NO_DEPRECATION_WARNING_END
Definition Platform.h:218
OPENVDB_NO_DEPRECATION_WARNING_BEGIN
#define OPENVDB_NO_DEPRECATION_WARNING_BEGIN
Bracket code with OPENVDB_NO_DEPRECATION_WARNING_BEGIN/_END, to inhibit warnings about deprecated cod...
Definition Platform.h:217
OPENVDB_UNLIKELY
#define OPENVDB_UNLIKELY(x)
Definition Platform.h:92
openvdb::v13_0::KeyError
Definition Exceptions.h:59
openvdb::v13_0::math::CoordBBox
Axis-aligned bounding box of signed integer coordinates.
Definition Coord.h:252
openvdb::v13_0::math::CoordBBox::min
const Coord & min() const
Definition Coord.h:324
openvdb::v13_0::math::CoordBBox::max
const Coord & max() const
Definition Coord.h:325
openvdb::v13_0::math::Coord
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:26
openvdb::v13_0::math::Coord::asVec3d
Vec3d asVec3d() const
Definition Coord.h:144
openvdb::v13_0::math::Coord::y
Int32 y() const
Definition Coord.h:132
openvdb::v13_0::math::Coord::x
Int32 x() const
Definition Coord.h:131
openvdb::v13_0::math::Coord::z
Int32 z() const
Definition Coord.h:133
openvdb::v13_0::math::Mat3
3x3 matrix class.
Definition Mat3.h:29
openvdb::v13_0::math::Mat3::col
Vec3< T > col(int j) const
Get jth column, e.g. Vec3d v = m.col(0);.
Definition Mat3.h:168
openvdb::v13_0::math::Mat3::setColumns
void setColumns(const Vec3< T > &v1, const Vec3< T > &v2, const Vec3< T > &v3)
Set the columns of this matrix to the vectors v1, v2, v3.
Definition Mat3.h:209
openvdb::v13_0::math::Quat::setIdentity
Quat & setIdentity()
Set "this" vector to identity.
Definition Quat.h:300
openvdb::v13_0::math::Vec3
Definition Vec3.h:25
openvdb::v13_0::math::Vec3::asPointer
T * asPointer()
Definition Vec3.h:95
openvdb::v13_0::points::AttributeHandle
Definition AttributeArray.h:764
openvdb::v13_0::points::AttributeHandle::size
Index size() const
Definition AttributeArray.h:786
openvdb::v13_0::points::AttributeHandle::get
ValueType get(Index n, Index m=0) const
Definition AttributeArray.h:2058
openvdb::v13_0::points::AttributeSet::Descriptor::GroupIndex
Util::GroupIndex GroupIndex
Definition AttributeSet.h:317
openvdb::v13_0::points::AttributeWriteHandle
Write-able version of AttributeHandle.
Definition AttributeArray.h:832
openvdb::v13_0::points::AttributeWriteHandle::collapse
void collapse()
Replace the existing array with a uniform value (zero if none provided).
Definition AttributeArray.h:2133
openvdb::v13_0::points::AttributeWriteHandle::set
void set(Index n, const ValueType &value)
Definition AttributeArray.h:2115
openvdb::v13_0::points::GroupFilter
Index filtering on group membership.
Definition AttributeGroup.h:136
openvdb::v13_0::points::GroupFilter::reset
void reset(const LeafT &leaf)
Definition AttributeGroup.h:151
openvdb::v13_0::points::GroupFilter::valid
bool valid(const IterT &iter) const
Definition AttributeGroup.h:156
openvdb::v13_0::points::GroupHandle
Definition AttributeGroup.h:74
openvdb::v13_0::points::GroupHandle::get
bool get(Index n) const
openvdb::v13_0::points::GroupHandle::UniquePtr
std::unique_ptr< GroupHandle > UniquePtr
Definition AttributeGroup.h:77
openvdb::v13_0::points::GroupHandle::getUnsafe
bool getUnsafe(Index n) const
openvdb::v13_0::points::GroupWriteHandle
Definition AttributeGroup.h:103
openvdb::v13_0::points::GroupWriteHandle::setUnsafe
void setUnsafe(Index n, bool on)
Set on at the given index n (assumes in-core and non-uniform).
openvdb::v13_0::tree::LeafManager
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition LeafManager.h:86
openvdb::v13_0::util::CpuTimer
Simple timer for basic profiling.
Definition CpuTimer.h:67
openvdb::v13_0::math::diagonalizeSymmetricMatrix
bool diagonalizeSymmetricMatrix(const Mat3< T > &input, Mat3< T > &Q, Vec3< T > &D, unsigned int MAX_ITERATIONS=250)
Use Jacobi iterations to decompose a symmetric 3x3 matrix (diagonalize and compute eigenvectors).
Definition Mat3.h:736
openvdb::v13_0::math::isApproxZero
bool isApproxZero(const Type &x)
Return true if x is equal to zero to within the default floating-point comparison tolerance.
Definition Math.h:362
openvdb::v13_0::math::Sqrt
float Sqrt(float x)
Return the square root of a floating-point value.
Definition Math.h:832
openvdb::v13_0::math::isApproxEqual
bool isApproxEqual(const Type &a, const Type &b, const Type &tolerance)
Return true if a is equal to b to within the given tolerance.
Definition Math.h:431
openvdb::v13_0::math::Mat3s
Mat3< float > Mat3s
Definition Mat3.h:832
openvdb::v13_0::math::Vec3d
Vec3< double > Vec3d
Definition Vec3.h:665
openvdb::v13_0::math::Round
float Round(float x)
Return x rounded to the nearest integer.
Definition Math.h:890
openvdb::v13_0::math::Pow3
Type Pow3(Type x)
Return x3.
Definition Math.h:577
openvdb::v13_0::math::Vec3i
Vec3< int32_t > Vec3i
Definition Vec3.h:662
openvdb::v13_0::math::Vec3s
Vec3< float > Vec3s
Definition Vec3.h:664
openvdb::v13_0::math::Pow2
Type Pow2(Type x)
Return x2.
Definition Math.h:573
openvdb::v13_0::points::pca_internal
Definition PrincipalComponentAnalysisImpl.h:23
openvdb::v13_0::points::pca_internal::WeightSumT
double WeightSumT
Definition PrincipalComponentAnalysisImpl.h:53
openvdb::v13_0::points::pca_internal::TimerT
NoTimer TimerT
Definition PrincipalComponentAnalysisImpl.h:32
openvdb::v13_0::points::pca_internal::descendingOrder
Vec3i descendingOrder(math::Vec3< Scalar > &vector)
Sort a vector into descending order and output a vector of the resulting order.
Definition PrincipalComponentAnalysisImpl.h:497
openvdb::v13_0::points::pca_internal::initPcaArrayAttribute
T * initPcaArrayAttribute(LeafNodeT &leaf, const size_t idx, const bool fill=true)
Definition PrincipalComponentAnalysisImpl.h:67
openvdb::v13_0::points::pca_internal::GroupIndexT
points::AttributeSet::Descriptor::GroupIndex GroupIndexT
Definition PrincipalComponentAnalysisImpl.h:55
openvdb::v13_0::points::pca_internal::decomposeSymmetricMatrix
bool decomposeSymmetricMatrix(const math::Mat3< Scalar > &mat, math::Mat3< Scalar > &U, math::Vec3< Scalar > &sigma)
Decomposes a symmetric matrix into its eigenvalues and a rotation matrix of eigenvectors....
Definition PrincipalComponentAnalysisImpl.h:524
openvdb::v13_0::points::pca_internal::WeightedPositionSumT
Vec3d WeightedPositionSumT
Definition PrincipalComponentAnalysisImpl.h:54
openvdb::v13_0::points
Definition AttributeArray.h:42
openvdb::v13_0::points::setGroup
void setGroup(PointDataTreeT &tree, const PointIndexTreeT &indexTree, const std::vector< short > &membership, const Name &group, const bool remove=false)
Sets group membership from a PointIndexTree-ordered vector.
Definition PointGroupImpl.h:438
openvdb::v13_0::points::appendGroup
void appendGroup(PointDataTreeT &tree, const Name &group)
Appends a new empty group to the VDB tree.
Definition PointGroupImpl.h:209
openvdb::v13_0::points::pca
void pca(PointDataGridT &points, const PcaSettings &settings, const PcaAttributes &attrs)
Calculate ellipsoid transformations from the local point distributions as described in Yu and Turk's ...
Definition PrincipalComponentAnalysisImpl.h:548
openvdb::v13_0::points::appendAttribute
void appendAttribute(PointDataTreeT &tree, const Name &name, const NamePair &type, const Index strideOrTotalSize=1, const bool constantStride=true, const Metadata *defaultValue=nullptr, const bool hidden=false, const bool transient=false)
Appends a new attribute to the VDB tree (this method does not require a templated AttributeType).
Definition PointAttributeImpl.h:103
openvdb::v13_0::points::dropAttributes
void dropAttributes(PointDataTreeT &tree, const std::vector< size_t > &indices)
Drops attributes from the VDB tree.
Definition PointAttributeImpl.h:238
openvdb::v13_0::points::rasterize
void rasterize(const PointDataTreeOrGridT &points, TransferT &transfer)
Perform potentially complex rasterization from a user defined transfer scheme. See below comments for...
Definition PointTransfer.h:700
openvdb::v13_0::tree
Definition PointDataGrid.h:170
openvdb::v13_0::util::wasInterrupted
bool wasInterrupted(T *i, int percent=-1)
Definition NullInterrupter.h:49
openvdb::v13_0::Index
Index32 Index
Definition Types.h:34
openvdb::v13_0::Vec3f
math::Vec3< float > Vec3f
Definition Types.h:55
openvdb::v13_0::Real
double Real
Definition Types.h:40
openvdb::v13_0::zeroVal
constexpr T zeroVal()
Return the value of type T that corresponds to zero.
Definition Math.h:71
openvdb::v13_0::typeNameAsString
const char * typeNameAsString()
Definition Types.h:583
openvdb
Definition Exceptions.h:13
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
CpuTimer.h
openvdb::v13_0::IsSpecializationOf
Helper metafunction used to determine if the first template parameter is a specialization of the clas...
Definition Types.h:244
openvdb::v13_0::math::Tolerance::value
static T value()
Definition Math.h:159
openvdb::v13_0::points::InterruptableTransfer::InterruptableTransfer
InterruptableTransfer(util::NullInterrupter *const interrupt)
Definition PointTransfer.h:232
openvdb::v13_0::points::NullCodec::name
static const char * name()
Definition AttributeArray.h:443
openvdb::v13_0::points::PcaAttributes
The persistent attributes created by the PCA methods.
Definition PrincipalComponentAnalysis.h:164
openvdb::v13_0::points::PcaAttributes::rotation
std::string rotation
Definition PrincipalComponentAnalysis.h:221
openvdb::v13_0::points::PcaAttributes::StretchT
math::Vec3< float > StretchT
Settings for the "stretch" attribute, a floating point vector attribute which represents the scaling ...
Definition PrincipalComponentAnalysis.h:189
openvdb::v13_0::points::PcaAttributes::positionWS
std::string positionWS
Definition PrincipalComponentAnalysis.h:206
openvdb::v13_0::points::PcaAttributes::ellipses
std::string ellipses
A point group to create that represents points which have valid ellipsoidal neighborhood....
Definition PrincipalComponentAnalysis.h:214
openvdb::v13_0::points::PcaAttributes::RotationT
math::Mat3< float > RotationT
Settings for the "xform" attribute, either a floating point matrix or quaternion attribute....
Definition PrincipalComponentAnalysis.h:195
openvdb::v13_0::points::PcaAttributes::stretch
std::string stretch
Definition PrincipalComponentAnalysis.h:190
openvdb::v13_0::points::PcaAttributes::QuatT
math::Quat< float > QuatT
Definition PrincipalComponentAnalysis.h:196
openvdb::v13_0::points::PcaAttributes::PosWsT
math::Vec3< double > PosWsT
Settings for the world space position of every point. This may end up being different to their actual...
Definition PrincipalComponentAnalysis.h:205
openvdb::v13_0::points::PcaAttributes::xformOutput
XformOutput xformOutput
Definition PrincipalComponentAnalysis.h:182
openvdb::v13_0::points::PcaAttributes::xform
std::string xform
Definition PrincipalComponentAnalysis.h:197
openvdb::v13_0::points::PcaAttributes::XformOutput::COMBINED_TRANSFORM
@ COMBINED_TRANSFORM
Definition PrincipalComponentAnalysis.h:170
openvdb::v13_0::points::PcaAttributes::XformOutput::STRETCH_AND_QUATERNION
@ STRETCH_AND_QUATERNION
Definition PrincipalComponentAnalysis.h:174
openvdb::v13_0::points::PcaSettings
Various settings for the neighborhood analysis of point distributions.
Definition PrincipalComponentAnalysis.h:67
openvdb::v13_0::points::PcaSettings::allowedAnisotropyRatio
float allowedAnisotropyRatio
Definition PrincipalComponentAnalysis.h:98
openvdb::v13_0::points::PcaSettings::averagePositions
float averagePositions
Definition PrincipalComponentAnalysis.h:151
openvdb::v13_0::points::PcaSettings::interrupter
util::NullInterrupter * interrupter
Definition PrincipalComponentAnalysis.h:153
openvdb::v13_0::points::PcaSettings::nonAnisotropicStretch
float nonAnisotropicStretch
Definition PrincipalComponentAnalysis.h:103
openvdb::v13_0::points::VolumeTransfer< PointDataTreeT >::VolumeTransfer
VolumeTransfer(TreeTypes *... trees)
openvdb::v13_0::points::VolumeTransfer< PointDataTreeT >::initialize
void initialize(const Coord &origin, const size_t, const CoordBBox &)
openvdb::v13_0::points::VolumeTransfer< PointDataTreeT >::buffer
ValueType< Idx > * buffer()
Definition PointTransfer.h:325
openvdb::v13_0::points::VolumeTransfer< PointDataTreeT >::mask
NodeMaskT * mask()
Definition PointTransfer.h:337
openvdb::v13_0::points::pca_internal::AttrIndices
Definition PrincipalComponentAnalysisImpl.h:58
openvdb::v13_0::points::pca_internal::AttrIndices::mCovMatrixIndex
size_t mCovMatrixIndex
Definition PrincipalComponentAnalysisImpl.h:61
openvdb::v13_0::points::pca_internal::AttrIndices::mPWsIndex
size_t mPWsIndex
Definition PrincipalComponentAnalysisImpl.h:62
openvdb::v13_0::points::pca_internal::AttrIndices::mPosSumIndex
size_t mPosSumIndex
Definition PrincipalComponentAnalysisImpl.h:59
openvdb::v13_0::points::pca_internal::AttrIndices::mWeightSumIndex
size_t mWeightSumIndex
Definition PrincipalComponentAnalysisImpl.h:60
openvdb::v13_0::points::pca_internal::AttrIndices::mEllipsesGroupIndex
GroupIndexT mEllipsesGroupIndex
Definition PrincipalComponentAnalysisImpl.h:63
openvdb::v13_0::points::pca_internal::CovarianceTransfer::initialize
void initialize(const Coord &origin, const size_t idx, const CoordBBox &bounds)
Definition PrincipalComponentAnalysisImpl.h:368
openvdb::v13_0::points::pca_internal::CovarianceTransfer::BaseT
PcaTransfer< PointDataTreeT > BaseT
Definition PrincipalComponentAnalysisImpl.h:345
openvdb::v13_0::points::pca_internal::CovarianceTransfer::finalize
bool finalize(const Coord &, size_t)
Definition PrincipalComponentAnalysisImpl.h:483
openvdb::v13_0::points::pca_internal::CovarianceTransfer::DIM
static const Index DIM
Definition PrincipalComponentAnalysisImpl.h:347
openvdb::v13_0::points::pca_internal::CovarianceTransfer::CovarianceTransfer
CovarianceTransfer(const AttrIndices &indices, const PcaSettings &settings, const Real vs, tree::LeafManager< PointDataTreeT > &manager, util::NullInterrupter *interrupt)
Definition PrincipalComponentAnalysisImpl.h:350
openvdb::v13_0::points::pca_internal::CovarianceTransfer::CovarianceTransfer
CovarianceTransfer(const CovarianceTransfer &other)
Definition PrincipalComponentAnalysisImpl.h:361
openvdb::v13_0::points::pca_internal::CovarianceTransfer::LOG2DIM
static const Index LOG2DIM
Definition PrincipalComponentAnalysisImpl.h:348
openvdb::v13_0::points::pca_internal::CovarianceTransfer::rasterizePoints
void rasterizePoints(const Coord &, const Index start, const Index end, const CoordBBox &bounds)
Definition PrincipalComponentAnalysisImpl.h:377
openvdb::v13_0::points::pca_internal::NoTimer
Definition PrincipalComponentAnalysisImpl.h:28
openvdb::v13_0::points::pca_internal::NoTimer::stop
void stop()
Definition PrincipalComponentAnalysisImpl.h:30
openvdb::v13_0::points::pca_internal::NoTimer::start
void start(const char *)
Definition PrincipalComponentAnalysisImpl.h:29
openvdb::v13_0::points::pca_internal::PcaTimer::PcaTimer
PcaTimer(util::NullInterrupter *const interrupt)
Definition PrincipalComponentAnalysisImpl.h:37
openvdb::v13_0::points::pca_internal::PcaTimer::stop
void stop()
Definition PrincipalComponentAnalysisImpl.h:45
openvdb::v13_0::points::pca_internal::PcaTimer::start
void start(const char *msg)
Definition PrincipalComponentAnalysisImpl.h:40
openvdb::v13_0::points::pca_internal::PcaTimer::mInterrupt
util::NullInterrupter *const mInterrupt
Definition PrincipalComponentAnalysisImpl.h:50
openvdb::v13_0::points::pca_internal::PcaTransfer::PcaTransfer
PcaTransfer(const AttrIndices &indices, const PcaSettings &settings, const Real vs, tree::LeafManager< PointDataTreeT > &manager, util::NullInterrupter *interrupt)
Definition PrincipalComponentAnalysisImpl.h:105
openvdb::v13_0::points::pca_internal::PcaTransfer::startPointLeaf
bool startPointLeaf(const typename PointDataTreeT::LeafNodeType &leaf)
Definition PrincipalComponentAnalysisImpl.h:166
openvdb::v13_0::points::pca_internal::PcaTransfer::maxSourcePointsPerVoxel
size_t maxSourcePointsPerVoxel() const
Definition PrincipalComponentAnalysisImpl.h:152
openvdb::v13_0::points::pca_internal::PcaTransfer::maxTargetPointsPerVoxel
size_t maxTargetPointsPerVoxel() const
Definition PrincipalComponentAnalysisImpl.h:153
openvdb::v13_0::points::pca_internal::PcaTransfer::range
Vec3i range() const
Definition PrincipalComponentAnalysisImpl.h:156
openvdb::v13_0::points::pca_internal::PcaTransfer::mIndices
const AttrIndices & mIndices
Definition PrincipalComponentAnalysisImpl.h:178
openvdb::v13_0::points::pca_internal::PcaTransfer::neighbourThreshold
size_t neighbourThreshold() const
Definition PrincipalComponentAnalysisImpl.h:151
openvdb::v13_0::points::pca_internal::PcaTransfer::mManager
const tree::LeafManager< PointDataTreeT > & mManager
Definition PrincipalComponentAnalysisImpl.h:181
openvdb::v13_0::points::pca_internal::PcaTransfer::initialize
LeafNodeType * initialize(const Coord &origin, const size_t idx, const CoordBBox &bounds)
Definition PrincipalComponentAnalysisImpl.h:158
openvdb::v13_0::points::pca_internal::PcaTransfer::searchRadius
float searchRadius() const
Definition PrincipalComponentAnalysisImpl.h:150
openvdb::v13_0::points::pca_internal::PcaTransfer::mTargetPosition
std::unique_ptr< PositionHandleT > mTargetPosition
Definition PrincipalComponentAnalysisImpl.h:182
openvdb::v13_0::points::pca_internal::PcaTransfer::range
Vec3i range(const Coord &, size_t) const
Definition PrincipalComponentAnalysisImpl.h:155
openvdb::v13_0::points::pca_internal::PcaTransfer::BaseT
VolumeTransfer< PointDataTreeT > BaseT
Definition PrincipalComponentAnalysisImpl.h:99
openvdb::v13_0::points::pca_internal::PcaTransfer::mSettings
const PcaSettings & mSettings
Definition PrincipalComponentAnalysisImpl.h:179
openvdb::v13_0::points::pca_internal::PcaTransfer::PcaTransfer
PcaTransfer(const PcaTransfer &other)
Definition PrincipalComponentAnalysisImpl.h:122
openvdb::v13_0::points::pca_internal::PcaTransfer::PositionHandleT
points::AttributeHandle< Vec3d, NullCodec > PositionHandleT
Definition PrincipalComponentAnalysisImpl.h:103
openvdb::v13_0::points::pca_internal::PcaTransfer::LeafNodeType
typename PointDataTreeT::LeafNodeType LeafNodeType
Definition PrincipalComponentAnalysisImpl.h:100
openvdb::v13_0::points::pca_internal::PcaTransfer::mDxInv
const Real mDxInv
Definition PrincipalComponentAnalysisImpl.h:180
openvdb::v13_0::points::pca_internal::PcaTransfer::endPointLeaf
bool endPointLeaf(const typename PointDataTreeT::LeafNodeType &)
Definition PrincipalComponentAnalysisImpl.h:175
openvdb::v13_0::points::pca_internal::PcaTransfer::mSourcePosition
std::unique_ptr< PositionHandleT > mSourcePosition
Definition PrincipalComponentAnalysisImpl.h:183
openvdb::v13_0::points::pca_internal::WeightPosSumsTransfer::finalize
bool finalize(const Coord &, size_t idx)
Definition PrincipalComponentAnalysisImpl.h:305
openvdb::v13_0::points::pca_internal::WeightPosSumsTransfer::WeightPosSumsTransfer
WeightPosSumsTransfer(const WeightPosSumsTransfer &other)
Definition PrincipalComponentAnalysisImpl.h:208
openvdb::v13_0::points::pca_internal::WeightPosSumsTransfer::initialize
void initialize(const Coord &origin, const size_t idx, const CoordBBox &bounds)
Definition PrincipalComponentAnalysisImpl.h:214
openvdb::v13_0::points::pca_internal::WeightPosSumsTransfer::BaseT
PcaTransfer< PointDataTreeT > BaseT
Definition PrincipalComponentAnalysisImpl.h:193
openvdb::v13_0::points::pca_internal::WeightPosSumsTransfer::DIM
static const Index DIM
Definition PrincipalComponentAnalysisImpl.h:195
openvdb::v13_0::points::pca_internal::WeightPosSumsTransfer::WeightPosSumsTransfer
WeightPosSumsTransfer(const AttrIndices &indices, const PcaSettings &settings, const Real vs, tree::LeafManager< PointDataTreeT > &manager, util::NullInterrupter *interrupt)
Definition PrincipalComponentAnalysisImpl.h:198
openvdb::v13_0::points::pca_internal::WeightPosSumsTransfer::LOG2DIM
static const Index LOG2DIM
Definition PrincipalComponentAnalysisImpl.h:196
openvdb::v13_0::points::pca_internal::WeightPosSumsTransfer::rasterizePoints
void rasterizePoints(const Coord &, const Index start, const Index end, const CoordBBox &bounds)
Definition PrincipalComponentAnalysisImpl.h:223
openvdb::v13_0::util::NullInterrupter
Base class for interrupters.
Definition NullInterrupter.h:26
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:218