Documentation • Installation • Quick start • Examples • Datasets • API reference
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
float32in metres; missing, negative, and non-finite values are0.0; - extrinsics are OpenCV-style
world_to_cameramatrices 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=Truerotates pixels by a right angle and applies the equivalent camera roll toworld_to_camera, so intrinsics always stay upper-triangular pinhole matrices;- RGB uses high-quality interpolation, while depth and discrete maps use nearest-neighbor;
- explicit
indicespreserve the requested order and cannot be combined withstep != 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)
| File | Size | Uploaded | |
|---|---|---|---|
| scenezoo-0.2.1.tar.gz | 184.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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