Instructions to use XXXpilar/Fastfill-test with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use XXXpilar/Fastfill-test with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
File size: 18,193 Bytes
e6f80f3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | """Derive a minimal rectangular-room bbox task from sealed SpatialLM geometry.
This builder qualifies geometric box axes, not asset semantic fronts. Parent
splits and labels are preserved; a recorded room-frame translation is the only
allowed numeric target transformation. Old datasets remain immutable.
"""
from __future__ import annotations
import argparse
from collections import Counter
from contextlib import ExitStack
from copy import deepcopy
import json
import math
from pathlib import Path
from shapely.geometry import Polygon
from .direct_layout import request_to_condition
from .io import fingerprint, safe_output
from .schema import validate_layout
SPATIALLM_IR_SHA256 = "f582b46d45b38e59b7b593d045f946f889d08942c10147bf4f077b7f33ef4410"
SPLITS = ("train", "validation", "test")
UNKNOWN_ROOM_TYPES = {"misc", "other", "other room", "unknown", "undefined", "none", ""}
YAW_SEMANTICS = "local_bbox_axes_not_certified_semantic_front"
def _finite(value):
return not isinstance(value, bool) and isinstance(value, (int, float)) and math.isfinite(value)
def _room_frame(room):
if not isinstance(room.get("room_type"), str) or room["room_type"].strip().lower() in UNKNOWN_ROOM_TYPES:
raise ValueError("room_type_unknown")
if room.get("boundary_known") is not True:
raise ValueError("boundary_unknown")
if room.get("floor_known") is not True or not _finite(room.get("floor_z_m")):
raise ValueError("floor_unknown")
points = room.get("floor_polygon_xy_m", [])
if len(points) < 3 or not all(len(p) == 2 and all(_finite(v) for v in p) for p in points):
raise ValueError("rectangle_invalid_polygon")
polygon = Polygon(points)
lo = [min(p[q] for p in points) for q in (0, 1)]
hi = [max(p[q] for p in points) for q in (0, 1)]
area = (hi[0] - lo[0]) * (hi[1] - lo[1])
if not polygon.is_valid or area <= 0 or abs(polygon.area - area) > max(1e-8, area * 1e-6):
raise ValueError("rectangle_not_axis_aligned")
height = room.get("height_m")
if height is not None and (not _finite(height) or height <= 0):
raise ValueError("height_invalid")
return [*lo, float(room["floor_z_m"])], [hi[q] - lo[q] for q in (0, 1)], height
def _source_room_metadata(sample, source_room):
room, provenance = sample["condition"]["room"], sample["provenance"]
if source_room.get("room_type") != room.get("room_type"):
raise ValueError("source_room_type_mismatch")
parent_height, source_height = room.get("height_m"), source_room.get("height")
recorded_drop = parent_height is None and provenance.get("legacy_height_dropped") is True
if parent_height != source_height and not recorded_drop:
raise ValueError("source_room_height_mismatch")
if source_height is not None and (not _finite(source_height) or source_height <= 0):
raise ValueError("source_room_height_invalid")
# Sealed SpatialLM IR uses source meters/Z-up and a canonical zero floor.
# An explicit source floor supports separately audited test/version frames.
source_floor = source_room.get("floor_z_m", 0.)
if not _finite(source_floor) or source_floor != room.get("floor_z_m"):
raise ValueError("source_floor_frame_mismatch")
if source_room.get("boundary_type") != "polygon":
raise ValueError("source_boundary_semantics_mismatch")
if room.get("frame") != "right_handed_z_up":
raise ValueError("source_floor_frame_axis_mismatch")
return source_height
def _source_geometry(sample, source_room):
provenance = sample["provenance"]
if (source_room.get("source") != "SpatialLM" or source_room.get("uid") != provenance.get("scene_id")
or source_room.get("group") != provenance.get("group")
or source_room.get("group") != provenance.get("house_id")):
raise ValueError("source_identity_mismatch")
source_height = _source_room_metadata(sample, source_room)
if source_room.get("boundary") != sample["condition"]["room"]["floor_polygon_xy_m"]:
raise ValueError("source_boundary_mismatch")
source_ids = provenance.get("target_source_ids", [])
targets = sample["target"]["objects"]
evidence = provenance.get("field_evidence", [])
if len(source_ids) != len(targets) or len(set(source_ids)) != len(source_ids) or len(evidence) != len(targets):
raise ValueError("source_identity_missing_or_duplicate")
source_objects = {obj["id"]: obj for obj in source_room.get("objects", [])}
for target, source_id, fields in zip(targets, source_ids, evidence):
obj = source_objects.get(source_id)
if obj is None:
raise ValueError("source_identity_missing_object")
if obj.get("tilted") is not False or fields.get("tilted") is not False:
raise ValueError("tilted_object")
if fields.get("size_semantics") != "canonical_source_IR":
raise ValueError("source_geometry_proxy_size")
if not _finite(obj.get("yaw")):
raise ValueError("source_geometry_invalid_yaw")
yaw = (obj["yaw"] + math.pi) % (2 * math.pi) - math.pi
if (obj.get("pos") != target.get("bottom_center_m") or obj.get("size") != target.get("target_size_local_m")
or yaw != target.get("yaw_rad")):
raise ValueError("source_geometry_mismatch")
return source_height
def _complete_geometry(sample):
targets = sample["target"]["objects"]
masks = sample.get("validity", {})
for key in ("position", "size"):
rows = masks.get(key, [])
if len(rows) != len(targets) or not all(len(row) == 3 and all(flag is True for flag in row) for row in rows):
raise ValueError("incomplete_geometry_labels")
for target in targets:
p, s, yaw = target.get("bottom_center_m", []), target.get("target_size_local_m", []), target.get("yaw_rad")
if len(p) != 3 or len(s) != 3 or not all(_finite(v) for v in p + s) or min(s) <= 0 or not _finite(yaw):
raise ValueError("invalid_geometry_labels")
def _inside_room(targets, origin, dimensions, height, tolerance):
xmin, ymin, floor = origin
xmax, ymax = xmin + dimensions[0], ymin + dimensions[1]
for obj in targets:
p, s, yaw = obj["bottom_center_m"], obj["target_size_local_m"], obj["yaw_rad"]
for sx, sy in ((-1, -1), (-1, 1), (1, -1), (1, 1)):
x = p[0] + (sx * s[0] * math.cos(yaw) - sy * s[1] * math.sin(yaw)) / 2
y = p[1] + (sx * s[0] * math.sin(yaw) + sy * s[1] * math.cos(yaw)) / 2
if not xmin - tolerance <= x <= xmax + tolerance or not ymin - tolerance <= y <= ymax + tolerance:
raise ValueError("xy_boundary_conflict")
if p[2] < floor - tolerance:
raise ValueError("floor_boundary_conflict")
if height is not None and p[2] + s[2] > floor + height + tolerance:
raise ValueError("ceiling_boundary_conflict")
def _validate_options(max_objects, tolerance_m, room_dimension_mode):
if isinstance(max_objects, bool) or not isinstance(max_objects, int) or max_objects < 1:
raise ValueError("max_objects must be a positive integer")
if not _finite(tolerance_m) or tolerance_m < 0:
raise ValueError("tolerance_m must be finite and nonnegative")
if room_dimension_mode not in {"xy", "xyz"}:
raise ValueError("room_dimension_mode must be xy or xyz")
def project_sample(sample, source_room, *, split, max_objects=128, tolerance_m=.001,
room_dimension_mode="xy"):
"""Reject uncertain source fields; use the exact public minimal input adapter."""
_validate_options(max_objects, tolerance_m, room_dimension_mode)
provenance = sample["provenance"]
if provenance.get("source") != "SpatialLM":
raise ValueError("source_not_qualified")
if provenance.get("split") != split or split not in SPLITS:
raise ValueError("source_split_mismatch")
room = sample["condition"]["room"]
origin, dimensions, height = _room_frame(room)
requests, targets = sample["condition"]["objects"], sample["target"]["objects"]
if not requests or len(requests) > max_objects:
raise ValueError("max_objects_exceeded_or_empty")
_complete_geometry(sample)
qualification_height = _source_geometry(sample, source_room)
_inside_room(targets, origin, dimensions, qualification_height, tolerance_m)
inventory = [{"id": obj["id"], "category": obj["category"], "description": obj["category"], "count": 1}
for obj in requests]
request_dimensions = dimensions + ([height] if room_dimension_mode == "xyz" and height is not None else [])
request = {"room_type": room["room_type"], "room_size_m": request_dimensions,
"furniture_list": inventory}
condition = request_to_condition(request, max_objects=max_objects)
translated = [{**deepcopy(obj), "bottom_center_m": [v - offset for v, offset in zip(obj["bottom_center_m"], origin)]}
for obj in targets]
target = {"schema_version": "fastfill.v2", "objects": translated}
validate_layout(target, condition)
n = len(translated)
return {"schema_version": "fastfill.v2", "condition": condition, "target": target,
"validity": {"position": [[True] * 3 for _ in range(n)], "size": [[True] * 3 for _ in range(n)],
"yaw": [True] * n, "yaw_symmetry_order": [2] * n},
"provenance": {**deepcopy(provenance), "minimal_request": request,
"condition_projection": "room_type_size_furniture_only-v1",
"room_dimension_mode": room_dimension_mode,
"qualification_source_height_m": qualification_height,
"frame_translation_m": [-value for value in origin],
"parent_yaw_validity": deepcopy(sample["validity"].get("yaw", [])),
"yaw_label_semantics": YAW_SEMANTICS,
"geometry_yaw_evidence": "sealed SpatialLM source IR: full local extents + yaw-only Bbox",
"correspondence": "fixed_request_identity",
"qualification_tolerance_m": tolerance_m,
"geometry_checks_scope": "OBB XY/floor/known ceiling only; collision/support/mesh/physics not certified"}}
def _parent_rows(parent):
for split in SPLITS:
with (parent / (split + ".jsonl")).open() as stream:
for number, line in enumerate(stream, 1):
if line.strip():
yield split, number, json.loads(line)
def _split_integrity(parent):
uids, groups = set(), {}
for split, number, row in _parent_rows(parent):
provenance = row["provenance"]
uid = provenance.get("scene_id")
if not uid or uid in uids or provenance.get("split") != split:
raise ValueError(f"parent UID/split integrity failure at {split}:{number}")
uids.add(uid)
for alias in (provenance.get("group"), provenance.get("house_id")):
if not isinstance(alias, str) or not alias:
raise ValueError("parent underlying group identity missing")
if alias in groups and groups[alias] != split:
raise ValueError("parent group alias has cross-split conflict")
groups[alias] = split
return {"parent_uids_checked": len(uids), "parent_group_aliases_checked": len(groups),
"duplicate_uids": 0, "group_cross_split_conflicts": 0, "split_rule": "inherit parent without re-splitting"}
def _source_index(source):
result = {}
with source.open() as stream:
for line in stream:
if not line.strip():
continue
row = json.loads(line)
if row.get("source") != "SpatialLM" or row.get("uid") in result:
raise ValueError("sealed SpatialLM source IR has invalid source or duplicate UID")
result[row["uid"]] = row
return result
def build_minimal_dataset(parent_root, source_ir, output, *, max_objects=128, tolerance_m=.001,
expected_source_sha256=SPATIALLM_IR_SHA256, room_dimension_mode="xy"):
"""Build a new, hash-pinned derivative. No source files are normalized in place."""
_validate_options(max_objects, tolerance_m, room_dimension_mode)
parent, source = Path(parent_root).resolve(), Path(source_ir).resolve()
target = safe_output(output)
if parent == target or parent in target.parents or target in parent.parents or source == target or target in source.parents:
raise ValueError("output must be new and outside parent/source data and ancestors")
inputs = [parent / (split + ".jsonl") for split in SPLITS] + [parent / "manifest.json", source]
before = {str(path): fingerprint(path) for path in inputs}
if before[str(source)] != expected_source_sha256:
raise ValueError("source IR SHA256 does not match the explicitly audited version")
integrity = _split_integrity(parent)
source_rows = _source_index(source)
counts, object_counts, rejected, inspected = Counter(), Counter(), Counter(), Counter()
target.mkdir(parents=True, exist_ok=False)
with ExitStack() as stack:
streams = {split: stack.enter_context((target / (split + ".jsonl")).open("x")) for split in SPLITS}
exclusions = stack.enter_context((target / "exclusions.jsonl").open("x"))
for split, number, row in _parent_rows(parent):
inspected[split] += 1
uid, source_name = row["provenance"]["scene_id"], row["provenance"]["source"]
try:
derived = project_sample(row, source_rows.get(uid, {}), split=split,
max_objects=max_objects, tolerance_m=tolerance_m,
room_dimension_mode=room_dimension_mode)
except ValueError as error:
reason = str(error)
rejected[reason] += 1
exclusions.write(json.dumps({"uid": uid, "split": split, "source": source_name,
"parent_line": number, "reason": reason}, allow_nan=False) + "\n")
continue
streams[split].write(json.dumps(derived, separators=(",", ":"), allow_nan=False) + "\n")
counts[split] += 1
object_counts[split] += len(derived["target"]["objects"])
if any(fingerprint(path) != before[str(path)] for path in inputs):
raise RuntimeError("input changed during immutable dataset build; output is not a valid release")
if not counts:
raise ValueError("no qualified minimal-condition examples; output is not a valid release")
manifest = {"schema_version": "fastfill.v2", "builder": "spatiallm-direct-bbox-v1",
"parent_root": str(parent), "source_ir": str(source), "source_ir_sha256": before[str(source)],
"parent_sha256": {path.name: before[str(path)] for path in inputs[:-1]},
"samples_written": sum(counts.values()), "objects_written": sum(object_counts.values()),
"split_samples": {s: counts[s] for s in SPLITS}, "split_objects": {s: object_counts[s] for s in SPLITS},
"split_input_samples": dict(inspected), "exclusion_counts": dict(rejected), "split_integrity": integrity,
"input_contract": "room_type + room_size_m" + ("[W,D]" if room_dimension_mode == "xy" else "[W,D,(H)]")
+ " + furniture_list; no assets/fixed/constraints/support",
"room_dimension_mode": room_dimension_mode,
"height_policy": "xy excludes source height from model input; any source height remains qualification evidence only; "
"xyz explicitly opts into known exported room height; missing height stays unknown",
"source_metadata_agreement": "room type, source/published height, canonical floor frame, polygon boundary "
"semantics and house/group must agree with pinned IR; recorded height drops never bypass source ceiling checks",
"yaw_label_semantics": YAW_SEMANTICS, "yaw_symmetry_order": 2,
"correspondence": "fixed_request_identity", "max_objects": max_objects, "tolerance_m": tolerance_m,
"qualification": "SpatialLM; known type/floor/boundary; valid polygon area=AABB within 1e-6; "
"complete position/size; upright; finite source-matched geometric yaw; all OBB corners in XY, "
"bottom above floor and top below any known ceiling within tolerance",
"source_data_modified": False, "parent_data_modified": False,
"target_changes": "recorded common translation only; local sizes and yaw unchanged; no snapping/scaling",
"geometry_scope": "Boundary-qualified OBBs; collision/support completeness and mesh/physics not certified",
"old_pilot_scope": "Earlier two-scene MultiScan pilot used richer conditions and did not train this contract",
"implementation_sha256": fingerprint(Path(__file__)),
"output_sha256": {path.name: fingerprint(path) for path in sorted(target.glob("*.jsonl"))}}
(target / "manifest.json").write_text(json.dumps(manifest, indent=2, allow_nan=False) + "\n")
return manifest
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--parent-root", required=True)
parser.add_argument("--source-ir", required=True)
parser.add_argument("--output", required=True)
parser.add_argument("--max-objects", type=int, default=128)
parser.add_argument("--tolerance-m", type=float, default=.001)
parser.add_argument("--room-dimensions", choices=("xy", "xyz"), default="xy")
args = parser.parse_args()
result = build_minimal_dataset(args.parent_root, args.source_ir, args.output,
max_objects=args.max_objects, tolerance_m=args.tolerance_m,
room_dimension_mode=args.room_dimensions)
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
|