Files
Mini-Nav/mini-nav/scenegraph/objectnode.py
SikongJueluo a127032e18 feat(scenegraph): add SceneGraphBuilder for pipeline-driven graph construction
Introduce SceneGraphBuilder + SceneGraphBuildConfig to decouple scene graph
construction from the verification notebook. The builder handles batch
inference, hash encoding, and object node creation internally.

- Add SceneGraphBuilder.build_from_room_views() as the main entry point
- Add SceneGraphBuildConfig for inference_batch_size and position strategy
- Add SceneGraphBuildArtifacts to carry cropped images and debug metadata
- Extend ObjectNode with optional detection metadata (label, confidence,
  bbox_xyxy, source_view_id, position_confidence)
- Add RoomView frozen dataclass as a structured view container
- Add flatten_room_views() utility to replace inline list comprehensions
- Refactor notebooks/verification.py to use the new builder API

BREAKING CHANGE: ObjectNode now accepts additional optional fields; direct
scene_graph.objects[obj_id] = ObjectNode(...) construction in the notebook
is replaced by builder.build_from_room_views(...).
2026-05-30 16:57:38 +08:00

42 lines
1.7 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
import numpy as np
# --- 1. 物品节点:极其扁平,只存"当前最新/平均"状态 ---
@dataclass
class ObjectNode:
obj_id: str # 唯一ID (例如: "obj_001")
room_id: str # 所属房间的直接外键,不搞复杂的层级关系
# 位置:在 debug 阶段,一切认准全局坐标,不要搞局部坐标系
position: np.ndarray # [x, y, z] 世界坐标系下的中心点或锚点
# 特征:直接存你压缩后的 512bit 结果,不搞历史缓存
visual_hash: bytes # 512bit 视觉特征 (用于外观检索)
semantic_hash: bytes # 512bit 语义特征 (用于类别对齐)
# Debug 必备:极简的生命周期管理,防止满屏"幽灵节点"
hit_count: int = 1 # 被观测到的次数。太低的可以直接过滤掉
last_seen_frame: int = 0 # 最后一次看到的帧号或时间戳
# Optional detection metadata (from M0 integration)
label: str | None = None
confidence: float | None = None
bbox_xyxy: tuple[float, float, float, float] | None = None
source_view_id: str | None = None
position_confidence: float | None = None
def __post_init__(self):
self.position = np.asarray(self.position, dtype=np.float32)
if len(self.visual_hash) != 64:
raise ValueError("visual_hash must be exactly 64 bytes (512 bits)")
if len(self.semantic_hash) != 64:
raise ValueError("semantic_hash must be exactly 64 bytes (512 bits)")
if self.position.shape != (3,):
raise ValueError("position must have shape (3,)")
if self.bbox_xyxy is not None and len(self.bbox_xyxy) != 4:
raise ValueError("bbox_xyxy must have exactly 4 elements")