Skip to main content

SceneZoo

Documentation • Installation • Quick start • Examples • Datasets • API reference

PyPI Documentation CI License

SceneZoo reads popular 3D indoor-scene datasets (such as ScanNet, ScanNet++, ARKitScenes, and Matterport3D) through one Python API. Point it at a dataset you have downloaded, and get meshes, point clouds, segmentation labels, 3D boxes, and synchronized RGB-D frames with cameras as NumPy arrays and Open3D objects, without writing a new loader for every dataset.

Installation

SceneZoo supports Python 3.10 to 3.14.

pip install scenezoo

For development, clone the repository and install it in editable mode with the test tools:

git clone https://github.com/eamonn-zh/scenezoo.git
cd scenezoo
pip install -e ".[dev]"

Video frame sources (ARKitScenes MOV, MultiScan, and ScanNet++ iPhone captures) are decoded frame-exactly with PyAV, whose wheels bundle FFmpeg; no system ffmpeg installation is needed.

Unified API

from scenezoo import get_dataset, get_dataset_info, list_datasets

dataset = get_dataset("scannetv2", "/path/to/scannet")
scene_id = dataset.get_ids("val")[0]

if dataset.supports("mesh"):
    mesh = dataset.get_mesh(scene_id)

if dataset.supports("points"):
    points = dataset.get_points(scene_id)

if dataset.supports("segmentation"):
    segmentation = dataset.get_segmentation(scene_id)
    print(segmentation.domain, segmentation.labels.shape)

if dataset.supports("boxes"):
    boxes_by_id, id_to_name = dataset.get_boxes(scene_id)

if dataset.supports("frames"):
    frames = dataset.get_frames(
        scene_id,
        step=30,
        items=("rgb", "depth", "rgb_intrinsics", "world_to_camera"),
        output_size=(640, 480),
        center_crop=True,
    )

print(list_datasets())
print(get_dataset_info("scannetv2"))  # canonical release metadata

list_datasets() and get_dataset_info() use a static built-in catalog, so they do not import Open3D, OpenCV, video decoders, or adapter modules. Inspect get_dataset_info(name).spec for operations, mesh/box/label variants, and frame sources. Accessing .dataset_class or calling get_dataset() intentionally loads only the selected adapter. The scenezoo.io and scenezoo.ops namespaces also resolve re-exported helpers lazily, so importing cache or frame utilities does not initialize unrelated video, annotation, or geometry runtimes.

The seven common dataset calls are:

  • check(*, sample_ids=None, require_complete=False, raise_on_error=False)
  • get_ids(split=None)
  • get_mesh(sample_id, *, mesh_type=None)
  • get_points(sample_id)
  • get_segmentation(sample_id)
  • get_boxes(sample_id, *, box_type=...)
  • get_frames(sample_id, *, indices=None, step=1, items=..., output_size=None, center_crop=False, rotate_to_up=True)

Unsupported operations and unsupported frame items raise UnsupportedOperationError. Unknown operation names, frame item names, and invalid argument combinations raise ValueError.

Check a download before using it

Dataset checking is explicit and never runs during construction or normal data access. It checks paths and file groups without decoding meshes, images, or videos:

dataset = get_dataset("scannetv2", "/path/to/scannet", offline=True)
report = dataset.check()
print(report)

# Check only the scenes a job is about to use. Missing requested IDs are errors.
focused = dataset.check(sample_ids=("scene0000_00", "scene0001_00"))

# Treat every missing ID from the official splits as an error and raise once.
dataset.check(require_complete=True, raise_on_error=True)

DatasetCheckReport exposes ok, errors, warnings, issues, and stats. Each CheckIssue contains a severity, stable code, explanation, relevant path, expected layout, and suggested action. An intentionally partial download reports missing split scenes as warnings by default; explicitly requested scenes are always required. raise_on_error=True raises DatasetCheckError carrying the same report.

The checkers understand each release's archive policy. ScanNet, ScanNet++, ARKitScenes, MultiScan, ASE, Matterport3D, Structured3D, S3DIS, and SceneNN need their download archives extracted. ScanNet keeps its official 2D annotation ZIPs, while 3RScan keeps each native sequence.zip sensor stream compressed.

Return semantics

get_points() returns a PointBatch. Its xyz array is always float32 with shape (N, 3); optional RGB, normals, semantic/instance labels, and source-view provenance are row-aligned. Call to_point_cloud() only when an Open3D object is needed.

get_segmentation() returns Segmentation3D with labels, id_to_name, domain ("point", "vertex", or "face"), and invalid_id.

get_boxes() always returns (boxes_by_id, id_to_name).

get_frames() returns a slot-based FrameBatch. indices are original video frame numbers (or stable source positions for filename-only captures), and frame_keys retains source names such as DSLR filenames. source_frame_count is the source sequence length. Every requested per-frame field has N as its first dimension, including constant intrinsics, which are expanded to (N, 3, 3).

The frame conventions are intentionally strict:

  • depth is float32 in metres; missing, negative, and non-finite values are 0.0;
  • extrinsics are OpenCV-style world_to_camera matrices with shape (N, 4, 4);
  • pixel coordinates follow OpenCV: integer coordinates are pixel centres;
  • RGB, albedo, depth, normal/instance/semantic/confidence maps, and intrinsics use the same resize/crop transform;
  • rotate_to_up=True rotates pixels by a right angle and applies the equivalent camera roll to world_to_camera, so intrinsics always stay upper-triangular pinhole matrices;
  • RGB uses high-quality interpolation, while depth and discrete maps use nearest-neighbor;
  • explicit indices preserve the requested order and cannot be combined with step != 1;
  • default sampling filters frames with invalid camera matrices before applying step.

Adapters for non-pinhole captures additionally return camera_model, distortion, and image_sizes. ScanNet++ uses these fields for its OPENCV_FISHEYE, OPENCV, PINHOLE, and EQUIRECTANGULAR sources instead of discarding the source camera model.

Built-in datasets

Canonical name Aliases Mesh Points Segmentation Boxes Frames
scannetv2 scannet raw/decimated/semantic no instance + NYU40, vertex yes RGB-D, camera, 2D labels, timestamps, IMU
scannetppv2 scannet++, scannetpp, scannet++v2 yes laser scan vertex, multilabel yes iPhone, DSLR, panorama
arkitscenes — yes no vertex (box-derived) yes multi-source RGB-D, confidence
multiscan — PLY/textured no object/part instance + semantic, face yes RGB-D, confidence, camera metadata
ase aria-ase, aria-synthetic-environments no semi-dense SLAM points no wall/door/window layout OBB fisheye RGB, ray-depth, instances, poses
3rscan threerscan textured/instance no instance + Global/NYU40/Eigen13/RIO27/RIO7, vertex yes RGB-D and camera
s3dis stanford3d, stanford-3d no XYZRGB + labels instance + 13-class semantic, point yes no
matterport3d — textured/house/region/Poisson no instance + semantic, face official OBB raw + undistorted RGB-D
scenenn — yes no vertex no no
structured3d structure3d reconstructed room layout RGB-D fusion semantic + instance, point official OBB perspective + panorama RGB-D and labels

Each dataset page linked above documents the expected download layout, dataset-specific options (label spaces, capture sources, region and room selectors), and extra methods such as calibration, IMU, and scene metadata.

Registering an adapter

Registration is atomic: the canonical name and all aliases are checked for conflicts before any entry is added.

from scenezoo import Dataset, register_dataset


@register_dataset("my-scenes", aliases=("mine",))
class MyScenes(Dataset):
    def _load_splits(self):
        return {"train": ["scene-001"]}

    def _load_metadata(self):
        return {"coordinate_system": "z-up"}

    def get_mesh(self, sample_id, *, mesh_type=None):
        return load_my_mesh(self.root_dir / sample_id)

splits and metadata are lazy cached_property values. Implement only the standard methods backed reliably by the dataset; supports() detects overridden methods automatically. Adapter constructors should use explicit keyword parameters rather than a catch-all **kwargs. Custom adapters may override the protected _check() hook to add source-specific layout diagnostics; normal users call only dataset.check().

Cache and offline mode

Remote split and metadata files are cached under SCENEZOO_CACHE_DIR (or ~/.cache/scenezoo). Pass offline=True to forbid network access while allowing previously cached remote files to be reused.

Development

pip install -e ".[dev]" -r docs/requirements.txt
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest
ruff check src test && ruff format --check src test
python -m sphinx -W -b html docs docs/_build/html

Real-data integration tests are opt-in through SCENEZOO_TEST_*_ROOT environment variables; no dataset path is embedded in the test suite. The documentation is built from docs/ and published at https://scenezoo.readthedocs.io/en/latest/.

License

See LICENSE.

Citation

Please cite SceneZoo if you use it in your research.

@misc{Zhang2026SceneZoo,
    author       = {Yiming Zhang},
    title        = {{SceneZoo}: A Unified {Python} Interface for {3D} Indoor-Scene Datasets},
    year         = {2026},
    howpublished = {\url{https://github.com/eamonn-zh/scenezoo}},
}

Metadata

Release files for scenezoo 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for scenezoo 0.2.1
File Size Uploaded
scenezoo-0.2.1.tar.gz 184.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for scenezoo 0.2.1
File Interpreter ABI Platform
scenezoo-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 347.7 kB

Release files / scenezoo-0.2.1.tar.gz

Download URL scenezoo-0.2.1.tar.gz
Size 184.9 kB
Tags Source
SHA-256 checksum
How to use checksums
8a12fb0aff1171694b03df821a61a6e4a779defac72c1d32b02f05c7468b28d5
BLAKE2b-256 checksum
How to use checksums
89d83183d12f18205678fd0dd115bc64eca88f525b169b5d5cb85f69f6a762f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / scenezoo-0.2.1-py3-none-any.whl

Download URL scenezoo-0.2.1-py3-none-any.whl
Size 162.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b8d1007760bca363c32afc3279bf601eb4b444045789cfad400f7bfd71edbbcb
BLAKE2b-256 checksum
How to use checksums
7549b0a98678ff1bad5d22b9479f1e6b8fa47d9856d5dfd53870c1297dfbd47f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.1

2 release files

0.3.0

2 release files

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page