aardvark-platform / aardvark.algodat

Aardvark.Algodat contains advanced geometric and photometric data structures and algorithms. It is part of the open-source Aardvark Platform for visual computing, real-time graphics, and visualization.
https://aardvarkians.com/
GNU Affero General Public License v3.0
34 stars 6 forks source link

NullReferenceException on import #13

Closed KuraiAndras closed 3 years ago

KuraiAndras commented 4 years ago

I'm trying to use this library to parse e57 files, but can't get the library to work.

The documentation on point clouds suggest just calling PointCloud.Import but it does not have an overload with 2 strings for the store, also it always throws a null reference exception from inside aadvark.

image

Sample code:

[Fact]
public void PointCloudDataHasPoints()
{
    var cloud = PointCloud.Import(@"C:\Path\To\My\scan.e57");

    Assert.False(cloud.IsEmpty);
    Assert.True(cloud.PointCount > 0);

    _logger.WriteLine($"Number of points:{cloud.PointCount}");
}

output:

System.NullReferenceException : Object reference not set to an instance of an object.
   at Aardvark.Data.Points.PointCloudFileFormat.ParseFile(String filename, ParseConfig config)
   at Aardvark.Geometry.Points.PointCloudFormatExtensions.ImportFile(PointCloudFileFormat self, String filename, ImportConfig config)
   at Aardvark.Geometry.Points.PointCloud.Import(String filename, ImportConfig config)
// my code trace
}

Added nugets:

<PackageReference Include="Aardvark.Data.E57" Version="5.0.11" />
<PackageReference Include="Aardvark.Geometry.PointSet" Version="5.0.11" />
<PackageReference Include="Aardvark.Geometry.PointTree" Version="5.0.11" />

Am I doing something wrong? Do I have to explicitly use an e57 converter somewhere? If so how/where and can you provide some kind of documentation on it? I can't seem to find it.

stefanmaierhofer commented 4 years ago

Hello, I'll look into it - please hang on - probably our documentation is not up to date.

stefanmaierhofer commented 4 years ago

Here are some quick usage examples! I hope this helps? (note to myself: clean up import and update documentation! ;-))

using Aardvark.Data.E57;
using Aardvark.Data.Points;
using Aardvark.Data.Points.Import;
using Aardvark.Geometry.Points;
using System;
using System.IO;
using Uncodium.SimpleStore;

namespace Aardvark.Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            var filename = @"C:\Path\To\My\scan.e57";

            //////////////////////////////////////////////////////////
            // Example 1:
            // read .e57 file in chunks 
            Console.WriteLine("Example 1");
            var chunks = E57.Chunks(filename, ParseConfig.Default);
            foreach (var chunk in chunks)
            {
                Console.WriteLine($"read {chunk.Count} points:  {chunk.Positions[0]:N3}, ...");
            }

            //////////////////////////////////////////////////////////
            // Example 2:
            // extract .e57 file structure (incl. meta data)
            Console.WriteLine("Example 2");
            using var stream = File.OpenRead(filename);
            var header = ASTM_E57.E57FileHeader.Parse(stream, new FileInfo(filename).Length);
            var bounds = header.E57Root.Data3D[0].CartesianBounds.Bounds;
            Console.WriteLine($"bounding box: {bounds:N3}");

            //////////////////////////////////////////////////////////
            // Example 3:
            // importing .e57 file into disk store (will create octree and levels-of-detail
            Console.WriteLine("Example 3");
            using var store = new SimpleDiskStore(@"C:\tmp\mystore").ToPointCloudStore();
            var cloud = PointCloud.Import(filename, ImportConfig.Default.WithStorage(store));
            var key = cloud.Id;
            Console.WriteLine($"imported point cloud with {cloud.PointCount:N0} points and id {key}");

            //////////////////////////////////////////////////////////
            // Example 4:
            // open point cloud from existing store
            Console.WriteLine("Example 4");
            var test = store.GetPointSet(key);
            Console.WriteLine($"opened point cloud {key} with {test.PointCount} points");
        }
    }
}
KuraiAndras commented 4 years ago

Thanks, these seem to work!

Also thanks for the fast response

stefanmaierhofer commented 4 years ago

Todo: clean up point cloud import functions and update documentation!

stefanmaierhofer commented 4 years ago

another example for importing into in-memory store

            //////////////////////////////////////////////////////////
            // Example 5:
            // importing .e57 file into in-memory store
            Console.WriteLine("Example 5");
            var cloudInMem = PointCloud.Import(filename, ImportConfig.Default.WithInMemoryStore());
            var keyInMem = cloudInMem.Id;
            Console.WriteLine($"imported point cloud with {cloudInMem.PointCount:N0} points and id {keyInMem}");
stefanmaierhofer commented 4 years ago

a first analysis shows that the NullReferenceException happens because the importer fails to register the .e57 parser via introspection although Aardvark.Data.E57 lib is referenced. As soon as any type inside Aardvark.Data.E57 is referenced by the program registration works again. E.g. var _ = typeof(ASTM_E57.E57Blob);

The compiler seems to remove the reference if a lib is not used, therefore the current introspection mechanism (dependency walker) does not see the lib and therefore cannot register the parser.

Todo:

krauthaufen commented 4 years ago

Hey, this has been that way ever since .NET existed, but you could use the [OnAardvarkInit] attribute on some method registering it, which will cause the method to be called as long as the DLL is somewhere in the OutputDirectory...

krauthaufen commented 4 years ago

Sadly .NET doesn't have a plugin mechanism afaik but as long as you're okay with calling Aardvark.Init() before using these things this should work. We're using the same mechanism for PixImage loading plugins...

Maybe the base-lib could also just try to load all the loader libs itself...

KuraiAndras commented 4 years ago

If I simply do:

foreach (var chunk in E57.Chunks(filename, ParseConfig.Default))
{
    for (var i = 0; i < chunk.Count; i++)
    {
        var position = chunk.Positions[i];
        var color = chunk.Colors[i];
        var normal = chunk.Normals[i];
    }
}

I get a null reference exception there too when accessing the normals. This however does not happen when iterating through chunks after loading into some store.

stefanmaierhofer commented 4 years ago

Hi, when iterating directly over a file (e.g. via E57.Chunks) you only get the data contained in the file. Because .e57 files do not contain normals (the .e57 specification does not provide for this) you get the exception. In general: you should access the various data in a chunk only after checking if it exists (e.g. chunk.HasNormals, chunk.HasColors, ...). Why are there normals in a store? By default, the import pipeline does generate normals if no normals are contained in the input (by locally fitting a plane to k-nearest neighbours of each point). That's why you get normals after importing into a store. Hope this helps!