feat(scenegraph): add depth-based 3D positioning via pinhole projection

- Add bbox_depth_center position strategy in SceneGraphBuilder using depth
  at bbox centre and configurable camera_hfov_degrees for pinhole projection.
- Add optional depth_sensor_uuid to HabitatSimulatorConfig; create depth
  sensor spec alongside RGB sensor.
- Add camera_position/camera_rotation fields to RoomView; capture pose from
  sensor_states when depth sensor is available.
- Update flatten_room_views for backward compatibility with legacy tuple
  format.
- Wired in depth sensor and bbox_depth_center strategy in verification
  notebook.
- Add tests for depth sensor support and new position strategies.
This commit is contained in:
2026-05-31 14:29:05 +08:00
parent a127032e18
commit 7a1e1ccf3f
7 changed files with 1729 additions and 38 deletions

View File

@@ -1,11 +1,23 @@
from __future__ import annotations
import ast
from pathlib import Path
NOTEBOOK = Path(__file__).resolve().parents[1] / "notebooks" / "verification.py"
def _find_calls(tree: ast.AST, func_name: str) -> list[ast.Call]:
"""Find all Call nodes for a named function (simple name, not attribute)."""
return [
node
for node in ast.walk(tree)
if isinstance(node, ast.Call)
and isinstance(node.func, ast.Name)
and node.func.id == func_name
]
def test_verification_notebook_uses_scenegraph_query_api():
source = NOTEBOOK.read_text()
@@ -20,7 +32,6 @@ def test_verification_notebook_uses_hash_codec_for_object_hashes():
# the builder handles it internally.
assert "bits_tensor_to_hash_bytes" not in source
assert "np.packbits" not in source
assert "scene_graph.objects[_obj_id] = ObjectNode" not in source
def test_verification_notebook_uses_scene_graph_builder():
@@ -30,4 +41,56 @@ def test_verification_notebook_uses_scene_graph_builder():
assert "SceneGraphBuildConfig" in source
assert "flatten_room_views" in source
assert "scene_graph, build_artifacts = builder.build_from_room_views" in source
assert "scene_graph.objects[_obj_id] = ObjectNode" not in source
# Verify no direct ObjectNode construction (builder handles it internally)
tree = ast.parse(source)
object_node_calls = _find_calls(tree, "ObjectNode")
assert len(object_node_calls) == 0, (
"Notebook should not construct ObjectNode() directly; "
"the builder handles it"
)
def test_verification_notebook_uses_bbox_depth_center_positioning():
source = NOTEBOOK.read_text()
tree = ast.parse(source)
# --- SceneGraphBuildConfig must use position_strategy='bbox_depth_center' ---
sg_config_calls = _find_calls(tree, "SceneGraphBuildConfig")
assert len(sg_config_calls) >= 1, (
"Notebook must instantiate SceneGraphBuildConfig"
)
config_kwargs = {
kw.arg: kw.value for kw in sg_config_calls[0].keywords if kw.arg is not None
}
assert "position_strategy" in config_kwargs
pos_val = config_kwargs["position_strategy"]
assert isinstance(pos_val, ast.Constant) and pos_val.value == "bbox_depth_center", (
f"Expected position_strategy='bbox_depth_center', got {pos_val!r}"
)
assert "camera_hfov_degrees" in config_kwargs, (
"SceneGraphBuildConfig should include camera_hfov_degrees"
)
# --- object_rows dict literals must include bbox_xyxy and position_confidence ---
found_bbox = False
found_pos_conf = False
for node in ast.walk(tree):
if isinstance(node, ast.Dict):
keys = {k.value for k in node.keys if isinstance(k, ast.Constant)}
if "obj_id" in keys:
if "bbox_xyxy" in keys:
found_bbox = True
if "position_confidence" in keys:
found_pos_conf = True
assert found_bbox, "object_rows must include bbox_xyxy column"
assert found_pos_conf, "object_rows must include position_confidence column"
# --- No direct ObjectNode( construction ---
object_node_calls = _find_calls(tree, "ObjectNode")
assert len(object_node_calls) == 0, (
"Notebook should not construct ObjectNode() directly"
)