Scalar Fields

Attach, manipulate, and move scalar fields alongside coordinates.

Creating with scalar fields

There are multiple ways of creating scalar fields. First is a dictionary can be passed upon initialisation of the object:

import numpy as np
from pchandler.core import PointCloudData

N = 10
xyz = np.random.rand(N, 3)

fields = {
   "intensity": np.random.rand(N),      # scalar field
   "quality": np.linspace(0, 1, N),     # another scalar field
}

pcd = PointCloudData(
   xyz=xyz,
   scalar_fields=fields
)

Also, it’s possible to add them directly to the scalar field manager.

from pchandler.scalar_fields import ScalarField, RGBFields

# Via the "create_field()" method of the ScalarFieldsManager / "scalar_fields" property
pcd.scalar_fields.create_field('new_field', np.random.rand(N))

# Adding an existing ScalarField object
new_displacement_field = ScalarField(np.random.rand(N) * 50, name='displacement')

pcd.scalar_fields.add_field(new_displacement_field)

# Or like adding a key-value pair to a dictionary
pcd.scalar_fields['displacements'] = np.random.rand(N)

pcd.scalar_fields['rgb'] = np.random.randint(0, 255, (N, 3)).astype(np.uint8)

Dedicated Scalar Fields

There are a number of special scalar fields where they have a dedicated namespace:

These can then be accessed directly from the PointCloudData base object like this

pcd.rgb
pcd.r
pcd.normals # All normals
pcd.ny      # Normal components on the y axis
pcd.intensity
pcd.reflectance

Setting RGB and normals

Likewise, these values can also be updated at the root level .. code-block:: python

import numpy as np from pchandler.core import PointCloudData

N = 5 pcd = PointCloudData(np.random.rand(N, 3), numerical_optimization_shift=None)

# RGB can be float in [0, 1] or uint8 in [0, 255] pcd.rgb = (np.random.rand(N, 3)).astype(float)

# Normals: Nx3 unit vectors (you may want to normalize your inputs first) normals = np.random.randn(N, 3).astype(float) pcd.normals = normals

Note

All normal values will be scaled to unit vectors
np.allclose(np.linalg.norm(pcd.normals, axis=1), 1)

Access and modify scalar fields

import numpy as np
from pchandler.core import PointCloudData

N = 8
pcd = PointCloudData(
    np.random.rand(N, 3),
    scalar_fields={
         "custom_field": np.random.rand(N),
         "intensity": np.random.rand(N),
         "field_2": np.random.rand(N)
    }
)

# Write or replace
pcd.scalar_fields['custom_field'] = np.linspace(0.0, 1.0, N)
print(f"Number of fields before: {len(pcd.scalar_fields)}")

# Removal can be done by setting to None or the ``remove_field()`` method
pcd.scalar_fields.remove_field('custom_field')
pcd.intensity = None
pcd.scalar_fields['field_2'] = None

print(f"Number of fields after: {len(pcd.scalar_fields)}")

Sampling preserves fields

import numpy as np
from pchandler.core import PointCloudData

N = 100
pcd = PointCloudData(
    np.random.rand(N, 3),
    scalar_fields={
        "intensity": np.random.rand(N),
        "class_id": np.random.randint(0, 5, size=N),
    },
    numerical_optimization_shift=None,
)

mask = pcd.xyz[:, 2] > 0.5
subset = pcd.sample(mask)

# All included fields are sampled consistently
assert len(subset) == mask.sum()

print(subset.intensity)