diff --git a/data_only_viz/iphone_usb_source.py b/data_only_viz/iphone_usb_source.py index d37d499..65076cc 100644 --- a/data_only_viz/iphone_usb_source.py +++ b/data_only_viz/iphone_usb_source.py @@ -127,8 +127,8 @@ class IphoneUSBSource: # Mirror the video horizontally so the performer facing the camera # interacts naturally (move right -> goes right; raise right arm -> # the right side responds). Env CONCERT_MIRROR=0 disables it. - import os as _os - self._mirror = mirror and (_os.environ.get("CONCERT_MIRROR", "1") != "0") + from .config import VizConfig as _VizConfig + self._mirror = mirror and _VizConfig.from_env().concert_mirror self._codec = av.codec.CodecContext.create("hevc", "r") if av is not None else None self._lock = threading.Lock() self._frame = None # latest BGR np.ndarray diff --git a/data_only_viz/lidar_calib.py b/data_only_viz/lidar_calib.py index ea0513e..e2979c1 100644 --- a/data_only_viz/lidar_calib.py +++ b/data_only_viz/lidar_calib.py @@ -54,7 +54,8 @@ def load_extrinsic(path: Path | None = None) -> Extrinsic: def _path_from_env() -> Path: - p = os.environ.get("ICP_LIDAR_EXTRINSIC") + from .config import VizConfig + p = VizConfig.from_env().icp_lidar_extrinsic return Path(p) if p else DEFAULT_EXTRINSIC_PATH diff --git a/data_only_viz/multi_hmr_worker.py b/data_only_viz/multi_hmr_worker.py index 9a50fbf..c6697b0 100644 --- a/data_only_viz/multi_hmr_worker.py +++ b/data_only_viz/multi_hmr_worker.py @@ -83,9 +83,8 @@ class MultiHMRWorker: # backend: 'pytorch' (default) or 'coreml'. CoreML uses the # .mlpackage at COREML_MLPACKAGE, bypasses MPS torch, and runs # on ANE/GPU/CPU via CoreML.framework natively (3-4x faster). - self.backend = (backend - or os.environ.get("MULTIHMR_BACKEND", "pytorch") - ).strip().lower() + from .config import VizConfig as _VizConfig + self.backend = (backend or _VizConfig.from_env().multihmr_backend).strip().lower() self._stop = threading.Event() self._thread: threading.Thread | None = None self._smooth_shape = [ @@ -107,7 +106,8 @@ class MultiHMRWorker: @staticmethod def is_available() -> bool: - backend = os.environ.get("MULTIHMR_BACKEND", "pytorch").strip().lower() + from .config import VizConfig as _VizConfig + backend = _VizConfig.from_env().multihmr_backend if backend == "coreml": return COREML_MLPACKAGE.exists() if backend == "remote": @@ -264,9 +264,10 @@ class MultiHMRWorker: # autocast MPS teste 2026-05-13 : plus lent (400ms vs 270ms # baseline) car overhead de cast dans le forward. Defaut OFF. # Opt-in via MULTIHMR_AUTOCAST=1. + from .config import VizConfig as _VizConfig self._use_autocast = ( device == "mps" - and os.environ.get("MULTIHMR_AUTOCAST", "0") == "1") + and _VizConfig.from_env().multihmr_autocast) if self._use_autocast: LOG.info("Multi-HMR PyTorch : MPS autocast (fp16) enabled") # torch.compile teste 2026-05-13 : plante en runtime avec @@ -575,12 +576,12 @@ class MultiHMRWorker: return try: if remote: + from .config import VizConfig as _VizConfig from .multihmr_remote import MultiHMRRemoteBackend - host = os.environ.get( - "MULTIHMR_REMOTE_HOST", "192.168.0.175") - port = int(os.environ.get( - "MULTIHMR_REMOTE_PORT", "57140")) - backend = MultiHMRRemoteBackend(host=host, port=port) + _rc = _VizConfig.from_env() + backend = MultiHMRRemoteBackend( + host=_rc.multihmr_remote_host, + port=_rc.multihmr_remote_port) LOG.info("Multi-HMR remote backend (%s:%d)", host, port) else: from .multihmr_coreml import MultiHMRCoreMLBackend diff --git a/data_only_viz/multihmr_coreml.py b/data_only_viz/multihmr_coreml.py index a33d7f3..d5a9ef7 100644 --- a/data_only_viz/multihmr_coreml.py +++ b/data_only_viz/multihmr_coreml.py @@ -184,7 +184,8 @@ class MultiHMRCoreMLBackend: # Standalone (no contention) FP32 = 139 ms = 7.2 fps. Default # stays CPU+GPU. Override with COREML_COMPUTE_UNITS env var # (`all`, `cpu_and_gpu`, `cpu_and_ne`, `cpu_only`) for A/B testing. - cu_env = os.environ.get("COREML_COMPUTE_UNITS", "").strip().lower() + from .config import VizConfig as _VizConfig + cu_env = _VizConfig.from_env().coreml_compute_units cu_map = {"cpu_only": 0, "cpu_and_gpu": 1, "all": 2, "cpu_and_ne": 3} cu = cu_map.get(cu_env, 1) diff --git a/data_only_viz/multihmr_remote.py b/data_only_viz/multihmr_remote.py index 58c3cfe..912d38c 100644 --- a/data_only_viz/multihmr_remote.py +++ b/data_only_viz/multihmr_remote.py @@ -232,10 +232,11 @@ class MultiHMRRemoteBackend: self._sock: socket.socket | None = None self._lock = threading.Lock() - self.use_jpeg = _env_flag("MULTIHMR_REMOTE_JPEG", True) - self.jpeg_quality = int(os.environ.get( - "MULTIHMR_REMOTE_JPEG_QUALITY", "80")) - self.use_async = _env_flag("MULTIHMR_REMOTE_ASYNC", True) + from .config import VizConfig as _VizConfig + _cfg = _VizConfig.from_env() + self.use_jpeg = _cfg.multihmr_remote_jpeg + self.jpeg_quality = _cfg.multihmr_remote_jpeg_quality + self.use_async = _cfg.multihmr_remote_async # Async pipeline state. # Multi-buffer queues (2 in / 3 out) absorb jitter without @@ -290,10 +291,10 @@ class MultiHMRRemoteBackend: @staticmethod def is_available(host: str | None = None, port: int | None = None ) -> bool: - host = host or os.environ.get( - "MULTIHMR_REMOTE_HOST", "192.168.0.175") - port = port or int(os.environ.get( - "MULTIHMR_REMOTE_PORT", "57140")) + from .config import VizConfig as _VizConfig + _cfg = _VizConfig.from_env() + host = host or _cfg.multihmr_remote_host + port = port or _cfg.multihmr_remote_port try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(1.0) diff --git a/data_only_viz/pose_bridge.py b/data_only_viz/pose_bridge.py index 7eafb79..d1ff84a 100644 --- a/data_only_viz/pose_bridge.py +++ b/data_only_viz/pose_bridge.py @@ -86,16 +86,15 @@ class PoseSoundBridge: self._client = SimpleUDPClient(sclang_host, sclang_port) # Broadcast secondaire vers AV-Live-Body (Swift) pour overlay # skeleton dans la fenetre RealityKit. Silent si pas connecte. - import os as _os - _avbody_host = _os.environ.get("AVBODY_HOST", "127.0.0.1") - self._avbody = SimpleUDPClient(_avbody_host, 57126) + from .config import VizConfig as _VizConfig + _cfg = _VizConfig.from_env() + self._avbody = SimpleUDPClient(_cfg.avbody_host, 57126) # Optional VDMX (VJ) OSC mirror. Off by default; when VDMX_OSC_HOST is # set, the VJ-useful pose signals (hands/kin/center/action/count) are # forwarded to VDMX's OSC input so the body can drive any VDMX param. - _vdmx_host = _os.environ.get("VDMX_OSC_HOST") self._vdmx = ( - SimpleUDPClient(_vdmx_host, int(_os.environ.get("VDMX_OSC_PORT", "1234"))) - if _vdmx_host else None + SimpleUDPClient(_cfg.vdmx_osc_host, _cfg.vdmx_osc_port) + if _cfg.vdmx_osc_host else None ) self._period = 1.0 / max(1.0, throttle_hz) self._last_t = 0.0 diff --git a/data_only_viz/pose_filter.py b/data_only_viz/pose_filter.py index 6e508f5..57535c6 100644 --- a/data_only_viz/pose_filter.py +++ b/data_only_viz/pose_filter.py @@ -457,7 +457,8 @@ class IKConstraints: # --------------------------- chain wrapper ------------------------------ def _parse_env_stages() -> tuple[str, ...]: - raw = os.environ.get("POSE_FILTER") + from .config import VizConfig + raw = VizConfig.from_env().pose_filter if raw is None: return DEFAULT_STAGES raw = raw.strip().lower() @@ -709,7 +710,8 @@ class ArkitFuse: # (median + Kalman 2D + lookahead) — no IK, no spring. def _parse_env_face_stages() -> tuple[str, ...]: - raw = os.environ.get("POSE_FILTER_FACE") + from .config import VizConfig + raw = VizConfig.from_env().pose_filter_face if raw is None: return ("median", "kalman", "lookahead") raw = raw.strip().lower() @@ -720,7 +722,8 @@ def _parse_env_face_stages() -> tuple[str, ...]: def _parse_env_hand_stages() -> tuple[str, ...]: - raw = os.environ.get("POSE_FILTER_HAND") + from .config import VizConfig + raw = VizConfig.from_env().pose_filter_hand if raw is None: return ("median", "kalman", "lookahead") raw = raw.strip().lower() diff --git a/data_only_viz/smplx_osc_sender.py b/data_only_viz/smplx_osc_sender.py index 9d3cb53..dbc67cf 100644 --- a/data_only_viz/smplx_osc_sender.py +++ b/data_only_viz/smplx_osc_sender.py @@ -45,9 +45,10 @@ class SMPLXTCPSender: def __init__(self, state: State, host: str = "127.0.0.1", port: int = PORT, target_fps: float = 30.0, enable_rigging: bool = True) -> None: - import os as _os + from .config import VizConfig as _VizConfig + _cfg = _VizConfig.from_env() self.state = state - self.host = _os.environ.get("AVBODY_HOST", host) + self.host = _cfg.avbody_host self.port = port self.period = 1.0 / max(1.0, target_fps) self._stop = threading.Event() @@ -57,7 +58,7 @@ class SMPLXTCPSender: # on translate le mesh via le delta pelvis Apple Vision (30 fps). # MULTIHMR_REID: 'dino' (try DINOv2 + IoU fusion, fallback IoU) / # 'iou' (pure IoU). Default: 'dino' if mlpackage exists. - reid_mode = os.environ.get("MULTIHMR_REID", "dino").lower() + reid_mode = _cfg.multihmr_reid dino = None if enable_rigging and reid_mode == "dino" and DinoReid is not None: try: @@ -70,7 +71,7 @@ class SMPLXTCPSender: except Exception as e: # noqa: BLE001 LOG.warning("MeshRigger: dino load failed (%s), IoU only", e) dino = None - dino_weight = float(os.environ.get("MULTIHMR_REID_ALPHA", "0.5")) + dino_weight = _cfg.multihmr_reid_alpha self._rigger = MeshRigger( state, dino_weight=dino_weight, dino_reid=dino) if enable_rigging else None