Skip to content

point_cloud

point_cloud #

The point-cloud file scalar; a loaded PointCloud is the file path.

It carries a cloud.point_count() reader that parses self. A point cloud is a non-core extension scalar (dotted reverse-DNS name), so its schema self-describes with kind + extensions and a reader without a PLY decoder can still store or skip the bytes. The format is PLY (Polygon File Format): each vertex carries x/y/z in metres and may carry red/green/blue colour and a confidence quality channel — a loaded value is the standard .ply path, ready for Open3D, trimesh, or MeshLab.

PointCloud #

Bases: FileScalar

A 3D point cloud as a PLY file; a loaded value is its file Path.

Source code in capturegraph-lib/capturegraph/types/scalars/geometry/point_cloud.py
class PointCloud(FileScalar, name="edu.cornell.point_cloud", extensions=("ply",)):
    """A 3D point cloud as a PLY file; a loaded value is its file ``Path``."""

    __slots__ = ()

    @classmethod
    def of_points(cls, positions: "np.ndarray", colors: "np.ndarray") -> Self:
        """A fresh cloud of ``positions`` (N×3 metres) and ``colors`` (N×3 ``uint8``).

        The file lives in the recipe workspace, as ``new`` allocates it.

        Raises:
            ScratchNotConfigured: If no scratch is configured.
        """
        from capturegraph.types.scalars.geometry.support.ply import write_points

        cloud = cls.new()
        write_points(cloud, positions, colors)
        return cloud

    def points(self) -> "tuple[np.ndarray, np.ndarray]":
        """The vertices as ``(positions, colors)``: N×3 ``float32`` metres and N×3 ``uint8``.

        A cloud without colour reads as mid-grey.

        Raises:
            ValueError: If ``self`` is not a binary little-endian PLY with positions.
        """
        import numpy as np

        from capturegraph.types.scalars.geometry.support.ply import vertex_columns

        columns = vertex_columns(self)
        positions = np.stack([columns["x"], columns["y"], columns["z"]], axis=1).astype(np.float32)
        if "red" in columns:
            colors = np.stack([columns["red"], columns["green"], columns["blue"]], axis=1)
        else:
            colors = np.full_like(positions, 127.0)
        return positions, colors.astype(np.uint8)

    def point_count(self) -> int:
        """The number of vertices, read from the PLY header (no decode of the body).

        Raises:
            ValueError: If the header declares no vertex element.
        """
        with open(self, "rb") as handle:
            for raw in handle:
                line = raw.decode("ascii", "replace").strip()
                if line.startswith("element vertex"):
                    return int(line.split()[-1])
                if line == "end_header":
                    break
        raise ValueError(f"{self}: PLY header declares no 'element vertex'")

of_points(positions, colors) classmethod #

A fresh cloud of positions (N×3 metres) and colors (N×3 uint8).

The file lives in the recipe workspace, as new allocates it.

Raises:

Type Description
ScratchNotConfigured

If no scratch is configured.

Source code in capturegraph-lib/capturegraph/types/scalars/geometry/point_cloud.py
@classmethod
def of_points(cls, positions: "np.ndarray", colors: "np.ndarray") -> Self:
    """A fresh cloud of ``positions`` (N×3 metres) and ``colors`` (N×3 ``uint8``).

    The file lives in the recipe workspace, as ``new`` allocates it.

    Raises:
        ScratchNotConfigured: If no scratch is configured.
    """
    from capturegraph.types.scalars.geometry.support.ply import write_points

    cloud = cls.new()
    write_points(cloud, positions, colors)
    return cloud

point_count() #

The number of vertices, read from the PLY header (no decode of the body).

Raises:

Type Description
ValueError

If the header declares no vertex element.

Source code in capturegraph-lib/capturegraph/types/scalars/geometry/point_cloud.py
def point_count(self) -> int:
    """The number of vertices, read from the PLY header (no decode of the body).

    Raises:
        ValueError: If the header declares no vertex element.
    """
    with open(self, "rb") as handle:
        for raw in handle:
            line = raw.decode("ascii", "replace").strip()
            if line.startswith("element vertex"):
                return int(line.split()[-1])
            if line == "end_header":
                break
    raise ValueError(f"{self}: PLY header declares no 'element vertex'")

points() #

The vertices as (positions, colors): N×3 float32 metres and N×3 uint8.

A cloud without colour reads as mid-grey.

Raises:

Type Description
ValueError

If self is not a binary little-endian PLY with positions.

Source code in capturegraph-lib/capturegraph/types/scalars/geometry/point_cloud.py
def points(self) -> "tuple[np.ndarray, np.ndarray]":
    """The vertices as ``(positions, colors)``: N×3 ``float32`` metres and N×3 ``uint8``.

    A cloud without colour reads as mid-grey.

    Raises:
        ValueError: If ``self`` is not a binary little-endian PLY with positions.
    """
    import numpy as np

    from capturegraph.types.scalars.geometry.support.ply import vertex_columns

    columns = vertex_columns(self)
    positions = np.stack([columns["x"], columns["y"], columns["z"]], axis=1).astype(np.float32)
    if "red" in columns:
        colors = np.stack([columns["red"], columns["green"], columns["blue"]], axis=1)
    else:
        colors = np.full_like(positions, 127.0)
    return positions, colors.astype(np.uint8)