Skip to content

Commit fc51728

Browse files
committed
5.2.21 [IPointCloudNode] DeleteWithClassification
1 parent 790d05d commit fc51728

File tree

3 files changed

+333
-0
lines changed

3 files changed

+333
-0
lines changed

RELEASE_NOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
### 5.2.21
2+
- [IPointCloudNode] DeleteWithClassification
3+
14
### 5.2.20
25
- [SimplePickTree] SimplePickPoint records generic attributes
36

src/Aardvark.Algodat.Tests/DeleteTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ You should have received a copy of the GNU Affero General Public License
1919
using System.Collections.Generic;
2020
using System.Linq;
2121
using System.Threading;
22+
using static Aardvark.Base.MultimethodTest;
2223

2324
namespace Aardvark.Geometry.Tests
2425
{
@@ -61,6 +62,24 @@ public static PointSet CreateRegularPointsInUnitCube(int n, int splitLimit)
6162
return PointCloud.Chunks(new Chunk(ps, null), config);
6263
}
6364

65+
public static PointSet CreateRandomClassifiedPoints(int n, int splitLimit)
66+
{
67+
var ps = new List<V3d>();
68+
var ks = new List<byte>();
69+
var rand = new RandomSystem();
70+
for (var x = 0; x < n; x++)
71+
{
72+
ps.Add(rand.UniformV3d());
73+
ks.Add((byte)rand.UniformInt(4));
74+
}
75+
var config = ImportConfig.Default
76+
.WithStorage(PointCloud.CreateInMemoryStore(cache: default))
77+
.WithKey("testaa")
78+
.WithOctreeSplitLimit(splitLimit)
79+
;
80+
return PointCloud.Chunks(new Chunk(ps, null, null, null, ks), config);
81+
}
82+
6483
[Test]
6584
public void DeleteCollapsesNodes()
6685
{
@@ -152,5 +171,21 @@ public void DeleteAll()
152171
Assert.IsTrue(b.PointCount == 0);
153172
Assert.IsTrue(a.Id != b.Id);
154173
}
174+
175+
[Test]
176+
public void DeleteWithClassifications()
177+
{
178+
for (int i = 0; i < 10; i++)
179+
{
180+
var q1 = new Box3d(new V3d(0.0), new V3d(0.23));
181+
var a = CreateRandomClassifiedPoints(10000, 256);
182+
var b = a.Delete(n => q1.Contains(n.BoundingBoxExactGlobal), n => !(q1.Contains(n.BoundingBoxExactGlobal) || q1.Intersects(n.BoundingBoxExactGlobal)), p => q1.Contains(p), a.Storage, CancellationToken.None);
183+
b.ValidateTree();
184+
Assert.IsTrue(b.Root?.Value.NoPointIn(p => q1.Contains(p)));
185+
var c = b.Root?.Value.DeleteWithClassifications(n => false, n => false, (p,k) => k==0, a.Storage, CancellationToken.None,256);
186+
// Did it really delete the classification 0u?
187+
c.ForEachNode(false, (node) => node.Classifications?.Value.ForEach((k) => Assert.IsTrue(k != 0)));
188+
}
189+
}
155190
}
156191
}

src/Aardvark.Geometry.PointSet/Octrees/Delete.cs

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,301 @@ int splitLimit
304304
;
305305

306306

307+
if (lodKd != null)
308+
{
309+
storage.Add(kdId, lodKd.Data);
310+
data = data.Add(Durable.Octree.PointRkdTreeFDataReference, kdId);
311+
}
312+
if (lodCs != null)
313+
{
314+
storage.Add(csId, lodCs);
315+
data = data.Add(Durable.Octree.Colors4bReference, csId);
316+
}
317+
if (lodNs != null)
318+
{
319+
storage.Add(nsId, lodNs);
320+
data = data.Add(Durable.Octree.Normals3fReference, nsId);
321+
}
322+
if (lodIs != null)
323+
{
324+
storage.Add(isId, lodIs);
325+
data = data.Add(Durable.Octree.Intensities1iReference, isId);
326+
}
327+
if (lodKs != null)
328+
{
329+
storage.Add(ksId, lodKs);
330+
data = data.Add(Durable.Octree.Classifications1bReference, ksId);
331+
}
332+
333+
// MinTreeDepth MaxTreeDepth SubNodeIds??
334+
return new PointSetNode(data, storage, writeToStore: true);
335+
}
336+
}
337+
}
338+
/// <summary>
339+
/// Returns new tree with all points deleted which are inside. Additionally tests visited points using classifications.
340+
/// </summary>
341+
public static IPointCloudNode DeleteWithClassifications(this IPointCloudNode root,
342+
Func<IPointCloudNode, bool> isNodeFullyInside,
343+
Func<IPointCloudNode, bool> isNodeFullyOutside,
344+
Func<V3d, byte, bool> isPositionInside,
345+
Storage storage, CancellationToken ct,
346+
int splitLimit
347+
)
348+
{
349+
if (root == null) return null;
350+
351+
if (root is FilteredNode f)
352+
{
353+
if (f.Filter is ISpatialFilter filter)
354+
{
355+
bool remove(IPointCloudNode n) => filter.IsFullyInside(n) && isNodeFullyInside(n);
356+
bool keep(IPointCloudNode n) => filter.IsFullyOutside(n) || isNodeFullyOutside(n);
357+
bool contains(V3d pt, byte c) => filter.Contains(pt) && isPositionInside(pt,c);
358+
var res = f.Node.DeleteWithClassifications(remove, keep, contains, storage, ct, splitLimit);
359+
return FilteredNode.Create(res, f.Filter);
360+
}
361+
else
362+
{
363+
throw new NotImplementedException("cannot delete on PointCloud with non-spatial filter");
364+
}
365+
}
366+
367+
368+
if (isNodeFullyInside(root)) return null;
369+
if (isNodeFullyOutside(root))
370+
{
371+
if (!root.IsMaterialized)
372+
{
373+
root = root.Materialize();
374+
}
375+
return root;
376+
}
377+
378+
if(root.IsLeaf)
379+
{
380+
var ps = root.HasPositions ? new List<V3f>() : null;
381+
var cs = root.HasColors ? new List<C4b>() : null;
382+
var ns = root.HasNormals ? new List<V3f>() : null;
383+
var js = root.HasIntensities ? new List<int>() : null;
384+
var ks = root.HasClassifications ? new List<byte>() : null;
385+
var oldPs = root.Positions?.Value;
386+
var oldCs = root.Colors?.Value;
387+
var oldNs = root.Normals?.Value;
388+
var oldIs = root.Intensities?.Value;
389+
var oldKs = root.Classifications?.Value;
390+
var bbabs = Box3d.Invalid;
391+
var bbloc = Box3f.Invalid;
392+
393+
for (var i = 0; i < oldPs.Length; i++)
394+
{
395+
var pabs = (V3d)oldPs[i] + root.Center;
396+
397+
if(oldKs == null) { throw new ArgumentException("Can not call DeleteWithClassifications for point cloud without classifications. edd662d4-1656-4800-96e0-cbe7856ab00c"); }
398+
var oldK = oldKs[i];
399+
400+
if (!isPositionInside(pabs, oldK))
401+
{
402+
if (oldPs != null) ps.Add(oldPs[i]);
403+
if (oldCs != null) cs.Add(oldCs[i]);
404+
if (oldNs != null) ns.Add(oldNs[i]);
405+
if (oldIs != null) js.Add(oldIs[i]);
406+
if (oldKs != null) ks.Add(oldKs[i]);
407+
bbabs.ExtendBy(pabs);
408+
bbloc.ExtendBy(oldPs[i]);
409+
}
410+
}
411+
412+
if (ps.Count == 0) return null;
413+
414+
var psa = ps.ToArray();
415+
var newId = Guid.NewGuid();
416+
var kd = psa.Length < 1 ? null : psa.BuildKdTree();
417+
418+
Guid psId = Guid.NewGuid();
419+
Guid kdId = kd != null ? Guid.NewGuid() : Guid.Empty;
420+
Guid csId = cs != null ? Guid.NewGuid() : Guid.Empty;
421+
Guid nsId = ns != null ? Guid.NewGuid() : Guid.Empty;
422+
Guid isId = js != null ? Guid.NewGuid() : Guid.Empty;
423+
Guid ksId = ks != null ? Guid.NewGuid() : Guid.Empty;
424+
425+
storage.Add(psId, psa);
426+
427+
var data = ImmutableDictionary<Durable.Def, object>.Empty
428+
.Add(Durable.Octree.NodeId, newId)
429+
.Add(Durable.Octree.Cell, root.Cell)
430+
.Add(Durable.Octree.BoundingBoxExactGlobal, bbabs)
431+
.Add(Durable.Octree.BoundingBoxExactLocal, bbloc)
432+
.Add(Durable.Octree.PositionsLocal3fReference, psId)
433+
.Add(Durable.Octree.PointCountCell, ps.Count)
434+
.Add(Durable.Octree.PointCountTreeLeafs, (long)ps.Count)
435+
.Add(Durable.Octree.MaxTreeDepth, 0)
436+
.Add(Durable.Octree.MinTreeDepth, 0)
437+
;
438+
439+
440+
if (kd != null)
441+
{
442+
storage.Add(kdId, kd.Data);
443+
data = data.Add(Durable.Octree.PointRkdTreeFDataReference, kdId);
444+
}
445+
if (cs != null)
446+
{
447+
storage.Add(csId, cs.ToArray());
448+
data = data.Add(Durable.Octree.Colors4bReference, csId);
449+
}
450+
if (ns != null)
451+
{
452+
storage.Add(nsId, ns.ToArray());
453+
data = data.Add(Durable.Octree.Normals3fReference, nsId);
454+
}
455+
if (js != null)
456+
{
457+
storage.Add(isId, js.ToArray());
458+
data = data.Add(Durable.Octree.Intensities1iReference, isId);
459+
}
460+
if (ks != null)
461+
{
462+
storage.Add(ksId, ks.ToArray());
463+
data = data.Add(Durable.Octree.Classifications1bReference, ksId);
464+
}
465+
466+
// MinTreeDepth MaxTreeDepth SubNodeIds??
467+
return new PointSetNode(data, storage, writeToStore: true);
468+
}
469+
else
470+
{
471+
var subnodes = root.Subnodes.Map((r) => r?.Value?.DeleteWithClassifications(isNodeFullyInside, isNodeFullyOutside, isPositionInside, storage, ct, splitLimit));
472+
var pointCountTree = subnodes.Sum((n) => n != null ? n.PointCountTree : 0);
473+
if (pointCountTree == 0)
474+
{
475+
return null;
476+
}
477+
else if (pointCountTree <= splitLimit)
478+
{
479+
var psabs = root.HasPositions ? new List<V3d>() : null;
480+
var cs = root.HasColors ? new List<C4b>() : null;
481+
var ns = root.HasNormals ? new List<V3f>() : null;
482+
var js = root.HasIntensities ? new List<int>() : null;
483+
var ks = root.HasClassifications ? new List<byte>() : null;
484+
foreach (var c in subnodes)
485+
{
486+
if (c != null) MergeExtensions.CollectEverything(c, psabs, cs, ns, js, ks);
487+
}
488+
Debug.Assert(psabs.Count == pointCountTree);
489+
var psa = psabs.MapToArray((p) => (V3f)(p - root.Center));
490+
var kd = psa.Length < 1 ? null : psa.BuildKdTree();
491+
492+
493+
Guid psId = Guid.NewGuid();
494+
Guid kdId = kd != null ? Guid.NewGuid() : Guid.Empty;
495+
Guid csId = cs != null ? Guid.NewGuid() : Guid.Empty;
496+
Guid nsId = ns != null ? Guid.NewGuid() : Guid.Empty;
497+
Guid isId = js != null ? Guid.NewGuid() : Guid.Empty;
498+
Guid ksId = ks != null ? Guid.NewGuid() : Guid.Empty;
499+
500+
var bbabs = new Box3d(psabs);
501+
502+
var newId = Guid.NewGuid();
503+
storage.Add(psId, psa);
504+
505+
var data = ImmutableDictionary<Durable.Def, object>.Empty
506+
.Add(Durable.Octree.NodeId, newId)
507+
.Add(Durable.Octree.Cell, root.Cell)
508+
.Add(Durable.Octree.BoundingBoxExactGlobal, bbabs)
509+
.Add(Durable.Octree.BoundingBoxExactLocal, (Box3f)(bbabs - root.Center))
510+
.Add(Durable.Octree.PositionsLocal3fReference, psId)
511+
.Add(Durable.Octree.PointCountCell, (int)pointCountTree)
512+
.Add(Durable.Octree.PointCountTreeLeafs, pointCountTree)
513+
.Add(Durable.Octree.MaxTreeDepth, 0)
514+
.Add(Durable.Octree.MinTreeDepth, 0)
515+
;
516+
if (kd != null)
517+
{
518+
storage.Add(kdId, kd.Data);
519+
data = data.Add(Durable.Octree.PointRkdTreeFDataReference, kdId);
520+
}
521+
if (cs != null)
522+
{
523+
storage.Add(csId, cs.ToArray());
524+
data = data.Add(Durable.Octree.Colors4bReference, csId);
525+
}
526+
if (ns != null)
527+
{
528+
storage.Add(nsId, ns.ToArray());
529+
data = data.Add(Durable.Octree.Normals3fReference, nsId);
530+
}
531+
if (js != null)
532+
{
533+
storage.Add(isId, js.ToArray());
534+
data = data.Add(Durable.Octree.Intensities1iReference, isId);
535+
}
536+
if (ks != null)
537+
{
538+
storage.Add(ksId, ks.ToArray());
539+
data = data.Add(Durable.Octree.Classifications1bReference, ksId);
540+
}
541+
542+
// MinTreeDepth MaxTreeDepth SubNodeIds??
543+
return new PointSetNode(data, storage, writeToStore: true);
544+
}
545+
else
546+
{
547+
var bbabs = new Box3d(subnodes.Map(n => n != null ? n.BoundingBoxExactGlobal : Box3d.Invalid));
548+
var subids = subnodes.Map(n => n != null ? n.Id : Guid.Empty);
549+
550+
var maxDepth = subnodes.Max(n => n != null ? n.MaxTreeDepth + 1 : 0);
551+
var minDepth = subnodes.Min(n => n != null ? n.MinTreeDepth + 1 : 0);
552+
553+
554+
var octreeSplitLimit = splitLimit;
555+
var fractions = LodExtensions.ComputeLodFractions(subnodes);
556+
var aggregateCount = Math.Min(octreeSplitLimit, subnodes.Sum(x => x?.PointCountCell) ?? 0);
557+
var counts = LodExtensions.ComputeLodCounts(aggregateCount, fractions);
558+
559+
// generate LoD data ...
560+
var needsCs = subnodes.Any(x => x != null && x.HasColors);
561+
var needsNs = subnodes.Any(x => x != null && x.HasNormals);
562+
var needsIs = subnodes.Any(x => x != null && x.HasIntensities);
563+
var needsKs = subnodes.Any(x => x != null && x.HasClassifications);
564+
565+
var subcenters = subnodes.Map(x => x?.Center);
566+
var lodPs = LodExtensions.AggregateSubPositions(counts, aggregateCount, root.Center, subcenters, subnodes.Map(x => x?.Positions?.Value));
567+
var lodCs = needsCs ? LodExtensions.AggregateSubArrays(counts, aggregateCount, subnodes.Map(x => x?.Colors?.Value)) : null;
568+
var lodNs = needsNs ? LodExtensions.AggregateSubArrays(counts, aggregateCount, subnodes.Map(x => x?.Normals?.Value)) : null;
569+
var lodIs = needsIs ? LodExtensions.AggregateSubArrays(counts, aggregateCount, subnodes.Map(x => x?.Intensities?.Value)) : null;
570+
var lodKs = needsKs ? LodExtensions.AggregateSubArrays(counts, aggregateCount, subnodes.Map(x => x?.Classifications?.Value)) : null;
571+
var lodKd = lodPs.Length < 1 ? null : lodPs.BuildKdTree();
572+
573+
574+
Guid psId = Guid.NewGuid();
575+
Guid kdId = lodKd != null ? Guid.NewGuid() : Guid.Empty;
576+
Guid csId = lodCs != null ? Guid.NewGuid() : Guid.Empty;
577+
Guid nsId = lodNs != null ? Guid.NewGuid() : Guid.Empty;
578+
Guid isId = lodIs != null ? Guid.NewGuid() : Guid.Empty;
579+
Guid ksId = lodKs != null ? Guid.NewGuid() : Guid.Empty;
580+
581+
582+
var newId = Guid.NewGuid();
583+
storage.Add(psId, lodPs);
584+
585+
var bbloc = new Box3f(lodPs);
586+
587+
// be inner node
588+
var data = ImmutableDictionary<Durable.Def, object>.Empty
589+
.Add(Durable.Octree.SubnodesGuids, subids)
590+
.Add(Durable.Octree.NodeId, newId)
591+
.Add(Durable.Octree.Cell, root.Cell)
592+
.Add(Durable.Octree.BoundingBoxExactGlobal, bbabs)
593+
.Add(Durable.Octree.BoundingBoxExactLocal, bbloc)
594+
.Add(Durable.Octree.PositionsLocal3fReference, psId)
595+
.Add(Durable.Octree.PointCountCell, lodPs.Length)
596+
.Add(Durable.Octree.PointCountTreeLeafs, pointCountTree)
597+
.Add(Durable.Octree.MaxTreeDepth, maxDepth)
598+
.Add(Durable.Octree.MinTreeDepth, minDepth)
599+
;
600+
601+
307602
if(lodKd != null)
308603
{
309604
storage.Add(kdId, lodKd.Data);

0 commit comments

Comments
 (0)