# Roomza Data Network: Schema Reference

Canonical field, file, and label spec for licensed indoor-scene data from furnished hotel rooms.

`schema_version: 2.0.0`

The Roomza schema favors stable identifiers, sidecar JSON manifests next to media files, and an explicit null contract over implicit defaults. A `room_id` minted in v1.x continues to address the same physical room in v2.x even when its fields, captures, or labels are revised. Manifests are content-addressable through `manifest_version` and `revision` integers, so a downstream pipeline can pin to an exact label release. Where a field is unknown rather than absent, the value is `null` and a sibling `*_reason` key declares why.

## 1. File conventions

A licensed shard is delivered as a single tar.zst archive. The bundle expands to a root `manifest.json`, per-room directories, and capture and label siblings alongside each medium.

```
roomza_shard_2026_05/
  manifest.json                       # shard index, schema_version, room_ids
  taxonomy/
    roomza.taxonomy.v2.json           # canonical label set
    crosswalk.{nyuv2_40,scannet_20,arkitscenes_17}.json
  rooms/room_8f3a/
    room.json                         # structured record (section 4)
    stills/cap_st_001.{jpg,json}      # binary + per-image manifest (intrinsics, EXIF subset)
    video/cap_v_001.{mp4,json}        # clip + per-clip manifest
    video/cap_v_001.imu.csv           # 200 Hz IMU stream
    video/cap_v_001.poses.jsonl       # per-frame world_T_camera
    spatial/cap_sp_001.usdz           # RoomPlan native (LiDAR Apple devices only)
    spatial/cap_sp_001.{ply,json}     # transcoded mesh + geometry summary
    labels/instances.v2.json          # ScanNet-style aggregation (section 6.1)
    labels/detections.coco.v2.json    # COCO 2D export (section 6.2)
    labels/bbox3d.v2.json             # oriented 3D bboxes
```

Every binary asset has a sibling `.json` with the same stem. Label files carry a `label_spec_version` pointer to a file under `taxonomy/`.

## 2. ID conventions

| ID | Format | Stability |
| --- | --- | --- |
| `property_id` | `prop_` + 8 hex chars | Immutable. Survives rebranding, ownership change. |
| `room_id` | `room_` + 4 hex chars (scoped to property) | Immutable. A renumbered room keeps its `room_id`; `room_number` changes. |
| `capture_id` | `cap_st_*` (still), `cap_v_*` (video), `cap_sp_*` (spatial) | Immutable per capture session. |
| `obj_*` | `obj_` + 6 hex chars (scoped to room) | Immutable across re-labels. A re-segmentation may add new `obj_*` but never reissue. |
| `manifest_version` | int, monotonically increasing | Per-room. Bumps on any label or capture revision. |
| `revision` | int per label or capture file | Bumps when contents change under the same `*_id`. |

IDs are immutable across schema versions. Field semantics may be re-keyed under a major bump (section 7), but no ID is ever reissued to a different entity.

## 3. Coordinate and unit conventions

- World: right-handed, +Y up, +X to the right of the room's primary entry door, origin at the geometric center of the floor plane.
- Geometry: millimeters by default. Legacy ARKitScenes-compatible exports use cm and carry `"units": "cm"` at the file root; absence of the key means mm.
- Camera: OpenCV convention (Z forward, X right, Y down). Intrinsics `(fx, fy, cx, cy)` in pixels. Pose `world_T_camera` as 4x4 row-major, translation in mm.
- Time: capture-time as ms since UNIX epoch (UTC); video timestamps as ms since clip start; IMU rows carry both.

## 4. Structured room record (`room.json`)

| Field | Type | Req | Null contract |
| --- | --- | --- | --- |
| `room_id`, `property_id`, `room_number` | string | yes | never null |
| `floor` | int | no | `null` + `floor_reason` |
| `bed_type` | enum | yes | `king` \| `queen` \| `double` \| `twin` \| `bunk` \| `sofa` \| `murphy` |
| `bed_count`, `max_occupancy` | int | yes | never null |
| `view` | enum | no | `city` \| `water` \| `mountain` \| `garden` \| `courtyard` \| `interior` \| `mixed`; nullable |
| `sq_ft` | float | no | `null` + `sq_ft_reason` (`unmeasured` \| `withheld_under_license`) |
| `ceiling_height_mm` | float | no | `null` + `_reason` |
| `ada_accessible`, `ada_roll_in_shower` | bool | no | `null` + `_reason` (`unverified`) |
| `bathtub`, `balcony`, `kitchenette`, `smoking_permitted` | bool | yes | never null |
| `pet_friendly`, `connecting_room` | bool | no | `null` + `_reason` |
| `window_count` | int | no | `null` + `_reason` |
| `hvac_type` | enum | no | `central` \| `ptac` \| `split` \| `mini_split` \| `none` |
| `context.category` | enum | yes | `economy` \| `midscale` \| `upscale` \| `luxury` \| `boutique` \| `extended_stay` \| `resort` |
| `context.vibes` | string[] | no | empty array allowed; never null |
| `context.renovation_year` | int | no | `null` + `_reason` |
| `context.condition_class` | enum | no | `pristine` \| `fresh` \| `serviceable` \| `tired` \| `distressed` |
| `captures.stills` | string[] | yes | array of `cap_st_*` ids, may be empty |
| `captures.video` | string[] | yes | array of `cap_v_*` ids, may be empty |
| `captures.spatial` | string[] | yes | array of `cap_sp_*` ids, may be empty |
| `manifest_version` | int | yes | never null |

Any `null` value must have a sibling `<field>_reason` string with one of: `unmeasured`, `unverified`, `withheld_under_license`. Absence of the reason key is a schema violation, not a null.

## 5. Capture-record schemas

| File | Field | Type | Notes |
| --- | --- | --- | --- |
| `cap_st_*.json` | `capture_id` | string | `cap_st_*` |
| | `format` | string | `image/jpeg` or `image/png` |
| | `resolution` | [int,int] | `[w, h]` px |
| | `capture_time_ms` | int | UNIX epoch ms |
| | `exif` | object | `{focal_mm (35mm-equiv), device, iso}`; `iso` nullable |
| | `camera_intrinsics` | object | `{fx, fy, cx, cy}` px when derivable; else `null` + `_reason` |
| `cap_v_*.json` | `capture_id` | string | `cap_v_*` |
| | `platform` | enum | `phone` \| `dedicated` (Aria, Quest, etc.) |
| | `modality` | string[] | subset of `rgb`, `rgb_imu`, `rgb_depth` |
| | `fps`, `duration_s` | float | nominal |
| | `resolution` | [int,int] | `[w, h]` px |
| | `camera_intrinsics` | object | `{fx, fy, cx, cy}` px |
| | `imu_available` | bool | |
| | `imu_file` | string | path to `cap_v_*.imu.csv`; null if absent |
| | `pose_recovery.method` | enum | `DROID-SLAM` \| `COLMAP` \| `ARKit` \| `none` |
| | `pose_recovery.mean_ATE_cm` | float | trajectory error when method != `none`; else null |
| | `poses_file` | string | path to `cap_v_*.poses.jsonl`, one row/frame: `{t_ms, world_T_camera}` |
| `cap_sp_*.json` | `capture_id` | string | `cap_sp_*` |
| | `format` | enum | `usdz` \| `ply` \| `glb`. USDZ is native iOS RoomPlan (LiDAR Apple devices only); PLY/GLB transcoded |
| | `depth_available` | bool | |
| | `point_count`, `mesh_face_count` | int | 0 indicates the other modality |
| | `room_geometry.walls` | object[] | `{polygon: [[x,z], ...], height_mm}` |
| | `room_geometry.openings` | object[] | `{type: "door"\|"window"\|"opening", polygon, normal}` |
| | `room_geometry.fixed_objects` | object[] | `{label, obj_id, bbox_3d}` for built-ins |

IMU CSV columns: `t_ms, t_unix_ms, ax, ay, az, gx, gy, gz, mx, my, mz` at nominal 200 Hz. Magnetometer fields are nullable.

## 6. Label manifest schema

### 6.1 ScanNet-style instance aggregation (`instances.v2.json`)

```
{ "label_spec_version": "taxonomy/roomza.taxonomy.v2.json", "revision": 3,
  "segGroups": [ {
    "id": 0, "objectId": "obj_4a91c2", "label": "headboard", "taxonomy_id": 217,
    "bbox_3d": {
      "centroid":       [x, y, z],         // mm
      "axesLengths":    [lx, ly, lz],      // mm
      "normalizedAxes": [[r00,r01,r02],[r10,r11,r12],[r20,r21,r22]]  // row-major 3x3
    } } ] }
```

Oriented 3D boxes follow the ARKitScenes convention: a centroid in the world frame, a length along each local axis, and a `normalizedAxes` 3x3 rotation matrix mapping local box axes into world axes. Default units are mm; an ARKitScenes-compatible export under the same `obj_*` ids is available with `"units": "cm"`.

### 6.2 COCO 2D export (`detections.coco.v2.json`)

Standard COCO layout: `images`, `annotations`, `categories`. Each `annotations` row carries `id`, `image_id` (matches `cap_st_*` or a video frame stem), `category_id`, `bbox` as `[x, y, w, h]` in pixels, `segmentation` as polygons or RLE, and `area`. `category_id` is the Roomza taxonomy id; the file's `categories` block restates the same taxonomy_id, name, and the crosswalk targets for NYUv2-40, ScanNet-20, and ARKitScenes-17.

### 6.3 Camera pose

Per-frame poses are stored as `world_T_camera` 4x4 row-major homogeneous matrices, translation in mm. For video, poses live in `cap_v_*.poses.jsonl`, one JSON object per line, keyed by `t_ms` (ms-from-clip-start). For stills with a derived pose, the matrix appears under `pose.world_T_camera` in the still's sibling JSON.

## 7. Versioning & compatibility

`schema_version` follows `major.minor.patch`. Major bumps change field semantics or enum domains (e.g. units flip, taxonomy re-root). Minor bumps add fields or enum values backward-compatibly. Patch bumps fix bugs without semantic change.

Deprecated v1.x fields are retained for one full major cycle and removed in v3.0.0. `view` formerly used `street` and `back` enum values, now mapped to `city` and `interior` respectively; `condition` was a free-text string and is now the `context.condition_class` enum.

## 8. License contract summary

Licensees receive data under capture-method-specific terms negotiated per shard. Provenance metadata (capture device, operator, capture date, property identifier) is available during diligence on request and may be withheld from the production delivery under the `withheld_under_license` null contract. Redistribution of raw or derived assets outside the licensed entity is prohibited. Model weights trained on licensed data are unrestricted; raw asset re-hosting is not.

## 9. Change log

- **2.0.0** (current). Adds egocentric walkthrough + IMU schema (section 5.2). Renames `view` enum values to align with ARKitScenes label conventions. Promotes oriented 3D boxes to first-class under the ARKitScenes centroid + axesLengths + normalizedAxes form. Adds explicit `*_reason` null contract. Default geometry units flip from cm to mm; ARKitScenes-compatible cm exports remain available with an explicit `"units"` key.
- **1.2.0**. Added `context.vibes`, `context.condition_class`, `manifest_version`.
- **1.0.0**. Initial public release: structured room record, RGB stills, RoomPlan USDZ, ScanNet-style instance aggregation.
