From 2b38ddacd1ad26a6074242a825e375e08301e8ac Mon Sep 17 00:00:00 2001 From: Cyril Achard Date: Thu, 9 Jul 2026 11:11:09 +0200 Subject: [PATCH 1/7] Build processors inside DLC worker when needed Add deferred processor construction via `ProcessorSpec` so processors that require worker-thread context can be built in `DLCLiveProcessor._worker_loop` instead of the GUI thread. Update both main window and service configuration paths to choose between immediate instantiation and worker-side specs, add lifecycle/context logging helpers, and improve processor shutdown/reset cleanup by calling processor `stop()` when appropriate. Also adjust DLC logging defaults to reduce timing noise while enabling targeted lifecycle diagnostics. --- dlclivegui/config.py | 3 +- dlclivegui/gui/main_window.py | 56 +++++++-- dlclivegui/processors/dlc_processor_socket.py | 5 + dlclivegui/processors/processor_utils.py | 60 ++++++++++ dlclivegui/services/dlc_processor.py | 106 ++++++++++++++++-- 5 files changed, 213 insertions(+), 17 deletions(-) diff --git a/dlclivegui/config.py b/dlclivegui/config.py index 585ead2..acc2a6d 100644 --- a/dlclivegui/config.py +++ b/dlclivegui/config.py @@ -30,7 +30,8 @@ SINGLE_CAMERA_WORKER_DO_LOG_TIMING: bool = False MULTI_CAMERA_WORKER_DO_LOG_TIMING: bool = False REC_DO_LOG_TIMING: bool = False -DLC_DO_LOG_TIMING: bool = True +DLC_DO_LOG_TIMING: bool = False +DLC_LIFECYCLE_EXTRA_LOGS: bool = True # MAIN_WINDOW_DO_LOG_TIMING: bool = False #### Backends BASLER_DO_LOG_TIMING: bool = False diff --git a/dlclivegui/gui/main_window.py b/dlclivegui/gui/main_window.py index 493335b..4e41edf 100644 --- a/dlclivegui/gui/main_window.py +++ b/dlclivegui/gui/main_window.py @@ -63,8 +63,11 @@ ) from ..processors.processor_utils import ( + create_spec_from_scan, default_processors_dir, instantiate_from_scan, + log_processor_context, + processor_builds_in_worker, scan_processor_folder, scan_processor_package, ) @@ -1953,33 +1956,70 @@ def _configure_dlc(self) -> bool: except (ValueError, RuntimeError, json.JSONDecodeError) as exc: self._show_error(f"Invalid DLCLive settings: {exc}") return False + if not settings.model_path: self._show_error("Please select a DLCLive model before starting inference.") return False - # Instantiate processor if selected processor = None + processor_spec = None + if self._processor_control_enabled(): selected_key = self.processor_combo.currentData() self._settings_store.set_processor_key(selected_key) if selected_key is not None and self._scanned_processors: try: - # For now, instantiate with no parameters - processor = instantiate_from_scan(self._scanned_processors, selected_key) - processor_name = self._scanned_processors[selected_key]["name"] + processor_info = self._scanned_processors[selected_key] + processor_class = processor_info["class"] + processor_name = processor_info.get("name", processor_class.__name__) + + if processor_builds_in_worker(processor_class): + processor_spec = create_spec_from_scan( + self._scanned_processors, + selected_key, + ) + processor = None + + log_processor_context( + f"MainWindow._configure_dlc - SPEC: {processor_class.__name__}", + logger, + ) + + else: + processor = instantiate_from_scan( + self._scanned_processors, + selected_key, + ) + processor_spec = None + + log_processor_context( + f"MainWindow._configure_dlc - INSTANCE: {type(processor).__name__}", + logger, + ) + self.statusBar().showMessage(f"Loaded processor: {processor_name}", 3000) + except Exception as e: - error_msg = f"Failed to instantiate processor: {e}" + error_msg = f"Failed to configure processor: {e}" self._show_error(error_msg) - logger.error(error_msg) + logger.exception(error_msg) return False + else: selected_key = self.processor_combo.currentData() if selected_key is not None: - self.statusBar().showMessage(f"Processor selection ignored (control disabled): {selected_key}", 3000) + self.statusBar().showMessage( + f"Processor selection ignored (control disabled): {selected_key}", + 3000, + ) + + self._dlc.configure( + settings, + processor=processor, + processor_spec=processor_spec, + ) - self._dlc.configure(settings, processor=processor) self._model_path_store.save_if_valid(settings.model_path) return True diff --git a/dlclivegui/processors/dlc_processor_socket.py b/dlclivegui/processors/dlc_processor_socket.py index 594512c..f165512 100644 --- a/dlclivegui/processors/dlc_processor_socket.py +++ b/dlclivegui/processors/dlc_processor_socket.py @@ -41,6 +41,11 @@ class BaseProcessorSocket(Processor): PROCESSOR_DESCRIPTION = "Base class for socket-based processors with multi-client support" PROCESSOR_PARAMS = {} + # Experimental: + # Socket/Teensy/Unity processors often start threads, sockets, serial ports, etc. + # Build them inside the DLCLive worker to match legacy Tk GUI behavior. + PROCESSOR_BUILD_IN_WORKER = False + def __init__( self, bind=None, diff --git a/dlclivegui/processors/processor_utils.py b/dlclivegui/processors/processor_utils.py index 467792b..ddfbc4b 100644 --- a/dlclivegui/processors/processor_utils.py +++ b/dlclivegui/processors/processor_utils.py @@ -5,13 +5,32 @@ import logging import pkgutil import sys +from dataclasses import dataclass, field from importlib import import_module from importlib.resources import as_file, files from pathlib import Path +from typing import Any + +from dlclivegui.config import DLC_LIFECYCLE_EXTRA_LOGS logger = logging.getLogger(__name__) +@dataclass +class ProcessorSpec: + cls: type + kwargs: dict[str, Any] = field(default_factory=dict) + + @property + def name(self) -> str: + return getattr(self.cls, "PROCESSOR_NAME", self.cls.__name__) + + def build(self) -> Any: + """Instantiate the processor class with the provided kwargs.""" + log_processor_context(f"ProcessorSpec.build: {self.name} with kwargs={self.kwargs}", logger) + return self.cls(**self.kwargs) + + def default_processors_dir() -> str: with as_file(files("dlclivegui").joinpath("processors")) as path: return str(path) @@ -184,6 +203,28 @@ def load_processors_from_file(file_path: str | Path): return {} +def create_spec_from_scan(processors_dict, processor_key, **kwargs) -> ProcessorSpec: + """Create a ProcessorSpec from scan_processor_folder results, without instantiating the processor yet.""" + if processor_key not in processors_dict: + available = ", ".join(processors_dict.keys()) + raise ValueError(f"Unknown processor '{processor_key}'. Available: {available}") + + processor_info = processors_dict[processor_key] + processor_class = processor_info["class"] + return ProcessorSpec(cls=processor_class, kwargs=kwargs) + + +def processor_builds_in_worker(processor_class: type) -> bool: + """ + Return True if this processor class requests construction inside DLCLiveWorker. + + Processors opt in by defining: + + PROCESSOR_BUILD_IN_WORKER = True + """ + return bool(getattr(processor_class, "PROCESSOR_BUILD_IN_WORKER", False)) + + def instantiate_from_scan(processors_dict, processor_key, **kwargs): """ Instantiate a processor from scan_processor_folder results. @@ -228,3 +269,22 @@ def display_processor_info(processors): print(f" - {param_name} ({param_info['type']})") print(f" Default: {param_info['default']}") print(f" {param_info['description']}") + + +def log_processor_context(label: str, custom_logger: logging.Logger = logger): + if not DLC_LIFECYCLE_EXTRA_LOGS: + return + + import multiprocessing as mp + import os + import threading + import time + + custom_logger.info( + "[CUSTOM PROCESSOR] %s | pid=%s process=%s thread=%s time=%.6f", + label, + os.getpid(), + mp.current_process().name, + threading.current_thread().name, + time.time(), + ) diff --git a/dlclivegui/services/dlc_processor.py b/dlclivegui/services/dlc_processor.py index 4d4df75..71a278e 100644 --- a/dlclivegui/services/dlc_processor.py +++ b/dlclivegui/services/dlc_processor.py @@ -17,7 +17,13 @@ from PySide6.QtCore import QObject, Signal from dlclivegui.config import DLC_DO_LOG_TIMING, DLCProcessorSettings, ModelType -from dlclivegui.processors.processor_utils import instantiate_from_scan +from dlclivegui.processors.dlc_processor_socket import BaseProcessorSocket +from dlclivegui.processors.processor_utils import ( + ProcessorSpec, + create_spec_from_scan, + instantiate_from_scan, + log_processor_context, +) from dlclivegui.temp import Engine # type: ignore # TODO use main package enum when released from dlclivegui.utils.stats import WorkerTimingStats from dlclivegui.utils.utils import format_thread_stack @@ -156,6 +162,8 @@ def __init__(self) -> None: self._settings = DLCProcessorSettings() self._dlc: Any | None = None self._processor: Any | None = None + self._processor_spec: ProcessorSpec | None = None + self.processor_built_from_spec = False # Worker thread and queue self._queue: queue.Queue[Any] | None = None self._worker_thread: threading.Thread | None = None @@ -194,15 +202,27 @@ def __init__(self) -> None: def get_model_backend(model_path: str) -> Engine: return Engine.from_model_path(model_path) - def configure(self, settings: DLCProcessorSettings, processor: Any | None = None) -> None: + def configure( + self, settings: DLCProcessorSettings, processor: Any | None = None, processor_spec: ProcessorSpec | None = None + ) -> None: with self._lifecycle_lock: if self._state != WorkerState.STOPPED: raise RuntimeError("Cannot configure DLCLiveProcessor while it is running. Please stop it first.") + if processor is not None and processor_spec is not None: + raise ValueError( + "Cannot provide both a processor instance and a processor_spec. Please provide only one." + ) self._settings = settings self._processor = processor + self._processor_spec = processor_spec + self.processor_built_from_spec = False - def reset(self) -> None: + def reset(self, reset_processor_plugin: bool = False) -> None: """Stop the worker thread and drop the current DLCLive instance.""" + had_runtime = ( + self._worker_thread is not None or self._dlc is not None or self._initialized or reset_processor_plugin + ) + stopped = self._stop_worker() if not stopped: with self._lifecycle_lock: @@ -211,6 +231,10 @@ def reset(self) -> None: "Reset requested but worker thread is still alive; skipping DLCLive reset to avoid potential issues." ) return + + if had_runtime and self._processor is not None: + self._cleanup_processor() + self._dlc = None self._initialized = False with self._stats_lock: @@ -226,10 +250,28 @@ def reset(self) -> None: self._gpu_inference_times.clear() self._processor_overhead_times.clear() + def _cleanup_processor(self) -> None: + proc = self._processor + if proc is None: + return + + stop = getattr(proc, "stop", None) + if callable(stop): + try: + log_processor_context(f"Stopping processor: {type(proc).__name__}", logger) + stop() + except Exception: + logger.exception("Failed to stop processor cleanly") + + self._processor = None + self._processor_built_from_spec = False + def shutdown(self) -> None: stopped = self._stop_worker() if not stopped: with self._lifecycle_lock: + if self._processor is not None: + self._cleanup_processor() self._pending_reset = True logger.warning( "Shutdown requested but worker thread is still alive; DLCLive instance may not be fully released." @@ -446,6 +488,7 @@ def _start_worker_locked(self, init_frame: np.ndarray, init_timestamp: float) -> self._queue = None self._stop_event.clear() self._state = WorkerState.STARTING + log_processor_context("Starting DLCLive worker thread", logger) self._worker_thread = threading.Thread( target=self._worker_loop, args=(init_frame, init_timestamp), @@ -578,6 +621,7 @@ def _process_frame( """ if self._dlc is None: raise RuntimeError("DLCLive instance is not initialized.") + # log_processor_context(f"DLCLiveProcessor._process_frame: timestamp={timestamp:.6f}", logger) # Time GPU inference (and processor overhead when present) with self._timing.measure("DLC.prepare_frame"): frame = self._prepare_input_frame(frame) @@ -641,10 +685,11 @@ def _process_frame( def _worker_loop(self, init_frame: np.ndarray, init_timestamp: float) -> None: try: - # -------- Initialization (unchanged) -------- + # -------- Initialization -------- if not self._settings.model_path: raise RuntimeError("No DLCLive model path configured.") + log_processor_context("DLCLiveProcessor._worker_loop", logger) with self._timing.measure("DLC.build_options"): dyn = self._settings.dynamic if not isinstance(dyn, (list, tuple)) or len(dyn) != 3: @@ -654,10 +699,33 @@ def _worker_loop(self, init_frame: np.ndarray, init_timestamp: float) -> None: raise RuntimeError("Invalid dynamic crop settings format.") from e enabled, margin, max_missing = dyn + custom_proc = None + + if self._processor is not None: + custom_proc = self._processor + log_processor_context( + f"Using existing processor instance: {type(custom_proc).__name__}", + logger, + ) + + elif self._processor_spec is not None: + log_processor_context( + f"Building processor from spec: {self._processor_spec.name}", + logger, + ) + custom_proc = self._processor_spec.build() + + with self._lifecycle_lock: + self._processor = custom_proc + self._processor_built_from_spec = True + + else: + custom_proc = None + options = { "model_path": self._settings.model_path, "model_type": self._settings.model_type, - "processor": self._processor, + "processor": custom_proc, "dynamic": [enabled, margin, max_missing], "resize": self._settings.resize, "precision": self._settings.precision, @@ -685,6 +753,7 @@ def _worker_loop(self, init_frame: np.ndarray, init_timestamp: float) -> None: ) with self._timing.measure("DLC.construct"): self._dlc = DLCLive(**options) + log_processor_context("DLCLive instance constructed", logger) self._timing.maybe_log() except Exception as exc: self._timing.note_error() @@ -712,6 +781,7 @@ def _worker_loop(self, init_frame: np.ndarray, init_timestamp: float) -> None: # First inference to initialize with self._timing.measure("DLC.init_inference"): self._dlc.init_inference(init_frame) + log_processor_context("DLCLive init_inference completed", logger) self._debug_log_dlc_runner_device() self._timing.note_frame() @@ -859,13 +929,33 @@ def configure(self, settings: DLCProcessorSettings, scanned_processors: dict, se raise RuntimeError("Cannot configure DLCLiveProcessor while it is running. Please stop it first.") processor = None + processor_spec = None + if selected_key is not None and scanned_processors: try: - processor = instantiate_from_scan(scanned_processors, selected_key) + processor_info = scanned_processors[selected_key] + processor_class = processor_info["class"] + + if BaseProcessorSocket.do_build_in_worker(processor_class): + processor_spec = create_spec_from_scan(scanned_processors, selected_key) + + log_processor_context( + f"DLCLiveProcessor.configure - SPEC: {processor_class.__name__}", + logger, + ) + else: + processor = instantiate_from_scan(scanned_processors, selected_key) + + log_processor_context( + f"DLCLiveProcessor.configure - INSTANCE: {type(processor).__name__}", + logger, + ) + except Exception as exc: - logger.error("Failed to instantiate processor: %s", exc) + logger.error("Failed to configure processor: %s", exc, exc_info=True) return False - self._proc.configure(settings, processor=processor) + + self._proc.configure(settings, processor=processor, processor_spec=processor_spec) return True def start(self): From 9bfddd545cec99ec772eaf28ccab3a82735a290d Mon Sep 17 00:00:00 2001 From: Cyril Achard Date: Thu, 9 Jul 2026 11:11:55 +0200 Subject: [PATCH 2/7] Add mock proc test fiels --- .../custom/mock_socket_processor.py | 532 ++ .../custom/mock_unity_socket_client.ipynb | 4609 +++++++++++++++++ 2 files changed, 5141 insertions(+) create mode 100644 dlclivegui/processors/custom/mock_socket_processor.py create mode 100644 dlclivegui/processors/custom/mock_unity_socket_client.ipynb diff --git a/dlclivegui/processors/custom/mock_socket_processor.py b/dlclivegui/processors/custom/mock_socket_processor.py new file mode 100644 index 0000000..788bde3 --- /dev/null +++ b/dlclivegui/processors/custom/mock_socket_processor.py @@ -0,0 +1,532 @@ +"""Standalone mock DLC processor plugin with socket listener support. + +This fixture intentionally avoids importing dlclive, dlclivegui, Teensy, serial, +NumPy, or project-specific processor base classes. + +It is designed to test two separate concerns without mixing them: + +1. GUI processor plugin discovery/configuration + - Exposes PROCESSOR_* metadata. + - Exposes PROCESSOR_BUILD_IN_WORKER = True. + - Exposes get_available_processors(), which your loader can consume without + requiring this class to inherit from dlclive.processor.Processor. + +2. Runtime socket listener behavior + - Starts a multiprocessing.connection.Listener. + - Accepts one or more clients on a background thread. + - Receives simple command dictionaries from clients. + - Broadcasts mock pose payloads to connected clients from process(). + +It does NOT mock Teensy serial acquisition. For the listener send/receive tests, +Teensy is not required: the Teensy path is a separate serial-reader concern. +""" + +from __future__ import annotations + +import logging +import pickle +import sys +import time +from collections import deque +from multiprocessing.connection import Client, Listener +from pathlib import Path +from threading import Event, Lock, Thread +from typing import Any + +logger = logging.getLogger(__name__) + +IP_ADDRESS = "127.0.0.1" +PORT = 6000 + + +class MockSocketProcessor: + """Standalone socket-based mock processor for tests. + + This intentionally reimplements the core listener/thread/control behavior + instead of inheriting from project classes. It is suitable for fixture usage + where external dependencies such as Teensy, serial, dlclive, or dlclivegui + should not be imported. + + Expected test usage: + + proc = MockSocketProcessor(bind=("127.0.0.1", free_port())) + conn = Client(proc.address, authkey=proc.authkey) + conn.send({"cmd": "ping"}) + assert conn.recv()["type"] == "pong" + proc.process([[1, 2, 0.9]]) + assert conn.recv()["type"] == "pose" + proc.stop() + + Notes: + - `process()` accepts any pose-like Python object. It does not validate + DLC shape because this mock is for socket lifecycle tests, not pose + validation tests. + - Payloads are sent through multiprocessing.connection, matching the + style used by the legacy socket processors. + """ + + PROCESSOR_NAME = "Mock Socket Processor" + PROCESSOR_DESCRIPTION = "Standalone mock socket processor without Teensy or DLCLive imports." + PROCESSOR_BUILD_IN_WORKER = False + PROCESSOR_PARAMS = { + "bind": { + "type": "tuple", + "default": (IP_ADDRESS, PORT), + "description": "Server bind address. Use port 0 to request an ephemeral port.", + }, + "authkey": { + "type": "bytes", + "default": b"secret password", + "description": "Authentication key for multiprocessing.connection clients.", + }, + "start_server": { + "type": "bool", + "default": True, + "description": "Whether to start the listener in __init__.", + }, + "socket_timeout": { + "type": "float", + "default": 0.05, + "description": "Accept-loop timeout in seconds.", + }, + "save_original": { + "type": "bool", + "default": False, + "description": "Whether to store raw pose payloads while recording.", + }, + } + + def __init__( + self, + bind: tuple[str, int] = (IP_ADDRESS, PORT), + authkey: bytes = b"secret password", + *, + start_server: bool = True, + socket_timeout: float = 0.05, + save_original: bool = False, + ) -> None: + self.address = bind + self.authkey = authkey + self._socket_timeout = float(socket_timeout) + self.save_original = bool(save_original) + + # Runtime listener/client state. + self.listener: Listener | None = None + self.conns: set[Any] = set() + self._conns_lock = Lock() + self._stop = Event() + self._accept_thread: Thread | None = None + self._rx_threads: set[Thread] = set() + + # Recording/control state compatible with socket-processor expectations. + self._recording = Event() + self._vid_recording = Event() + self._session_name = "test_session" + self.filename: str | None = None + + # Minimal data buffers for save/get_data tests. + self.start_time = time.time() + self.time_stamp = deque() + self.step = deque() + self.frame_time = deque() + self.pose_time = deque() + self.original_pose = deque() if self.save_original else None + self.received_commands = deque() + self.broadcast_count = 0 + self.curr_step = 0 + + if start_server: + self.start_server(bind, authkey=authkey, timeout=self._socket_timeout) + + # ------------------------------------------------------------------ + # Properties matching the real socket processors + # ------------------------------------------------------------------ + @property + def recording(self) -> bool: + return self._recording.is_set() + + @property + def video_recording(self) -> bool: + return self._vid_recording.is_set() + + @property + def session_name(self) -> str: + return self._session_name + + @session_name.setter + def session_name(self, name: str) -> None: + self._session_name = str(name) + self.filename = f"{self._session_name}_mock_processor_data.pkl" + + # ------------------------------------------------------------------ + # Listener lifecycle + # ------------------------------------------------------------------ + def start_server( + self, + bind: tuple[str, int] | None = None, + authkey: bytes | None = None, + *, + timeout: float | None = None, + ) -> None: + """Start the socket listener if it is not already running.""" + if self.listener is not None: + return + + if bind is not None: + self.address = bind + if authkey is not None: + self.authkey = authkey + if timeout is not None: + self._socket_timeout = float(timeout) + + self._stop.clear() + self.listener = Listener(self.address, authkey=self.authkey) + + # If bind used port 0, update address to the actual ephemeral port. + self.address = self._actual_listener_address(self.listener, fallback=self.address) + + self._set_listener_timeout(self.listener, self._socket_timeout) + + self._accept_thread = Thread(target=self._accept_loop, name="MockSocketProcessorAccept", daemon=True) + self._accept_thread.start() + logger.info("MockSocketProcessor listening on %s:%s", self.address[0], self.address[1]) + + @staticmethod + def _actual_listener_address(listener: Listener, fallback: tuple[str, int]) -> tuple[str, int]: + """Best-effort extraction of the actual listener address.""" + try: + raw = getattr(listener, "_listener", None) + sock = getattr(raw, "_socket", None) + if sock is not None: + addr = sock.getsockname() + return (str(addr[0]), int(addr[1])) + except Exception: + pass + try: + addr = listener.address + return (str(addr[0]), int(addr[1])) + except Exception: + return fallback + + @staticmethod + def _set_listener_timeout(listener: Listener, timeout: float) -> None: + """Set accept timeout on CPython listener internals, best effort.""" + raw = getattr(listener, "_listener", None) + for candidate in (raw, getattr(raw, "_socket", None)): + try: + if candidate is not None and hasattr(candidate, "settimeout"): + candidate.settimeout(timeout) + return + except Exception: + pass + + def _accept_loop(self) -> None: + while not self._stop.is_set(): + try: + if self.listener is None: + return + conn = self.listener.accept() + except TimeoutError: + continue + except (OSError, EOFError): + if self._stop.is_set(): + break + continue + except Exception: + if self._stop.is_set(): + break + logger.exception("Unexpected accept-loop error") + continue + + with self._conns_lock: + self.conns.add(conn) + + rx = Thread(target=self._rx_loop, args=(conn,), name="MockSocketProcessorRx", daemon=True) + self._rx_threads.add(rx) + rx.start() + logger.info("MockSocketProcessor client connected") + + def _rx_loop(self, conn: Any) -> None: + while not self._stop.is_set(): + try: + if conn.poll(0.05): + msg = conn.recv() + self._handle_client_message(msg, conn=conn) + continue + + if getattr(conn, "closed", False): + break + + except (EOFError, OSError, ConnectionError, BrokenPipeError): + break + except Exception: + logger.exception("Unexpected receive-loop error") + break + + self._close_conn(conn) + + def _close_conn(self, conn: Any) -> None: + try: + conn.close() + except Exception: + pass + with self._conns_lock: + self.conns.discard(conn) + + def stop(self) -> None: + """Stop listener, close clients, and join background threads best-effort.""" + if self._stop.is_set(): + return + + self._stop.set() + + # Wake accept() if needed. + try: + Client(self.address, authkey=self.authkey).close() + except Exception: + pass + + with self._conns_lock: + conns = list(self.conns) + for conn in conns: + self._close_conn(conn) + + try: + if self.listener is not None: + self.listener.close() + except Exception: + pass + self.listener = None + + if self._accept_thread is not None: + self._accept_thread.join(timeout=1.0) + self._accept_thread = None + + for thread in list(self._rx_threads): + try: + thread.join(timeout=0.5) + except Exception: + pass + self._rx_threads.clear() + + if sys.platform.startswith("win"): + time.sleep(0.05) + + close = stop + + def __del__(self) -> None: + try: + self.stop() + except Exception: + pass + + # ------------------------------------------------------------------ + # Client command handling + # ------------------------------------------------------------------ + def _handle_client_message(self, msg: Any, *, conn: Any | None = None) -> None: + self.received_commands.append(msg) + + if not isinstance(msg, dict): + self._send_to(conn, {"type": "error", "error": "message must be a dict"}) + return + + cmd = msg.get("cmd") + + if cmd == "ping": + self._send_to( + conn, + { + "type": "pong", + "timestamp": time.time(), + "session_name": self.session_name, + "recording": self.recording, + "video_recording": self.video_recording, + "clients": self.client_count(), + }, + ) + + elif cmd == "status": + self._send_to(conn, self.status_payload()) + + elif cmd == "set_session_name": + self.session_name = msg.get("session_name", "default_session") + self._send_to(conn, {"type": "ack", "cmd": cmd, "session_name": self.session_name}) + + elif cmd == "start_recording": + self.start_recording() + self._send_to(conn, {"type": "ack", "cmd": cmd, "recording": True}) + + elif cmd == "stop_recording": + self.stop_recording() + self._send_to(conn, {"type": "ack", "cmd": cmd, "recording": False}) + + elif cmd == "save": + file = msg.get("filename", self.filename) + result = self.save(file) + self._send_to(conn, {"type": "ack", "cmd": cmd, "result": result, "filename": file}) + + elif cmd == "close": + self._send_to(conn, {"type": "ack", "cmd": cmd}) + if conn is not None: + self._close_conn(conn) + + else: + self._send_to(conn, {"type": "error", "error": f"unknown cmd: {cmd!r}"}) + + @staticmethod + def _send_to(conn: Any | None, payload: Any) -> bool: + if conn is None: + return False + try: + conn.send(payload) + return True + except Exception: + return False + + def client_count(self) -> int: + with self._conns_lock: + return len(self.conns) + + def status_payload(self) -> dict[str, Any]: + return { + "type": "status", + "session_name": self.session_name, + "recording": self.recording, + "video_recording": self.video_recording, + "clients": self.client_count(), + "steps": self.curr_step, + "broadcast_count": self.broadcast_count, + "address": self.address, + } + + # ------------------------------------------------------------------ + # Recording helpers + # ------------------------------------------------------------------ + def start_recording(self) -> None: + self._recording.set() + self._vid_recording.set() + self._clear_data_queues() + self.curr_step = 0 + + def stop_recording(self) -> None: + self._recording.clear() + self._vid_recording.clear() + + def _clear_data_queues(self) -> None: + self.time_stamp.clear() + self.step.clear() + self.frame_time.clear() + self.pose_time.clear() + if self.original_pose is not None: + self.original_pose.clear() + + # ------------------------------------------------------------------ + # Process/broadcast path + # ------------------------------------------------------------------ + def process(self, pose: Any, **kwargs: Any) -> Any: + """Mock DLCLive processor callback. + + Records minimal metadata when recording is active and broadcasts a simple + pose payload to all connected clients. + """ + now = time.time() + self.curr_step += 1 + + if self.recording: + self.time_stamp.append(now) + self.step.append(self.curr_step) + self.frame_time.append(kwargs.get("frame_time", -1)) + if "pose_time" in kwargs: + self.pose_time.append(kwargs["pose_time"]) + if self.original_pose is not None: + self.original_pose.append(pose) + + payload = { + "type": "pose", + "timestamp": now, + "step": self.curr_step, + "pose": self._make_pickle_safe_pose(pose), + "frame_time": kwargs.get("frame_time", None), + "pose_time": kwargs.get("pose_time", None), + "recording": self.recording, + } + self.broadcast(payload) + return pose + + @staticmethod + def _make_pickle_safe_pose(pose: Any) -> Any: + """Convert common array-likes to socket-safe Python types.""" + tolist = getattr(pose, "tolist", None) + if callable(tolist): + try: + return tolist() + except Exception: + pass + return pose + + def broadcast(self, payload: Any) -> None: + with self._conns_lock: + conns = list(self.conns) + + dead = [] + for conn in conns: + try: + conn.send(payload) + self.broadcast_count += 1 + except Exception: + dead.append(conn) + + for conn in dead: + self._close_conn(conn) + + # ------------------------------------------------------------------ + # Save/get_data helpers + # ------------------------------------------------------------------ + def get_data(self) -> dict[str, Any]: + return { + "start_time": self.start_time, + "session_name": self.session_name, + "time_stamp": list(self.time_stamp), + "step": list(self.step), + "frame_time": list(self.frame_time), + "pose_time": list(self.pose_time), + "recording": self.recording, + "video_recording": self.video_recording, + "received_commands": list(self.received_commands), + "broadcast_count": self.broadcast_count, + } + + def save(self, file: str | Path | None = None) -> int: + if not file: + return 0 + try: + path = Path(file) + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("wb") as fh: + pickle.dump(self.get_data(), fh) + return 1 + except Exception: + logger.exception("MockSocketProcessor save failed") + return -1 + + +# Optional aliases useful in different test styles. +MockPDSocketProcessor = MockSocketProcessor +MockUnitySocketProcessor = MockSocketProcessor + + +def get_available_processors() -> dict[str, dict[str, Any]]: + """Plugin-discovery entrypoint used by dlclivegui.processor_utils. + + This avoids requiring the class to inherit from dlclive.processor.Processor + during tests. The loader path that prefers get_available_processors() can + still discover this processor as a GUI plugin fixture. + """ + return { + "MockSocketProcessor": { + "class": MockSocketProcessor, + "name": getattr(MockSocketProcessor, "PROCESSOR_NAME", "MockSocketProcessor"), + "description": getattr(MockSocketProcessor, "PROCESSOR_DESCRIPTION", ""), + "params": getattr(MockSocketProcessor, "PROCESSOR_PARAMS", {}), + } + } diff --git a/dlclivegui/processors/custom/mock_unity_socket_client.ipynb b/dlclivegui/processors/custom/mock_unity_socket_client.ipynb new file mode 100644 index 0000000..63e4569 --- /dev/null +++ b/dlclivegui/processors/custom/mock_unity_socket_client.ipynb @@ -0,0 +1,4609 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", + "metadata": {}, + "source": [ + "# Mock Unity Socket Client for DLCLive Processor\n", + "\n", + "This notebook acts as the **Unity-side socket client** for the legacy DLC socket processor.\n", + "\n", + "## Which processor style this targets\n", + "\n", + "The legacy processor chain you showed is:\n", + "\n", + "```text\n", + "dlc_inference_w_pd_sync\n", + " -> dlc_inference_w_pd\n", + " -> MyProcessor_socket\n", + "```\n", + "\n", + "`MyProcessor_socket` opens a `multiprocessing.connection.Listener`, defaulting to:\n", + "\n", + "```python\n", + "(\"127.0.0.1\", 6000)\n", + "authkey=b\"secret password\"\n", + "```\n", + "\n", + "It sends payloads from `process()` shaped like:\n", + "\n", + "```python\n", + "[time.time(), x, y, heading, head_angle, signal]\n", + "```\n", + "\n", + "This notebook connects as the **client** and tries to catch those pose/kinematics packets.\n", + "\n", + "## Important ordering note\n", + "\n", + "The legacy `MyProcessor_socket` does **not** have a background accept thread. It accepts a client only when `process()` runs. That means if DLC inference is not producing poses yet, `Client(...)` may block or time out. If that happens, start/continue DLC inference and retry." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "acae54e37e7d407bbb7b55eff062a284", + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import annotations\n", + "\n", + "import json\n", + "import queue\n", + "import threading\n", + "import time\n", + "from dataclasses import asdict, dataclass\n", + "from multiprocessing.connection import Client\n", + "from pathlib import Path\n", + "from typing import Any" + ] + }, + { + "cell_type": "markdown", + "id": "9a63283cbaf04dbcab1f6479b197f3a8", + "metadata": {}, + "source": [ + "## Configuration\n", + "\n", + "Adjust these if the processor uses a different port or auth key.\n", + "\n", + "For the legacy processor, the defaults are usually:\n", + "\n", + "```python\n", + "ADDRESS = (\"127.0.0.1\", 6000)\n", + "AUTHKEY = b\"secret password\"\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "8dd0d8092fe74a7c96281538738b07e2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Target processor socket: ('127.0.0.1', 6000)\n" + ] + } + ], + "source": [ + "ADDRESS = (\"127.0.0.1\", 6000)\n", + "AUTHKEY = b\"secret password\"\n", + "\n", + "# How long to wait for a connection attempt before considering it failed.\n", + "CONNECT_TIMEOUT_S = 10.0\n", + "\n", + "# How long the receive loop should poll while waiting for new packets.\n", + "POLL_INTERVAL_S = 0.05\n", + "\n", + "print(\"Target processor socket:\", ADDRESS)" + ] + }, + { + "cell_type": "markdown", + "id": "72eea5119410473aa328ad9291626812", + "metadata": {}, + "source": [ + "## Client helpers\n", + "\n", + "`multiprocessing.connection.Client(...)` can block if the server has not called `accept()` yet. To avoid freezing the notebook, `connect_with_timeout()` performs the connection attempt in a background thread." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8edb47106e1a46a883d545849b8ab81b", + "metadata": {}, + "outputs": [], + "source": [ + "class ConnectTimeoutError(TimeoutError):\n", + " pass\n", + "\n", + "\n", + "def connect_with_timeout(address, authkey: bytes, timeout_s: float = 10.0):\n", + " \"\"\"Connect to a multiprocessing.connection.Listener without freezing the notebook forever.\"\"\"\n", + " result_q: queue.Queue[tuple[str, Any]] = queue.Queue(maxsize=1)\n", + "\n", + " def worker():\n", + " try:\n", + " conn = Client(address, authkey=authkey)\n", + " result_q.put((\"ok\", conn))\n", + " except Exception as exc:\n", + " result_q.put((\"error\", exc))\n", + "\n", + " t = threading.Thread(target=worker, name=\"MockUnityConnect\", daemon=True)\n", + " t.start()\n", + " t.join(timeout_s)\n", + "\n", + " if t.is_alive():\n", + " raise ConnectTimeoutError(\n", + " f\"Timed out after {timeout_s:.1f}s while connecting to {address}. \"\n", + " \"For legacy MyProcessor_socket, this can happen if DLC process() has not called listener.accept() yet.\"\n", + " )\n", + "\n", + " status, payload = result_q.get_nowait()\n", + " if status == \"ok\":\n", + " return payload\n", + " raise payload\n", + "\n", + "\n", + "@dataclass\n", + "class LegacyPosePacket:\n", + " timestamp: float\n", + " x: float\n", + " y: float\n", + " heading: float\n", + " head_angle: float\n", + " signal: float\n", + " raw: Any\n", + "\n", + "\n", + "def decode_payload(payload: Any) -> dict[str, Any]:\n", + " \"\"\"Decode either legacy list payloads or newer dict/list mock payloads.\"\"\"\n", + " # Legacy MyProcessor_socket payload:\n", + " # [time.time(), x, y, heading, head_angle, signal]\n", + " if isinstance(payload, list) and len(payload) == 6:\n", + " pkt = LegacyPosePacket(\n", + " timestamp=float(payload[0]),\n", + " x=float(payload[1]),\n", + " y=float(payload[2]),\n", + " heading=float(payload[3]),\n", + " head_angle=float(payload[4]),\n", + " signal=float(payload[5]),\n", + " raw=payload,\n", + " )\n", + " return {\"kind\": \"legacy_pose\", **asdict(pkt)}\n", + "\n", + " # Newer/base mock payloads may be dictionaries.\n", + " if isinstance(payload, dict):\n", + " kind = payload.get(\"type\", \"dict\")\n", + " return {\"kind\": kind, \"raw\": payload}\n", + "\n", + " # Some processors broadcast [timestamp, pose].\n", + " if isinstance(payload, list) and len(payload) == 2:\n", + " return {\"kind\": \"timestamp_pose\", \"timestamp\": payload[0], \"pose\": payload[1], \"raw\": payload}\n", + "\n", + " return {\"kind\": \"unknown\", \"raw\": payload}" + ] + }, + { + "cell_type": "markdown", + "id": "10185d26023b46108eb7d9f57d49d2b3", + "metadata": {}, + "source": [ + "## Connect to the DLC processor socket\n", + "\n", + "Run this cell once the processor has been created and its listener should be available.\n", + "\n", + "If it times out, it likely means either:\n", + "\n", + "1. the processor has not been instantiated yet,\n", + "2. the address/authkey are wrong,\n", + "3. the legacy processor is waiting until `process()` runs before accepting the connection,\n", + "4. another process is using the port." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "8763a12b2bbd4a93a75aff182afb95dc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to processor socket: ('127.0.0.1', 6000)\n" + ] + } + ], + "source": [ + "conn = connect_with_timeout(ADDRESS, AUTHKEY, timeout_s=CONNECT_TIMEOUT_S)\n", + "print(\"Connected to processor socket:\", ADDRESS)" + ] + }, + { + "cell_type": "markdown", + "id": "7623eae2785240b9bd12b16a66d81610", + "metadata": {}, + "source": [ + "## Optional: send a ping/status command\n", + "\n", + "Only use this for processors that implement command handling, such as the newer `BaseProcessorSocket` or the standalone mock processor.\n", + "\n", + "The legacy `MyProcessor_socket` does **not** read commands from the client, so skip this cell for that processor." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "7cdc8c89c7104fffa095e18ddfef8986", + "metadata": {}, + "outputs": [], + "source": [ + "# Uncomment only for BaseProcessorSocket-style processors or the standalone mock.\n", + "# conn.send({\"cmd\": \"ping\"})\n", + "# if conn.poll(2.0):\n", + "# print(\"Response:\", conn.recv())\n", + "# else:\n", + "# print(\"No response. This is expected for legacy MyProcessor_socket.\")" + ] + }, + { + "cell_type": "markdown", + "id": "b118ea5561624da68c537baed56e602f", + "metadata": {}, + "source": [ + "## Receive pose packets\n", + "\n", + "This cell listens for up to `duration_s` seconds and prints decoded packets. For legacy `MyProcessor_socket`, you should see `legacy_pose` packets once `process()` is called by DLC inference." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "938c804e27f84196a10c8828c723f798", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588105.750286,\n", + " \"step\": 12,\n", + " \"pose\": [\n", + " [\n", + " 288.7191162109375,\n", + " 298.63250732421875,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.0001220703125,\n", + " 307.95672607421875,\n", + " 0.973543643951416\n", + " ],\n", + " [\n", + " 297.29254150390625,\n", + " 314.52410888671875,\n", + " 0.6293796896934509\n", + " ],\n", + " [\n", + " 290.9452819824219,\n", + " 310.3515319824219,\n", + " 0.640852153301239\n", + " ],\n", + " [\n", + " 304.86236572265625,\n", + " 313.2810974121094,\n", + " 0.6921122670173645\n", + " ],\n", + " [\n", + " 284.668212890625,\n", + " 266.52752685546875,\n", + " 0.9321146607398987\n", + " ],\n", + " [\n", + " 284.6539001464844,\n", + " 237.49722290039062,\n", + " 0.4642881155014038\n", + " ],\n", + " [\n", + " 298.547607421875,\n", + " 218.42587280273438,\n", + " 0.26315996050834656\n", + " ],\n", + " [\n", + " 182.9195098876953,\n", + " 312.5831604003906,\n", + " 0.4563772678375244\n", + " ],\n", + " [\n", + " 181.693359375,\n", + " 306.1708068847656,\n", + " 0.39262306690216064\n", + " ],\n", + " [\n", + " 309.70013427734375,\n", + " 273.22198486328125,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.6557922363281,\n", + " 278.49371337890625,\n", + " 0.5969972014427185\n", + " ],\n", + " [\n", + " 362.46343994140625,\n", + " 283.172119140625,\n", + " 0.4280658960342407\n", + " ],\n", + " [\n", + " 185.8195343017578,\n", + " 311.7103271484375,\n", + " 0.27244052290916443\n", + " ],\n", + " [\n", + " 192.61837768554688,\n", + " 305.4747009277344,\n", + " 0.39853179454803467\n", + " ],\n", + " [\n", + " 355.7668151855469,\n", + " 339.8248596191406,\n", + " 0.40097054839134216\n", + " ],\n", + " [\n", + " 364.0209655761719,\n", + " 341.5860595703125,\n", + " 0.5437613129615784\n", + " ],\n", + " [\n", + " 335.2928771972656,\n", + " 344.40032958984375,\n", + " 0.3498704731464386\n", + " ],\n", + " [\n", + " 331.4139404296875,\n", + " 356.1664123535156,\n", + " 0.4611521065235138\n", + " ],\n", + " [\n", + " 366.796142578125,\n", + " 344.254150390625,\n", + " 0.47546425461769104\n", + " ],\n", + " [\n", + " 270.3355712890625,\n", + " 358.1495666503906,\n", + " 0.4529607594013214\n", + " ],\n", + " [\n", + " 379.02679443359375,\n", + " 356.05645751953125,\n", + " 0.4878201186656952\n", + " ],\n", + " [\n", + " 268.0301818847656,\n", + " 359.48583984375,\n", + " 0.366856187582016\n", + " ],\n", + " [\n", + " 599.5546264648438,\n", + " 377.7467956542969,\n", + " 0.564258873462677\n", + " ],\n", + " [\n", + " 185.41236877441406,\n", + " 207.2476043701172,\n", + " 0.3415561616420746\n", + " ],\n", + " [\n", + " 518.7420043945312,\n", + " 391.21240234375,\n", + " 0.3360799551010132\n", + " ],\n", + " [\n", + " 516.0563354492188,\n", + " 429.2253723144531,\n", + " 0.7114025354385376\n", + " ],\n", + " [\n", + " 164.67864990234375,\n", + " 212.08599853515625,\n", + " 0.22173050045967102\n", + " ],\n", + " [\n", + " 508.5932312011719,\n", + " 391.6039123535156,\n", + " 0.30660656094551086\n", + " ],\n", + " [\n", + " 511.993896484375,\n", + " 447.9190368652344,\n", + " 0.5064514875411987\n", + " ],\n", + " [\n", + " 591.1927490234375,\n", + " 409.2403564453125,\n", + " 0.5136002898216248\n", + " ],\n", + " [\n", + " 106.3792495727539,\n", + " 271.0600891113281,\n", + " 0.14335250854492188\n", + " ],\n", + " [\n", + " 112.1520767211914,\n", + " 279.07525634765625,\n", + " 0.2755976915359497\n", + " ],\n", + " [\n", + " 572.9004516601562,\n", + " 393.2549743652344,\n", + " 0.39857158064842224\n", + " ],\n", + " [\n", + " 144.4106903076172,\n", + " 313.3831787109375,\n", + " 0.27947890758514404\n", + " ],\n", + " [\n", + " 550.626953125,\n", + " 466.50970458984375,\n", + " 0.5572487115859985\n", + " ],\n", + " [\n", + " 466.3507385253906,\n", + " 413.0749206542969,\n", + " 0.39403966069221497\n", + " ],\n", + " [\n", + " 461.47198486328125,\n", + " 400.29541015625,\n", + " 0.3778141140937805\n", + " ],\n", + " [\n", + " 460.13824462890625,\n", + " 403.57171630859375,\n", + " 0.5188782811164856\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588105.7246952,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588105.750286\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588105.78031,\n", + " \"step\": 13,\n", + " \"pose\": [\n", + " [\n", + " 287.8656921386719,\n", + " 298.9954833984375,\n", + " 1.0\n", + " ],\n", + " [\n", + " 292.98870849609375,\n", + " 308.14825439453125,\n", + " 0.9376083016395569\n", + " ],\n", + " [\n", + " 297.0699462890625,\n", + " 315.7899169921875,\n", + " 0.605503499507904\n", + " ],\n", + " [\n", + " 290.7883605957031,\n", + " 311.0892333984375,\n", + " 0.653724193572998\n", + " ],\n", + " [\n", + " 304.9418029785156,\n", + " 314.27423095703125,\n", + " 0.6581617593765259\n", + " ],\n", + " [\n", + " 284.5467224121094,\n", + " 266.99755859375,\n", + " 0.9386962056159973\n", + " ],\n", + " [\n", + " 285.4912109375,\n", + " 236.9186248779297,\n", + " 0.48566538095474243\n", + " ],\n", + " [\n", + " 282.66357421875,\n", + " 236.26953125,\n", + " 0.2793366611003876\n", + " ],\n", + " [\n", + " 182.8148193359375,\n", + " 314.7862548828125,\n", + " 0.4555658996105194\n", + " ],\n", + " [\n", + " 183.0238037109375,\n", + " 309.2024230957031,\n", + " 0.3395337760448456\n", + " ],\n", + " [\n", + " 310.2979431152344,\n", + " 272.9541015625,\n", + " 1.0\n", + " ],\n", + " [\n", + " 362.39019775390625,\n", + " 278.04864501953125,\n", + " 0.7049410343170166\n", + " ],\n", + " [\n", + " 362.625,\n", + " 281.328369140625,\n", + " 0.4549161195755005\n", + " ],\n", + " [\n", + " 357.58203125,\n", + " 282.10272216796875,\n", + " 0.26718243956565857\n", + " ],\n", + " [\n", + " 194.67514038085938,\n", + " 306.698974609375,\n", + " 0.45219412446022034\n", + " ],\n", + " [\n", + " 355.5292663574219,\n", + " 339.20318603515625,\n", + " 0.41419631242752075\n", + " ],\n", + " [\n", + " 367.3896179199219,\n", + " 340.3309326171875,\n", + " 0.5223292112350464\n", + " ],\n", + " [\n", + " 335.11529541015625,\n", + " 343.81201171875,\n", + " 0.347744345664978\n", + " ],\n", + " [\n", + " 332.03765869140625,\n", + " 356.7191162109375,\n", + " 0.4733559191226959\n", + " ],\n", + " [\n", + " 367.4532470703125,\n", + " 344.79302978515625,\n", + " 0.4827011823654175\n", + " ],\n", + " [\n", + " 270.3110046386719,\n", + " 357.83343505859375,\n", + " 0.4700448215007782\n", + " ],\n", + " [\n", + " 378.0578308105469,\n", + " 356.4914855957031,\n", + " 0.4333026707172394\n", + " ],\n", + " [\n", + " 267.2149963378906,\n", + " 359.4980773925781,\n", + " 0.39140474796295166\n", + " ],\n", + " [\n", + " 596.2949829101562,\n", + " 378.6457824707031,\n", + " 0.5882202386856079\n", + " ],\n", + " [\n", + " 185.2808837890625,\n", + " 208.80474853515625,\n", + " 0.4029167890548706\n", + " ],\n", + " [\n", + " 563.7208862304688,\n", + " 387.8863220214844,\n", + " 0.390456885099411\n", + " ],\n", + " [\n", + " 519.1982421875,\n", + " 433.89654541015625,\n", + " 0.5289698839187622\n", + " ],\n", + " [\n", + " 442.9023132324219,\n", + " 356.9894714355469,\n", + " 0.2825982868671417\n", + " ],\n", + " [\n", + " 568.7670288085938,\n", + " 195.95079040527344,\n", + " 0.43732380867004395\n", + " ],\n", + " [\n", + " 512.254638671875,\n", + " 449.7223815917969,\n", + " 0.6101775169372559\n", + " ],\n", + " [\n", + " 591.0804443359375,\n", + " 409.246337890625,\n", + " 0.5442432761192322\n", + " ],\n", + " [\n", + " 470.5218200683594,\n", + " 402.7597961425781,\n", + " 0.1880091279745102\n", + " ],\n", + " [\n", + " 107.73304748535156,\n", + " 270.0023193359375,\n", + " 0.14866414666175842\n", + " ],\n", + " [\n", + " 572.8255615234375,\n", + " 393.6127624511719,\n", + " 0.5601691603660583\n", + " ],\n", + " [\n", + " 481.23486328125,\n", + " 431.833984375,\n", + " 0.2556696832180023\n", + " ],\n", + " [\n", + " 550.4242553710938,\n", + " 467.0722961425781,\n", + " 0.4299638867378235\n", + " ],\n", + " [\n", + " 466.45404052734375,\n", + " 412.8644714355469,\n", + " 0.38715386390686035\n", + " ],\n", + " [\n", + " 462.144775390625,\n", + " 401.0147705078125,\n", + " 0.3404390513896942\n", + " ],\n", + " [\n", + " 459.4046325683594,\n", + " 403.1348571777344,\n", + " 0.4926401376724243\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588105.7554483,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588105.78031\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588105.821648,\n", + " \"step\": 14,\n", + " \"pose\": [\n", + " [\n", + " 288.8023681640625,\n", + " 298.86077880859375,\n", + " 1.0\n", + " ],\n", + " [\n", + " 292.8272399902344,\n", + " 308.04949951171875,\n", + " 0.9570764899253845\n", + " ],\n", + " [\n", + " 296.77056884765625,\n", + " 315.70367431640625,\n", + " 0.631300687789917\n", + " ],\n", + " [\n", + " 289.9299621582031,\n", + " 310.9530944824219,\n", + " 0.6520562171936035\n", + " ],\n", + " [\n", + " 305.0509338378906,\n", + " 313.7876892089844,\n", + " 0.7196981310844421\n", + " ],\n", + " [\n", + " 284.6484680175781,\n", + " 266.7134704589844,\n", + " 0.9353039860725403\n", + " ],\n", + " [\n", + " 285.0174560546875,\n", + " 238.79736328125,\n", + " 0.4836384952068329\n", + " ],\n", + " [\n", + " 154.6018829345703,\n", + " 315.4358215332031,\n", + " 0.2590964734554291\n", + " ],\n", + " [\n", + " 336.0355529785156,\n", + " 466.2019348144531,\n", + " 0.37860575318336487\n", + " ],\n", + " [\n", + " 181.9645233154297,\n", + " 308.6495361328125,\n", + " 0.3605335056781769\n", + " ],\n", + " [\n", + " 309.65972900390625,\n", + " 273.17425537109375,\n", + " 1.0\n", + " ],\n", + " [\n", + " 362.54388427734375,\n", + " 277.6341857910156,\n", + " 0.5562496781349182\n", + " ],\n", + " [\n", + " 361.87298583984375,\n", + " 282.2453308105469,\n", + " 0.477169394493103\n", + " ],\n", + " [\n", + " 358.46246337890625,\n", + " 282.86480712890625,\n", + " 0.2796511948108673\n", + " ],\n", + " [\n", + " 189.29440307617188,\n", + " 305.3114929199219,\n", + " 0.41088035702705383\n", + " ],\n", + " [\n", + " 355.5824890136719,\n", + " 341.4902648925781,\n", + " 0.3962235450744629\n", + " ],\n", + " [\n", + " 363.149658203125,\n", + " 340.7292785644531,\n", + " 0.48571205139160156\n", + " ],\n", + " [\n", + " 332.39642333984375,\n", + " 340.5425720214844,\n", + " 0.32239603996276855\n", + " ],\n", + " [\n", + " 331.2975769042969,\n", + " 355.4726257324219,\n", + " 0.4472481310367584\n", + " ],\n", + " [\n", + " 364.4246520996094,\n", + " 346.6827087402344,\n", + " 0.4476216733455658\n", + " ],\n", + " [\n", + " 269.8668212890625,\n", + " 359.0860900878906,\n", + " 0.43882080912590027\n", + " ],\n", + " [\n", + " 379.4068298339844,\n", + " 356.64239501953125,\n", + " 0.4417465329170227\n", + " ],\n", + " [\n", + " 266.974853515625,\n", + " 360.31402587890625,\n", + " 0.33691221475601196\n", + " ],\n", + " [\n", + " 596.21826171875,\n", + " 377.820068359375,\n", + " 0.5398204922676086\n", + " ],\n", + " [\n", + " 183.42034912109375,\n", + " 208.78756713867188,\n", + " 0.34466955065727234\n", + " ],\n", + " [\n", + " 564.060791015625,\n", + " 388.1324157714844,\n", + " 0.4286687970161438\n", + " ],\n", + " [\n", + " 517.4417724609375,\n", + " 430.2832336425781,\n", + " 0.5827439427375793\n", + " ],\n", + " [\n", + " 169.510009765625,\n", + " 212.77224731445312,\n", + " 0.23520678281784058\n", + " ],\n", + " [\n", + " 509.5054016113281,\n", + " 390.251708984375,\n", + " 0.2855446934700012\n", + " ],\n", + " [\n", + " 511.0235290527344,\n", + " 447.9842224121094,\n", + " 0.5595549941062927\n", + " ],\n", + " [\n", + " 591.0059204101562,\n", + " 408.37109375,\n", + " 0.5015893578529358\n", + " ],\n", + " [\n", + " 475.4132385253906,\n", + " 422.1249084472656,\n", + " 0.19282308220863342\n", + " ],\n", + " [\n", + " 110.6484146118164,\n", + " 276.17138671875,\n", + " 0.3112924098968506\n", + " ],\n", + " [\n", + " 572.998046875,\n", + " 393.3849182128906,\n", + " 0.4534406363964081\n", + " ],\n", + " [\n", + " 481.1611022949219,\n", + " 432.43438720703125,\n", + " 0.2341541200876236\n", + " ],\n", + " [\n", + " 551.3787231445312,\n", + " 466.6352844238281,\n", + " 0.5391212105751038\n", + " ],\n", + " [\n", + " 467.7240905761719,\n", + " 412.1317138671875,\n", + " 0.3793080151081085\n", + " ],\n", + " [\n", + " 465.2247619628906,\n", + " 409.2422180175781,\n", + " 0.33666175603866577\n", + " ],\n", + " [\n", + " 460.2418212890625,\n", + " 403.50830078125,\n", + " 0.42937344312667847\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588105.7889726,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588105.821648\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588105.851803,\n", + " \"step\": 15,\n", + " \"pose\": [\n", + " [\n", + " 288.8399963378906,\n", + " 298.389892578125,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.063232421875,\n", + " 307.6488952636719,\n", + " 0.9409026503562927\n", + " ],\n", + " [\n", + " 296.73828125,\n", + " 314.7473449707031,\n", + " 0.6393375992774963\n", + " ],\n", + " [\n", + " 290.7121887207031,\n", + " 310.599609375,\n", + " 0.6521811485290527\n", + " ],\n", + " [\n", + " 304.9431457519531,\n", + " 311.9217224121094,\n", + " 0.7004058957099915\n", + " ],\n", + " [\n", + " 284.689697265625,\n", + " 267.53924560546875,\n", + " 0.9271823167800903\n", + " ],\n", + " [\n", + " 182.9373779296875,\n", + " 313.93133544921875,\n", + " 0.5212528109550476\n", + " ],\n", + " [\n", + " 153.7313690185547,\n", + " 313.92779541015625,\n", + " 0.3010638356208801\n", + " ],\n", + " [\n", + " 181.32296752929688,\n", + " 312.8819580078125,\n", + " 0.5419734120368958\n", + " ],\n", + " [\n", + " 183.34437561035156,\n", + " 306.7300720214844,\n", + " 0.37734419107437134\n", + " ],\n", + " [\n", + " 309.9617004394531,\n", + " 273.3157653808594,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.39080810546875,\n", + " 277.68914794921875,\n", + " 0.7047985792160034\n", + " ],\n", + " [\n", + " 362.3508605957031,\n", + " 282.02313232421875,\n", + " 0.4799898564815521\n", + " ],\n", + " [\n", + " 179.46484375,\n", + " 315.53436279296875,\n", + " 0.3219224810600281\n", + " ],\n", + " [\n", + " 193.1734161376953,\n", + " 304.5758056640625,\n", + " 0.4651084244251251\n", + " ],\n", + " [\n", + " 355.8511047363281,\n", + " 340.7768859863281,\n", + " 0.42659905552864075\n", + " ],\n", + " [\n", + " 364.3086242675781,\n", + " 341.3064270019531,\n", + " 0.5809048414230347\n", + " ],\n", + " [\n", + " 310.90179443359375,\n", + " 347.79620361328125,\n", + " 0.37011390924453735\n", + " ],\n", + " [\n", + " 329.7757263183594,\n", + " 356.66290283203125,\n", + " 0.5357598066329956\n", + " ],\n", + " [\n", + " 369.3732604980469,\n", + " 347.80084228515625,\n", + " 0.5102704167366028\n", + " ],\n", + " [\n", + " 269.4798583984375,\n", + " 358.50128173828125,\n", + " 0.36626455187797546\n", + " ],\n", + " [\n", + " 379.74285888671875,\n", + " 356.38800048828125,\n", + " 0.4842686653137207\n", + " ],\n", + " [\n", + " 265.9881286621094,\n", + " 360.00592041015625,\n", + " 0.27803361415863037\n", + " ],\n", + " [\n", + " 596.4786376953125,\n", + " 379.2182922363281,\n", + " 0.5961218476295471\n", + " ],\n", + " [\n", + " 254.6011505126953,\n", + " 382.6790466308594,\n", + " 0.4003625512123108\n", + " ],\n", + " [\n", + " 563.052001953125,\n", + " 386.73590087890625,\n", + " 0.36300426721572876\n", + " ],\n", + " [\n", + " 517.0165405273438,\n", + " 430.59613037109375,\n", + " 0.5643782019615173\n", + " ],\n", + " [\n", + " 164.36508178710938,\n", + " 213.65438842773438,\n", + " 0.2149515450000763\n", + " ],\n", + " [\n", + " 509.01336669921875,\n", + " 392.5158996582031,\n", + " 0.23822596669197083\n", + " ],\n", + " [\n", + " 510.94085693359375,\n", + " 449.33477783203125,\n", + " 0.6286779642105103\n", + " ],\n", + " [\n", + " 590.7813110351562,\n", + " 409.1065368652344,\n", + " 0.4439380168914795\n", + " ],\n", + " [\n", + " 463.3310546875,\n", + " 401.2489318847656,\n", + " 0.1755678504705429\n", + " ],\n", + " [\n", + " 109.76718139648438,\n", + " 271.8696594238281,\n", + " 0.2973553240299225\n", + " ],\n", + " [\n", + " 572.5289306640625,\n", + " 393.1651916503906,\n", + " 0.5296833515167236\n", + " ],\n", + " [\n", + " 480.9592590332031,\n", + " 432.1273193359375,\n", + " 0.21754036843776703\n", + " ],\n", + " [\n", + " 550.6238403320312,\n", + " 466.08721923828125,\n", + " 0.5377715229988098\n", + " ],\n", + " [\n", + " 468.291015625,\n", + " 413.2549133300781,\n", + " 0.3708413243293762\n", + " ],\n", + " [\n", + " 136.44790649414062,\n", + " 171.77438354492188,\n", + " 0.3511451184749603\n", + " ],\n", + " [\n", + " 460.23358154296875,\n", + " 404.2395935058594,\n", + " 0.47236377000808716\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588105.8206406,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588105.8528142\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588105.8770585,\n", + " \"step\": 16,\n", + " \"pose\": [\n", + " [\n", + " 289.1402282714844,\n", + " 299.0330810546875,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.5198974609375,\n", + " 308.2564392089844,\n", + " 0.9859002232551575\n", + " ],\n", + " [\n", + " 297.6006164550781,\n", + " 314.6565856933594,\n", + " 0.6514089107513428\n", + " ],\n", + " [\n", + " 290.924072265625,\n", + " 311.3872985839844,\n", + " 0.6389972567558289\n", + " ],\n", + " [\n", + " 304.9306945800781,\n", + " 313.3708801269531,\n", + " 0.7217590808868408\n", + " ],\n", + " [\n", + " 284.5622863769531,\n", + " 267.4127502441406,\n", + " 0.9266435503959656\n", + " ],\n", + " [\n", + " 184.82669067382812,\n", + " 312.5155334472656,\n", + " 0.5616798400878906\n", + " ],\n", + " [\n", + " 154.43678283691406,\n", + " 314.49652099609375,\n", + " 0.24991558492183685\n", + " ],\n", + " [\n", + " 182.59487915039062,\n", + " 312.3272705078125,\n", + " 0.592433512210846\n", + " ],\n", + " [\n", + " 182.8374786376953,\n", + " 306.06494140625,\n", + " 0.419148325920105\n", + " ],\n", + " [\n", + " 310.11920166015625,\n", + " 273.36737060546875,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.7231140136719,\n", + " 277.9724426269531,\n", + " 0.5769408345222473\n", + " ],\n", + " [\n", + " 362.3317565917969,\n", + " 282.26922607421875,\n", + " 0.43514740467071533\n", + " ],\n", + " [\n", + " 184.4752655029297,\n", + " 311.1407470703125,\n", + " 0.38407573103904724\n", + " ],\n", + " [\n", + " 187.931640625,\n", + " 302.62933349609375,\n", + " 0.4633309543132782\n", + " ],\n", + " [\n", + " 356.0732421875,\n", + " 340.64599609375,\n", + " 0.4025139808654785\n", + " ],\n", + " [\n", + " 362.9293212890625,\n", + " 341.8232727050781,\n", + " 0.5162671208381653\n", + " ],\n", + " [\n", + " 309.4438781738281,\n", + " 345.9781799316406,\n", + " 0.33583828806877136\n", + " ],\n", + " [\n", + " 331.2337341308594,\n", + " 355.5598449707031,\n", + " 0.4219924509525299\n", + " ],\n", + " [\n", + " 366.71087646484375,\n", + " 345.36962890625,\n", + " 0.4366249144077301\n", + " ],\n", + " [\n", + " 269.210693359375,\n", + " 358.0929260253906,\n", + " 0.3690962493419647\n", + " ],\n", + " [\n", + " 424.5083923339844,\n", + " 376.5726013183594,\n", + " 0.42966294288635254\n", + " ],\n", + " [\n", + " 303.0454406738281,\n", + " 218.3123779296875,\n", + " 0.31208333373069763\n", + " ],\n", + " [\n", + " 598.5891723632812,\n", + " 378.4424133300781,\n", + " 0.5889440178871155\n", + " ],\n", + " [\n", + " 197.69949340820312,\n", + " 217.56845092773438,\n", + " 0.3414533734321594\n", + " ],\n", + " [\n", + " 563.177734375,\n", + " 386.8681945800781,\n", + " 0.39073020219802856\n", + " ],\n", + " [\n", + " 516.6555786132812,\n", + " 428.7958679199219,\n", + " 0.5345339179039001\n", + " ],\n", + " [\n", + " 442.6798095703125,\n", + " 357.461181640625,\n", + " 0.23529931902885437\n", + " ],\n", + " [\n", + " 507.02862548828125,\n", + " 392.94842529296875,\n", + " 0.26026907563209534\n", + " ],\n", + " [\n", + " 511.629150390625,\n", + " 447.88116455078125,\n", + " 0.5966296792030334\n", + " ],\n", + " [\n", + " 592.001708984375,\n", + " 408.8056945800781,\n", + " 0.5580258965492249\n", + " ],\n", + " [\n", + " 463.76220703125,\n", + " 402.1715087890625,\n", + " 0.16743294894695282\n", + " ],\n", + " [\n", + " 111.18097686767578,\n", + " 276.2333679199219,\n", + " 0.2950418293476105\n", + " ],\n", + " [\n", + " 573.3688354492188,\n", + " 393.168701171875,\n", + " 0.4543166756629944\n", + " ],\n", + " [\n", + " 481.0332946777344,\n", + " 433.19580078125,\n", + " 0.2525552809238434\n", + " ],\n", + " [\n", + " 551.67578125,\n", + " 466.51300048828125,\n", + " 0.48711535334587097\n", + " ],\n", + " [\n", + " 468.5693054199219,\n", + " 413.4539489746094,\n", + " 0.38309159874916077\n", + " ],\n", + " [\n", + " 135.75698852539062,\n", + " 170.189697265625,\n", + " 0.30588316917419434\n", + " ],\n", + " [\n", + " 459.2412109375,\n", + " 403.5185852050781,\n", + " 0.3879672586917877\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588105.8528142,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588105.8770585\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588105.9255464,\n", + " \"step\": 17,\n", + " \"pose\": [\n", + " [\n", + " 289.01116943359375,\n", + " 299.0413818359375,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.1086730957031,\n", + " 308.51885986328125,\n", + " 0.95180344581604\n", + " ],\n", + " [\n", + " 296.3152770996094,\n", + " 316.4967956542969,\n", + " 0.5928277373313904\n", + " ],\n", + " [\n", + " 290.5437927246094,\n", + " 311.6708679199219,\n", + " 0.6084922552108765\n", + " ],\n", + " [\n", + " 306.17803955078125,\n", + " 313.6896057128906,\n", + " 0.7187818288803101\n", + " ],\n", + " [\n", + " 284.83319091796875,\n", + " 267.0993957519531,\n", + " 0.9082852602005005\n", + " ],\n", + " [\n", + " 285.2369384765625,\n", + " 239.99192810058594,\n", + " 0.4807704985141754\n", + " ],\n", + " [\n", + " 281.5351257324219,\n", + " 244.12281799316406,\n", + " 0.25091421604156494\n", + " ],\n", + " [\n", + " 183.10739135742188,\n", + " 313.957275390625,\n", + " 0.47192785143852234\n", + " ],\n", + " [\n", + " 182.70115661621094,\n", + " 306.3948974609375,\n", + " 0.3859936594963074\n", + " ],\n", + " [\n", + " 310.44354248046875,\n", + " 273.25341796875,\n", + " 1.0\n", + " ],\n", + " [\n", + " 362.1278076171875,\n", + " 276.0966796875,\n", + " 0.66349196434021\n", + " ],\n", + " [\n", + " 363.0843200683594,\n", + " 282.9954528808594,\n", + " 0.44170665740966797\n", + " ],\n", + " [\n", + " 182.47335815429688,\n", + " 311.0877685546875,\n", + " 0.2967713475227356\n", + " ],\n", + " [\n", + " 188.00137329101562,\n", + " 304.48931884765625,\n", + " 0.45905861258506775\n", + " ],\n", + " [\n", + " 354.645263671875,\n", + " 339.31219482421875,\n", + " 0.3976733684539795\n", + " ],\n", + " [\n", + " 363.1065979003906,\n", + " 339.9534606933594,\n", + " 0.5860384702682495\n", + " ],\n", + " [\n", + " 316.1658935546875,\n", + " 342.28369140625,\n", + " 0.3572511076927185\n", + " ],\n", + " [\n", + " 331.590087890625,\n", + " 355.1735534667969,\n", + " 0.4502539336681366\n", + " ],\n", + " [\n", + " 363.5017395019531,\n", + " 344.60235595703125,\n", + " 0.5015437006950378\n", + " ],\n", + " [\n", + " 268.5982666015625,\n", + " 358.1197204589844,\n", + " 0.36430323123931885\n", + " ],\n", + " [\n", + " 378.5502014160156,\n", + " 355.8092041015625,\n", + " 0.42007115483283997\n", + " ],\n", + " [\n", + " 303.5655517578125,\n", + " 217.58612060546875,\n", + " 0.33906295895576477\n", + " ],\n", + " [\n", + " 596.765380859375,\n", + " 379.427001953125,\n", + " 0.5411680340766907\n", + " ],\n", + " [\n", + " 199.35903930664062,\n", + " 222.4739990234375,\n", + " 0.29007023572921753\n", + " ],\n", + " [\n", + " 562.4085083007812,\n", + " 386.65509033203125,\n", + " 0.3170825242996216\n", + " ],\n", + " [\n", + " 517.340087890625,\n", + " 430.5169677734375,\n", + " 0.6213215589523315\n", + " ],\n", + " [\n", + " 441.74444580078125,\n", + " 357.04644775390625,\n", + " 0.2545863389968872\n", + " ],\n", + " [\n", + " 503.5122375488281,\n", + " 393.9283447265625,\n", + " 0.30190107226371765\n", + " ],\n", + " [\n", + " 510.5965576171875,\n", + " 450.106201171875,\n", + " 0.5517356991767883\n", + " ],\n", + " [\n", + " 591.1541748046875,\n", + " 408.65826416015625,\n", + " 0.49750760197639465\n", + " ],\n", + " [\n", + " 475.8544921875,\n", + " 422.05078125,\n", + " 0.1733374446630478\n", + " ],\n", + " [\n", + " 109.9720687866211,\n", + " 271.4387512207031,\n", + " 0.2799350619316101\n", + " ],\n", + " [\n", + " 572.795166015625,\n", + " 393.3072204589844,\n", + " 0.3979363441467285\n", + " ],\n", + " [\n", + " 479.5787353515625,\n", + " 432.65380859375,\n", + " 0.22706195712089539\n", + " ],\n", + " [\n", + " 550.501708984375,\n", + " 467.0165100097656,\n", + " 0.4965691566467285\n", + " ],\n", + " [\n", + " 468.164306640625,\n", + " 412.325927734375,\n", + " 0.3681727349758148\n", + " ],\n", + " [\n", + " 461.05029296875,\n", + " 400.1574401855469,\n", + " 0.36365073919296265\n", + " ],\n", + " [\n", + " 460.8140563964844,\n", + " 402.9066467285156,\n", + " 0.4981914758682251\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588105.8993094,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588105.9255464\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588105.9609218,\n", + " \"step\": 18,\n", + " \"pose\": [\n", + " [\n", + " 288.9327392578125,\n", + " 299.13629150390625,\n", + " 1.0\n", + " ],\n", + " [\n", + " 292.8038330078125,\n", + " 308.6182861328125,\n", + " 0.965558648109436\n", + " ],\n", + " [\n", + " 297.1647644042969,\n", + " 315.5074157714844,\n", + " 0.6364358067512512\n", + " ],\n", + " [\n", + " 290.55609130859375,\n", + " 311.7894287109375,\n", + " 0.6080928444862366\n", + " ],\n", + " [\n", + " 305.4518127441406,\n", + " 313.89239501953125,\n", + " 0.7770905494689941\n", + " ],\n", + " [\n", + " 284.95733642578125,\n", + " 267.11175537109375,\n", + " 0.9243097305297852\n", + " ],\n", + " [\n", + " 285.8481750488281,\n", + " 238.2171630859375,\n", + " 0.49581968784332275\n", + " ],\n", + " [\n", + " 281.67034912109375,\n", + " 238.3629608154297,\n", + " 0.2495463341474533\n", + " ],\n", + " [\n", + " 181.5650177001953,\n", + " 314.2547912597656,\n", + " 0.5168197751045227\n", + " ],\n", + " [\n", + " 182.05751037597656,\n", + " 306.9068908691406,\n", + " 0.36109668016433716\n", + " ],\n", + " [\n", + " 310.2335205078125,\n", + " 273.6082763671875,\n", + " 1.0\n", + " ],\n", + " [\n", + " 362.219482421875,\n", + " 276.6759948730469,\n", + " 0.618270754814148\n", + " ],\n", + " [\n", + " 362.4667663574219,\n", + " 280.8567199707031,\n", + " 0.40474116802215576\n", + " ],\n", + " [\n", + " 184.80101013183594,\n", + " 316.924560546875,\n", + " 0.3415667712688446\n", + " ],\n", + " [\n", + " 189.2113800048828,\n", + " 305.0552062988281,\n", + " 0.44207993149757385\n", + " ],\n", + " [\n", + " 354.8471984863281,\n", + " 341.1282043457031,\n", + " 0.4782800078392029\n", + " ],\n", + " [\n", + " 363.4027404785156,\n", + " 341.2732238769531,\n", + " 0.518268346786499\n", + " ],\n", + " [\n", + " 316.40673828125,\n", + " 342.68695068359375,\n", + " 0.3713846504688263\n", + " ],\n", + " [\n", + " 330.7171630859375,\n", + " 355.67803955078125,\n", + " 0.46434029936790466\n", + " ],\n", + " [\n", + " 367.51568603515625,\n", + " 344.2020568847656,\n", + " 0.4221855401992798\n", + " ],\n", + " [\n", + " 269.15869140625,\n", + " 357.6059265136719,\n", + " 0.43143007159233093\n", + " ],\n", + " [\n", + " 377.92431640625,\n", + " 355.83026123046875,\n", + " 0.42217832803726196\n", + " ],\n", + " [\n", + " 266.35400390625,\n", + " 359.1109313964844,\n", + " 0.36265674233436584\n", + " ],\n", + " [\n", + " 598.98876953125,\n", + " 378.3539123535156,\n", + " 0.5779925584793091\n", + " ],\n", + " [\n", + " 521.8517456054688,\n", + " 317.60223388671875,\n", + " 0.3474227786064148\n", + " ],\n", + " [\n", + " 563.2191162109375,\n", + " 386.7420654296875,\n", + " 0.3701111972332001\n", + " ],\n", + " [\n", + " 516.5422973632812,\n", + " 430.6274108886719,\n", + " 0.5754683613777161\n", + " ],\n", + " [\n", + " 566.955078125,\n", + " 197.94085693359375,\n", + " 0.35363760590553284\n", + " ],\n", + " [\n", + " 573.0870971679688,\n", + " 198.2352294921875,\n", + " 0.5906892418861389\n", + " ],\n", + " [\n", + " 511.6949157714844,\n", + " 449.3252258300781,\n", + " 0.6369988322257996\n", + " ],\n", + " [\n", + " 591.0794067382812,\n", + " 408.18438720703125,\n", + " 0.5102192759513855\n", + " ],\n", + " [\n", + " 508.6004638671875,\n", + " 381.352783203125,\n", + " 0.2138703316450119\n", + " ],\n", + " [\n", + " 109.30960083007812,\n", + " 272.1946716308594,\n", + " 0.336846262216568\n", + " ],\n", + " [\n", + " 572.595703125,\n", + " 393.81610107421875,\n", + " 0.48220178484916687\n", + " ],\n", + " [\n", + " 482.5830383300781,\n", + " 431.64202880859375,\n", + " 0.18888558447360992\n", + " ],\n", + " [\n", + " 549.8436279296875,\n", + " 465.8060302734375,\n", + " 0.6060653924942017\n", + " ],\n", + " [\n", + " 470.1370544433594,\n", + " 413.1282653808594,\n", + " 0.4226745069026947\n", + " ],\n", + " [\n", + " 134.86871337890625,\n", + " 172.37551879882812,\n", + " 0.34861159324645996\n", + " ],\n", + " [\n", + " 460.8798522949219,\n", + " 403.1666564941406,\n", + " 0.4525850713253021\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588105.933552,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588105.9609218\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588105.996339,\n", + " \"step\": 19,\n", + " \"pose\": [\n", + " [\n", + " 289.3584289550781,\n", + " 299.6400451660156,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.6114807128906,\n", + " 308.7494201660156,\n", + " 0.9362009763717651\n", + " ],\n", + " [\n", + " 296.5362548828125,\n", + " 315.8563537597656,\n", + " 0.6216031908988953\n", + " ],\n", + " [\n", + " 290.3965759277344,\n", + " 311.8982849121094,\n", + " 0.5908151865005493\n", + " ],\n", + " [\n", + " 305.3473205566406,\n", + " 314.0240783691406,\n", + " 0.6830025911331177\n", + " ],\n", + " [\n", + " 284.38623046875,\n", + " 267.3375244140625,\n", + " 0.938642144203186\n", + " ],\n", + " [\n", + " 183.94895935058594,\n", + " 312.9803771972656,\n", + " 0.5159981846809387\n", + " ],\n", + " [\n", + " 153.8936767578125,\n", + " 314.8314514160156,\n", + " 0.26622244715690613\n", + " ],\n", + " [\n", + " 181.40602111816406,\n", + " 314.3370056152344,\n", + " 0.5327624082565308\n", + " ],\n", + " [\n", + " 182.94410705566406,\n", + " 307.2097473144531,\n", + " 0.4217340648174286\n", + " ],\n", + " [\n", + " 310.5429992675781,\n", + " 273.63134765625,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.03759765625,\n", + " 275.5606994628906,\n", + " 0.6987367868423462\n", + " ],\n", + " [\n", + " 363.2841491699219,\n", + " 282.46881103515625,\n", + " 0.45098239183425903\n", + " ],\n", + " [\n", + " 183.90602111816406,\n", + " 316.1698913574219,\n", + " 0.39084556698799133\n", + " ],\n", + " [\n", + " 193.3860321044922,\n", + " 305.0615234375,\n", + " 0.507167398929596\n", + " ],\n", + " [\n", + " 355.57421875,\n", + " 341.30316162109375,\n", + " 0.4452424645423889\n", + " ],\n", + " [\n", + " 363.03021240234375,\n", + " 340.71435546875,\n", + " 0.5201398730278015\n", + " ],\n", + " [\n", + " 317.2238464355469,\n", + " 345.26666259765625,\n", + " 0.34468573331832886\n", + " ],\n", + " [\n", + " 332.4403991699219,\n", + " 356.6281433105469,\n", + " 0.46254584193229675\n", + " ],\n", + " [\n", + " 363.12640380859375,\n", + " 345.547119140625,\n", + " 0.42061465978622437\n", + " ],\n", + " [\n", + " 269.6493225097656,\n", + " 357.64892578125,\n", + " 0.4019133448600769\n", + " ],\n", + " [\n", + " 378.178466796875,\n", + " 356.5284729003906,\n", + " 0.4181462824344635\n", + " ],\n", + " [\n", + " 266.38238525390625,\n", + " 358.6065368652344,\n", + " 0.33007708191871643\n", + " ],\n", + " [\n", + " 599.5531005859375,\n", + " 377.76666259765625,\n", + " 0.629446804523468\n", + " ],\n", + " [\n", + " 184.41952514648438,\n", + " 208.114013671875,\n", + " 0.4009157419204712\n", + " ],\n", + " [\n", + " 519.3410034179688,\n", + " 390.22406005859375,\n", + " 0.3750861883163452\n", + " ],\n", + " [\n", + " 516.8319702148438,\n", + " 428.9804382324219,\n", + " 0.657153844833374\n", + " ],\n", + " [\n", + " 567.7550659179688,\n", + " 193.29896545410156,\n", + " 0.34581509232521057\n", + " ],\n", + " [\n", + " 509.476806640625,\n", + " 386.754150390625,\n", + " 0.3405891954898834\n", + " ],\n", + " [\n", + " 516.4321899414062,\n", + " 443.82684326171875,\n", + " 0.5484656095504761\n", + " ],\n", + " [\n", + " 594.040283203125,\n", + " 409.7172546386719,\n", + " 0.5296698212623596\n", + " ],\n", + " [\n", + " 508.0539855957031,\n", + " 380.4469299316406,\n", + " 0.20867124199867249\n", + " ],\n", + " [\n", + " 115.30363464355469,\n", + " 228.87652587890625,\n", + " 0.15687808394432068\n", + " ],\n", + " [\n", + " 573.0440063476562,\n", + " 393.09100341796875,\n", + " 0.39379948377609253\n", + " ],\n", + " [\n", + " 480.1328125,\n", + " 431.7356262207031,\n", + " 0.2691611051559448\n", + " ],\n", + " [\n", + " 550.718505859375,\n", + " 466.6239318847656,\n", + " 0.5681474804878235\n", + " ],\n", + " [\n", + " 469.20660400390625,\n", + " 413.4725036621094,\n", + " 0.39115187525749207\n", + " ],\n", + " [\n", + " 133.34088134765625,\n", + " 172.20274353027344,\n", + " 0.3368547558784485\n", + " ],\n", + " [\n", + " 459.3551025390625,\n", + " 403.5044250488281,\n", + " 0.45792460441589355\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588105.9642372,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588105.996339\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.0221846,\n", + " \"step\": 20,\n", + " \"pose\": [\n", + " [\n", + " 288.93731689453125,\n", + " 298.9955749511719,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.2713928222656,\n", + " 308.37408447265625,\n", + " 0.943423330783844\n", + " ],\n", + " [\n", + " 296.5384826660156,\n", + " 315.9530944824219,\n", + " 0.603498101234436\n", + " ],\n", + " [\n", + " 290.7826843261719,\n", + " 311.4992980957031,\n", + " 0.6115655899047852\n", + " ],\n", + " [\n", + " 304.9278259277344,\n", + " 313.83294677734375,\n", + " 0.7098742127418518\n", + " ],\n", + " [\n", + " 284.4320373535156,\n", + " 267.431884765625,\n", + " 0.9166838526725769\n", + " ],\n", + " [\n", + " 287.8583984375,\n", + " 231.46864318847656,\n", + " 0.4841695725917816\n", + " ],\n", + " [\n", + " 294.63043212890625,\n", + " 218.80035400390625,\n", + " 0.29108670353889465\n", + " ],\n", + " [\n", + " 181.39418029785156,\n", + " 314.2521057128906,\n", + " 0.5189660787582397\n", + " ],\n", + " [\n", + " 304.42047119140625,\n", + " 209.36367797851562,\n", + " 0.40350794792175293\n", + " ],\n", + " [\n", + " 310.4044494628906,\n", + " 273.7499694824219,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.916259765625,\n", + " 275.2131042480469,\n", + " 0.6535776853561401\n", + " ],\n", + " [\n", + " 364.2809753417969,\n", + " 282.39776611328125,\n", + " 0.40148383378982544\n", + " ],\n", + " [\n", + " 180.97071838378906,\n", + " 317.89715576171875,\n", + " 0.3581520915031433\n", + " ],\n", + " [\n", + " 187.54930114746094,\n", + " 302.6390686035156,\n", + " 0.4738801419734955\n", + " ],\n", + " [\n", + " 354.7167663574219,\n", + " 341.0120849609375,\n", + " 0.38925886154174805\n", + " ],\n", + " [\n", + " 362.65179443359375,\n", + " 341.0782775878906,\n", + " 0.5246103405952454\n", + " ],\n", + " [\n", + " 333.5454406738281,\n", + " 342.33709716796875,\n", + " 0.3726454973220825\n", + " ],\n", + " [\n", + " 331.2152099609375,\n", + " 355.3799133300781,\n", + " 0.5273229479789734\n", + " ],\n", + " [\n", + " 292.8047790527344,\n", + " 357.6714172363281,\n", + " 0.4529617428779602\n", + " ],\n", + " [\n", + " 269.3791198730469,\n", + " 357.7329406738281,\n", + " 0.33708542585372925\n", + " ],\n", + " [\n", + " 413.47406005859375,\n", + " 371.240478515625,\n", + " 0.38202741742134094\n", + " ],\n", + " [\n", + " 266.7149658203125,\n", + " 358.42596435546875,\n", + " 0.255717396736145\n", + " ],\n", + " [\n", + " 599.5459594726562,\n", + " 377.59088134765625,\n", + " 0.6171793937683105\n", + " ],\n", + " [\n", + " 182.58990478515625,\n", + " 208.6776580810547,\n", + " 0.3807677626609802\n", + " ],\n", + " [\n", + " 563.1406860351562,\n", + " 387.25885009765625,\n", + " 0.4175715744495392\n", + " ],\n", + " [\n", + " 517.3438110351562,\n", + " 431.8337097167969,\n", + " 0.5834364295005798\n", + " ],\n", + " [\n", + " 571.3803100585938,\n", + " 186.68905639648438,\n", + " 0.3315950930118561\n", + " ],\n", + " [\n", + " 572.1192016601562,\n", + " 190.96864318847656,\n", + " 0.44181355834007263\n", + " ],\n", + " [\n", + " 513.9149169921875,\n", + " 444.82080078125,\n", + " 0.5983200073242188\n", + " ],\n", + " [\n", + " 590.0543212890625,\n", + " 409.4712219238281,\n", + " 0.48646411299705505\n", + " ],\n", + " [\n", + " 474.74908447265625,\n", + " 422.23394775390625,\n", + " 0.2016405165195465\n", + " ],\n", + " [\n", + " 108.44601440429688,\n", + " 270.20269775390625,\n", + " 0.18030454218387604\n", + " ],\n", + " [\n", + " 572.8609008789062,\n", + " 393.09124755859375,\n", + " 0.4335196018218994\n", + " ],\n", + " [\n", + " 479.59942626953125,\n", + " 431.8648376464844,\n", + " 0.26634445786476135\n", + " ],\n", + " [\n", + " 551.6661376953125,\n", + " 466.70135498046875,\n", + " 0.5170703530311584\n", + " ],\n", + " [\n", + " 468.4554443359375,\n", + " 413.3636779785156,\n", + " 0.391674280166626\n", + " ],\n", + " [\n", + " 135.02671813964844,\n", + " 171.04122924804688,\n", + " 0.3395681083202362\n", + " ],\n", + " [\n", + " 460.0732727050781,\n", + " 402.4387512207031,\n", + " 0.43441158533096313\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588105.9977791,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.0241945\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.0568671,\n", + " \"step\": 21,\n", + " \"pose\": [\n", + " [\n", + " 289.0395202636719,\n", + " 299.1026611328125,\n", + " 1.0\n", + " ],\n", + " [\n", + " 294.0700378417969,\n", + " 308.541015625,\n", + " 0.9578058123588562\n", + " ],\n", + " [\n", + " 296.70440673828125,\n", + " 315.8837890625,\n", + " 0.6122352480888367\n", + " ],\n", + " [\n", + " 291.8658752441406,\n", + " 311.9002685546875,\n", + " 0.5979498624801636\n", + " ],\n", + " [\n", + " 306.3223876953125,\n", + " 312.81304931640625,\n", + " 0.7840145826339722\n", + " ],\n", + " [\n", + " 284.8127136230469,\n", + " 267.5986328125,\n", + " 0.9234873652458191\n", + " ],\n", + " [\n", + " 295.66680908203125,\n", + " 222.97425842285156,\n", + " 0.5263190865516663\n", + " ],\n", + " [\n", + " 296.760009765625,\n", + " 216.06723022460938,\n", + " 0.33805859088897705\n", + " ],\n", + " [\n", + " 182.27871704101562,\n", + " 313.2104187011719,\n", + " 0.4452279508113861\n", + " ],\n", + " [\n", + " 302.8914794921875,\n", + " 207.4759521484375,\n", + " 0.4147292673587799\n", + " ],\n", + " [\n", + " 310.67901611328125,\n", + " 273.2028503417969,\n", + " 1.0\n", + " ],\n", + " [\n", + " 364.8524169921875,\n", + " 274.2853088378906,\n", + " 0.6022342443466187\n", + " ],\n", + " [\n", + " 363.9833679199219,\n", + " 282.55401611328125,\n", + " 0.4224339723587036\n", + " ],\n", + " [\n", + " 181.81272888183594,\n", + " 307.6803283691406,\n", + " 0.26984354853630066\n", + " ],\n", + " [\n", + " 194.29331970214844,\n", + " 306.2869873046875,\n", + " 0.40715906023979187\n", + " ],\n", + " [\n", + " 351.9360046386719,\n", + " 343.8916015625,\n", + " 0.3862766921520233\n", + " ],\n", + " [\n", + " 363.7510986328125,\n", + " 342.71826171875,\n", + " 0.5607332587242126\n", + " ],\n", + " [\n", + " 333.4976501464844,\n", + " 343.6668701171875,\n", + " 0.35671743750572205\n", + " ],\n", + " [\n", + " 332.6026916503906,\n", + " 355.7337341308594,\n", + " 0.49907931685447693\n", + " ],\n", + " [\n", + " 281.2688903808594,\n", + " 357.4167175292969,\n", + " 0.5524068474769592\n", + " ],\n", + " [\n", + " 270.1759338378906,\n", + " 357.4426574707031,\n", + " 0.47422295808792114\n", + " ],\n", + " [\n", + " 379.6234436035156,\n", + " 356.0726318359375,\n", + " 0.4222455620765686\n", + " ],\n", + " [\n", + " 266.31121826171875,\n", + " 358.93865966796875,\n", + " 0.4061802625656128\n", + " ],\n", + " [\n", + " 598.6159057617188,\n", + " 377.72808837890625,\n", + " 0.5579972863197327\n", + " ],\n", + " [\n", + " 522.2592163085938,\n", + " 318.6231994628906,\n", + " 0.3378920257091522\n", + " ],\n", + " [\n", + " 518.2576293945312,\n", + " 390.44976806640625,\n", + " 0.41228482127189636\n", + " ],\n", + " [\n", + " 515.7354736328125,\n", + " 429.0234069824219,\n", + " 0.5634108185768127\n", + " ],\n", + " [\n", + " 443.417724609375,\n", + " 356.7914733886719,\n", + " 0.26663434505462646\n", + " ],\n", + " [\n", + " 507.91925048828125,\n", + " 388.6474304199219,\n", + " 0.35248884558677673\n", + " ],\n", + " [\n", + " 512.753173828125,\n", + " 449.7189636230469,\n", + " 0.578186571598053\n", + " ],\n", + " [\n", + " 581.30078125,\n", + " 414.9436340332031,\n", + " 0.4276328682899475\n", + " ],\n", + " [\n", + " 463.5183410644531,\n", + " 402.17388916015625,\n", + " 0.13629432022571564\n", + " ],\n", + " [\n", + " 111.8991470336914,\n", + " 278.3125915527344,\n", + " 0.151170015335083\n", + " ],\n", + " [\n", + " 575.8933715820312,\n", + " 398.4580993652344,\n", + " 0.40502646565437317\n", + " ],\n", + " [\n", + " 143.69412231445312,\n", + " 345.78521728515625,\n", + " 0.24476872384548187\n", + " ],\n", + " [\n", + " 143.1280517578125,\n", + " 373.794189453125,\n", + " 0.4513229429721832\n", + " ],\n", + " [\n", + " 468.4044189453125,\n", + " 411.8726806640625,\n", + " 0.37500157952308655\n", + " ],\n", + " [\n", + " 462.43206787109375,\n", + " 399.8568115234375,\n", + " 0.3841189444065094\n", + " ],\n", + " [\n", + " 461.3127136230469,\n", + " 403.23052978515625,\n", + " 0.5030932426452637\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588106.0300262,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.0578823\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.0853903,\n", + " \"step\": 22,\n", + " \"pose\": [\n", + " [\n", + " 289.0032653808594,\n", + " 299.5898132324219,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.6610412597656,\n", + " 308.364501953125,\n", + " 0.9171743392944336\n", + " ],\n", + " [\n", + " 297.53411865234375,\n", + " 315.0213928222656,\n", + " 0.5967923998832703\n", + " ],\n", + " [\n", + " 291.45599365234375,\n", + " 310.9009094238281,\n", + " 0.6069502234458923\n", + " ],\n", + " [\n", + " 305.15667724609375,\n", + " 312.3479919433594,\n", + " 0.7363993525505066\n", + " ],\n", + " [\n", + " 284.5779724121094,\n", + " 267.80059814453125,\n", + " 0.8958398699760437\n", + " ],\n", + " [\n", + " 287.5237121582031,\n", + " 231.7471923828125,\n", + " 0.5256384611129761\n", + " ],\n", + " [\n", + " 297.3427734375,\n", + " 217.41281127929688,\n", + " 0.32152754068374634\n", + " ],\n", + " [\n", + " 182.88433837890625,\n", + " 314.53692626953125,\n", + " 0.489521861076355\n", + " ],\n", + " [\n", + " 303.3013916015625,\n", + " 209.451171875,\n", + " 0.42159581184387207\n", + " ],\n", + " [\n", + " 310.30352783203125,\n", + " 273.47393798828125,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.04669189453125,\n", + " 275.4956970214844,\n", + " 0.6113792061805725\n", + " ],\n", + " [\n", + " 364.1795959472656,\n", + " 282.3038024902344,\n", + " 0.44920432567596436\n", + " ],\n", + " [\n", + " 185.60179138183594,\n", + " 312.93963623046875,\n", + " 0.2924720048904419\n", + " ],\n", + " [\n", + " 188.79063415527344,\n", + " 306.9405822753906,\n", + " 0.4584471881389618\n", + " ],\n", + " [\n", + " 355.7679443359375,\n", + " 340.5166320800781,\n", + " 0.4532307982444763\n", + " ],\n", + " [\n", + " 362.6515197753906,\n", + " 342.4863586425781,\n", + " 0.5490198731422424\n", + " ],\n", + " [\n", + " 334.1615905761719,\n", + " 344.5025939941406,\n", + " 0.33372119069099426\n", + " ],\n", + " [\n", + " 331.80181884765625,\n", + " 355.52691650390625,\n", + " 0.4720294773578644\n", + " ],\n", + " [\n", + " 290.5385437011719,\n", + " 358.4304504394531,\n", + " 0.4409693479537964\n", + " ],\n", + " [\n", + " 270.8581237792969,\n", + " 356.5791931152344,\n", + " 0.4198722839355469\n", + " ],\n", + " [\n", + " 424.2137145996094,\n", + " 375.2749328613281,\n", + " 0.4370681345462799\n", + " ],\n", + " [\n", + " 268.27886962890625,\n", + " 357.68585205078125,\n", + " 0.36227190494537354\n", + " ],\n", + " [\n", + " 599.3430786132812,\n", + " 377.6954650878906,\n", + " 0.5684486031532288\n", + " ],\n", + " [\n", + " 520.1165771484375,\n", + " 318.81927490234375,\n", + " 0.3776821196079254\n", + " ],\n", + " [\n", + " 562.5358276367188,\n", + " 387.04693603515625,\n", + " 0.3308621942996979\n", + " ],\n", + " [\n", + " 516.3358154296875,\n", + " 429.0692443847656,\n", + " 0.5608440637588501\n", + " ],\n", + " [\n", + " 566.9133911132812,\n", + " 193.52452087402344,\n", + " 0.33105629682540894\n", + " ],\n", + " [\n", + " 570.7525634765625,\n", + " 196.58692932128906,\n", + " 0.4755719006061554\n", + " ],\n", + " [\n", + " 512.5366821289062,\n", + " 448.55328369140625,\n", + " 0.5968109369277954\n", + " ],\n", + " [\n", + " 590.8397216796875,\n", + " 408.89471435546875,\n", + " 0.4935222268104553\n", + " ],\n", + " [\n", + " 110.8057632446289,\n", + " 271.4251403808594,\n", + " 0.16251139342784882\n", + " ],\n", + " [\n", + " 113.5406494140625,\n", + " 279.87841796875,\n", + " 0.38004982471466064\n", + " ],\n", + " [\n", + " 576.237548828125,\n", + " 397.9443664550781,\n", + " 0.3451719284057617\n", + " ],\n", + " [\n", + " 144.29734802246094,\n", + " 315.7247009277344,\n", + " 0.25869646668434143\n", + " ],\n", + " [\n", + " 549.8232421875,\n", + " 465.3925476074219,\n", + " 0.483270525932312\n", + " ],\n", + " [\n", + " 141.22862243652344,\n", + " 190.88340759277344,\n", + " 0.3440597653388977\n", + " ],\n", + " [\n", + " 462.27349853515625,\n", + " 407.5166931152344,\n", + " 0.39141225814819336\n", + " ],\n", + " [\n", + " 459.8392333984375,\n", + " 402.88421630859375,\n", + " 0.48188316822052\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588106.0603576,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.0853903\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.113898,\n", + " \"step\": 23,\n", + " \"pose\": [\n", + " [\n", + " 289.3703918457031,\n", + " 299.01654052734375,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.63714599609375,\n", + " 308.040771484375,\n", + " 0.9116361141204834\n", + " ],\n", + " [\n", + " 297.54144287109375,\n", + " 314.6742248535156,\n", + " 0.5906339287757874\n", + " ],\n", + " [\n", + " 292.0235290527344,\n", + " 310.41552734375,\n", + " 0.6293807029724121\n", + " ],\n", + " [\n", + " 305.7689514160156,\n", + " 312.5812072753906,\n", + " 0.6732482314109802\n", + " ],\n", + " [\n", + " 284.58123779296875,\n", + " 267.2427673339844,\n", + " 0.9067515134811401\n", + " ],\n", + " [\n", + " 183.6278076171875,\n", + " 312.8146667480469,\n", + " 0.5189109444618225\n", + " ],\n", + " [\n", + " 373.2416076660156,\n", + " 300.8122863769531,\n", + " 0.3220520615577698\n", + " ],\n", + " [\n", + " 182.9404754638672,\n", + " 312.4514465332031,\n", + " 0.5909151434898376\n", + " ],\n", + " [\n", + " 303.0302429199219,\n", + " 210.0631561279297,\n", + " 0.4749321937561035\n", + " ],\n", + " [\n", + " 310.4212951660156,\n", + " 273.31060791015625,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.5233459472656,\n", + " 275.4824523925781,\n", + " 0.5813355445861816\n", + " ],\n", + " [\n", + " 363.8905029296875,\n", + " 281.9700622558594,\n", + " 0.42637211084365845\n", + " ],\n", + " [\n", + " 185.95431518554688,\n", + " 311.6904296875,\n", + " 0.34200912714004517\n", + " ],\n", + " [\n", + " 192.42385864257812,\n", + " 305.3067932128906,\n", + " 0.4337136447429657\n", + " ],\n", + " [\n", + " 355.9394836425781,\n", + " 340.25689697265625,\n", + " 0.43801149725914\n", + " ],\n", + " [\n", + " 363.40325927734375,\n", + " 341.9284362792969,\n", + " 0.5555760264396667\n", + " ],\n", + " [\n", + " 314.46746826171875,\n", + " 342.5721130371094,\n", + " 0.35660916566848755\n", + " ],\n", + " [\n", + " 339.87835693359375,\n", + " 359.97998046875,\n", + " 0.4949004054069519\n", + " ],\n", + " [\n", + " 290.6855773925781,\n", + " 358.2529602050781,\n", + " 0.4353252649307251\n", + " ],\n", + " [\n", + " 270.9769287109375,\n", + " 356.5190124511719,\n", + " 0.40807199478149414\n", + " ],\n", + " [\n", + " 424.1128845214844,\n", + " 374.64202880859375,\n", + " 0.42985209822654724\n", + " ],\n", + " [\n", + " 267.4349670410156,\n", + " 357.9966125488281,\n", + " 0.3516447842121124\n", + " ],\n", + " [\n", + " 597.1614990234375,\n", + " 379.19207763671875,\n", + " 0.5612181425094604\n", + " ],\n", + " [\n", + " 200.91233825683594,\n", + " 229.2719268798828,\n", + " 0.3809609115123749\n", + " ],\n", + " [\n", + " 563.5918579101562,\n", + " 387.2767028808594,\n", + " 0.3711811602115631\n", + " ],\n", + " [\n", + " 516.4181518554688,\n", + " 428.2336120605469,\n", + " 0.6263866424560547\n", + " ],\n", + " [\n", + " 570.1748046875,\n", + " 189.33961486816406,\n", + " 0.3523023724555969\n", + " ],\n", + " [\n", + " 569.3735961914062,\n", + " 194.79763793945312,\n", + " 0.36297234892845154\n", + " ],\n", + " [\n", + " 512.5416259765625,\n", + " 448.44085693359375,\n", + " 0.5241063833236694\n", + " ],\n", + " [\n", + " 155.5992889404297,\n", + " 363.5351867675781,\n", + " 0.46883660554885864\n", + " ],\n", + " [\n", + " 508.84210205078125,\n", + " 377.6853332519531,\n", + " 0.1948014795780182\n", + " ],\n", + " [\n", + " 112.65641021728516,\n", + " 278.4239501953125,\n", + " 0.18293216824531555\n", + " ],\n", + " [\n", + " 572.6771850585938,\n", + " 392.94891357421875,\n", + " 0.39113080501556396\n", + " ],\n", + " [\n", + " 481.0816650390625,\n", + " 432.840087890625,\n", + " 0.22011691331863403\n", + " ],\n", + " [\n", + " 145.10130310058594,\n", + " 373.3096618652344,\n", + " 0.4219147264957428\n", + " ],\n", + " [\n", + " 468.0207824707031,\n", + " 413.1219787597656,\n", + " 0.3619706630706787\n", + " ],\n", + " [\n", + " 136.87318420410156,\n", + " 172.54913330078125,\n", + " 0.3659115135669708\n", + " ],\n", + " [\n", + " 459.65655517578125,\n", + " 403.489013671875,\n", + " 0.46539226174354553\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588106.0933394,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.113898\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.1507206,\n", + " \"step\": 24,\n", + " \"pose\": [\n", + " [\n", + " 289.14605712890625,\n", + " 298.5685729980469,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.7758483886719,\n", + " 308.03326416015625,\n", + " 0.9337582588195801\n", + " ],\n", + " [\n", + " 297.73248291015625,\n", + " 315.2346496582031,\n", + " 0.6327982544898987\n", + " ],\n", + " [\n", + " 291.4134216308594,\n", + " 311.653564453125,\n", + " 0.609774649143219\n", + " ],\n", + " [\n", + " 305.1743469238281,\n", + " 313.22369384765625,\n", + " 0.7303615808486938\n", + " ],\n", + " [\n", + " 285.11077880859375,\n", + " 267.58795166015625,\n", + " 0.9044018387794495\n", + " ],\n", + " [\n", + " 287.801025390625,\n", + " 230.8278045654297,\n", + " 0.5132544636726379\n", + " ],\n", + " [\n", + " 294.9792785644531,\n", + " 217.2659912109375,\n", + " 0.3110557496547699\n", + " ],\n", + " [\n", + " 182.70785522460938,\n", + " 312.7431640625,\n", + " 0.4303653836250305\n", + " ],\n", + " [\n", + " 302.67926025390625,\n", + " 209.3670196533203,\n", + " 0.3863958716392517\n", + " ],\n", + " [\n", + " 310.23931884765625,\n", + " 273.41265869140625,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.62945556640625,\n", + " 276.4300842285156,\n", + " 0.6256653070449829\n", + " ],\n", + " [\n", + " 363.4427185058594,\n", + " 281.51226806640625,\n", + " 0.4842453896999359\n", + " ],\n", + " [\n", + " 357.33184814453125,\n", + " 280.7920227050781,\n", + " 0.27625176310539246\n", + " ],\n", + " [\n", + " 194.59628295898438,\n", + " 306.9119873046875,\n", + " 0.41847968101501465\n", + " ],\n", + " [\n", + " 355.71160888671875,\n", + " 338.88134765625,\n", + " 0.44307053089141846\n", + " ],\n", + " [\n", + " 362.9523010253906,\n", + " 339.22442626953125,\n", + " 0.6132069230079651\n", + " ],\n", + " [\n", + " 317.2615051269531,\n", + " 345.85675048828125,\n", + " 0.35067668557167053\n", + " ],\n", + " [\n", + " 331.7022705078125,\n", + " 355.4823303222656,\n", + " 0.45521220564842224\n", + " ],\n", + " [\n", + " 363.580810546875,\n", + " 345.0674743652344,\n", + " 0.4948749542236328\n", + " ],\n", + " [\n", + " 269.8246765136719,\n", + " 356.2699279785156,\n", + " 0.5052472949028015\n", + " ],\n", + " [\n", + " 423.73480224609375,\n", + " 374.68438720703125,\n", + " 0.4521806836128235\n", + " ],\n", + " [\n", + " 266.7372131347656,\n", + " 357.82562255859375,\n", + " 0.44915807247161865\n", + " ],\n", + " [\n", + " 598.9044189453125,\n", + " 377.9191589355469,\n", + " 0.5668219923973083\n", + " ],\n", + " [\n", + " 185.02847290039062,\n", + " 209.2593536376953,\n", + " 0.29630619287490845\n", + " ],\n", + " [\n", + " 232.27960205078125,\n", + " 424.1368408203125,\n", + " 0.35069289803504944\n", + " ],\n", + " [\n", + " 516.3990478515625,\n", + " 429.19805908203125,\n", + " 0.6708835959434509\n", + " ],\n", + " [\n", + " 443.72869873046875,\n", + " 355.66790771484375,\n", + " 0.18464210629463196\n", + " ],\n", + " [\n", + " 509.17938232421875,\n", + " 388.7170104980469,\n", + " 0.4232410788536072\n", + " ],\n", + " [\n", + " 513.5040893554688,\n", + " 449.72918701171875,\n", + " 0.5325906872749329\n", + " ],\n", + " [\n", + " 583.7595825195312,\n", + " 413.1138916015625,\n", + " 0.4442688822746277\n", + " ],\n", + " [\n", + " 115.84835052490234,\n", + " 221.41104125976562,\n", + " 0.1638604700565338\n", + " ],\n", + " [\n", + " 116.47493743896484,\n", + " 283.7770690917969,\n", + " 0.2362251728773117\n", + " ],\n", + " [\n", + " 576.6185913085938,\n", + " 399.1842041015625,\n", + " 0.4103768765926361\n", + " ],\n", + " [\n", + " 144.26768493652344,\n", + " 343.4598693847656,\n", + " 0.2693617641925812\n", + " ],\n", + " [\n", + " 550.6482543945312,\n", + " 465.9261474609375,\n", + " 0.52559894323349\n", + " ],\n", + " [\n", + " 467.72491455078125,\n", + " 412.5487365722656,\n", + " 0.3213626742362976\n", + " ],\n", + " [\n", + " 466.04193115234375,\n", + " 410.77960205078125,\n", + " 0.32539424300193787\n", + " ],\n", + " [\n", + " 459.31622314453125,\n", + " 402.8010559082031,\n", + " 0.4295216500759125\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588106.1260552,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.1507206\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.1780674,\n", + " \"step\": 25,\n", + " \"pose\": [\n", + " [\n", + " 289.6149597167969,\n", + " 299.89166259765625,\n", + " 1.0\n", + " ],\n", + " [\n", + " 294.52008056640625,\n", + " 308.8827209472656,\n", + " 0.9231247305870056\n", + " ],\n", + " [\n", + " 298.28369140625,\n", + " 316.6026916503906,\n", + " 0.6108699440956116\n", + " ],\n", + " [\n", + " 292.314697265625,\n", + " 312.0814514160156,\n", + " 0.5867233276367188\n", + " ],\n", + " [\n", + " 306.8653564453125,\n", + " 313.03192138671875,\n", + " 0.7602759599685669\n", + " ],\n", + " [\n", + " 284.97998046875,\n", + " 268.31109619140625,\n", + " 0.9025271534919739\n", + " ],\n", + " [\n", + " 285.36627197265625,\n", + " 239.4760284423828,\n", + " 0.45559558272361755\n", + " ],\n", + " [\n", + " 278.97967529296875,\n", + " 251.67454528808594,\n", + " 0.3040803074836731\n", + " ],\n", + " [\n", + " 182.5161590576172,\n", + " 314.0823974609375,\n", + " 0.487441748380661\n", + " ],\n", + " [\n", + " 303.5965881347656,\n", + " 209.8799591064453,\n", + " 0.39406585693359375\n", + " ],\n", + " [\n", + " 310.803466796875,\n", + " 273.83941650390625,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.4359130859375,\n", + " 276.5120849609375,\n", + " 0.5850675106048584\n", + " ],\n", + " [\n", + " 364.39306640625,\n", + " 281.8566589355469,\n", + " 0.43770086765289307\n", + " ],\n", + " [\n", + " 184.96409606933594,\n", + " 312.2420349121094,\n", + " 0.32801908254623413\n", + " ],\n", + " [\n", + " 188.65463256835938,\n", + " 306.26580810546875,\n", + " 0.44451019167900085\n", + " ],\n", + " [\n", + " 356.0030517578125,\n", + " 340.06390380859375,\n", + " 0.393046498298645\n", + " ],\n", + " [\n", + " 361.8909606933594,\n", + " 340.0927429199219,\n", + " 0.5161962509155273\n", + " ],\n", + " [\n", + " 315.6810607910156,\n", + " 343.1770935058594,\n", + " 0.37498360872268677\n", + " ],\n", + " [\n", + " 330.904052734375,\n", + " 355.0949401855469,\n", + " 0.5339792370796204\n", + " ],\n", + " [\n", + " 367.3807067871094,\n", + " 343.9208679199219,\n", + " 0.41494372487068176\n", + " ],\n", + " [\n", + " 270.63043212890625,\n", + " 356.5071716308594,\n", + " 0.39088016748428345\n", + " ],\n", + " [\n", + " 424.5980529785156,\n", + " 377.47625732421875,\n", + " 0.40465661883354187\n", + " ],\n", + " [\n", + " 267.8541259765625,\n", + " 358.0108642578125,\n", + " 0.33039915561676025\n", + " ],\n", + " [\n", + " 599.1412353515625,\n", + " 378.35345458984375,\n", + " 0.5644627213478088\n", + " ],\n", + " [\n", + " 184.03726196289062,\n", + " 209.42005920410156,\n", + " 0.459592342376709\n", + " ],\n", + " [\n", + " 511.7866516113281,\n", + " 408.0044860839844,\n", + " 0.3771219551563263\n", + " ],\n", + " [\n", + " 516.994873046875,\n", + " 430.9440612792969,\n", + " 0.5924205780029297\n", + " ],\n", + " [\n", + " 443.1100158691406,\n", + " 355.68621826171875,\n", + " 0.2457418441772461\n", + " ],\n", + " [\n", + " 499.76837158203125,\n", + " 422.5597839355469,\n", + " 0.24084235727787018\n", + " ],\n", + " [\n", + " 511.1026611328125,\n", + " 445.35107421875,\n", + " 0.5383387804031372\n", + " ],\n", + " [\n", + " 589.8642578125,\n", + " 408.1378173828125,\n", + " 0.456050306558609\n", + " ],\n", + " [\n", + " 507.7047119140625,\n", + " 379.2082214355469,\n", + " 0.16193071007728577\n", + " ],\n", + " [\n", + " 112.13590240478516,\n", + " 276.6860656738281,\n", + " 0.282148540019989\n", + " ],\n", + " [\n", + " 573.1347045898438,\n", + " 393.6588439941406,\n", + " 0.4297298192977905\n", + " ],\n", + " [\n", + " 482.9963073730469,\n", + " 432.75811767578125,\n", + " 0.2714385986328125\n", + " ],\n", + " [\n", + " 550.9261474609375,\n", + " 467.3409118652344,\n", + " 0.5166769623756409\n", + " ],\n", + " [\n", + " 468.4465026855469,\n", + " 413.2013244628906,\n", + " 0.42883676290512085\n", + " ],\n", + " [\n", + " 133.96446228027344,\n", + " 171.50291442871094,\n", + " 0.35597431659698486\n", + " ],\n", + " [\n", + " 465.5216369628906,\n", + " 407.3671875,\n", + " 0.4744454324245453\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588106.1553848,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.1780674\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.2132318,\n", + " \"step\": 26,\n", + " \"pose\": [\n", + " [\n", + " 289.6811218261719,\n", + " 299.3060607910156,\n", + " 1.0\n", + " ],\n", + " [\n", + " 294.1546630859375,\n", + " 308.6109924316406,\n", + " 0.9218483567237854\n", + " ],\n", + " [\n", + " 297.23516845703125,\n", + " 316.22552490234375,\n", + " 0.5849137902259827\n", + " ],\n", + " [\n", + " 292.8453369140625,\n", + " 312.3377990722656,\n", + " 0.5977075695991516\n", + " ],\n", + " [\n", + " 306.42193603515625,\n", + " 313.16546630859375,\n", + " 0.73295658826828\n", + " ],\n", + " [\n", + " 285.2742919921875,\n", + " 268.0978698730469,\n", + " 0.8919780254364014\n", + " ],\n", + " [\n", + " 285.22930908203125,\n", + " 240.1131134033203,\n", + " 0.48513516783714294\n", + " ],\n", + " [\n", + " 278.72900390625,\n", + " 252.45925903320312,\n", + " 0.30949145555496216\n", + " ],\n", + " [\n", + " 182.2201690673828,\n", + " 313.8691711425781,\n", + " 0.4198264181613922\n", + " ],\n", + " [\n", + " 302.0194091796875,\n", + " 209.4342803955078,\n", + " 0.3796064555644989\n", + " ],\n", + " [\n", + " 311.20037841796875,\n", + " 273.72052001953125,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.5647888183594,\n", + " 275.8450622558594,\n", + " 0.551364004611969\n", + " ],\n", + " [\n", + " 361.8849792480469,\n", + " 280.7410583496094,\n", + " 0.4334920346736908\n", + " ],\n", + " [\n", + " 185.7280731201172,\n", + " 312.75677490234375,\n", + " 0.29518699645996094\n", + " ],\n", + " [\n", + " 188.90249633789062,\n", + " 306.63177490234375,\n", + " 0.43662378191947937\n", + " ],\n", + " [\n", + " 355.60833740234375,\n", + " 340.0720520019531,\n", + " 0.4073914885520935\n", + " ],\n", + " [\n", + " 362.4900817871094,\n", + " 339.8585205078125,\n", + " 0.5256603956222534\n", + " ],\n", + " [\n", + " 310.6485900878906,\n", + " 346.7264404296875,\n", + " 0.3758726119995117\n", + " ],\n", + " [\n", + " 331.7028503417969,\n", + " 357.2618408203125,\n", + " 0.5182921886444092\n", + " ],\n", + " [\n", + " 282.8375244140625,\n", + " 356.63385009765625,\n", + " 0.47025105357170105\n", + " ],\n", + " [\n", + " 271.1107482910156,\n", + " 356.7911376953125,\n", + " 0.49355366826057434\n", + " ],\n", + " [\n", + " 424.5859680175781,\n", + " 375.9132080078125,\n", + " 0.3878205716609955\n", + " ],\n", + " [\n", + " 267.3672180175781,\n", + " 358.4584655761719,\n", + " 0.4485253393650055\n", + " ],\n", + " [\n", + " 598.8714599609375,\n", + " 377.88165283203125,\n", + " 0.5623369216918945\n", + " ],\n", + " [\n", + " 184.80422973632812,\n", + " 207.90528869628906,\n", + " 0.4335136115550995\n", + " ],\n", + " [\n", + " 513.0913696289062,\n", + " 407.55938720703125,\n", + " 0.34691154956817627\n", + " ],\n", + " [\n", + " 517.757080078125,\n", + " 432.6747131347656,\n", + " 0.5728744864463806\n", + " ],\n", + " [\n", + " 163.63232421875,\n", + " 212.3854217529297,\n", + " 0.32033267617225647\n", + " ],\n", + " [\n", + " 570.7784423828125,\n", + " 193.9116973876953,\n", + " 0.40288540720939636\n", + " ],\n", + " [\n", + " 513.4071655273438,\n", + " 445.5836486816406,\n", + " 0.6099948883056641\n", + " ],\n", + " [\n", + " 590.9609985351562,\n", + " 408.857421875,\n", + " 0.4939699172973633\n", + " ],\n", + " [\n", + " 476.909423828125,\n", + " 420.9211120605469,\n", + " 0.17517627775669098\n", + " ],\n", + " [\n", + " 111.49516296386719,\n", + " 278.0699462890625,\n", + " 0.20062971115112305\n", + " ],\n", + " [\n", + " 572.727294921875,\n", + " 393.2834167480469,\n", + " 0.39421916007995605\n", + " ],\n", + " [\n", + " 481.32647705078125,\n", + " 431.9410400390625,\n", + " 0.2601860463619232\n", + " ],\n", + " [\n", + " 511.23797607421875,\n", + " 458.18768310546875,\n", + " 0.5182275772094727\n", + " ],\n", + " [\n", + " 468.54132080078125,\n", + " 413.6605529785156,\n", + " 0.40847253799438477\n", + " ],\n", + " [\n", + " 464.9053649902344,\n", + " 410.059326171875,\n", + " 0.35141322016716003\n", + " ],\n", + " [\n", + " 459.9055480957031,\n", + " 403.2681579589844,\n", + " 0.4966220259666443\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588106.1875935,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.2132318\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.2483606,\n", + " \"step\": 27,\n", + " \"pose\": [\n", + " [\n", + " 289.65423583984375,\n", + " 298.561767578125,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.9566955566406,\n", + " 308.3065490722656,\n", + " 0.9470576047897339\n", + " ],\n", + " [\n", + " 297.505859375,\n", + " 314.407958984375,\n", + " 0.6088659763336182\n", + " ],\n", + " [\n", + " 291.20574951171875,\n", + " 310.8966064453125,\n", + " 0.607652485370636\n", + " ],\n", + " [\n", + " 306.73492431640625,\n", + " 312.7744445800781,\n", + " 0.7566508054733276\n", + " ],\n", + " [\n", + " 285.1037902832031,\n", + " 267.3839416503906,\n", + " 0.906174898147583\n", + " ],\n", + " [\n", + " 185.3070526123047,\n", + " 311.79327392578125,\n", + " 0.5088176727294922\n", + " ],\n", + " [\n", + " 154.76300048828125,\n", + " 314.7828674316406,\n", + " 0.29141828417778015\n", + " ],\n", + " [\n", + " 182.6195068359375,\n", + " 312.42791748046875,\n", + " 0.5560340285301208\n", + " ],\n", + " [\n", + " 304.5721435546875,\n", + " 209.04302978515625,\n", + " 0.4600820243358612\n", + " ],\n", + " [\n", + " 310.9137878417969,\n", + " 273.2908935546875,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.1856994628906,\n", + " 276.47381591796875,\n", + " 0.5621199011802673\n", + " ],\n", + " [\n", + " 363.20672607421875,\n", + " 281.5467224121094,\n", + " 0.427944153547287\n", + " ],\n", + " [\n", + " 181.0316619873047,\n", + " 311.6895446777344,\n", + " 0.3624846041202545\n", + " ],\n", + " [\n", + " 188.46315002441406,\n", + " 304.592529296875,\n", + " 0.5054963231086731\n", + " ],\n", + " [\n", + " 353.033447265625,\n", + " 342.5238037109375,\n", + " 0.40231525897979736\n", + " ],\n", + " [\n", + " 362.5194091796875,\n", + " 339.60711669921875,\n", + " 0.6008350849151611\n", + " ],\n", + " [\n", + " 317.26544189453125,\n", + " 342.3359069824219,\n", + " 0.3960641920566559\n", + " ],\n", + " [\n", + " 331.4060974121094,\n", + " 356.75445556640625,\n", + " 0.5937938690185547\n", + " ],\n", + " [\n", + " 293.347412109375,\n", + " 357.84515380859375,\n", + " 0.4450031816959381\n", + " ],\n", + " [\n", + " 269.11407470703125,\n", + " 357.3741455078125,\n", + " 0.4057061970233917\n", + " ],\n", + " [\n", + " 378.8157653808594,\n", + " 355.3031921386719,\n", + " 0.39472222328186035\n", + " ],\n", + " [\n", + " 265.99078369140625,\n", + " 358.6902160644531,\n", + " 0.3461160957813263\n", + " ],\n", + " [\n", + " 598.9271850585938,\n", + " 377.77618408203125,\n", + " 0.5730097889900208\n", + " ],\n", + " [\n", + " 184.35238647460938,\n", + " 209.6236572265625,\n", + " 0.3743739724159241\n", + " ],\n", + " [\n", + " 513.5267333984375,\n", + " 407.8452453613281,\n", + " 0.394991397857666\n", + " ],\n", + " [\n", + " 517.43359375,\n", + " 432.714111328125,\n", + " 0.6186484694480896\n", + " ],\n", + " [\n", + " 566.1676635742188,\n", + " 200.06082153320312,\n", + " 0.36133381724357605\n", + " ],\n", + " [\n", + " 570.642333984375,\n", + " 203.29977416992188,\n", + " 0.2848742604255676\n", + " ],\n", + " [\n", + " 513.6658935546875,\n", + " 447.7688903808594,\n", + " 0.566241979598999\n", + " ],\n", + " [\n", + " 590.916259765625,\n", + " 409.0061950683594,\n", + " 0.5227742791175842\n", + " ],\n", + " [\n", + " 476.4045104980469,\n", + " 421.7107849121094,\n", + " 0.23629191517829895\n", + " ],\n", + " [\n", + " 109.81532287597656,\n", + " 272.0874938964844,\n", + " 0.2973298132419586\n", + " ],\n", + " [\n", + " 572.2440185546875,\n", + " 393.4117431640625,\n", + " 0.4230390191078186\n", + " ],\n", + " [\n", + " 481.6334228515625,\n", + " 434.35992431640625,\n", + " 0.20489251613616943\n", + " ],\n", + " [\n", + " 511.52740478515625,\n", + " 458.0361633300781,\n", + " 0.47707173228263855\n", + " ],\n", + " [\n", + " 468.145751953125,\n", + " 413.9035339355469,\n", + " 0.4124965965747833\n", + " ],\n", + " [\n", + " 465.691650390625,\n", + " 411.6900329589844,\n", + " 0.3242393732070923\n", + " ],\n", + " [\n", + " 459.133544921875,\n", + " 403.29656982421875,\n", + " 0.4457142651081085\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588106.2229764,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.2503667\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.2815137,\n", + " \"step\": 28,\n", + " \"pose\": [\n", + " [\n", + " 289.7947998046875,\n", + " 299.4747314453125,\n", + " 1.0\n", + " ],\n", + " [\n", + " 294.1377868652344,\n", + " 308.9644470214844,\n", + " 0.9098001718521118\n", + " ],\n", + " [\n", + " 297.0553894042969,\n", + " 316.2694396972656,\n", + " 0.5839970111846924\n", + " ],\n", + " [\n", + " 292.7187194824219,\n", + " 312.2520446777344,\n", + " 0.5812930464744568\n", + " ],\n", + " [\n", + " 306.51007080078125,\n", + " 313.4143981933594,\n", + " 0.7014763355255127\n", + " ],\n", + " [\n", + " 285.4446105957031,\n", + " 267.66400146484375,\n", + " 0.885840654373169\n", + " ],\n", + " [\n", + " 287.2601623535156,\n", + " 232.3909912109375,\n", + " 0.47544583678245544\n", + " ],\n", + " [\n", + " 279.16656494140625,\n", + " 250.76365661621094,\n", + " 0.29200607538223267\n", + " ],\n", + " [\n", + " 182.51055908203125,\n", + " 314.830322265625,\n", + " 0.4986189305782318\n", + " ],\n", + " [\n", + " 304.1475524902344,\n", + " 207.91217041015625,\n", + " 0.5152255892753601\n", + " ],\n", + " [\n", + " 311.350830078125,\n", + " 273.5013122558594,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.06219482421875,\n", + " 276.67120361328125,\n", + " 0.5790920853614807\n", + " ],\n", + " [\n", + " 362.5161437988281,\n", + " 283.22723388671875,\n", + " 0.4104999601840973\n", + " ],\n", + " [\n", + " 181.13710021972656,\n", + " 312.584716796875,\n", + " 0.29986241459846497\n", + " ],\n", + " [\n", + " 188.63394165039062,\n", + " 303.74395751953125,\n", + " 0.45568570494651794\n", + " ],\n", + " [\n", + " 357.4764099121094,\n", + " 339.7001953125,\n", + " 0.4089646339416504\n", + " ],\n", + " [\n", + " 362.9626770019531,\n", + " 339.128662109375,\n", + " 0.4946100115776062\n", + " ],\n", + " [\n", + " 311.04779052734375,\n", + " 346.371826171875,\n", + " 0.39371228218078613\n", + " ],\n", + " [\n", + " 339.0541687011719,\n", + " 359.549560546875,\n", + " 0.5586241483688354\n", + " ],\n", + " [\n", + " 283.3900146484375,\n", + " 356.6086120605469,\n", + " 0.4299786686897278\n", + " ],\n", + " [\n", + " 270.4642333984375,\n", + " 357.4127502441406,\n", + " 0.40563082695007324\n", + " ],\n", + " [\n", + " 423.99005126953125,\n", + " 375.4238586425781,\n", + " 0.43916311860084534\n", + " ],\n", + " [\n", + " 268.08233642578125,\n", + " 358.42919921875,\n", + " 0.3316787779331207\n", + " ],\n", + " [\n", + " 598.7156982421875,\n", + " 377.3929138183594,\n", + " 0.5510212182998657\n", + " ],\n", + " [\n", + " 182.943115234375,\n", + " 208.30332946777344,\n", + " 0.44296297430992126\n", + " ],\n", + " [\n", + " 564.0025024414062,\n", + " 386.8399353027344,\n", + " 0.39091676473617554\n", + " ],\n", + " [\n", + " 517.2132568359375,\n", + " 432.1662292480469,\n", + " 0.6276928186416626\n", + " ],\n", + " [\n", + " 570.1396484375,\n", + " 190.4938507080078,\n", + " 0.2375359982252121\n", + " ],\n", + " [\n", + " 492.12896728515625,\n", + " 413.6061096191406,\n", + " 0.3405477702617645\n", + " ],\n", + " [\n", + " 512.7205200195312,\n", + " 450.4252624511719,\n", + " 0.6235648393630981\n", + " ],\n", + " [\n", + " 590.1025390625,\n", + " 409.0594787597656,\n", + " 0.5204903483390808\n", + " ],\n", + " [\n", + " 476.5130310058594,\n", + " 422.0225524902344,\n", + " 0.14199496805667877\n", + " ],\n", + " [\n", + " 110.05477142333984,\n", + " 271.470703125,\n", + " 0.22186370193958282\n", + " ],\n", + " [\n", + " 571.6903686523438,\n", + " 393.01318359375,\n", + " 0.4487222135066986\n", + " ],\n", + " [\n", + " 142.70831298828125,\n", + " 346.6425476074219,\n", + " 0.2570325434207916\n", + " ],\n", + " [\n", + " 511.7834167480469,\n", + " 456.3761901855469,\n", + " 0.5804262161254883\n", + " ],\n", + " [\n", + " 468.8331298828125,\n", + " 413.937744140625,\n", + " 0.36150258779525757\n", + " ],\n", + " [\n", + " 465.01104736328125,\n", + " 410.2015380859375,\n", + " 0.3218684196472168\n", + " ],\n", + " [\n", + " 460.2878723144531,\n", + " 402.61614990234375,\n", + " 0.44286683201789856\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588106.2539957,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.2815137\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.313648,\n", + " \"step\": 29,\n", + " \"pose\": [\n", + " [\n", + " 290.2019958496094,\n", + " 299.82733154296875,\n", + " 1.0\n", + " ],\n", + " [\n", + " 294.038818359375,\n", + " 309.61590576171875,\n", + " 0.8994060754776001\n", + " ],\n", + " [\n", + " 297.1882019042969,\n", + " 316.2828369140625,\n", + " 0.580872118473053\n", + " ],\n", + " [\n", + " 292.24969482421875,\n", + " 311.94366455078125,\n", + " 0.553147554397583\n", + " ],\n", + " [\n", + " 305.8408508300781,\n", + " 312.88555908203125,\n", + " 0.7357133626937866\n", + " ],\n", + " [\n", + " 285.2379455566406,\n", + " 267.34576416015625,\n", + " 0.8884855508804321\n", + " ],\n", + " [\n", + " 288.9740905761719,\n", + " 231.48561096191406,\n", + " 0.5197221636772156\n", + " ],\n", + " [\n", + " 299.1871032714844,\n", + " 218.01211547851562,\n", + " 0.3429694175720215\n", + " ],\n", + " [\n", + " 181.70339965820312,\n", + " 317.9532165527344,\n", + " 0.46619749069213867\n", + " ],\n", + " [\n", + " 304.53472900390625,\n", + " 208.62942504882812,\n", + " 0.45269933342933655\n", + " ],\n", + " [\n", + " 311.17626953125,\n", + " 274.0134582519531,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.29833984375,\n", + " 278.41729736328125,\n", + " 0.5592008233070374\n", + " ],\n", + " [\n", + " 363.13763427734375,\n", + " 281.8921813964844,\n", + " 0.45556432008743286\n", + " ],\n", + " [\n", + " 357.6162414550781,\n", + " 281.98394775390625,\n", + " 0.30312928557395935\n", + " ],\n", + " [\n", + " 187.75889587402344,\n", + " 305.17547607421875,\n", + " 0.44207459688186646\n", + " ],\n", + " [\n", + " 355.2580261230469,\n", + " 341.6760559082031,\n", + " 0.38897281885147095\n", + " ],\n", + " [\n", + " 363.04852294921875,\n", + " 339.8902893066406,\n", + " 0.5060890316963196\n", + " ],\n", + " [\n", + " 314.8399963378906,\n", + " 341.91680908203125,\n", + " 0.40578165650367737\n", + " ],\n", + " [\n", + " 338.7541198730469,\n", + " 359.4593200683594,\n", + " 0.548823893070221\n", + " ],\n", + " [\n", + " 366.559814453125,\n", + " 343.24029541015625,\n", + " 0.4393633306026459\n", + " ],\n", + " [\n", + " 270.7618103027344,\n", + " 357.1903991699219,\n", + " 0.4302641451358795\n", + " ],\n", + " [\n", + " 379.385498046875,\n", + " 355.3668212890625,\n", + " 0.45166778564453125\n", + " ],\n", + " [\n", + " 268.61431884765625,\n", + " 357.9519348144531,\n", + " 0.36507532000541687\n", + " ],\n", + " [\n", + " 597.203125,\n", + " 379.3829345703125,\n", + " 0.587417721748352\n", + " ],\n", + " [\n", + " 182.3574676513672,\n", + " 208.2609100341797,\n", + " 0.39093875885009766\n", + " ],\n", + " [\n", + " 512.343994140625,\n", + " 407.104736328125,\n", + " 0.3616585433483124\n", + " ],\n", + " [\n", + " 517.728759765625,\n", + " 431.97174072265625,\n", + " 0.5530462265014648\n", + " ],\n", + " [\n", + " 567.7471313476562,\n", + " 195.76194763183594,\n", + " 0.41424036026000977\n", + " ],\n", + " [\n", + " 568.5385131835938,\n", + " 196.24961853027344,\n", + " 0.3196858763694763\n", + " ],\n", + " [\n", + " 512.130615234375,\n", + " 450.2174072265625,\n", + " 0.6944491863250732\n", + " ],\n", + " [\n", + " 154.75747680664062,\n", + " 365.33026123046875,\n", + " 0.46970120072364807\n", + " ],\n", + " [\n", + " 476.37847900390625,\n", + " 421.13104248046875,\n", + " 0.2024911344051361\n", + " ],\n", + " [\n", + " 109.27305603027344,\n", + " 276.56787109375,\n", + " 0.22318890690803528\n", + " ],\n", + " [\n", + " 573.2276611328125,\n", + " 393.43096923828125,\n", + " 0.45069870352745056\n", + " ],\n", + " [\n", + " 144.93081665039062,\n", + " 343.8260803222656,\n", + " 0.26525822281837463\n", + " ],\n", + " [\n", + " 510.0262145996094,\n", + " 457.258544921875,\n", + " 0.6254387497901917\n", + " ],\n", + " [\n", + " 468.2686767578125,\n", + " 412.5702209472656,\n", + " 0.40957126021385193\n", + " ],\n", + " [\n", + " 464.2906188964844,\n", + " 410.6726989746094,\n", + " 0.32647469639778137\n", + " ],\n", + " [\n", + " 460.0201721191406,\n", + " 403.037841796875,\n", + " 0.41243088245391846\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588106.285935,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.313648\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.3619356,\n", + " \"step\": 30,\n", + " \"pose\": [\n", + " [\n", + " 290.1323547363281,\n", + " 299.7147521972656,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.99139404296875,\n", + " 309.4753112792969,\n", + " 0.8872848749160767\n", + " ],\n", + " [\n", + " 297.3484802246094,\n", + " 315.24835205078125,\n", + " 0.5460883975028992\n", + " ],\n", + " [\n", + " 291.71209716796875,\n", + " 310.8031311035156,\n", + " 0.5654347538948059\n", + " ],\n", + " [\n", + " 305.3967590332031,\n", + " 312.7275085449219,\n", + " 0.7120907306671143\n", + " ],\n", + " [\n", + " 285.19940185546875,\n", + " 267.3405456542969,\n", + " 0.8942697048187256\n", + " ],\n", + " [\n", + " 289.3546447753906,\n", + " 231.18653869628906,\n", + " 0.483479768037796\n", + " ],\n", + " [\n", + " 299.21112060546875,\n", + " 218.4276580810547,\n", + " 0.4001926779747009\n", + " ],\n", + " [\n", + " 181.76585388183594,\n", + " 313.5556945800781,\n", + " 0.46045035123825073\n", + " ],\n", + " [\n", + " 309.8026428222656,\n", + " 210.481689453125,\n", + " 0.5369957089424133\n", + " ],\n", + " [\n", + " 310.97064208984375,\n", + " 273.8341369628906,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.9340515136719,\n", + " 277.03912353515625,\n", + " 0.6095738410949707\n", + " ],\n", + " [\n", + " 362.3292541503906,\n", + " 282.6873474121094,\n", + " 0.5387850999832153\n", + " ],\n", + " [\n", + " 183.47048950195312,\n", + " 311.634521484375,\n", + " 0.30759158730506897\n", + " ],\n", + " [\n", + " 193.97259521484375,\n", + " 306.5625,\n", + " 0.4332640469074249\n", + " ],\n", + " [\n", + " 355.8443908691406,\n", + " 341.6749267578125,\n", + " 0.3820899724960327\n", + " ],\n", + " [\n", + " 362.45452880859375,\n", + " 340.09088134765625,\n", + " 0.493086040019989\n", + " ],\n", + " [\n", + " 315.4454650878906,\n", + " 343.0971984863281,\n", + " 0.3856670558452606\n", + " ],\n", + " [\n", + " 339.348388671875,\n", + " 359.60870361328125,\n", + " 0.55845707654953\n", + " ],\n", + " [\n", + " 284.3851623535156,\n", + " 356.5133972167969,\n", + " 0.4924909174442291\n", + " ],\n", + " [\n", + " 271.4208679199219,\n", + " 357.4637451171875,\n", + " 0.3702590763568878\n", + " ],\n", + " [\n", + " 423.9642028808594,\n", + " 375.77801513671875,\n", + " 0.42082488536834717\n", + " ],\n", + " [\n", + " 269.3265075683594,\n", + " 358.4918212890625,\n", + " 0.3108918368816376\n", + " ],\n", + " [\n", + " 595.7271728515625,\n", + " 378.74505615234375,\n", + " 0.6383695006370544\n", + " ],\n", + " [\n", + " 182.5079803466797,\n", + " 209.4701690673828,\n", + " 0.3769323527812958\n", + " ],\n", + " [\n", + " 563.5107421875,\n", + " 388.12860107421875,\n", + " 0.34879517555236816\n", + " ],\n", + " [\n", + " 516.7620239257812,\n", + " 429.2193908691406,\n", + " 0.6218969225883484\n", + " ],\n", + " [\n", + " 443.3829650878906,\n", + " 357.2695007324219,\n", + " 0.2294222116470337\n", + " ],\n", + " [\n", + " 508.9411926269531,\n", + " 390.8059387207031,\n", + " 0.27742016315460205\n", + " ],\n", + " [\n", + " 513.4525756835938,\n", + " 451.084228515625,\n", + " 0.588313102722168\n", + " ],\n", + " [\n", + " 590.62939453125,\n", + " 409.5919494628906,\n", + " 0.49015718698501587\n", + " ],\n", + " [\n", + " 505.6385498046875,\n", + " 379.8114929199219,\n", + " 0.16522188484668732\n", + " ],\n", + " [\n", + " 108.85206604003906,\n", + " 271.24273681640625,\n", + " 0.2543381452560425\n", + " ],\n", + " [\n", + " 573.4502563476562,\n", + " 393.5893249511719,\n", + " 0.49820849299430847\n", + " ],\n", + " [\n", + " 142.85484313964844,\n", + " 314.2268981933594,\n", + " 0.19713541865348816\n", + " ],\n", + " [\n", + " 549.9910278320312,\n", + " 465.56768798828125,\n", + " 0.5062677264213562\n", + " ],\n", + " [\n", + " 469.6101379394531,\n", + " 412.96905517578125,\n", + " 0.3788171112537384\n", + " ],\n", + " [\n", + " 465.2295837402344,\n", + " 411.72161865234375,\n", + " 0.343596875667572\n", + " ],\n", + " [\n", + " 460.37811279296875,\n", + " 402.3249206542969,\n", + " 0.46701255440711975\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588106.3327684,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.3619356\n", + "}\n", + "{\n", + " \"kind\": \"pose\",\n", + " \"raw\": {\n", + " \"type\": \"pose\",\n", + " \"timestamp\": 1783588106.3911848,\n", + " \"step\": 31,\n", + " \"pose\": [\n", + " [\n", + " 289.88616943359375,\n", + " 299.245849609375,\n", + " 1.0\n", + " ],\n", + " [\n", + " 293.7210693359375,\n", + " 308.9605407714844,\n", + " 0.8916722536087036\n", + " ],\n", + " [\n", + " 297.2877502441406,\n", + " 315.5369873046875,\n", + " 0.5746937394142151\n", + " ],\n", + " [\n", + " 292.2024230957031,\n", + " 311.0600891113281,\n", + " 0.5595524907112122\n", + " ],\n", + " [\n", + " 305.3230285644531,\n", + " 313.09588623046875,\n", + " 0.7339632511138916\n", + " ],\n", + " [\n", + " 285.2215576171875,\n", + " 266.96453857421875,\n", + " 0.894293487071991\n", + " ],\n", + " [\n", + " 297.3047180175781,\n", + " 221.42364501953125,\n", + " 0.5297285914421082\n", + " ],\n", + " [\n", + " 295.8182067871094,\n", + " 216.30775451660156,\n", + " 0.38408026099205017\n", + " ],\n", + " [\n", + " 183.77606201171875,\n", + " 316.7649841308594,\n", + " 0.4275979697704315\n", + " ],\n", + " [\n", + " 308.4361572265625,\n", + " 210.48973083496094,\n", + " 0.5560570359230042\n", + " ],\n", + " [\n", + " 311.0546875,\n", + " 273.9168395996094,\n", + " 1.0\n", + " ],\n", + " [\n", + " 363.8443298339844,\n", + " 277.56488037109375,\n", + " 0.632280170917511\n", + " ],\n", + " [\n", + " 363.19598388671875,\n", + " 283.2601013183594,\n", + " 0.5504708290100098\n", + " ],\n", + " [\n", + " 360.5338439941406,\n", + " 282.9017028808594,\n", + " 0.3014270067214966\n", + " ],\n", + " [\n", + " 187.5973663330078,\n", + " 304.839111328125,\n", + " 0.4563285708427429\n", + " ],\n", + " [\n", + " 355.62762451171875,\n", + " 341.2684020996094,\n", + " 0.4316878914833069\n", + " ],\n", + " [\n", + " 362.1797180175781,\n", + " 340.63848876953125,\n", + " 0.523749828338623\n", + " ],\n", + " [\n", + " 310.7569580078125,\n", + " 347.2984313964844,\n", + " 0.3826305568218231\n", + " ],\n", + " [\n", + " 338.3757629394531,\n", + " 360.44317626953125,\n", + " 0.5659022927284241\n", + " ],\n", + " [\n", + " 367.23004150390625,\n", + " 344.0614929199219,\n", + " 0.43424880504608154\n", + " ],\n", + " [\n", + " 272.1240234375,\n", + " 355.71612548828125,\n", + " 0.5028250217437744\n", + " ],\n", + " [\n", + " 379.81280517578125,\n", + " 355.9754943847656,\n", + " 0.38737767934799194\n", + " ],\n", + " [\n", + " 268.998046875,\n", + " 357.2021789550781,\n", + " 0.44784843921661377\n", + " ],\n", + " [\n", + " 596.336181640625,\n", + " 379.1740417480469,\n", + " 0.545729398727417\n", + " ],\n", + " [\n", + " 201.0037841796875,\n", + " 228.0155029296875,\n", + " 0.42291054129600525\n", + " ],\n", + " [\n", + " 562.3812866210938,\n", + " 386.2914123535156,\n", + " 0.36510559916496277\n", + " ],\n", + " [\n", + " 517.466796875,\n", + " 433.1764831542969,\n", + " 0.5716246962547302\n", + " ],\n", + " [\n", + " 570.5004272460938,\n", + " 194.27825927734375,\n", + " 0.31970250606536865\n", + " ],\n", + " [\n", + " 572.74462890625,\n", + " 201.8236541748047,\n", + " 0.39004698395729065\n", + " ],\n", + " [\n", + " 513.2064208984375,\n", + " 451.0941162109375,\n", + " 0.6408159732818604\n", + " ],\n", + " [\n", + " 590.6119995117188,\n", + " 408.5152282714844,\n", + " 0.5167672634124756\n", + " ],\n", + " [\n", + " 114.4150619506836,\n", + " 235.0469207763672,\n", + " 0.13695929944515228\n", + " ],\n", + " [\n", + " 112.1463623046875,\n", + " 279.28265380859375,\n", + " 0.23755685985088348\n", + " ],\n", + " [\n", + " 571.991943359375,\n", + " 393.113525390625,\n", + " 0.4135165214538574\n", + " ],\n", + " [\n", + " 144.2115478515625,\n", + " 313.7943115234375,\n", + " 0.23972396552562714\n", + " ],\n", + " [\n", + " 510.1828918457031,\n", + " 456.5469055175781,\n", + " 0.503180205821991\n", + " ],\n", + " [\n", + " 468.22821044921875,\n", + " 413.3734436035156,\n", + " 0.37297725677490234\n", + " ],\n", + " [\n", + " 465.0234069824219,\n", + " 410.9199523925781,\n", + " 0.36548110842704773\n", + " ],\n", + " [\n", + " 465.41192626953125,\n", + " 406.6351623535156,\n", + " 0.4397272765636444\n", + " ]\n", + " ],\n", + " \"frame_time\": 1783588106.3645778,\n", + " \"pose_time\": null,\n", + " \"recording\": false\n", + " },\n", + " \"received_at\": 1783588106.3911848\n", + "}\n", + "Received 20 packet(s).\n" + ] + } + ], + "source": [ + "def receive_packets(conn, duration_s: float = 30.0, max_packets: int | None = None):\n", + " start = time.time()\n", + " packets: list[dict[str, Any]] = []\n", + "\n", + " while time.time() - start < duration_s:\n", + " if conn.poll(POLL_INTERVAL_S):\n", + " payload = conn.recv()\n", + " decoded = decode_payload(payload)\n", + " decoded[\"received_at\"] = time.time()\n", + " packets.append(decoded)\n", + " print(json.dumps(decoded, default=str, indent=2))\n", + "\n", + " if max_packets is not None and len(packets) >= max_packets:\n", + " break\n", + "\n", + " print(f\"Received {len(packets)} packet(s).\")\n", + " return packets\n", + "\n", + "\n", + "packets = receive_packets(conn, duration_s=30.0, max_packets=20)" + ] + }, + { + "cell_type": "markdown", + "id": "504fb2a444614c0babb325280ed9130a", + "metadata": {}, + "source": [ + "## Save captured packets\n", + "\n", + "This is useful if you want to inspect the stream shape after a test run." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "59bbdb311c014d738909a11f9e486628", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Saved 20 packet(s) to C:\\Users\\Cyril A\\Desktop\\Code\\DeepLabCut-live-GUI\\dlclivegui\\processors\\custom\\mock_unity_captured_packets.json\n" + ] + } + ], + "source": [ + "out_path = Path(\"mock_unity_captured_packets.json\")\n", + "out_path.write_text(json.dumps(packets, default=str, indent=2), encoding=\"utf-8\")\n", + "print(\"Saved\", len(packets), \"packet(s) to\", out_path.resolve())" + ] + }, + { + "cell_type": "markdown", + "id": "b43b363d81ae4b689946ece5c682cd59", + "metadata": {}, + "source": [ + "## Close the client connection" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "8a65eabff63a45729fe45fb5ade58bdc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connection closed\n" + ] + } + ], + "source": [ + "try:\n", + " conn.close()\n", + " print(\"Connection closed\")\n", + "except Exception as exc:\n", + " print(\"Close failed:\", exc)" + ] + }, + { + "cell_type": "markdown", + "id": "c3933fab20d04ec698c2621248eb3be0", + "metadata": {}, + "source": [ + "## Optional local smoke test with a standalone mock server\n", + "\n", + "Use this only if you want to validate the notebook client logic without running DLCLiveGUI.\n", + "\n", + "This requires `mock_socket_processor.py` to be importable, for example by placing it next to this notebook or adding its directory to `sys.path`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "4dd4641cc4064e0191573fe9c69df29b", + "metadata": {}, + "outputs": [], + "source": [ + "# Optional smoke test. Leave commented unless you have mock_socket_processor.py available.\n", + "#\n", + "# import socket\n", + "# import sys\n", + "# from multiprocessing.connection import Client\n", + "#\n", + "# from mock_socket_processor import MockSocketProcessor\n", + "#\n", + "# def free_port():\n", + "# s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n", + "# s.bind((\"127.0.0.1\", 0))\n", + "# port = s.getsockname()[1]\n", + "# s.close()\n", + "# return port\n", + "#\n", + "# port = free_port()\n", + "# mock = MockSocketProcessor(bind=(\"127.0.0.1\", port), authkey=AUTHKEY)\n", + "# test_conn = Client(mock.address, authkey=AUTHKEY)\n", + "# test_conn.send({\"cmd\": \"ping\"})\n", + "# print(test_conn.recv())\n", + "# mock.process([[1, 2, 0.9]], frame_time=123.456)\n", + "# print(decode_payload(test_conn.recv()))\n", + "# test_conn.close()\n", + "# mock.stop()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "deeplabcut-live-gui (3.12.12)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 6a0846c64acca2122575b06208057b906ff92fdf Mon Sep 17 00:00:00 2001 From: Cyril Achard Date: Thu, 9 Jul 2026 16:35:03 +0200 Subject: [PATCH 3/7] Update main_window.py --- dlclivegui/gui/main_window.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/dlclivegui/gui/main_window.py b/dlclivegui/gui/main_window.py index 4e41edf..bcf17e6 100644 --- a/dlclivegui/gui/main_window.py +++ b/dlclivegui/gui/main_window.py @@ -1830,7 +1830,42 @@ def worker(): daemon=True, ).start() + def _save_processor_data_if_available(self) -> None: + """Best-effort generic processor save. + + The GUI intentionally does not pass a path here. This lets custom processors + use their own save_path / filename / internal policy. + + Expected processor contract: + processor.save() -> int | bool | None + + Return values are only logged; failure should not crash the GUI. + """ + processor = getattr(self._dlc, "_processor", None) + + # Fallback in case DLCLive owns the processor but _processor was not updated. + if processor is None: + dlc_obj = getattr(self._dlc, "_dlc", None) + processor = getattr(dlc_obj, "processor", None) if dlc_obj is not None else None + + if processor is None: + logger.debug("Processor save skipped: no processor instance available.") + return + + save = getattr(processor, "save", None) + if not callable(save): + logger.debug("Processor save skipped: processor has no callable save().") + return + + try: + result = save() + logger.info("Processor save() completed with result: %r", result) + except Exception: + logger.exception("Processor save() failed.") + def _on_recording_stopped_async(self) -> None: + self._save_processor_data_if_available() + self._recording_stopping = False self.start_record_button.setEnabled(True) self.stop_record_button.setEnabled(False) From 4b3478ea3c744538285e5769f8d4f3f543f37169 Mon Sep 17 00:00:00 2001 From: C-Achard Date: Fri, 10 Jul 2026 11:54:27 +0200 Subject: [PATCH 4/7] Add recording file context and recorder path accessors RecordingManager now captures and exposes a current/last recording file context, including run/session directories plus per-camera video and timestamp sidecar paths, so downstream processor hooks can still resolve finalized files after stop_all(). VideoRecorder adds explicit output_path and timestamp_json_path properties, and timestamp saving now reuses the shared timestamp path accessor. --- dlclivegui/services/recording_manager.py | 72 ++++++++++++++++++++++++ dlclivegui/services/video_recorder.py | 12 +++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/dlclivegui/services/recording_manager.py b/dlclivegui/services/recording_manager.py index 8fb1d60..829a97e 100644 --- a/dlclivegui/services/recording_manager.py +++ b/dlclivegui/services/recording_manager.py @@ -32,6 +32,9 @@ def __init__(self): self._dispatch_thread: threading.Thread | None = None self._dispatch_stop = threading.Event() + # Utility for operation on latest recording file context (e.g., for processor hooks) + self._last_recording_file_context: dict | None = None + @property def is_active(self) -> bool: with self._lock: @@ -265,6 +268,9 @@ def start_all( self._run_dir = None return None + with self._lock: + self._last_recording_file_context = self._build_recording_file_context_unlocked() + return run_dir def stop_all(self) -> None: @@ -272,6 +278,11 @@ def stop_all(self) -> None: with self._lock: recorders = list(self._recorders.items()) + run_dir = self._run_dir + session_dir = self._session_dir + self._last_recording_file_context = self._build_recording_file_context_unlocked( + recorders=recorders, run_dir=run_dir, session_dir=session_dir + ) self._recorders.clear() for cam_id, rec in recorders: @@ -423,3 +434,64 @@ def get_stats_summary(self) -> str: f"backlog {totals['backlog']} | " f"dropped {totals['dropped']}" ) + + def _build_recording_file_context_unlocked( + self, + recorders: list[tuple[str, VideoRecorder]] | None = None, + run_dir: Path | None = None, + session_dir: Path | None = None, + ) -> dict: + """Build a file context for active or recently stopped recorders. + + Must be called with self._lock held if using internal state. + """ + if recorders is None: + recorders = list(self._recorders.items()) + + if run_dir is None: + run_dir = self._run_dir + + if session_dir is None: + session_dir = self._session_dir + + video_files: dict[str, Path] = {} + timestamp_json_files: dict[str, Path] = {} + + for cam_id, recorder in recorders: + video_path = getattr(recorder, "output_path", None) + timestamp_path = getattr(recorder, "timestamp_json_path", None) + + if video_path is not None: + video_files[str(cam_id)] = Path(video_path) + + if timestamp_path is not None: + timestamp_json_files[str(cam_id)] = Path(timestamp_path) + + return { + "run_dir": run_dir, + "session_dir": session_dir, + "video_files": video_files, + "timestamp_json_files": timestamp_json_files, + } + + def get_recording_file_context(self) -> dict: + """Return current or last recording file context. + + This is used by optional custom processors to save compatibility sidecars + next to the videos after RecordingManager.stop_all() has finalized them. + """ + with self._lock: + if self._recorders: + context = self._build_recording_file_context_unlocked() + self._last_recording_file_context = context + return dict(context) + + if self._last_recording_file_context is not None: + return dict(self._last_recording_file_context) + + return { + "run_dir": self._run_dir, + "session_dir": self._session_dir, + "video_files": {}, + "timestamp_json_files": {}, + } diff --git a/dlclivegui/services/video_recorder.py b/dlclivegui/services/video_recorder.py index b1831d0..ecb8f4f 100644 --- a/dlclivegui/services/video_recorder.py +++ b/dlclivegui/services/video_recorder.py @@ -130,6 +130,16 @@ def __init__( def is_running(self) -> bool: return self._writer_thread is not None and self._writer_thread.is_alive() + @property + def output_path(self) -> Path: + """Video output path.""" + return self._output + + @property + def timestamp_json_path(self) -> Path: + """Timestamp JSON sidecar path written by _save_timestamps().""" + return self._output.with_suffix("").with_suffix(self._output.suffix + "_timestamps.json") + def start(self) -> None: if WriteGear is None: raise RuntimeError("vidgear is required for video recording. Install it with 'pip install vidgear'.") @@ -581,7 +591,7 @@ def _save_timestamps(self) -> None: logger.info("No timestamps to save") return - timestamp_file = self._output.with_suffix("").with_suffix(self._output.suffix + "_timestamps.json") + timestamp_file = self.timestamp_json_path try: with self._stats_lock: From 56d4c55086c604be07651bf560e8ddf824882572 Mon Sep 17 00:00:00 2001 From: C-Achard Date: Fri, 10 Jul 2026 11:55:06 +0200 Subject: [PATCH 5/7] Add recording lifecycle hooks for processors Wire the main window to notify custom DLC processors when recording starts/stops, with a shared recording context (run dir, filename stem, and file metadata). Refactor processor lookup into a helper and only fall back to generic save() if no stop hook handles persistence. Extend BaseProcessorSocket with recording context/save-path state, start/stop hook methods, and a stop(save=...) option. Update save() to use an explicit path or configured default path, create parent directories, and improve logging for skipped/failed saves. --- dlclivegui/gui/main_window.py | 98 +++++++++++++++++-- dlclivegui/processors/dlc_processor_socket.py | 78 +++++++++++++-- 2 files changed, 162 insertions(+), 14 deletions(-) diff --git a/dlclivegui/gui/main_window.py b/dlclivegui/gui/main_window.py index bcf17e6..c0c83ee 100644 --- a/dlclivegui/gui/main_window.py +++ b/dlclivegui/gui/main_window.py @@ -1789,6 +1789,7 @@ def _start_multi_camera_recording(self) -> None: if run_dir is None: self._show_error("Failed to start recording.") return + self._notify_processor_recording_started(run_dir) self.multi_camera_controller.set_recording_sink(self._rec_manager.write_frame) self.multi_camera_controller.set_recording_frame_do_emit(True) @@ -1830,6 +1831,20 @@ def worker(): daemon=True, ).start() + def _get_dlc_processor_instance(self): + """Return the active custom DLC processor instance, if available.""" + processor = getattr(self._dlc, "_processor", None) + + if processor is not None: + return processor + + # Fallback: if DLCLive owns it internally. + dlc_obj = getattr(self._dlc, "_dlc", None) + if dlc_obj is not None: + return getattr(dlc_obj, "processor", None) + + return None + def _save_processor_data_if_available(self) -> None: """Best-effort generic processor save. @@ -1841,12 +1856,7 @@ def _save_processor_data_if_available(self) -> None: Return values are only logged; failure should not crash the GUI. """ - processor = getattr(self._dlc, "_processor", None) - - # Fallback in case DLCLive owns the processor but _processor was not updated. - if processor is None: - dlc_obj = getattr(self._dlc, "_dlc", None) - processor = getattr(dlc_obj, "processor", None) if dlc_obj is not None else None + processor = self._get_dlc_processor_instance() if processor is None: logger.debug("Processor save skipped: no processor instance available.") @@ -1863,8 +1873,82 @@ def _save_processor_data_if_available(self) -> None: except Exception: logger.exception("Processor save() failed.") + def _notify_processor_recording_started(self, run_dir) -> None: + processor = self._get_dlc_processor_instance() + if processor is None: + return + + hook = getattr(processor, "on_recording_started", None) + if not callable(hook): + return + + try: + context = self._build_processor_recording_context(run_dir) + hook(context) + logger.info("Notified processor recording started: %s", context) + except Exception: + logger.exception("Processor on_recording_started hook failed") + + from pathlib import Path + + def _build_processor_recording_context(self, run_dir) -> dict: + run_dir = Path(run_dir) if run_dir is not None else None + + file_context = {} + try: + file_context = self._rec_manager.get_recording_file_context() + except Exception: + logger.exception("Failed to get recording file context from RecordingManager") + file_context = {} + + if run_dir is None: + run_dir = file_context.get("run_dir", None) + if run_dir is not None: + run_dir = Path(run_dir) + + session_name = "" + if hasattr(self, "session_name_edit"): + session_name = self.session_name_edit.text().strip() + + filename = "" + if hasattr(self, "filename_edit"): + filename = self.filename_edit.text().strip() + + filename_stem = Path(filename or session_name or "recording").stem + + ctx = { + "run_dir": run_dir, + "session_name": session_name, + "filename": filename, + "filename_stem": filename_stem, + "processor_base_path": run_dir / filename_stem if run_dir is not None else None, + } + ctx.update(file_context) + return ctx + + def _notify_processor_recording_stopped(self) -> None: + processor = self._get_dlc_processor_instance() + if processor is None: + return False + + hook = getattr(processor, "on_recording_stopped", None) + if not callable(hook): + return False + + try: + run_dir = getattr(self._rec_manager, "run_dir", None) + context = self._build_processor_recording_context(run_dir) + hook(context) + logger.info("Notified processor recording stopped") + return True + except Exception: + logger.exception("Processor on_recording_stopped hook failed") + return False + def _on_recording_stopped_async(self) -> None: - self._save_processor_data_if_available() + handled_by_stop_hook = self._notify_processor_recording_stopped() + if not handled_by_stop_hook: + self._save_processor_data_if_available() self._recording_stopping = False self.start_record_button.setEnabled(True) diff --git a/dlclivegui/processors/dlc_processor_socket.py b/dlclivegui/processors/dlc_processor_socket.py index f165512..f232790 100644 --- a/dlclivegui/processors/dlc_processor_socket.py +++ b/dlclivegui/processors/dlc_processor_socket.py @@ -86,6 +86,8 @@ def __init__( self._vid_recording = Event() self.curr_step = 0 self.save_original = save_original + self.recording_context = {} + self.save_path = None # Networking (optional) self.address = bind @@ -265,8 +267,14 @@ def stop_recording(self): # STOP / SHUTDOWN # -------------------------------------------------------------------------------------- - def stop(self): + def stop(self, save: bool = False, file=None): """Gracefully stop listener and clients.""" + if save: + try: + self.save(file) + except Exception: + logger.exception("Failed to save processor data on stop") + if self._stop.is_set(): return @@ -370,23 +378,79 @@ def _clear_data_queues(self): self.original_pose.clear() def save(self, file=None): - if not file: + target = file + + if target is None: + target = getattr(self, "save_path", None) + + if target is None: + logger.warning("Processor save skipped: no file or save_path provided.") return 0 + try: save_dict = self.get_data() - path2save = Path(__file__).parent.parent.parent / "data" / file - path2save.parent.mkdir(parents=True, exist_ok=True) + save_path = Path(target) + save_path.parent.mkdir(parents=True, exist_ok=True) + if self.save_original: original_pose = save_dict.pop("original_pose") - self.save_original_pose(original_pose, save_dict["frame_time"], save_dict["time_stamp"], path2save) - with open(path2save, "wb") as f: + self.save_original_pose( + original_pose, + save_dict["frame_time"], + save_dict["time_stamp"], + save_path, + ) + + with open(save_path, "wb") as f: pickle.dump(save_dict, f) - logger.info(f"Saved data to {path2save}") + + logger.info(f"Saved processor data to {save_path}") return 1 + except Exception as e: logger.error(f"Save failed: {e}") return -1 + def set_recording_context(self, context: dict | None) -> None: + """Set GUI-provided recording context. + + This is intentionally generic. Custom processors may use it to derive + processor-specific output paths. + + Expected keys may include: + run_dir + session_name + filename + filename_stem + processor_base_path + video_files + timestamp_files + """ + self.recording_context = dict(context or {}) + + base_path = self.recording_context.get("processor_base_path") + if base_path is not None: + self.save_path = Path(base_path) + + def set_save_path(self, path) -> None: + """Set default save path used by save() when no file is provided.""" + self.save_path = Path(path) if path is not None else None + + def get_save_path(self): + """Return default save path, if any.""" + return getattr(self, "save_path", None) + + def on_recording_started(self, context: dict) -> None: + """Optional hook called by GUI when recording starts.""" + self.set_recording_context(context) + + def on_recording_stopped(self, context: dict) -> None: + """Optional hook called by GUI when recording stops. + + Base implementation only updates context. Custom processors can override. + """ + self.set_recording_context(context) + def save_original_pose( self, original_pose: np.ndarray, From e1f39c69a893ca4877a88d94d48b9cb5f7050efe Mon Sep 17 00:00:00 2001 From: C-Achard Date: Fri, 10 Jul 2026 11:56:03 +0200 Subject: [PATCH 6/7] Add processor recording-context tests Expand custom processor test coverage around recording context and save-path behavior. This adds a new test module for BaseProcessorSocket and DLCLiveMainWindow recording hook interactions, including optional hook handling and processor lookup paths. The existing base processor tests were also cleaned up to use pytest `tmp_path` for file outputs instead of writing into module data directories, remove manual cleanup blocks, and tighten assertions/formatting for save and recording flows. --- .../custom_processors/test_base_processor.py | 190 +++++------- .../test_processor_rec_context.py | 292 ++++++++++++++++++ 2 files changed, 361 insertions(+), 121 deletions(-) create mode 100644 tests/custom_processors/test_processor_rec_context.py diff --git a/tests/custom_processors/test_base_processor.py b/tests/custom_processors/test_base_processor.py index 94dabab..a422c8f 100644 --- a/tests/custom_processors/test_base_processor.py +++ b/tests/custom_processors/test_base_processor.py @@ -1,11 +1,10 @@ -# tests/processors/test_dlc_processor_socket.py +# tests/custom_processors/test_base_processor.py from __future__ import annotations import importlib import pickle import sys import types -from pathlib import Path import numpy as np import pandas as pd @@ -56,11 +55,6 @@ def example_processor_mod(monkeypatch): return importlib.import_module(mod_name) -def _module_data_dir(socket_mod) -> Path: - """Compute the data/ directory where save() writes artifacts.""" - return Path(socket_mod.__file__).parent.parent.parent / "data" - - def _mk_bodyparts(n: int) -> list[str]: return [f"bp{i}" for i in range(n)] @@ -71,7 +65,6 @@ def _mk_pose(n_keypoints: int = 5) -> np.ndarray: Base class does not interpret pose content—only broadcasts/logs it. """ pose = np.zeros((n_keypoints, 3), dtype=float) - # Fill with simple coordinates & confidence for i in range(n_keypoints): pose[i, :] = [10.0 + i, 20.0 + i, 0.9] return pose @@ -83,25 +76,25 @@ def test_base_init_and_stop(socket_mod): and ensure stop() is idempotent. """ BaseProcessorSocket = socket_mod.BaseProcessorSocket - proc = BaseProcessorSocket(bind=("127.0.0.1", 0), use_perf_counter=True, save_original=False) + proc = BaseProcessorSocket( + bind=("127.0.0.1", 0), + use_perf_counter=True, + save_original=False, + ) try: - # Core attributes exist assert hasattr(proc, "listener") assert callable(proc.timing_func) - # perf_counter chosen + import time as _t assert proc.timing_func is _t.perf_counter - # Initial flags & counters assert proc.recording is False assert proc.video_recording is False assert proc.curr_step == 0 assert isinstance(proc.conns, set) finally: - # stop must be safe and idempotent proc.stop() - proc.stop() # second call should be a no-op def test_base_recording_flags_and_session_name(socket_mod): @@ -111,23 +104,19 @@ def test_base_recording_flags_and_session_name(socket_mod): BaseProcessorSocket = socket_mod.BaseProcessorSocket proc = BaseProcessorSocket(bind=("127.0.0.1", 0)) try: - # Start recording proc._handle_client_message({"cmd": "start_recording"}) assert proc.recording is True assert proc.video_recording is True - assert proc.curr_step == 0 # reset + assert proc.curr_step == 0 - # Set a session name proc._handle_client_message({"cmd": "set_session_name", "session_name": "unit_test"}) assert proc.session_name == "unit_test" assert proc.filename == "unit_test_dlc_processor_data.pkl" - # Stop recording proc._handle_client_message({"cmd": "stop_recording"}) assert proc.recording is False assert proc.video_recording is False - # Unknown / invalid messages must not crash proc._handle_client_message(None) proc._handle_client_message({"cmd": "does_not_exist"}) finally: @@ -139,27 +128,27 @@ def test_base_process_without_and_with_recording(socket_mod): BaseProcessorSocket.process() should: - increment curr_step always, - when recording, append time/step/frame_time/pose_time, - - when save_original=True, store copies of pose arrays only while recording. + - when save_original=True, store copies of pose arrays only while recording. """ BaseProcessorSocket = socket_mod.BaseProcessorSocket proc = BaseProcessorSocket(bind=("127.0.0.1", 0), save_original=True) + try: pose = _mk_pose() - # Not recording yet: curr_step increments, no logs appended before_step = proc.curr_step ret = proc.process(pose, frame_time=0.012, pose_time=0.013) + assert ret is pose assert proc.curr_step == before_step + 1 assert len(proc.time_stamp) == 0 assert len(proc.step) == 0 assert len(proc.frame_time) == 0 assert len(proc.pose_time) == 0 - # Raw poses must stay aligned with recorded metadata. + assert proc.original_pose is not None assert len(proc.original_pose) == 0 - # Start recording and push two frames proc._handle_client_message({"cmd": "start_recording"}) for _ in range(2): proc.process(pose, frame_time=0.01, pose_time=0.011) @@ -169,25 +158,23 @@ def test_base_process_without_and_with_recording(socket_mod): assert len(proc.frame_time) == 2 assert len(proc.pose_time) == 2 assert len(proc.original_pose) == 2 + np.testing.assert_allclose(proc.original_pose[0], pose) np.testing.assert_allclose(proc.original_pose[1], pose) - # Data snapshot integrity data = proc.get_data() assert "start_time" in data assert isinstance(data["time_stamp"], np.ndarray) assert isinstance(data["step"], np.ndarray) assert isinstance(data["frame_time"], np.ndarray) - # pose_time can be None if never provided; here it is provided. assert isinstance(data["pose_time"], np.ndarray) - # original_pose is included when save_original=True assert isinstance(data["original_pose"], np.ndarray) finally: proc.stop() -def test_save_ignores_pre_recording_original_pose_frames(socket_mod): +def test_save_ignores_pre_recording_original_pose_frames(socket_mod, tmp_path): """ save_original data must stay aligned with recorded metadata even if process() is called before recording starts. @@ -213,18 +200,16 @@ def test_save_ignores_pre_recording_original_pose_frames(socket_mod): proc.process(pose, frame_time=0.01, pose_time=0.02) proc._handle_client_message({"cmd": "stop_recording"}) - filename = "unit_test_pre_recording_frames.pkl" - ret = proc.save(filename) - assert ret == 1 + pkl_path = tmp_path / "unit_test_pre_recording_frames.pkl" + h5_path = tmp_path / "unit_test_pre_recording_frames_DLC.hdf5" - data_dir = _module_data_dir(socket_mod) - pkl_path = data_dir / filename - h5_path = data_dir / (Path(filename).stem + "_DLC.hdf5") + ret = proc.save(pkl_path) + assert ret == 1 assert pkl_path.exists() assert h5_path.exists() - with open(pkl_path, "rb") as f: + with pkl_path.open("rb") as f: payload = pickle.load(f) assert len(payload["frame_time"]) == 2 @@ -238,11 +223,6 @@ def test_save_ignores_pre_recording_original_pose_frames(socket_mod): finally: proc.stop() - try: - pkl_path.unlink(missing_ok=True) - h5_path.unlink(missing_ok=True) - except Exception: - pass @pytest.mark.parametrize( @@ -253,7 +233,11 @@ def test_save_ignores_pre_recording_original_pose_frames(socket_mod): ], ) def test_subclass_save_ignores_pre_recording_original_pose_frames( - socket_mod, example_processor_mod, class_name, n_keypoints + socket_mod, + example_processor_mod, + class_name, + n_keypoints, + tmp_path, ): """ Concrete processors must keep original_pose aligned with recorded metadata @@ -279,18 +263,16 @@ def test_subclass_save_ignores_pre_recording_original_pose_frames( proc.process(pose, frame_time=0.01, pose_time=0.02) proc._handle_client_message({"cmd": "stop_recording"}) - filename = f"unit_test_{class_name}.pkl" - ret = proc.save(filename) - assert ret == 1 + pkl_path = tmp_path / f"unit_test_{class_name}.pkl" + h5_path = tmp_path / f"unit_test_{class_name}_DLC.hdf5" - data_dir = _module_data_dir(socket_mod) - pkl_path = data_dir / filename - h5_path = data_dir / (Path(filename).stem + "_DLC.hdf5") + ret = proc.save(pkl_path) + assert ret == 1 assert pkl_path.exists() assert h5_path.exists() - with open(pkl_path, "rb") as f: + with pkl_path.open("rb") as f: payload = pickle.load(f) assert len(payload["frame_time"]) == 3 @@ -304,11 +286,6 @@ def test_subclass_save_ignores_pre_recording_original_pose_frames( finally: proc.stop() - try: - pkl_path.unlink(missing_ok=True) - h5_path.unlink(missing_ok=True) - except Exception: - pass def test_base_broadcast_handles_bad_connections(socket_mod): @@ -319,7 +296,6 @@ def test_base_broadcast_handles_bad_connections(socket_mod): class BadConn: def __init__(self): - # Minimal attributes to satisfy _close_conn class Sock: def shutdown(self, *_args, **_kwargs): raise RuntimeError("shutdown fail") @@ -333,7 +309,6 @@ def close(self): raise RuntimeError("close fail") def __hash__(self): - # allow put in a set return id(self) def __eq__(self, other): @@ -341,18 +316,20 @@ def __eq__(self, other): BaseProcessorSocket = socket_mod.BaseProcessorSocket proc = BaseProcessorSocket(bind=("127.0.0.1", 0)) + try: bad = BadConn() proc.conns.add(bad) - # Should not raise + proc.broadcast(["ts", "payload"]) - # bad conn should be discarded + assert bad not in proc.conns + finally: proc.stop() -def test_save_writes_pkl_and_hdf5_with_labels(socket_mod, caplog): +def test_save_writes_pkl_and_hdf5_with_labels(socket_mod, tmp_path, caplog): """ End-to-end save() with save_original=True and a matching dlc_cfg bodypart list. Verifies: @@ -369,56 +346,47 @@ def test_save_writes_pkl_and_hdf5_with_labels(socket_mod, caplog): dlc_cfg = {"metadata": {"bodyparts": bodyparts}} proc.set_dlc_cfg(dlc_cfg) - # create 3 frames pose = _mk_pose(n_keypoints=n_keypoints) + proc._handle_client_message({"cmd": "start_recording"}) for _ in range(3): proc.process(pose, frame_time=0.01, pose_time=0.011) proc._handle_client_message({"cmd": "stop_recording"}) - # deterministic relative filename - filename = "unit_test_session.pkl" - ret = proc.save(filename) - assert ret == 1 + pkl_path = tmp_path / "unit_test_session.pkl" + h5_path = tmp_path / "unit_test_session_DLC.hdf5" - data_dir = _module_data_dir(socket_mod) - pkl_path = data_dir / filename - h5_path = data_dir / (Path(filename).stem + "_DLC.hdf5") + ret = proc.save(pkl_path) + assert ret == 1 assert pkl_path.exists(), f"Missing {pkl_path}" assert h5_path.exists(), f"Missing {h5_path}" - # verify pkl payload - with open(pkl_path, "rb") as f: + with pkl_path.open("rb") as f: payload = pickle.load(f) - assert "original_pose" not in payload # popped out before pickling + assert "original_pose" not in payload assert "dlc_cfg" in payload assert payload["dlc_cfg"] == dlc_cfg - # verify HDF5 contents (skip if tables is not installed) pytest.importorskip("tables") df = pd.read_hdf(h5_path, key="df_with_missing") - # Expect rows == frames + assert df.shape[0] == 3 - # Confirm the labeled columns exist for all bodyparts x (x, y, likelihood) expected_cols = pd.MultiIndex.from_product( [bodyparts, ["x", "y", "likelihood"]], names=["bodyparts", "coords"], ) - # Some pandas versions will allow mixing multiindex + string cols; - # so just check presence of expected label tuples: + for col in expected_cols: assert col in df.columns - # frame_time & pose_time columns are present assert "frame_time" in df.columns assert "pose_time" in df.columns assert list(df["frame_time"]) == [0.01, 0.01, 0.01] assert list(df["pose_time"]) == list(payload["time_stamp"]) - # sanity check values for first row for i, bp in enumerate(bodyparts): assert np.isclose(df[(bp, "x")].iloc[0], 10.0 + i) assert np.isclose(df[(bp, "y")].iloc[0], 20.0 + i) @@ -426,61 +394,45 @@ def test_save_writes_pkl_and_hdf5_with_labels(socket_mod, caplog): finally: proc.stop() - # cleanup - try: - pkl_path.unlink(missing_ok=True) - h5_path.unlink(missing_ok=True) - except Exception: - pass -def test_save_without_dlc_cfg_unlabeled_columns(socket_mod, caplog): +def test_save_without_dlc_cfg_unlabeled_columns(socket_mod, tmp_path, caplog): """ Ensure that without dlc_cfg, save() still writes HDF5 with unlabeled columns - and logs a warning (no crash). + and logs a warning or at least succeeds without crashing. """ BaseProcessorSocket = socket_mod.BaseProcessorSocket proc = BaseProcessorSocket(bind=("127.0.0.1", 0), save_original=True) try: pose = _mk_pose(3) + proc._handle_client_message({"cmd": "start_recording"}) proc.process(pose, frame_time=0.01, pose_time=0.02) proc._handle_client_message({"cmd": "stop_recording"}) - filename = "unit_test_no_dlc_cfg.pkl" - ret = proc.save(filename) - assert ret == 1 + pkl_path = tmp_path / "unit_test_no_dlc_cfg.pkl" + h5_path = tmp_path / "unit_test_no_dlc_cfg_DLC.hdf5" - data_dir = _module_data_dir(socket_mod) - pkl_path = data_dir / filename - h5_path = data_dir / (Path(filename).stem + "_DLC.hdf5") + ret = proc.save(pkl_path) + assert ret == 1 assert pkl_path.exists() assert h5_path.exists() - # Check warning logged - # (Depending on logger config in tests, you may need to set level to capture warnings) + # Depending on logger config in tests, caplog may or may not catch this. [rec for rec in caplog.records if "saving without column labels" in rec.message] - # It's okay if caplog didn't catch it due to logger level; we mainly ensure no crash and files exist. - # Verify HDF5 loads (skip if tables not installed) pytest.importorskip("tables") df = pd.read_hdf(h5_path, key="df_with_missing") - assert df.shape[0] == 1 # 1 frame saved - # Expect unlabeled numeric columns for pose plus "frame_time" and "pose_time" - # We can't rely on a MultiIndex here; just ensure numeric columns exist + + assert df.shape[0] == 1 + numeric_cols = [c for c in df.columns if c not in ("frame_time", "pose_time")] - assert len(numeric_cols) == 3 * 3 # 3 keypoints * 3 coords + assert len(numeric_cols) == 3 * 3 finally: proc.stop() - # cleanup - try: - pkl_path.unlink(missing_ok=True) - h5_path.unlink(missing_ok=True) - except Exception: - pass def test_get_data_includes_dlc_cfg(socket_mod): @@ -489,38 +441,34 @@ def test_get_data_includes_dlc_cfg(socket_mod): """ BaseProcessorSocket = socket_mod.BaseProcessorSocket proc = BaseProcessorSocket(bind=("127.0.0.1", 0), save_original=False) + try: dlc_cfg = {"metadata": {"bodyparts": ["a", "b"]}} proc.set_dlc_cfg(dlc_cfg) + data = proc.get_data() + assert "dlc_cfg" in data assert data["dlc_cfg"] == dlc_cfg + finally: proc.stop() -def test_save_handles_empty_original_pose(socket_mod): +def test_save_handles_empty_original_pose(socket_mod, tmp_path): """ - With save_original=True but no process() calls, save() should not crash. - Depending on pandas behavior, HDF5 should exist with 0 rows or be created successfully. + With save_original=True but no process() calls, save() should not raise. + The exact return value is implementation-dependent for empty data. """ BaseProcessorSocket = socket_mod.BaseProcessorSocket proc = BaseProcessorSocket(bind=("127.0.0.1", 0), save_original=True) + try: - filename = "unit_test_empty_original.pkl" - ret = proc.save(filename) - # If nothing to save, your implementation returns 1 (saved) or could be 0; current code returns 1 - assert ret in (1, 0, -1) # accept current behavior; adjust if you standardize - data_dir = _module_data_dir(socket_mod) - pkl_path = data_dir / filename - h5_path = data_dir / (Path(filename).stem + "_DLC.hdf5") - # pkl exists if ret == 1; hdf5 may or may not depending on your final logic - # Leave assertions lenient; the main check is that no exception bubbles up. + pkl_path = tmp_path / "unit_test_empty_original.pkl" + + ret = proc.save(pkl_path) + + assert ret in (1, 0, -1) + finally: proc.stop() - # cleanup - try: - pkl_path.unlink(missing_ok=True) - h5_path.unlink(missing_ok=True) - except Exception: - pass diff --git a/tests/custom_processors/test_processor_rec_context.py b/tests/custom_processors/test_processor_rec_context.py new file mode 100644 index 0000000..16a1461 --- /dev/null +++ b/tests/custom_processors/test_processor_rec_context.py @@ -0,0 +1,292 @@ +# tests/processors/test_processor_recording_context.py +from __future__ import annotations + +import importlib +import pickle +import sys +import types +from types import SimpleNamespace + +import pytest + +# ----------------------------------------------------------------------------- +# Shared fixtures +# ----------------------------------------------------------------------------- + + +def _mock_dlclive(monkeypatch): + """Install a tiny dlclive.processor.Processor mock before importing processors.""" + + class Processor: + def __init__(self, *args, **kwargs): + pass + + def process(self, pose, **kwargs): + return pose + + dlclive_mod = types.ModuleType("dlclive") + processor_mod = types.ModuleType("dlclive.processor") + + dlclive_mod.Processor = Processor + processor_mod.Processor = Processor + + monkeypatch.setitem(sys.modules, "dlclive", dlclive_mod) + monkeypatch.setitem(sys.modules, "dlclive.processor", processor_mod) + + +@pytest.fixture +def socket_mod(monkeypatch): + """Import dlclivegui.processors.dlc_processor_socket with dlclive mocked.""" + _mock_dlclive(monkeypatch) + mod_name = "dlclivegui.processors.dlc_processor_socket" + sys.modules.pop(mod_name, None) + return importlib.import_module(mod_name) + + +class DummyLineEdit: + def __init__(self, value: str): + self._value = value + + def text(self) -> str: + return self._value + + +class HookProcessor: + def __init__(self): + self.started_contexts = [] + self.stopped_contexts = [] + self.save_calls = 0 + + def on_recording_started(self, context): + self.started_contexts.append(context) + + def on_recording_stopped(self, context): + self.stopped_contexts.append(context) + + def save(self): + self.save_calls += 1 + return 1 + + +class SaveOnlyProcessor: + def __init__(self): + self.save_calls = 0 + + def save(self): + self.save_calls += 1 + return 1 + + +# ----------------------------------------------------------------------------- +# BaseProcessorSocket generic recording-context API +# ----------------------------------------------------------------------------- + + +def test_base_processor_recording_context_sets_save_path(socket_mod, tmp_path): + BaseProcessorSocket = socket_mod.BaseProcessorSocket + proc = BaseProcessorSocket(bind=("127.0.0.1", 0)) + + try: + base_path = tmp_path / "MouseA_2026-07-10_1" + context = { + "run_dir": tmp_path, + "session_name": "MouseA", + "filename": "MouseA_2026-07-10_1.avi", + "filename_stem": "MouseA_2026-07-10_1", + "processor_base_path": base_path, + } + + proc.set_recording_context(context) + + assert proc.recording_context == context + assert proc.get_save_path() == base_path + assert proc.save_path == base_path + + finally: + proc.stop() + + +def test_base_processor_recording_hooks_update_context(socket_mod, tmp_path): + BaseProcessorSocket = socket_mod.BaseProcessorSocket + proc = BaseProcessorSocket(bind=("127.0.0.1", 0)) + + try: + started_context = {"processor_base_path": tmp_path / "started"} + stopped_context = {"processor_base_path": tmp_path / "stopped"} + + proc.on_recording_started(started_context) + assert proc.recording_context == started_context + assert proc.get_save_path() == tmp_path / "started" + + proc.on_recording_stopped(stopped_context) + assert proc.recording_context == stopped_context + assert proc.get_save_path() == tmp_path / "stopped" + + finally: + proc.stop() + + +def test_base_processor_save_uses_save_path_when_file_is_none(socket_mod, tmp_path): + BaseProcessorSocket = socket_mod.BaseProcessorSocket + proc = BaseProcessorSocket(bind=("127.0.0.1", 0), save_original=False) + + try: + save_path = tmp_path / "legacy" / "MouseA_2026-07-10_1_PROC" + proc.set_save_path(save_path) + + proc.start_recording() + proc.process([[1.0, 2.0, 0.9]], frame_time=12.34, pose_time=12.35) + proc.stop_recording() + + ret = proc.save() + assert ret == 1 + assert save_path.exists() + + with save_path.open("rb") as f: + payload = pickle.load(f) + + assert "time_stamp" in payload + assert "step" in payload + assert "frame_time" in payload + assert len(payload["frame_time"]) == 1 + + finally: + proc.stop() + + +def test_base_processor_save_writes_to_explicit_absolute_file(socket_mod, tmp_path): + BaseProcessorSocket = socket_mod.BaseProcessorSocket + proc = BaseProcessorSocket(bind=("127.0.0.1", 0), save_original=False) + + try: + explicit_path = tmp_path / "explicit" / "processor_data.pkl" + ret = proc.save(explicit_path) + + assert ret == 1 + assert explicit_path.exists() + + with explicit_path.open("rb") as f: + payload = pickle.load(f) + + assert "start_time" in payload + + finally: + proc.stop() + + +def test_base_processor_save_without_file_or_save_path_returns_zero(socket_mod): + BaseProcessorSocket = socket_mod.BaseProcessorSocket + proc = BaseProcessorSocket(bind=("127.0.0.1", 0), save_original=False) + + try: + proc.save_path = None + assert proc.save() == 0 + finally: + proc.stop() + + +def test_base_processor_stop_save_true_uses_save_path(socket_mod, tmp_path): + BaseProcessorSocket = socket_mod.BaseProcessorSocket + proc = BaseProcessorSocket(bind=("127.0.0.1", 0), save_original=False) + + save_path = tmp_path / "processor_on_stop.pkl" + proc.set_save_path(save_path) + + # stop(save=True) should save, close listener, and be idempotent. + proc.stop(save=True) + assert save_path.exists() + assert proc.listener is None + + proc.stop(save=True) # no crash + + +# ----------------------------------------------------------------------------- +# DLCLiveMainWindow recording-context helpers +# ----------------------------------------------------------------------------- + + +@pytest.fixture +def main_window_cls(): + pytest.importorskip("PySide6") + mod = importlib.import_module("dlclivegui.gui.main_window") + return mod.DLCLiveMainWindow + + +def make_window_shell(main_window_cls, processor=None, run_dir=None): + """Create a DLCLiveMainWindow shell without running QMainWindow.__init__.""" + win = main_window_cls.__new__(main_window_cls) + win._dlc = SimpleNamespace(_processor=processor, _dlc=None) + win._rec_manager = SimpleNamespace(run_dir=run_dir) + win.session_name_edit = DummyLineEdit("MouseA") + win.filename_edit = DummyLineEdit("MouseA_2026-07-10_1.avi") + return win + + +def test_main_window_build_processor_recording_context(main_window_cls, tmp_path): + win = make_window_shell(main_window_cls, run_dir=tmp_path) + + context = win._build_processor_recording_context(tmp_path) + + assert context["run_dir"] == tmp_path + assert context["session_name"] == "MouseA" + assert context["filename"] == "MouseA_2026-07-10_1.avi" + assert context["filename_stem"] == "MouseA_2026-07-10_1" + assert context["processor_base_path"] == tmp_path / "MouseA_2026-07-10_1" + + +def test_main_window_get_processor_instance_prefers_direct_processor(main_window_cls): + processor = HookProcessor() + win = make_window_shell(main_window_cls, processor=processor) + + assert win._get_dlc_processor_instance() is processor + + +def test_main_window_get_processor_instance_falls_back_to_dlclive_processor(main_window_cls): + processor = HookProcessor() + win = main_window_cls.__new__(main_window_cls) + win._dlc = SimpleNamespace(_processor=None, _dlc=SimpleNamespace(processor=processor)) + + assert win._get_dlc_processor_instance() is processor + + +def test_main_window_notify_processor_recording_started_calls_hook(main_window_cls, tmp_path): + processor = HookProcessor() + win = make_window_shell(main_window_cls, processor=processor, run_dir=tmp_path) + + win._notify_processor_recording_started(tmp_path) + + assert len(processor.started_contexts) == 1 + context = processor.started_contexts[0] + assert context["processor_base_path"] == tmp_path / "MouseA_2026-07-10_1" + + +def test_main_window_notify_processor_recording_stopped_calls_hook(main_window_cls, tmp_path): + processor = HookProcessor() + win = make_window_shell(main_window_cls, processor=processor, run_dir=tmp_path) + + win._notify_processor_recording_stopped() + + assert len(processor.stopped_contexts) == 1 + context = processor.stopped_contexts[0] + assert context["processor_base_path"] == tmp_path / "MouseA_2026-07-10_1" + + +def test_main_window_save_processor_data_calls_save(main_window_cls, tmp_path): + processor = SaveOnlyProcessor() + win = make_window_shell(main_window_cls, processor=processor, run_dir=tmp_path) + + win._save_processor_data_if_available() + + assert processor.save_calls == 1 + + +def test_main_window_processor_hooks_are_optional(main_window_cls, tmp_path): + class NoHooks: + pass + + win = make_window_shell(main_window_cls, processor=NoHooks(), run_dir=tmp_path) + + # Optional hooks/save absence should not crash. + win._notify_processor_recording_started(tmp_path) + win._notify_processor_recording_stopped() + win._save_processor_data_if_available() From 137010e632592c189acfa84d50cace7dfa97123e Mon Sep 17 00:00:00 2001 From: C-Achard Date: Fri, 10 Jul 2026 16:09:27 +0200 Subject: [PATCH 7/7] Remove custom proc testing folder --- .../custom/mock_socket_processor.py | 532 -- .../custom/mock_unity_socket_client.ipynb | 4609 ----------------- 2 files changed, 5141 deletions(-) delete mode 100644 dlclivegui/processors/custom/mock_socket_processor.py delete mode 100644 dlclivegui/processors/custom/mock_unity_socket_client.ipynb diff --git a/dlclivegui/processors/custom/mock_socket_processor.py b/dlclivegui/processors/custom/mock_socket_processor.py deleted file mode 100644 index 788bde3..0000000 --- a/dlclivegui/processors/custom/mock_socket_processor.py +++ /dev/null @@ -1,532 +0,0 @@ -"""Standalone mock DLC processor plugin with socket listener support. - -This fixture intentionally avoids importing dlclive, dlclivegui, Teensy, serial, -NumPy, or project-specific processor base classes. - -It is designed to test two separate concerns without mixing them: - -1. GUI processor plugin discovery/configuration - - Exposes PROCESSOR_* metadata. - - Exposes PROCESSOR_BUILD_IN_WORKER = True. - - Exposes get_available_processors(), which your loader can consume without - requiring this class to inherit from dlclive.processor.Processor. - -2. Runtime socket listener behavior - - Starts a multiprocessing.connection.Listener. - - Accepts one or more clients on a background thread. - - Receives simple command dictionaries from clients. - - Broadcasts mock pose payloads to connected clients from process(). - -It does NOT mock Teensy serial acquisition. For the listener send/receive tests, -Teensy is not required: the Teensy path is a separate serial-reader concern. -""" - -from __future__ import annotations - -import logging -import pickle -import sys -import time -from collections import deque -from multiprocessing.connection import Client, Listener -from pathlib import Path -from threading import Event, Lock, Thread -from typing import Any - -logger = logging.getLogger(__name__) - -IP_ADDRESS = "127.0.0.1" -PORT = 6000 - - -class MockSocketProcessor: - """Standalone socket-based mock processor for tests. - - This intentionally reimplements the core listener/thread/control behavior - instead of inheriting from project classes. It is suitable for fixture usage - where external dependencies such as Teensy, serial, dlclive, or dlclivegui - should not be imported. - - Expected test usage: - - proc = MockSocketProcessor(bind=("127.0.0.1", free_port())) - conn = Client(proc.address, authkey=proc.authkey) - conn.send({"cmd": "ping"}) - assert conn.recv()["type"] == "pong" - proc.process([[1, 2, 0.9]]) - assert conn.recv()["type"] == "pose" - proc.stop() - - Notes: - - `process()` accepts any pose-like Python object. It does not validate - DLC shape because this mock is for socket lifecycle tests, not pose - validation tests. - - Payloads are sent through multiprocessing.connection, matching the - style used by the legacy socket processors. - """ - - PROCESSOR_NAME = "Mock Socket Processor" - PROCESSOR_DESCRIPTION = "Standalone mock socket processor without Teensy or DLCLive imports." - PROCESSOR_BUILD_IN_WORKER = False - PROCESSOR_PARAMS = { - "bind": { - "type": "tuple", - "default": (IP_ADDRESS, PORT), - "description": "Server bind address. Use port 0 to request an ephemeral port.", - }, - "authkey": { - "type": "bytes", - "default": b"secret password", - "description": "Authentication key for multiprocessing.connection clients.", - }, - "start_server": { - "type": "bool", - "default": True, - "description": "Whether to start the listener in __init__.", - }, - "socket_timeout": { - "type": "float", - "default": 0.05, - "description": "Accept-loop timeout in seconds.", - }, - "save_original": { - "type": "bool", - "default": False, - "description": "Whether to store raw pose payloads while recording.", - }, - } - - def __init__( - self, - bind: tuple[str, int] = (IP_ADDRESS, PORT), - authkey: bytes = b"secret password", - *, - start_server: bool = True, - socket_timeout: float = 0.05, - save_original: bool = False, - ) -> None: - self.address = bind - self.authkey = authkey - self._socket_timeout = float(socket_timeout) - self.save_original = bool(save_original) - - # Runtime listener/client state. - self.listener: Listener | None = None - self.conns: set[Any] = set() - self._conns_lock = Lock() - self._stop = Event() - self._accept_thread: Thread | None = None - self._rx_threads: set[Thread] = set() - - # Recording/control state compatible with socket-processor expectations. - self._recording = Event() - self._vid_recording = Event() - self._session_name = "test_session" - self.filename: str | None = None - - # Minimal data buffers for save/get_data tests. - self.start_time = time.time() - self.time_stamp = deque() - self.step = deque() - self.frame_time = deque() - self.pose_time = deque() - self.original_pose = deque() if self.save_original else None - self.received_commands = deque() - self.broadcast_count = 0 - self.curr_step = 0 - - if start_server: - self.start_server(bind, authkey=authkey, timeout=self._socket_timeout) - - # ------------------------------------------------------------------ - # Properties matching the real socket processors - # ------------------------------------------------------------------ - @property - def recording(self) -> bool: - return self._recording.is_set() - - @property - def video_recording(self) -> bool: - return self._vid_recording.is_set() - - @property - def session_name(self) -> str: - return self._session_name - - @session_name.setter - def session_name(self, name: str) -> None: - self._session_name = str(name) - self.filename = f"{self._session_name}_mock_processor_data.pkl" - - # ------------------------------------------------------------------ - # Listener lifecycle - # ------------------------------------------------------------------ - def start_server( - self, - bind: tuple[str, int] | None = None, - authkey: bytes | None = None, - *, - timeout: float | None = None, - ) -> None: - """Start the socket listener if it is not already running.""" - if self.listener is not None: - return - - if bind is not None: - self.address = bind - if authkey is not None: - self.authkey = authkey - if timeout is not None: - self._socket_timeout = float(timeout) - - self._stop.clear() - self.listener = Listener(self.address, authkey=self.authkey) - - # If bind used port 0, update address to the actual ephemeral port. - self.address = self._actual_listener_address(self.listener, fallback=self.address) - - self._set_listener_timeout(self.listener, self._socket_timeout) - - self._accept_thread = Thread(target=self._accept_loop, name="MockSocketProcessorAccept", daemon=True) - self._accept_thread.start() - logger.info("MockSocketProcessor listening on %s:%s", self.address[0], self.address[1]) - - @staticmethod - def _actual_listener_address(listener: Listener, fallback: tuple[str, int]) -> tuple[str, int]: - """Best-effort extraction of the actual listener address.""" - try: - raw = getattr(listener, "_listener", None) - sock = getattr(raw, "_socket", None) - if sock is not None: - addr = sock.getsockname() - return (str(addr[0]), int(addr[1])) - except Exception: - pass - try: - addr = listener.address - return (str(addr[0]), int(addr[1])) - except Exception: - return fallback - - @staticmethod - def _set_listener_timeout(listener: Listener, timeout: float) -> None: - """Set accept timeout on CPython listener internals, best effort.""" - raw = getattr(listener, "_listener", None) - for candidate in (raw, getattr(raw, "_socket", None)): - try: - if candidate is not None and hasattr(candidate, "settimeout"): - candidate.settimeout(timeout) - return - except Exception: - pass - - def _accept_loop(self) -> None: - while not self._stop.is_set(): - try: - if self.listener is None: - return - conn = self.listener.accept() - except TimeoutError: - continue - except (OSError, EOFError): - if self._stop.is_set(): - break - continue - except Exception: - if self._stop.is_set(): - break - logger.exception("Unexpected accept-loop error") - continue - - with self._conns_lock: - self.conns.add(conn) - - rx = Thread(target=self._rx_loop, args=(conn,), name="MockSocketProcessorRx", daemon=True) - self._rx_threads.add(rx) - rx.start() - logger.info("MockSocketProcessor client connected") - - def _rx_loop(self, conn: Any) -> None: - while not self._stop.is_set(): - try: - if conn.poll(0.05): - msg = conn.recv() - self._handle_client_message(msg, conn=conn) - continue - - if getattr(conn, "closed", False): - break - - except (EOFError, OSError, ConnectionError, BrokenPipeError): - break - except Exception: - logger.exception("Unexpected receive-loop error") - break - - self._close_conn(conn) - - def _close_conn(self, conn: Any) -> None: - try: - conn.close() - except Exception: - pass - with self._conns_lock: - self.conns.discard(conn) - - def stop(self) -> None: - """Stop listener, close clients, and join background threads best-effort.""" - if self._stop.is_set(): - return - - self._stop.set() - - # Wake accept() if needed. - try: - Client(self.address, authkey=self.authkey).close() - except Exception: - pass - - with self._conns_lock: - conns = list(self.conns) - for conn in conns: - self._close_conn(conn) - - try: - if self.listener is not None: - self.listener.close() - except Exception: - pass - self.listener = None - - if self._accept_thread is not None: - self._accept_thread.join(timeout=1.0) - self._accept_thread = None - - for thread in list(self._rx_threads): - try: - thread.join(timeout=0.5) - except Exception: - pass - self._rx_threads.clear() - - if sys.platform.startswith("win"): - time.sleep(0.05) - - close = stop - - def __del__(self) -> None: - try: - self.stop() - except Exception: - pass - - # ------------------------------------------------------------------ - # Client command handling - # ------------------------------------------------------------------ - def _handle_client_message(self, msg: Any, *, conn: Any | None = None) -> None: - self.received_commands.append(msg) - - if not isinstance(msg, dict): - self._send_to(conn, {"type": "error", "error": "message must be a dict"}) - return - - cmd = msg.get("cmd") - - if cmd == "ping": - self._send_to( - conn, - { - "type": "pong", - "timestamp": time.time(), - "session_name": self.session_name, - "recording": self.recording, - "video_recording": self.video_recording, - "clients": self.client_count(), - }, - ) - - elif cmd == "status": - self._send_to(conn, self.status_payload()) - - elif cmd == "set_session_name": - self.session_name = msg.get("session_name", "default_session") - self._send_to(conn, {"type": "ack", "cmd": cmd, "session_name": self.session_name}) - - elif cmd == "start_recording": - self.start_recording() - self._send_to(conn, {"type": "ack", "cmd": cmd, "recording": True}) - - elif cmd == "stop_recording": - self.stop_recording() - self._send_to(conn, {"type": "ack", "cmd": cmd, "recording": False}) - - elif cmd == "save": - file = msg.get("filename", self.filename) - result = self.save(file) - self._send_to(conn, {"type": "ack", "cmd": cmd, "result": result, "filename": file}) - - elif cmd == "close": - self._send_to(conn, {"type": "ack", "cmd": cmd}) - if conn is not None: - self._close_conn(conn) - - else: - self._send_to(conn, {"type": "error", "error": f"unknown cmd: {cmd!r}"}) - - @staticmethod - def _send_to(conn: Any | None, payload: Any) -> bool: - if conn is None: - return False - try: - conn.send(payload) - return True - except Exception: - return False - - def client_count(self) -> int: - with self._conns_lock: - return len(self.conns) - - def status_payload(self) -> dict[str, Any]: - return { - "type": "status", - "session_name": self.session_name, - "recording": self.recording, - "video_recording": self.video_recording, - "clients": self.client_count(), - "steps": self.curr_step, - "broadcast_count": self.broadcast_count, - "address": self.address, - } - - # ------------------------------------------------------------------ - # Recording helpers - # ------------------------------------------------------------------ - def start_recording(self) -> None: - self._recording.set() - self._vid_recording.set() - self._clear_data_queues() - self.curr_step = 0 - - def stop_recording(self) -> None: - self._recording.clear() - self._vid_recording.clear() - - def _clear_data_queues(self) -> None: - self.time_stamp.clear() - self.step.clear() - self.frame_time.clear() - self.pose_time.clear() - if self.original_pose is not None: - self.original_pose.clear() - - # ------------------------------------------------------------------ - # Process/broadcast path - # ------------------------------------------------------------------ - def process(self, pose: Any, **kwargs: Any) -> Any: - """Mock DLCLive processor callback. - - Records minimal metadata when recording is active and broadcasts a simple - pose payload to all connected clients. - """ - now = time.time() - self.curr_step += 1 - - if self.recording: - self.time_stamp.append(now) - self.step.append(self.curr_step) - self.frame_time.append(kwargs.get("frame_time", -1)) - if "pose_time" in kwargs: - self.pose_time.append(kwargs["pose_time"]) - if self.original_pose is not None: - self.original_pose.append(pose) - - payload = { - "type": "pose", - "timestamp": now, - "step": self.curr_step, - "pose": self._make_pickle_safe_pose(pose), - "frame_time": kwargs.get("frame_time", None), - "pose_time": kwargs.get("pose_time", None), - "recording": self.recording, - } - self.broadcast(payload) - return pose - - @staticmethod - def _make_pickle_safe_pose(pose: Any) -> Any: - """Convert common array-likes to socket-safe Python types.""" - tolist = getattr(pose, "tolist", None) - if callable(tolist): - try: - return tolist() - except Exception: - pass - return pose - - def broadcast(self, payload: Any) -> None: - with self._conns_lock: - conns = list(self.conns) - - dead = [] - for conn in conns: - try: - conn.send(payload) - self.broadcast_count += 1 - except Exception: - dead.append(conn) - - for conn in dead: - self._close_conn(conn) - - # ------------------------------------------------------------------ - # Save/get_data helpers - # ------------------------------------------------------------------ - def get_data(self) -> dict[str, Any]: - return { - "start_time": self.start_time, - "session_name": self.session_name, - "time_stamp": list(self.time_stamp), - "step": list(self.step), - "frame_time": list(self.frame_time), - "pose_time": list(self.pose_time), - "recording": self.recording, - "video_recording": self.video_recording, - "received_commands": list(self.received_commands), - "broadcast_count": self.broadcast_count, - } - - def save(self, file: str | Path | None = None) -> int: - if not file: - return 0 - try: - path = Path(file) - path.parent.mkdir(parents=True, exist_ok=True) - with path.open("wb") as fh: - pickle.dump(self.get_data(), fh) - return 1 - except Exception: - logger.exception("MockSocketProcessor save failed") - return -1 - - -# Optional aliases useful in different test styles. -MockPDSocketProcessor = MockSocketProcessor -MockUnitySocketProcessor = MockSocketProcessor - - -def get_available_processors() -> dict[str, dict[str, Any]]: - """Plugin-discovery entrypoint used by dlclivegui.processor_utils. - - This avoids requiring the class to inherit from dlclive.processor.Processor - during tests. The loader path that prefers get_available_processors() can - still discover this processor as a GUI plugin fixture. - """ - return { - "MockSocketProcessor": { - "class": MockSocketProcessor, - "name": getattr(MockSocketProcessor, "PROCESSOR_NAME", "MockSocketProcessor"), - "description": getattr(MockSocketProcessor, "PROCESSOR_DESCRIPTION", ""), - "params": getattr(MockSocketProcessor, "PROCESSOR_PARAMS", {}), - } - } diff --git a/dlclivegui/processors/custom/mock_unity_socket_client.ipynb b/dlclivegui/processors/custom/mock_unity_socket_client.ipynb deleted file mode 100644 index 63e4569..0000000 --- a/dlclivegui/processors/custom/mock_unity_socket_client.ipynb +++ /dev/null @@ -1,4609 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "7fb27b941602401d91542211134fc71a", - "metadata": {}, - "source": [ - "# Mock Unity Socket Client for DLCLive Processor\n", - "\n", - "This notebook acts as the **Unity-side socket client** for the legacy DLC socket processor.\n", - "\n", - "## Which processor style this targets\n", - "\n", - "The legacy processor chain you showed is:\n", - "\n", - "```text\n", - "dlc_inference_w_pd_sync\n", - " -> dlc_inference_w_pd\n", - " -> MyProcessor_socket\n", - "```\n", - "\n", - "`MyProcessor_socket` opens a `multiprocessing.connection.Listener`, defaulting to:\n", - "\n", - "```python\n", - "(\"127.0.0.1\", 6000)\n", - "authkey=b\"secret password\"\n", - "```\n", - "\n", - "It sends payloads from `process()` shaped like:\n", - "\n", - "```python\n", - "[time.time(), x, y, heading, head_angle, signal]\n", - "```\n", - "\n", - "This notebook connects as the **client** and tries to catch those pose/kinematics packets.\n", - "\n", - "## Important ordering note\n", - "\n", - "The legacy `MyProcessor_socket` does **not** have a background accept thread. It accepts a client only when `process()` runs. That means if DLC inference is not producing poses yet, `Client(...)` may block or time out. If that happens, start/continue DLC inference and retry." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "acae54e37e7d407bbb7b55eff062a284", - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import annotations\n", - "\n", - "import json\n", - "import queue\n", - "import threading\n", - "import time\n", - "from dataclasses import asdict, dataclass\n", - "from multiprocessing.connection import Client\n", - "from pathlib import Path\n", - "from typing import Any" - ] - }, - { - "cell_type": "markdown", - "id": "9a63283cbaf04dbcab1f6479b197f3a8", - "metadata": {}, - "source": [ - "## Configuration\n", - "\n", - "Adjust these if the processor uses a different port or auth key.\n", - "\n", - "For the legacy processor, the defaults are usually:\n", - "\n", - "```python\n", - "ADDRESS = (\"127.0.0.1\", 6000)\n", - "AUTHKEY = b\"secret password\"\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "8dd0d8092fe74a7c96281538738b07e2", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Target processor socket: ('127.0.0.1', 6000)\n" - ] - } - ], - "source": [ - "ADDRESS = (\"127.0.0.1\", 6000)\n", - "AUTHKEY = b\"secret password\"\n", - "\n", - "# How long to wait for a connection attempt before considering it failed.\n", - "CONNECT_TIMEOUT_S = 10.0\n", - "\n", - "# How long the receive loop should poll while waiting for new packets.\n", - "POLL_INTERVAL_S = 0.05\n", - "\n", - "print(\"Target processor socket:\", ADDRESS)" - ] - }, - { - "cell_type": "markdown", - "id": "72eea5119410473aa328ad9291626812", - "metadata": {}, - "source": [ - "## Client helpers\n", - "\n", - "`multiprocessing.connection.Client(...)` can block if the server has not called `accept()` yet. To avoid freezing the notebook, `connect_with_timeout()` performs the connection attempt in a background thread." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "8edb47106e1a46a883d545849b8ab81b", - "metadata": {}, - "outputs": [], - "source": [ - "class ConnectTimeoutError(TimeoutError):\n", - " pass\n", - "\n", - "\n", - "def connect_with_timeout(address, authkey: bytes, timeout_s: float = 10.0):\n", - " \"\"\"Connect to a multiprocessing.connection.Listener without freezing the notebook forever.\"\"\"\n", - " result_q: queue.Queue[tuple[str, Any]] = queue.Queue(maxsize=1)\n", - "\n", - " def worker():\n", - " try:\n", - " conn = Client(address, authkey=authkey)\n", - " result_q.put((\"ok\", conn))\n", - " except Exception as exc:\n", - " result_q.put((\"error\", exc))\n", - "\n", - " t = threading.Thread(target=worker, name=\"MockUnityConnect\", daemon=True)\n", - " t.start()\n", - " t.join(timeout_s)\n", - "\n", - " if t.is_alive():\n", - " raise ConnectTimeoutError(\n", - " f\"Timed out after {timeout_s:.1f}s while connecting to {address}. \"\n", - " \"For legacy MyProcessor_socket, this can happen if DLC process() has not called listener.accept() yet.\"\n", - " )\n", - "\n", - " status, payload = result_q.get_nowait()\n", - " if status == \"ok\":\n", - " return payload\n", - " raise payload\n", - "\n", - "\n", - "@dataclass\n", - "class LegacyPosePacket:\n", - " timestamp: float\n", - " x: float\n", - " y: float\n", - " heading: float\n", - " head_angle: float\n", - " signal: float\n", - " raw: Any\n", - "\n", - "\n", - "def decode_payload(payload: Any) -> dict[str, Any]:\n", - " \"\"\"Decode either legacy list payloads or newer dict/list mock payloads.\"\"\"\n", - " # Legacy MyProcessor_socket payload:\n", - " # [time.time(), x, y, heading, head_angle, signal]\n", - " if isinstance(payload, list) and len(payload) == 6:\n", - " pkt = LegacyPosePacket(\n", - " timestamp=float(payload[0]),\n", - " x=float(payload[1]),\n", - " y=float(payload[2]),\n", - " heading=float(payload[3]),\n", - " head_angle=float(payload[4]),\n", - " signal=float(payload[5]),\n", - " raw=payload,\n", - " )\n", - " return {\"kind\": \"legacy_pose\", **asdict(pkt)}\n", - "\n", - " # Newer/base mock payloads may be dictionaries.\n", - " if isinstance(payload, dict):\n", - " kind = payload.get(\"type\", \"dict\")\n", - " return {\"kind\": kind, \"raw\": payload}\n", - "\n", - " # Some processors broadcast [timestamp, pose].\n", - " if isinstance(payload, list) and len(payload) == 2:\n", - " return {\"kind\": \"timestamp_pose\", \"timestamp\": payload[0], \"pose\": payload[1], \"raw\": payload}\n", - "\n", - " return {\"kind\": \"unknown\", \"raw\": payload}" - ] - }, - { - "cell_type": "markdown", - "id": "10185d26023b46108eb7d9f57d49d2b3", - "metadata": {}, - "source": [ - "## Connect to the DLC processor socket\n", - "\n", - "Run this cell once the processor has been created and its listener should be available.\n", - "\n", - "If it times out, it likely means either:\n", - "\n", - "1. the processor has not been instantiated yet,\n", - "2. the address/authkey are wrong,\n", - "3. the legacy processor is waiting until `process()` runs before accepting the connection,\n", - "4. another process is using the port." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "8763a12b2bbd4a93a75aff182afb95dc", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Connected to processor socket: ('127.0.0.1', 6000)\n" - ] - } - ], - "source": [ - "conn = connect_with_timeout(ADDRESS, AUTHKEY, timeout_s=CONNECT_TIMEOUT_S)\n", - "print(\"Connected to processor socket:\", ADDRESS)" - ] - }, - { - "cell_type": "markdown", - "id": "7623eae2785240b9bd12b16a66d81610", - "metadata": {}, - "source": [ - "## Optional: send a ping/status command\n", - "\n", - "Only use this for processors that implement command handling, such as the newer `BaseProcessorSocket` or the standalone mock processor.\n", - "\n", - "The legacy `MyProcessor_socket` does **not** read commands from the client, so skip this cell for that processor." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "7cdc8c89c7104fffa095e18ddfef8986", - "metadata": {}, - "outputs": [], - "source": [ - "# Uncomment only for BaseProcessorSocket-style processors or the standalone mock.\n", - "# conn.send({\"cmd\": \"ping\"})\n", - "# if conn.poll(2.0):\n", - "# print(\"Response:\", conn.recv())\n", - "# else:\n", - "# print(\"No response. This is expected for legacy MyProcessor_socket.\")" - ] - }, - { - "cell_type": "markdown", - "id": "b118ea5561624da68c537baed56e602f", - "metadata": {}, - "source": [ - "## Receive pose packets\n", - "\n", - "This cell listens for up to `duration_s` seconds and prints decoded packets. For legacy `MyProcessor_socket`, you should see `legacy_pose` packets once `process()` is called by DLC inference." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "938c804e27f84196a10c8828c723f798", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588105.750286,\n", - " \"step\": 12,\n", - " \"pose\": [\n", - " [\n", - " 288.7191162109375,\n", - " 298.63250732421875,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.0001220703125,\n", - " 307.95672607421875,\n", - " 0.973543643951416\n", - " ],\n", - " [\n", - " 297.29254150390625,\n", - " 314.52410888671875,\n", - " 0.6293796896934509\n", - " ],\n", - " [\n", - " 290.9452819824219,\n", - " 310.3515319824219,\n", - " 0.640852153301239\n", - " ],\n", - " [\n", - " 304.86236572265625,\n", - " 313.2810974121094,\n", - " 0.6921122670173645\n", - " ],\n", - " [\n", - " 284.668212890625,\n", - " 266.52752685546875,\n", - " 0.9321146607398987\n", - " ],\n", - " [\n", - " 284.6539001464844,\n", - " 237.49722290039062,\n", - " 0.4642881155014038\n", - " ],\n", - " [\n", - " 298.547607421875,\n", - " 218.42587280273438,\n", - " 0.26315996050834656\n", - " ],\n", - " [\n", - " 182.9195098876953,\n", - " 312.5831604003906,\n", - " 0.4563772678375244\n", - " ],\n", - " [\n", - " 181.693359375,\n", - " 306.1708068847656,\n", - " 0.39262306690216064\n", - " ],\n", - " [\n", - " 309.70013427734375,\n", - " 273.22198486328125,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.6557922363281,\n", - " 278.49371337890625,\n", - " 0.5969972014427185\n", - " ],\n", - " [\n", - " 362.46343994140625,\n", - " 283.172119140625,\n", - " 0.4280658960342407\n", - " ],\n", - " [\n", - " 185.8195343017578,\n", - " 311.7103271484375,\n", - " 0.27244052290916443\n", - " ],\n", - " [\n", - " 192.61837768554688,\n", - " 305.4747009277344,\n", - " 0.39853179454803467\n", - " ],\n", - " [\n", - " 355.7668151855469,\n", - " 339.8248596191406,\n", - " 0.40097054839134216\n", - " ],\n", - " [\n", - " 364.0209655761719,\n", - " 341.5860595703125,\n", - " 0.5437613129615784\n", - " ],\n", - " [\n", - " 335.2928771972656,\n", - " 344.40032958984375,\n", - " 0.3498704731464386\n", - " ],\n", - " [\n", - " 331.4139404296875,\n", - " 356.1664123535156,\n", - " 0.4611521065235138\n", - " ],\n", - " [\n", - " 366.796142578125,\n", - " 344.254150390625,\n", - " 0.47546425461769104\n", - " ],\n", - " [\n", - " 270.3355712890625,\n", - " 358.1495666503906,\n", - " 0.4529607594013214\n", - " ],\n", - " [\n", - " 379.02679443359375,\n", - " 356.05645751953125,\n", - " 0.4878201186656952\n", - " ],\n", - " [\n", - " 268.0301818847656,\n", - " 359.48583984375,\n", - " 0.366856187582016\n", - " ],\n", - " [\n", - " 599.5546264648438,\n", - " 377.7467956542969,\n", - " 0.564258873462677\n", - " ],\n", - " [\n", - " 185.41236877441406,\n", - " 207.2476043701172,\n", - " 0.3415561616420746\n", - " ],\n", - " [\n", - " 518.7420043945312,\n", - " 391.21240234375,\n", - " 0.3360799551010132\n", - " ],\n", - " [\n", - " 516.0563354492188,\n", - " 429.2253723144531,\n", - " 0.7114025354385376\n", - " ],\n", - " [\n", - " 164.67864990234375,\n", - " 212.08599853515625,\n", - " 0.22173050045967102\n", - " ],\n", - " [\n", - " 508.5932312011719,\n", - " 391.6039123535156,\n", - " 0.30660656094551086\n", - " ],\n", - " [\n", - " 511.993896484375,\n", - " 447.9190368652344,\n", - " 0.5064514875411987\n", - " ],\n", - " [\n", - " 591.1927490234375,\n", - " 409.2403564453125,\n", - " 0.5136002898216248\n", - " ],\n", - " [\n", - " 106.3792495727539,\n", - " 271.0600891113281,\n", - " 0.14335250854492188\n", - " ],\n", - " [\n", - " 112.1520767211914,\n", - " 279.07525634765625,\n", - " 0.2755976915359497\n", - " ],\n", - " [\n", - " 572.9004516601562,\n", - " 393.2549743652344,\n", - " 0.39857158064842224\n", - " ],\n", - " [\n", - " 144.4106903076172,\n", - " 313.3831787109375,\n", - " 0.27947890758514404\n", - " ],\n", - " [\n", - " 550.626953125,\n", - " 466.50970458984375,\n", - " 0.5572487115859985\n", - " ],\n", - " [\n", - " 466.3507385253906,\n", - " 413.0749206542969,\n", - " 0.39403966069221497\n", - " ],\n", - " [\n", - " 461.47198486328125,\n", - " 400.29541015625,\n", - " 0.3778141140937805\n", - " ],\n", - " [\n", - " 460.13824462890625,\n", - " 403.57171630859375,\n", - " 0.5188782811164856\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588105.7246952,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588105.750286\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588105.78031,\n", - " \"step\": 13,\n", - " \"pose\": [\n", - " [\n", - " 287.8656921386719,\n", - " 298.9954833984375,\n", - " 1.0\n", - " ],\n", - " [\n", - " 292.98870849609375,\n", - " 308.14825439453125,\n", - " 0.9376083016395569\n", - " ],\n", - " [\n", - " 297.0699462890625,\n", - " 315.7899169921875,\n", - " 0.605503499507904\n", - " ],\n", - " [\n", - " 290.7883605957031,\n", - " 311.0892333984375,\n", - " 0.653724193572998\n", - " ],\n", - " [\n", - " 304.9418029785156,\n", - " 314.27423095703125,\n", - " 0.6581617593765259\n", - " ],\n", - " [\n", - " 284.5467224121094,\n", - " 266.99755859375,\n", - " 0.9386962056159973\n", - " ],\n", - " [\n", - " 285.4912109375,\n", - " 236.9186248779297,\n", - " 0.48566538095474243\n", - " ],\n", - " [\n", - " 282.66357421875,\n", - " 236.26953125,\n", - " 0.2793366611003876\n", - " ],\n", - " [\n", - " 182.8148193359375,\n", - " 314.7862548828125,\n", - " 0.4555658996105194\n", - " ],\n", - " [\n", - " 183.0238037109375,\n", - " 309.2024230957031,\n", - " 0.3395337760448456\n", - " ],\n", - " [\n", - " 310.2979431152344,\n", - " 272.9541015625,\n", - " 1.0\n", - " ],\n", - " [\n", - " 362.39019775390625,\n", - " 278.04864501953125,\n", - " 0.7049410343170166\n", - " ],\n", - " [\n", - " 362.625,\n", - " 281.328369140625,\n", - " 0.4549161195755005\n", - " ],\n", - " [\n", - " 357.58203125,\n", - " 282.10272216796875,\n", - " 0.26718243956565857\n", - " ],\n", - " [\n", - " 194.67514038085938,\n", - " 306.698974609375,\n", - " 0.45219412446022034\n", - " ],\n", - " [\n", - " 355.5292663574219,\n", - " 339.20318603515625,\n", - " 0.41419631242752075\n", - " ],\n", - " [\n", - " 367.3896179199219,\n", - " 340.3309326171875,\n", - " 0.5223292112350464\n", - " ],\n", - " [\n", - " 335.11529541015625,\n", - " 343.81201171875,\n", - " 0.347744345664978\n", - " ],\n", - " [\n", - " 332.03765869140625,\n", - " 356.7191162109375,\n", - " 0.4733559191226959\n", - " ],\n", - " [\n", - " 367.4532470703125,\n", - " 344.79302978515625,\n", - " 0.4827011823654175\n", - " ],\n", - " [\n", - " 270.3110046386719,\n", - " 357.83343505859375,\n", - " 0.4700448215007782\n", - " ],\n", - " [\n", - " 378.0578308105469,\n", - " 356.4914855957031,\n", - " 0.4333026707172394\n", - " ],\n", - " [\n", - " 267.2149963378906,\n", - " 359.4980773925781,\n", - " 0.39140474796295166\n", - " ],\n", - " [\n", - " 596.2949829101562,\n", - " 378.6457824707031,\n", - " 0.5882202386856079\n", - " ],\n", - " [\n", - " 185.2808837890625,\n", - " 208.80474853515625,\n", - " 0.4029167890548706\n", - " ],\n", - " [\n", - " 563.7208862304688,\n", - " 387.8863220214844,\n", - " 0.390456885099411\n", - " ],\n", - " [\n", - " 519.1982421875,\n", - " 433.89654541015625,\n", - " 0.5289698839187622\n", - " ],\n", - " [\n", - " 442.9023132324219,\n", - " 356.9894714355469,\n", - " 0.2825982868671417\n", - " ],\n", - " [\n", - " 568.7670288085938,\n", - " 195.95079040527344,\n", - " 0.43732380867004395\n", - " ],\n", - " [\n", - " 512.254638671875,\n", - " 449.7223815917969,\n", - " 0.6101775169372559\n", - " ],\n", - " [\n", - " 591.0804443359375,\n", - " 409.246337890625,\n", - " 0.5442432761192322\n", - " ],\n", - " [\n", - " 470.5218200683594,\n", - " 402.7597961425781,\n", - " 0.1880091279745102\n", - " ],\n", - " [\n", - " 107.73304748535156,\n", - " 270.0023193359375,\n", - " 0.14866414666175842\n", - " ],\n", - " [\n", - " 572.8255615234375,\n", - " 393.6127624511719,\n", - " 0.5601691603660583\n", - " ],\n", - " [\n", - " 481.23486328125,\n", - " 431.833984375,\n", - " 0.2556696832180023\n", - " ],\n", - " [\n", - " 550.4242553710938,\n", - " 467.0722961425781,\n", - " 0.4299638867378235\n", - " ],\n", - " [\n", - " 466.45404052734375,\n", - " 412.8644714355469,\n", - " 0.38715386390686035\n", - " ],\n", - " [\n", - " 462.144775390625,\n", - " 401.0147705078125,\n", - " 0.3404390513896942\n", - " ],\n", - " [\n", - " 459.4046325683594,\n", - " 403.1348571777344,\n", - " 0.4926401376724243\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588105.7554483,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588105.78031\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588105.821648,\n", - " \"step\": 14,\n", - " \"pose\": [\n", - " [\n", - " 288.8023681640625,\n", - " 298.86077880859375,\n", - " 1.0\n", - " ],\n", - " [\n", - " 292.8272399902344,\n", - " 308.04949951171875,\n", - " 0.9570764899253845\n", - " ],\n", - " [\n", - " 296.77056884765625,\n", - " 315.70367431640625,\n", - " 0.631300687789917\n", - " ],\n", - " [\n", - " 289.9299621582031,\n", - " 310.9530944824219,\n", - " 0.6520562171936035\n", - " ],\n", - " [\n", - " 305.0509338378906,\n", - " 313.7876892089844,\n", - " 0.7196981310844421\n", - " ],\n", - " [\n", - " 284.6484680175781,\n", - " 266.7134704589844,\n", - " 0.9353039860725403\n", - " ],\n", - " [\n", - " 285.0174560546875,\n", - " 238.79736328125,\n", - " 0.4836384952068329\n", - " ],\n", - " [\n", - " 154.6018829345703,\n", - " 315.4358215332031,\n", - " 0.2590964734554291\n", - " ],\n", - " [\n", - " 336.0355529785156,\n", - " 466.2019348144531,\n", - " 0.37860575318336487\n", - " ],\n", - " [\n", - " 181.9645233154297,\n", - " 308.6495361328125,\n", - " 0.3605335056781769\n", - " ],\n", - " [\n", - " 309.65972900390625,\n", - " 273.17425537109375,\n", - " 1.0\n", - " ],\n", - " [\n", - " 362.54388427734375,\n", - " 277.6341857910156,\n", - " 0.5562496781349182\n", - " ],\n", - " [\n", - " 361.87298583984375,\n", - " 282.2453308105469,\n", - " 0.477169394493103\n", - " ],\n", - " [\n", - " 358.46246337890625,\n", - " 282.86480712890625,\n", - " 0.2796511948108673\n", - " ],\n", - " [\n", - " 189.29440307617188,\n", - " 305.3114929199219,\n", - " 0.41088035702705383\n", - " ],\n", - " [\n", - " 355.5824890136719,\n", - " 341.4902648925781,\n", - " 0.3962235450744629\n", - " ],\n", - " [\n", - " 363.149658203125,\n", - " 340.7292785644531,\n", - " 0.48571205139160156\n", - " ],\n", - " [\n", - " 332.39642333984375,\n", - " 340.5425720214844,\n", - " 0.32239603996276855\n", - " ],\n", - " [\n", - " 331.2975769042969,\n", - " 355.4726257324219,\n", - " 0.4472481310367584\n", - " ],\n", - " [\n", - " 364.4246520996094,\n", - " 346.6827087402344,\n", - " 0.4476216733455658\n", - " ],\n", - " [\n", - " 269.8668212890625,\n", - " 359.0860900878906,\n", - " 0.43882080912590027\n", - " ],\n", - " [\n", - " 379.4068298339844,\n", - " 356.64239501953125,\n", - " 0.4417465329170227\n", - " ],\n", - " [\n", - " 266.974853515625,\n", - " 360.31402587890625,\n", - " 0.33691221475601196\n", - " ],\n", - " [\n", - " 596.21826171875,\n", - " 377.820068359375,\n", - " 0.5398204922676086\n", - " ],\n", - " [\n", - " 183.42034912109375,\n", - " 208.78756713867188,\n", - " 0.34466955065727234\n", - " ],\n", - " [\n", - " 564.060791015625,\n", - " 388.1324157714844,\n", - " 0.4286687970161438\n", - " ],\n", - " [\n", - " 517.4417724609375,\n", - " 430.2832336425781,\n", - " 0.5827439427375793\n", - " ],\n", - " [\n", - " 169.510009765625,\n", - " 212.77224731445312,\n", - " 0.23520678281784058\n", - " ],\n", - " [\n", - " 509.5054016113281,\n", - " 390.251708984375,\n", - " 0.2855446934700012\n", - " ],\n", - " [\n", - " 511.0235290527344,\n", - " 447.9842224121094,\n", - " 0.5595549941062927\n", - " ],\n", - " [\n", - " 591.0059204101562,\n", - " 408.37109375,\n", - " 0.5015893578529358\n", - " ],\n", - " [\n", - " 475.4132385253906,\n", - " 422.1249084472656,\n", - " 0.19282308220863342\n", - " ],\n", - " [\n", - " 110.6484146118164,\n", - " 276.17138671875,\n", - " 0.3112924098968506\n", - " ],\n", - " [\n", - " 572.998046875,\n", - " 393.3849182128906,\n", - " 0.4534406363964081\n", - " ],\n", - " [\n", - " 481.1611022949219,\n", - " 432.43438720703125,\n", - " 0.2341541200876236\n", - " ],\n", - " [\n", - " 551.3787231445312,\n", - " 466.6352844238281,\n", - " 0.5391212105751038\n", - " ],\n", - " [\n", - " 467.7240905761719,\n", - " 412.1317138671875,\n", - " 0.3793080151081085\n", - " ],\n", - " [\n", - " 465.2247619628906,\n", - " 409.2422180175781,\n", - " 0.33666175603866577\n", - " ],\n", - " [\n", - " 460.2418212890625,\n", - " 403.50830078125,\n", - " 0.42937344312667847\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588105.7889726,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588105.821648\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588105.851803,\n", - " \"step\": 15,\n", - " \"pose\": [\n", - " [\n", - " 288.8399963378906,\n", - " 298.389892578125,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.063232421875,\n", - " 307.6488952636719,\n", - " 0.9409026503562927\n", - " ],\n", - " [\n", - " 296.73828125,\n", - " 314.7473449707031,\n", - " 0.6393375992774963\n", - " ],\n", - " [\n", - " 290.7121887207031,\n", - " 310.599609375,\n", - " 0.6521811485290527\n", - " ],\n", - " [\n", - " 304.9431457519531,\n", - " 311.9217224121094,\n", - " 0.7004058957099915\n", - " ],\n", - " [\n", - " 284.689697265625,\n", - " 267.53924560546875,\n", - " 0.9271823167800903\n", - " ],\n", - " [\n", - " 182.9373779296875,\n", - " 313.93133544921875,\n", - " 0.5212528109550476\n", - " ],\n", - " [\n", - " 153.7313690185547,\n", - " 313.92779541015625,\n", - " 0.3010638356208801\n", - " ],\n", - " [\n", - " 181.32296752929688,\n", - " 312.8819580078125,\n", - " 0.5419734120368958\n", - " ],\n", - " [\n", - " 183.34437561035156,\n", - " 306.7300720214844,\n", - " 0.37734419107437134\n", - " ],\n", - " [\n", - " 309.9617004394531,\n", - " 273.3157653808594,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.39080810546875,\n", - " 277.68914794921875,\n", - " 0.7047985792160034\n", - " ],\n", - " [\n", - " 362.3508605957031,\n", - " 282.02313232421875,\n", - " 0.4799898564815521\n", - " ],\n", - " [\n", - " 179.46484375,\n", - " 315.53436279296875,\n", - " 0.3219224810600281\n", - " ],\n", - " [\n", - " 193.1734161376953,\n", - " 304.5758056640625,\n", - " 0.4651084244251251\n", - " ],\n", - " [\n", - " 355.8511047363281,\n", - " 340.7768859863281,\n", - " 0.42659905552864075\n", - " ],\n", - " [\n", - " 364.3086242675781,\n", - " 341.3064270019531,\n", - " 0.5809048414230347\n", - " ],\n", - " [\n", - " 310.90179443359375,\n", - " 347.79620361328125,\n", - " 0.37011390924453735\n", - " ],\n", - " [\n", - " 329.7757263183594,\n", - " 356.66290283203125,\n", - " 0.5357598066329956\n", - " ],\n", - " [\n", - " 369.3732604980469,\n", - " 347.80084228515625,\n", - " 0.5102704167366028\n", - " ],\n", - " [\n", - " 269.4798583984375,\n", - " 358.50128173828125,\n", - " 0.36626455187797546\n", - " ],\n", - " [\n", - " 379.74285888671875,\n", - " 356.38800048828125,\n", - " 0.4842686653137207\n", - " ],\n", - " [\n", - " 265.9881286621094,\n", - " 360.00592041015625,\n", - " 0.27803361415863037\n", - " ],\n", - " [\n", - " 596.4786376953125,\n", - " 379.2182922363281,\n", - " 0.5961218476295471\n", - " ],\n", - " [\n", - " 254.6011505126953,\n", - " 382.6790466308594,\n", - " 0.4003625512123108\n", - " ],\n", - " [\n", - " 563.052001953125,\n", - " 386.73590087890625,\n", - " 0.36300426721572876\n", - " ],\n", - " [\n", - " 517.0165405273438,\n", - " 430.59613037109375,\n", - " 0.5643782019615173\n", - " ],\n", - " [\n", - " 164.36508178710938,\n", - " 213.65438842773438,\n", - " 0.2149515450000763\n", - " ],\n", - " [\n", - " 509.01336669921875,\n", - " 392.5158996582031,\n", - " 0.23822596669197083\n", - " ],\n", - " [\n", - " 510.94085693359375,\n", - " 449.33477783203125,\n", - " 0.6286779642105103\n", - " ],\n", - " [\n", - " 590.7813110351562,\n", - " 409.1065368652344,\n", - " 0.4439380168914795\n", - " ],\n", - " [\n", - " 463.3310546875,\n", - " 401.2489318847656,\n", - " 0.1755678504705429\n", - " ],\n", - " [\n", - " 109.76718139648438,\n", - " 271.8696594238281,\n", - " 0.2973553240299225\n", - " ],\n", - " [\n", - " 572.5289306640625,\n", - " 393.1651916503906,\n", - " 0.5296833515167236\n", - " ],\n", - " [\n", - " 480.9592590332031,\n", - " 432.1273193359375,\n", - " 0.21754036843776703\n", - " ],\n", - " [\n", - " 550.6238403320312,\n", - " 466.08721923828125,\n", - " 0.5377715229988098\n", - " ],\n", - " [\n", - " 468.291015625,\n", - " 413.2549133300781,\n", - " 0.3708413243293762\n", - " ],\n", - " [\n", - " 136.44790649414062,\n", - " 171.77438354492188,\n", - " 0.3511451184749603\n", - " ],\n", - " [\n", - " 460.23358154296875,\n", - " 404.2395935058594,\n", - " 0.47236377000808716\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588105.8206406,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588105.8528142\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588105.8770585,\n", - " \"step\": 16,\n", - " \"pose\": [\n", - " [\n", - " 289.1402282714844,\n", - " 299.0330810546875,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.5198974609375,\n", - " 308.2564392089844,\n", - " 0.9859002232551575\n", - " ],\n", - " [\n", - " 297.6006164550781,\n", - " 314.6565856933594,\n", - " 0.6514089107513428\n", - " ],\n", - " [\n", - " 290.924072265625,\n", - " 311.3872985839844,\n", - " 0.6389972567558289\n", - " ],\n", - " [\n", - " 304.9306945800781,\n", - " 313.3708801269531,\n", - " 0.7217590808868408\n", - " ],\n", - " [\n", - " 284.5622863769531,\n", - " 267.4127502441406,\n", - " 0.9266435503959656\n", - " ],\n", - " [\n", - " 184.82669067382812,\n", - " 312.5155334472656,\n", - " 0.5616798400878906\n", - " ],\n", - " [\n", - " 154.43678283691406,\n", - " 314.49652099609375,\n", - " 0.24991558492183685\n", - " ],\n", - " [\n", - " 182.59487915039062,\n", - " 312.3272705078125,\n", - " 0.592433512210846\n", - " ],\n", - " [\n", - " 182.8374786376953,\n", - " 306.06494140625,\n", - " 0.419148325920105\n", - " ],\n", - " [\n", - " 310.11920166015625,\n", - " 273.36737060546875,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.7231140136719,\n", - " 277.9724426269531,\n", - " 0.5769408345222473\n", - " ],\n", - " [\n", - " 362.3317565917969,\n", - " 282.26922607421875,\n", - " 0.43514740467071533\n", - " ],\n", - " [\n", - " 184.4752655029297,\n", - " 311.1407470703125,\n", - " 0.38407573103904724\n", - " ],\n", - " [\n", - " 187.931640625,\n", - " 302.62933349609375,\n", - " 0.4633309543132782\n", - " ],\n", - " [\n", - " 356.0732421875,\n", - " 340.64599609375,\n", - " 0.4025139808654785\n", - " ],\n", - " [\n", - " 362.9293212890625,\n", - " 341.8232727050781,\n", - " 0.5162671208381653\n", - " ],\n", - " [\n", - " 309.4438781738281,\n", - " 345.9781799316406,\n", - " 0.33583828806877136\n", - " ],\n", - " [\n", - " 331.2337341308594,\n", - " 355.5598449707031,\n", - " 0.4219924509525299\n", - " ],\n", - " [\n", - " 366.71087646484375,\n", - " 345.36962890625,\n", - " 0.4366249144077301\n", - " ],\n", - " [\n", - " 269.210693359375,\n", - " 358.0929260253906,\n", - " 0.3690962493419647\n", - " ],\n", - " [\n", - " 424.5083923339844,\n", - " 376.5726013183594,\n", - " 0.42966294288635254\n", - " ],\n", - " [\n", - " 303.0454406738281,\n", - " 218.3123779296875,\n", - " 0.31208333373069763\n", - " ],\n", - " [\n", - " 598.5891723632812,\n", - " 378.4424133300781,\n", - " 0.5889440178871155\n", - " ],\n", - " [\n", - " 197.69949340820312,\n", - " 217.56845092773438,\n", - " 0.3414533734321594\n", - " ],\n", - " [\n", - " 563.177734375,\n", - " 386.8681945800781,\n", - " 0.39073020219802856\n", - " ],\n", - " [\n", - " 516.6555786132812,\n", - " 428.7958679199219,\n", - " 0.5345339179039001\n", - " ],\n", - " [\n", - " 442.6798095703125,\n", - " 357.461181640625,\n", - " 0.23529931902885437\n", - " ],\n", - " [\n", - " 507.02862548828125,\n", - " 392.94842529296875,\n", - " 0.26026907563209534\n", - " ],\n", - " [\n", - " 511.629150390625,\n", - " 447.88116455078125,\n", - " 0.5966296792030334\n", - " ],\n", - " [\n", - " 592.001708984375,\n", - " 408.8056945800781,\n", - " 0.5580258965492249\n", - " ],\n", - " [\n", - " 463.76220703125,\n", - " 402.1715087890625,\n", - " 0.16743294894695282\n", - " ],\n", - " [\n", - " 111.18097686767578,\n", - " 276.2333679199219,\n", - " 0.2950418293476105\n", - " ],\n", - " [\n", - " 573.3688354492188,\n", - " 393.168701171875,\n", - " 0.4543166756629944\n", - " ],\n", - " [\n", - " 481.0332946777344,\n", - " 433.19580078125,\n", - " 0.2525552809238434\n", - " ],\n", - " [\n", - " 551.67578125,\n", - " 466.51300048828125,\n", - " 0.48711535334587097\n", - " ],\n", - " [\n", - " 468.5693054199219,\n", - " 413.4539489746094,\n", - " 0.38309159874916077\n", - " ],\n", - " [\n", - " 135.75698852539062,\n", - " 170.189697265625,\n", - " 0.30588316917419434\n", - " ],\n", - " [\n", - " 459.2412109375,\n", - " 403.5185852050781,\n", - " 0.3879672586917877\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588105.8528142,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588105.8770585\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588105.9255464,\n", - " \"step\": 17,\n", - " \"pose\": [\n", - " [\n", - " 289.01116943359375,\n", - " 299.0413818359375,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.1086730957031,\n", - " 308.51885986328125,\n", - " 0.95180344581604\n", - " ],\n", - " [\n", - " 296.3152770996094,\n", - " 316.4967956542969,\n", - " 0.5928277373313904\n", - " ],\n", - " [\n", - " 290.5437927246094,\n", - " 311.6708679199219,\n", - " 0.6084922552108765\n", - " ],\n", - " [\n", - " 306.17803955078125,\n", - " 313.6896057128906,\n", - " 0.7187818288803101\n", - " ],\n", - " [\n", - " 284.83319091796875,\n", - " 267.0993957519531,\n", - " 0.9082852602005005\n", - " ],\n", - " [\n", - " 285.2369384765625,\n", - " 239.99192810058594,\n", - " 0.4807704985141754\n", - " ],\n", - " [\n", - " 281.5351257324219,\n", - " 244.12281799316406,\n", - " 0.25091421604156494\n", - " ],\n", - " [\n", - " 183.10739135742188,\n", - " 313.957275390625,\n", - " 0.47192785143852234\n", - " ],\n", - " [\n", - " 182.70115661621094,\n", - " 306.3948974609375,\n", - " 0.3859936594963074\n", - " ],\n", - " [\n", - " 310.44354248046875,\n", - " 273.25341796875,\n", - " 1.0\n", - " ],\n", - " [\n", - " 362.1278076171875,\n", - " 276.0966796875,\n", - " 0.66349196434021\n", - " ],\n", - " [\n", - " 363.0843200683594,\n", - " 282.9954528808594,\n", - " 0.44170665740966797\n", - " ],\n", - " [\n", - " 182.47335815429688,\n", - " 311.0877685546875,\n", - " 0.2967713475227356\n", - " ],\n", - " [\n", - " 188.00137329101562,\n", - " 304.48931884765625,\n", - " 0.45905861258506775\n", - " ],\n", - " [\n", - " 354.645263671875,\n", - " 339.31219482421875,\n", - " 0.3976733684539795\n", - " ],\n", - " [\n", - " 363.1065979003906,\n", - " 339.9534606933594,\n", - " 0.5860384702682495\n", - " ],\n", - " [\n", - " 316.1658935546875,\n", - " 342.28369140625,\n", - " 0.3572511076927185\n", - " ],\n", - " [\n", - " 331.590087890625,\n", - " 355.1735534667969,\n", - " 0.4502539336681366\n", - " ],\n", - " [\n", - " 363.5017395019531,\n", - " 344.60235595703125,\n", - " 0.5015437006950378\n", - " ],\n", - " [\n", - " 268.5982666015625,\n", - " 358.1197204589844,\n", - " 0.36430323123931885\n", - " ],\n", - " [\n", - " 378.5502014160156,\n", - " 355.8092041015625,\n", - " 0.42007115483283997\n", - " ],\n", - " [\n", - " 303.5655517578125,\n", - " 217.58612060546875,\n", - " 0.33906295895576477\n", - " ],\n", - " [\n", - " 596.765380859375,\n", - " 379.427001953125,\n", - " 0.5411680340766907\n", - " ],\n", - " [\n", - " 199.35903930664062,\n", - " 222.4739990234375,\n", - " 0.29007023572921753\n", - " ],\n", - " [\n", - " 562.4085083007812,\n", - " 386.65509033203125,\n", - " 0.3170825242996216\n", - " ],\n", - " [\n", - " 517.340087890625,\n", - " 430.5169677734375,\n", - " 0.6213215589523315\n", - " ],\n", - " [\n", - " 441.74444580078125,\n", - " 357.04644775390625,\n", - " 0.2545863389968872\n", - " ],\n", - " [\n", - " 503.5122375488281,\n", - " 393.9283447265625,\n", - " 0.30190107226371765\n", - " ],\n", - " [\n", - " 510.5965576171875,\n", - " 450.106201171875,\n", - " 0.5517356991767883\n", - " ],\n", - " [\n", - " 591.1541748046875,\n", - " 408.65826416015625,\n", - " 0.49750760197639465\n", - " ],\n", - " [\n", - " 475.8544921875,\n", - " 422.05078125,\n", - " 0.1733374446630478\n", - " ],\n", - " [\n", - " 109.9720687866211,\n", - " 271.4387512207031,\n", - " 0.2799350619316101\n", - " ],\n", - " [\n", - " 572.795166015625,\n", - " 393.3072204589844,\n", - " 0.3979363441467285\n", - " ],\n", - " [\n", - " 479.5787353515625,\n", - " 432.65380859375,\n", - " 0.22706195712089539\n", - " ],\n", - " [\n", - " 550.501708984375,\n", - " 467.0165100097656,\n", - " 0.4965691566467285\n", - " ],\n", - " [\n", - " 468.164306640625,\n", - " 412.325927734375,\n", - " 0.3681727349758148\n", - " ],\n", - " [\n", - " 461.05029296875,\n", - " 400.1574401855469,\n", - " 0.36365073919296265\n", - " ],\n", - " [\n", - " 460.8140563964844,\n", - " 402.9066467285156,\n", - " 0.4981914758682251\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588105.8993094,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588105.9255464\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588105.9609218,\n", - " \"step\": 18,\n", - " \"pose\": [\n", - " [\n", - " 288.9327392578125,\n", - " 299.13629150390625,\n", - " 1.0\n", - " ],\n", - " [\n", - " 292.8038330078125,\n", - " 308.6182861328125,\n", - " 0.965558648109436\n", - " ],\n", - " [\n", - " 297.1647644042969,\n", - " 315.5074157714844,\n", - " 0.6364358067512512\n", - " ],\n", - " [\n", - " 290.55609130859375,\n", - " 311.7894287109375,\n", - " 0.6080928444862366\n", - " ],\n", - " [\n", - " 305.4518127441406,\n", - " 313.89239501953125,\n", - " 0.7770905494689941\n", - " ],\n", - " [\n", - " 284.95733642578125,\n", - " 267.11175537109375,\n", - " 0.9243097305297852\n", - " ],\n", - " [\n", - " 285.8481750488281,\n", - " 238.2171630859375,\n", - " 0.49581968784332275\n", - " ],\n", - " [\n", - " 281.67034912109375,\n", - " 238.3629608154297,\n", - " 0.2495463341474533\n", - " ],\n", - " [\n", - " 181.5650177001953,\n", - " 314.2547912597656,\n", - " 0.5168197751045227\n", - " ],\n", - " [\n", - " 182.05751037597656,\n", - " 306.9068908691406,\n", - " 0.36109668016433716\n", - " ],\n", - " [\n", - " 310.2335205078125,\n", - " 273.6082763671875,\n", - " 1.0\n", - " ],\n", - " [\n", - " 362.219482421875,\n", - " 276.6759948730469,\n", - " 0.618270754814148\n", - " ],\n", - " [\n", - " 362.4667663574219,\n", - " 280.8567199707031,\n", - " 0.40474116802215576\n", - " ],\n", - " [\n", - " 184.80101013183594,\n", - " 316.924560546875,\n", - " 0.3415667712688446\n", - " ],\n", - " [\n", - " 189.2113800048828,\n", - " 305.0552062988281,\n", - " 0.44207993149757385\n", - " ],\n", - " [\n", - " 354.8471984863281,\n", - " 341.1282043457031,\n", - " 0.4782800078392029\n", - " ],\n", - " [\n", - " 363.4027404785156,\n", - " 341.2732238769531,\n", - " 0.518268346786499\n", - " ],\n", - " [\n", - " 316.40673828125,\n", - " 342.68695068359375,\n", - " 0.3713846504688263\n", - " ],\n", - " [\n", - " 330.7171630859375,\n", - " 355.67803955078125,\n", - " 0.46434029936790466\n", - " ],\n", - " [\n", - " 367.51568603515625,\n", - " 344.2020568847656,\n", - " 0.4221855401992798\n", - " ],\n", - " [\n", - " 269.15869140625,\n", - " 357.6059265136719,\n", - " 0.43143007159233093\n", - " ],\n", - " [\n", - " 377.92431640625,\n", - " 355.83026123046875,\n", - " 0.42217832803726196\n", - " ],\n", - " [\n", - " 266.35400390625,\n", - " 359.1109313964844,\n", - " 0.36265674233436584\n", - " ],\n", - " [\n", - " 598.98876953125,\n", - " 378.3539123535156,\n", - " 0.5779925584793091\n", - " ],\n", - " [\n", - " 521.8517456054688,\n", - " 317.60223388671875,\n", - " 0.3474227786064148\n", - " ],\n", - " [\n", - " 563.2191162109375,\n", - " 386.7420654296875,\n", - " 0.3701111972332001\n", - " ],\n", - " [\n", - " 516.5422973632812,\n", - " 430.6274108886719,\n", - " 0.5754683613777161\n", - " ],\n", - " [\n", - " 566.955078125,\n", - " 197.94085693359375,\n", - " 0.35363760590553284\n", - " ],\n", - " [\n", - " 573.0870971679688,\n", - " 198.2352294921875,\n", - " 0.5906892418861389\n", - " ],\n", - " [\n", - " 511.6949157714844,\n", - " 449.3252258300781,\n", - " 0.6369988322257996\n", - " ],\n", - " [\n", - " 591.0794067382812,\n", - " 408.18438720703125,\n", - " 0.5102192759513855\n", - " ],\n", - " [\n", - " 508.6004638671875,\n", - " 381.352783203125,\n", - " 0.2138703316450119\n", - " ],\n", - " [\n", - " 109.30960083007812,\n", - " 272.1946716308594,\n", - " 0.336846262216568\n", - " ],\n", - " [\n", - " 572.595703125,\n", - " 393.81610107421875,\n", - " 0.48220178484916687\n", - " ],\n", - " [\n", - " 482.5830383300781,\n", - " 431.64202880859375,\n", - " 0.18888558447360992\n", - " ],\n", - " [\n", - " 549.8436279296875,\n", - " 465.8060302734375,\n", - " 0.6060653924942017\n", - " ],\n", - " [\n", - " 470.1370544433594,\n", - " 413.1282653808594,\n", - " 0.4226745069026947\n", - " ],\n", - " [\n", - " 134.86871337890625,\n", - " 172.37551879882812,\n", - " 0.34861159324645996\n", - " ],\n", - " [\n", - " 460.8798522949219,\n", - " 403.1666564941406,\n", - " 0.4525850713253021\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588105.933552,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588105.9609218\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588105.996339,\n", - " \"step\": 19,\n", - " \"pose\": [\n", - " [\n", - " 289.3584289550781,\n", - " 299.6400451660156,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.6114807128906,\n", - " 308.7494201660156,\n", - " 0.9362009763717651\n", - " ],\n", - " [\n", - " 296.5362548828125,\n", - " 315.8563537597656,\n", - " 0.6216031908988953\n", - " ],\n", - " [\n", - " 290.3965759277344,\n", - " 311.8982849121094,\n", - " 0.5908151865005493\n", - " ],\n", - " [\n", - " 305.3473205566406,\n", - " 314.0240783691406,\n", - " 0.6830025911331177\n", - " ],\n", - " [\n", - " 284.38623046875,\n", - " 267.3375244140625,\n", - " 0.938642144203186\n", - " ],\n", - " [\n", - " 183.94895935058594,\n", - " 312.9803771972656,\n", - " 0.5159981846809387\n", - " ],\n", - " [\n", - " 153.8936767578125,\n", - " 314.8314514160156,\n", - " 0.26622244715690613\n", - " ],\n", - " [\n", - " 181.40602111816406,\n", - " 314.3370056152344,\n", - " 0.5327624082565308\n", - " ],\n", - " [\n", - " 182.94410705566406,\n", - " 307.2097473144531,\n", - " 0.4217340648174286\n", - " ],\n", - " [\n", - " 310.5429992675781,\n", - " 273.63134765625,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.03759765625,\n", - " 275.5606994628906,\n", - " 0.6987367868423462\n", - " ],\n", - " [\n", - " 363.2841491699219,\n", - " 282.46881103515625,\n", - " 0.45098239183425903\n", - " ],\n", - " [\n", - " 183.90602111816406,\n", - " 316.1698913574219,\n", - " 0.39084556698799133\n", - " ],\n", - " [\n", - " 193.3860321044922,\n", - " 305.0615234375,\n", - " 0.507167398929596\n", - " ],\n", - " [\n", - " 355.57421875,\n", - " 341.30316162109375,\n", - " 0.4452424645423889\n", - " ],\n", - " [\n", - " 363.03021240234375,\n", - " 340.71435546875,\n", - " 0.5201398730278015\n", - " ],\n", - " [\n", - " 317.2238464355469,\n", - " 345.26666259765625,\n", - " 0.34468573331832886\n", - " ],\n", - " [\n", - " 332.4403991699219,\n", - " 356.6281433105469,\n", - " 0.46254584193229675\n", - " ],\n", - " [\n", - " 363.12640380859375,\n", - " 345.547119140625,\n", - " 0.42061465978622437\n", - " ],\n", - " [\n", - " 269.6493225097656,\n", - " 357.64892578125,\n", - " 0.4019133448600769\n", - " ],\n", - " [\n", - " 378.178466796875,\n", - " 356.5284729003906,\n", - " 0.4181462824344635\n", - " ],\n", - " [\n", - " 266.38238525390625,\n", - " 358.6065368652344,\n", - " 0.33007708191871643\n", - " ],\n", - " [\n", - " 599.5531005859375,\n", - " 377.76666259765625,\n", - " 0.629446804523468\n", - " ],\n", - " [\n", - " 184.41952514648438,\n", - " 208.114013671875,\n", - " 0.4009157419204712\n", - " ],\n", - " [\n", - " 519.3410034179688,\n", - " 390.22406005859375,\n", - " 0.3750861883163452\n", - " ],\n", - " [\n", - " 516.8319702148438,\n", - " 428.9804382324219,\n", - " 0.657153844833374\n", - " ],\n", - " [\n", - " 567.7550659179688,\n", - " 193.29896545410156,\n", - " 0.34581509232521057\n", - " ],\n", - " [\n", - " 509.476806640625,\n", - " 386.754150390625,\n", - " 0.3405891954898834\n", - " ],\n", - " [\n", - " 516.4321899414062,\n", - " 443.82684326171875,\n", - " 0.5484656095504761\n", - " ],\n", - " [\n", - " 594.040283203125,\n", - " 409.7172546386719,\n", - " 0.5296698212623596\n", - " ],\n", - " [\n", - " 508.0539855957031,\n", - " 380.4469299316406,\n", - " 0.20867124199867249\n", - " ],\n", - " [\n", - " 115.30363464355469,\n", - " 228.87652587890625,\n", - " 0.15687808394432068\n", - " ],\n", - " [\n", - " 573.0440063476562,\n", - " 393.09100341796875,\n", - " 0.39379948377609253\n", - " ],\n", - " [\n", - " 480.1328125,\n", - " 431.7356262207031,\n", - " 0.2691611051559448\n", - " ],\n", - " [\n", - " 550.718505859375,\n", - " 466.6239318847656,\n", - " 0.5681474804878235\n", - " ],\n", - " [\n", - " 469.20660400390625,\n", - " 413.4725036621094,\n", - " 0.39115187525749207\n", - " ],\n", - " [\n", - " 133.34088134765625,\n", - " 172.20274353027344,\n", - " 0.3368547558784485\n", - " ],\n", - " [\n", - " 459.3551025390625,\n", - " 403.5044250488281,\n", - " 0.45792460441589355\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588105.9642372,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588105.996339\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.0221846,\n", - " \"step\": 20,\n", - " \"pose\": [\n", - " [\n", - " 288.93731689453125,\n", - " 298.9955749511719,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.2713928222656,\n", - " 308.37408447265625,\n", - " 0.943423330783844\n", - " ],\n", - " [\n", - " 296.5384826660156,\n", - " 315.9530944824219,\n", - " 0.603498101234436\n", - " ],\n", - " [\n", - " 290.7826843261719,\n", - " 311.4992980957031,\n", - " 0.6115655899047852\n", - " ],\n", - " [\n", - " 304.9278259277344,\n", - " 313.83294677734375,\n", - " 0.7098742127418518\n", - " ],\n", - " [\n", - " 284.4320373535156,\n", - " 267.431884765625,\n", - " 0.9166838526725769\n", - " ],\n", - " [\n", - " 287.8583984375,\n", - " 231.46864318847656,\n", - " 0.4841695725917816\n", - " ],\n", - " [\n", - " 294.63043212890625,\n", - " 218.80035400390625,\n", - " 0.29108670353889465\n", - " ],\n", - " [\n", - " 181.39418029785156,\n", - " 314.2521057128906,\n", - " 0.5189660787582397\n", - " ],\n", - " [\n", - " 304.42047119140625,\n", - " 209.36367797851562,\n", - " 0.40350794792175293\n", - " ],\n", - " [\n", - " 310.4044494628906,\n", - " 273.7499694824219,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.916259765625,\n", - " 275.2131042480469,\n", - " 0.6535776853561401\n", - " ],\n", - " [\n", - " 364.2809753417969,\n", - " 282.39776611328125,\n", - " 0.40148383378982544\n", - " ],\n", - " [\n", - " 180.97071838378906,\n", - " 317.89715576171875,\n", - " 0.3581520915031433\n", - " ],\n", - " [\n", - " 187.54930114746094,\n", - " 302.6390686035156,\n", - " 0.4738801419734955\n", - " ],\n", - " [\n", - " 354.7167663574219,\n", - " 341.0120849609375,\n", - " 0.38925886154174805\n", - " ],\n", - " [\n", - " 362.65179443359375,\n", - " 341.0782775878906,\n", - " 0.5246103405952454\n", - " ],\n", - " [\n", - " 333.5454406738281,\n", - " 342.33709716796875,\n", - " 0.3726454973220825\n", - " ],\n", - " [\n", - " 331.2152099609375,\n", - " 355.3799133300781,\n", - " 0.5273229479789734\n", - " ],\n", - " [\n", - " 292.8047790527344,\n", - " 357.6714172363281,\n", - " 0.4529617428779602\n", - " ],\n", - " [\n", - " 269.3791198730469,\n", - " 357.7329406738281,\n", - " 0.33708542585372925\n", - " ],\n", - " [\n", - " 413.47406005859375,\n", - " 371.240478515625,\n", - " 0.38202741742134094\n", - " ],\n", - " [\n", - " 266.7149658203125,\n", - " 358.42596435546875,\n", - " 0.255717396736145\n", - " ],\n", - " [\n", - " 599.5459594726562,\n", - " 377.59088134765625,\n", - " 0.6171793937683105\n", - " ],\n", - " [\n", - " 182.58990478515625,\n", - " 208.6776580810547,\n", - " 0.3807677626609802\n", - " ],\n", - " [\n", - " 563.1406860351562,\n", - " 387.25885009765625,\n", - " 0.4175715744495392\n", - " ],\n", - " [\n", - " 517.3438110351562,\n", - " 431.8337097167969,\n", - " 0.5834364295005798\n", - " ],\n", - " [\n", - " 571.3803100585938,\n", - " 186.68905639648438,\n", - " 0.3315950930118561\n", - " ],\n", - " [\n", - " 572.1192016601562,\n", - " 190.96864318847656,\n", - " 0.44181355834007263\n", - " ],\n", - " [\n", - " 513.9149169921875,\n", - " 444.82080078125,\n", - " 0.5983200073242188\n", - " ],\n", - " [\n", - " 590.0543212890625,\n", - " 409.4712219238281,\n", - " 0.48646411299705505\n", - " ],\n", - " [\n", - " 474.74908447265625,\n", - " 422.23394775390625,\n", - " 0.2016405165195465\n", - " ],\n", - " [\n", - " 108.44601440429688,\n", - " 270.20269775390625,\n", - " 0.18030454218387604\n", - " ],\n", - " [\n", - " 572.8609008789062,\n", - " 393.09124755859375,\n", - " 0.4335196018218994\n", - " ],\n", - " [\n", - " 479.59942626953125,\n", - " 431.8648376464844,\n", - " 0.26634445786476135\n", - " ],\n", - " [\n", - " 551.6661376953125,\n", - " 466.70135498046875,\n", - " 0.5170703530311584\n", - " ],\n", - " [\n", - " 468.4554443359375,\n", - " 413.3636779785156,\n", - " 0.391674280166626\n", - " ],\n", - " [\n", - " 135.02671813964844,\n", - " 171.04122924804688,\n", - " 0.3395681083202362\n", - " ],\n", - " [\n", - " 460.0732727050781,\n", - " 402.4387512207031,\n", - " 0.43441158533096313\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588105.9977791,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.0241945\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.0568671,\n", - " \"step\": 21,\n", - " \"pose\": [\n", - " [\n", - " 289.0395202636719,\n", - " 299.1026611328125,\n", - " 1.0\n", - " ],\n", - " [\n", - " 294.0700378417969,\n", - " 308.541015625,\n", - " 0.9578058123588562\n", - " ],\n", - " [\n", - " 296.70440673828125,\n", - " 315.8837890625,\n", - " 0.6122352480888367\n", - " ],\n", - " [\n", - " 291.8658752441406,\n", - " 311.9002685546875,\n", - " 0.5979498624801636\n", - " ],\n", - " [\n", - " 306.3223876953125,\n", - " 312.81304931640625,\n", - " 0.7840145826339722\n", - " ],\n", - " [\n", - " 284.8127136230469,\n", - " 267.5986328125,\n", - " 0.9234873652458191\n", - " ],\n", - " [\n", - " 295.66680908203125,\n", - " 222.97425842285156,\n", - " 0.5263190865516663\n", - " ],\n", - " [\n", - " 296.760009765625,\n", - " 216.06723022460938,\n", - " 0.33805859088897705\n", - " ],\n", - " [\n", - " 182.27871704101562,\n", - " 313.2104187011719,\n", - " 0.4452279508113861\n", - " ],\n", - " [\n", - " 302.8914794921875,\n", - " 207.4759521484375,\n", - " 0.4147292673587799\n", - " ],\n", - " [\n", - " 310.67901611328125,\n", - " 273.2028503417969,\n", - " 1.0\n", - " ],\n", - " [\n", - " 364.8524169921875,\n", - " 274.2853088378906,\n", - " 0.6022342443466187\n", - " ],\n", - " [\n", - " 363.9833679199219,\n", - " 282.55401611328125,\n", - " 0.4224339723587036\n", - " ],\n", - " [\n", - " 181.81272888183594,\n", - " 307.6803283691406,\n", - " 0.26984354853630066\n", - " ],\n", - " [\n", - " 194.29331970214844,\n", - " 306.2869873046875,\n", - " 0.40715906023979187\n", - " ],\n", - " [\n", - " 351.9360046386719,\n", - " 343.8916015625,\n", - " 0.3862766921520233\n", - " ],\n", - " [\n", - " 363.7510986328125,\n", - " 342.71826171875,\n", - " 0.5607332587242126\n", - " ],\n", - " [\n", - " 333.4976501464844,\n", - " 343.6668701171875,\n", - " 0.35671743750572205\n", - " ],\n", - " [\n", - " 332.6026916503906,\n", - " 355.7337341308594,\n", - " 0.49907931685447693\n", - " ],\n", - " [\n", - " 281.2688903808594,\n", - " 357.4167175292969,\n", - " 0.5524068474769592\n", - " ],\n", - " [\n", - " 270.1759338378906,\n", - " 357.4426574707031,\n", - " 0.47422295808792114\n", - " ],\n", - " [\n", - " 379.6234436035156,\n", - " 356.0726318359375,\n", - " 0.4222455620765686\n", - " ],\n", - " [\n", - " 266.31121826171875,\n", - " 358.93865966796875,\n", - " 0.4061802625656128\n", - " ],\n", - " [\n", - " 598.6159057617188,\n", - " 377.72808837890625,\n", - " 0.5579972863197327\n", - " ],\n", - " [\n", - " 522.2592163085938,\n", - " 318.6231994628906,\n", - " 0.3378920257091522\n", - " ],\n", - " [\n", - " 518.2576293945312,\n", - " 390.44976806640625,\n", - " 0.41228482127189636\n", - " ],\n", - " [\n", - " 515.7354736328125,\n", - " 429.0234069824219,\n", - " 0.5634108185768127\n", - " ],\n", - " [\n", - " 443.417724609375,\n", - " 356.7914733886719,\n", - " 0.26663434505462646\n", - " ],\n", - " [\n", - " 507.91925048828125,\n", - " 388.6474304199219,\n", - " 0.35248884558677673\n", - " ],\n", - " [\n", - " 512.753173828125,\n", - " 449.7189636230469,\n", - " 0.578186571598053\n", - " ],\n", - " [\n", - " 581.30078125,\n", - " 414.9436340332031,\n", - " 0.4276328682899475\n", - " ],\n", - " [\n", - " 463.5183410644531,\n", - " 402.17388916015625,\n", - " 0.13629432022571564\n", - " ],\n", - " [\n", - " 111.8991470336914,\n", - " 278.3125915527344,\n", - " 0.151170015335083\n", - " ],\n", - " [\n", - " 575.8933715820312,\n", - " 398.4580993652344,\n", - " 0.40502646565437317\n", - " ],\n", - " [\n", - " 143.69412231445312,\n", - " 345.78521728515625,\n", - " 0.24476872384548187\n", - " ],\n", - " [\n", - " 143.1280517578125,\n", - " 373.794189453125,\n", - " 0.4513229429721832\n", - " ],\n", - " [\n", - " 468.4044189453125,\n", - " 411.8726806640625,\n", - " 0.37500157952308655\n", - " ],\n", - " [\n", - " 462.43206787109375,\n", - " 399.8568115234375,\n", - " 0.3841189444065094\n", - " ],\n", - " [\n", - " 461.3127136230469,\n", - " 403.23052978515625,\n", - " 0.5030932426452637\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588106.0300262,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.0578823\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.0853903,\n", - " \"step\": 22,\n", - " \"pose\": [\n", - " [\n", - " 289.0032653808594,\n", - " 299.5898132324219,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.6610412597656,\n", - " 308.364501953125,\n", - " 0.9171743392944336\n", - " ],\n", - " [\n", - " 297.53411865234375,\n", - " 315.0213928222656,\n", - " 0.5967923998832703\n", - " ],\n", - " [\n", - " 291.45599365234375,\n", - " 310.9009094238281,\n", - " 0.6069502234458923\n", - " ],\n", - " [\n", - " 305.15667724609375,\n", - " 312.3479919433594,\n", - " 0.7363993525505066\n", - " ],\n", - " [\n", - " 284.5779724121094,\n", - " 267.80059814453125,\n", - " 0.8958398699760437\n", - " ],\n", - " [\n", - " 287.5237121582031,\n", - " 231.7471923828125,\n", - " 0.5256384611129761\n", - " ],\n", - " [\n", - " 297.3427734375,\n", - " 217.41281127929688,\n", - " 0.32152754068374634\n", - " ],\n", - " [\n", - " 182.88433837890625,\n", - " 314.53692626953125,\n", - " 0.489521861076355\n", - " ],\n", - " [\n", - " 303.3013916015625,\n", - " 209.451171875,\n", - " 0.42159581184387207\n", - " ],\n", - " [\n", - " 310.30352783203125,\n", - " 273.47393798828125,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.04669189453125,\n", - " 275.4956970214844,\n", - " 0.6113792061805725\n", - " ],\n", - " [\n", - " 364.1795959472656,\n", - " 282.3038024902344,\n", - " 0.44920432567596436\n", - " ],\n", - " [\n", - " 185.60179138183594,\n", - " 312.93963623046875,\n", - " 0.2924720048904419\n", - " ],\n", - " [\n", - " 188.79063415527344,\n", - " 306.9405822753906,\n", - " 0.4584471881389618\n", - " ],\n", - " [\n", - " 355.7679443359375,\n", - " 340.5166320800781,\n", - " 0.4532307982444763\n", - " ],\n", - " [\n", - " 362.6515197753906,\n", - " 342.4863586425781,\n", - " 0.5490198731422424\n", - " ],\n", - " [\n", - " 334.1615905761719,\n", - " 344.5025939941406,\n", - " 0.33372119069099426\n", - " ],\n", - " [\n", - " 331.80181884765625,\n", - " 355.52691650390625,\n", - " 0.4720294773578644\n", - " ],\n", - " [\n", - " 290.5385437011719,\n", - " 358.4304504394531,\n", - " 0.4409693479537964\n", - " ],\n", - " [\n", - " 270.8581237792969,\n", - " 356.5791931152344,\n", - " 0.4198722839355469\n", - " ],\n", - " [\n", - " 424.2137145996094,\n", - " 375.2749328613281,\n", - " 0.4370681345462799\n", - " ],\n", - " [\n", - " 268.27886962890625,\n", - " 357.68585205078125,\n", - " 0.36227190494537354\n", - " ],\n", - " [\n", - " 599.3430786132812,\n", - " 377.6954650878906,\n", - " 0.5684486031532288\n", - " ],\n", - " [\n", - " 520.1165771484375,\n", - " 318.81927490234375,\n", - " 0.3776821196079254\n", - " ],\n", - " [\n", - " 562.5358276367188,\n", - " 387.04693603515625,\n", - " 0.3308621942996979\n", - " ],\n", - " [\n", - " 516.3358154296875,\n", - " 429.0692443847656,\n", - " 0.5608440637588501\n", - " ],\n", - " [\n", - " 566.9133911132812,\n", - " 193.52452087402344,\n", - " 0.33105629682540894\n", - " ],\n", - " [\n", - " 570.7525634765625,\n", - " 196.58692932128906,\n", - " 0.4755719006061554\n", - " ],\n", - " [\n", - " 512.5366821289062,\n", - " 448.55328369140625,\n", - " 0.5968109369277954\n", - " ],\n", - " [\n", - " 590.8397216796875,\n", - " 408.89471435546875,\n", - " 0.4935222268104553\n", - " ],\n", - " [\n", - " 110.8057632446289,\n", - " 271.4251403808594,\n", - " 0.16251139342784882\n", - " ],\n", - " [\n", - " 113.5406494140625,\n", - " 279.87841796875,\n", - " 0.38004982471466064\n", - " ],\n", - " [\n", - " 576.237548828125,\n", - " 397.9443664550781,\n", - " 0.3451719284057617\n", - " ],\n", - " [\n", - " 144.29734802246094,\n", - " 315.7247009277344,\n", - " 0.25869646668434143\n", - " ],\n", - " [\n", - " 549.8232421875,\n", - " 465.3925476074219,\n", - " 0.483270525932312\n", - " ],\n", - " [\n", - " 141.22862243652344,\n", - " 190.88340759277344,\n", - " 0.3440597653388977\n", - " ],\n", - " [\n", - " 462.27349853515625,\n", - " 407.5166931152344,\n", - " 0.39141225814819336\n", - " ],\n", - " [\n", - " 459.8392333984375,\n", - " 402.88421630859375,\n", - " 0.48188316822052\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588106.0603576,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.0853903\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.113898,\n", - " \"step\": 23,\n", - " \"pose\": [\n", - " [\n", - " 289.3703918457031,\n", - " 299.01654052734375,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.63714599609375,\n", - " 308.040771484375,\n", - " 0.9116361141204834\n", - " ],\n", - " [\n", - " 297.54144287109375,\n", - " 314.6742248535156,\n", - " 0.5906339287757874\n", - " ],\n", - " [\n", - " 292.0235290527344,\n", - " 310.41552734375,\n", - " 0.6293807029724121\n", - " ],\n", - " [\n", - " 305.7689514160156,\n", - " 312.5812072753906,\n", - " 0.6732482314109802\n", - " ],\n", - " [\n", - " 284.58123779296875,\n", - " 267.2427673339844,\n", - " 0.9067515134811401\n", - " ],\n", - " [\n", - " 183.6278076171875,\n", - " 312.8146667480469,\n", - " 0.5189109444618225\n", - " ],\n", - " [\n", - " 373.2416076660156,\n", - " 300.8122863769531,\n", - " 0.3220520615577698\n", - " ],\n", - " [\n", - " 182.9404754638672,\n", - " 312.4514465332031,\n", - " 0.5909151434898376\n", - " ],\n", - " [\n", - " 303.0302429199219,\n", - " 210.0631561279297,\n", - " 0.4749321937561035\n", - " ],\n", - " [\n", - " 310.4212951660156,\n", - " 273.31060791015625,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.5233459472656,\n", - " 275.4824523925781,\n", - " 0.5813355445861816\n", - " ],\n", - " [\n", - " 363.8905029296875,\n", - " 281.9700622558594,\n", - " 0.42637211084365845\n", - " ],\n", - " [\n", - " 185.95431518554688,\n", - " 311.6904296875,\n", - " 0.34200912714004517\n", - " ],\n", - " [\n", - " 192.42385864257812,\n", - " 305.3067932128906,\n", - " 0.4337136447429657\n", - " ],\n", - " [\n", - " 355.9394836425781,\n", - " 340.25689697265625,\n", - " 0.43801149725914\n", - " ],\n", - " [\n", - " 363.40325927734375,\n", - " 341.9284362792969,\n", - " 0.5555760264396667\n", - " ],\n", - " [\n", - " 314.46746826171875,\n", - " 342.5721130371094,\n", - " 0.35660916566848755\n", - " ],\n", - " [\n", - " 339.87835693359375,\n", - " 359.97998046875,\n", - " 0.4949004054069519\n", - " ],\n", - " [\n", - " 290.6855773925781,\n", - " 358.2529602050781,\n", - " 0.4353252649307251\n", - " ],\n", - " [\n", - " 270.9769287109375,\n", - " 356.5190124511719,\n", - " 0.40807199478149414\n", - " ],\n", - " [\n", - " 424.1128845214844,\n", - " 374.64202880859375,\n", - " 0.42985209822654724\n", - " ],\n", - " [\n", - " 267.4349670410156,\n", - " 357.9966125488281,\n", - " 0.3516447842121124\n", - " ],\n", - " [\n", - " 597.1614990234375,\n", - " 379.19207763671875,\n", - " 0.5612181425094604\n", - " ],\n", - " [\n", - " 200.91233825683594,\n", - " 229.2719268798828,\n", - " 0.3809609115123749\n", - " ],\n", - " [\n", - " 563.5918579101562,\n", - " 387.2767028808594,\n", - " 0.3711811602115631\n", - " ],\n", - " [\n", - " 516.4181518554688,\n", - " 428.2336120605469,\n", - " 0.6263866424560547\n", - " ],\n", - " [\n", - " 570.1748046875,\n", - " 189.33961486816406,\n", - " 0.3523023724555969\n", - " ],\n", - " [\n", - " 569.3735961914062,\n", - " 194.79763793945312,\n", - " 0.36297234892845154\n", - " ],\n", - " [\n", - " 512.5416259765625,\n", - " 448.44085693359375,\n", - " 0.5241063833236694\n", - " ],\n", - " [\n", - " 155.5992889404297,\n", - " 363.5351867675781,\n", - " 0.46883660554885864\n", - " ],\n", - " [\n", - " 508.84210205078125,\n", - " 377.6853332519531,\n", - " 0.1948014795780182\n", - " ],\n", - " [\n", - " 112.65641021728516,\n", - " 278.4239501953125,\n", - " 0.18293216824531555\n", - " ],\n", - " [\n", - " 572.6771850585938,\n", - " 392.94891357421875,\n", - " 0.39113080501556396\n", - " ],\n", - " [\n", - " 481.0816650390625,\n", - " 432.840087890625,\n", - " 0.22011691331863403\n", - " ],\n", - " [\n", - " 145.10130310058594,\n", - " 373.3096618652344,\n", - " 0.4219147264957428\n", - " ],\n", - " [\n", - " 468.0207824707031,\n", - " 413.1219787597656,\n", - " 0.3619706630706787\n", - " ],\n", - " [\n", - " 136.87318420410156,\n", - " 172.54913330078125,\n", - " 0.3659115135669708\n", - " ],\n", - " [\n", - " 459.65655517578125,\n", - " 403.489013671875,\n", - " 0.46539226174354553\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588106.0933394,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.113898\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.1507206,\n", - " \"step\": 24,\n", - " \"pose\": [\n", - " [\n", - " 289.14605712890625,\n", - " 298.5685729980469,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.7758483886719,\n", - " 308.03326416015625,\n", - " 0.9337582588195801\n", - " ],\n", - " [\n", - " 297.73248291015625,\n", - " 315.2346496582031,\n", - " 0.6327982544898987\n", - " ],\n", - " [\n", - " 291.4134216308594,\n", - " 311.653564453125,\n", - " 0.609774649143219\n", - " ],\n", - " [\n", - " 305.1743469238281,\n", - " 313.22369384765625,\n", - " 0.7303615808486938\n", - " ],\n", - " [\n", - " 285.11077880859375,\n", - " 267.58795166015625,\n", - " 0.9044018387794495\n", - " ],\n", - " [\n", - " 287.801025390625,\n", - " 230.8278045654297,\n", - " 0.5132544636726379\n", - " ],\n", - " [\n", - " 294.9792785644531,\n", - " 217.2659912109375,\n", - " 0.3110557496547699\n", - " ],\n", - " [\n", - " 182.70785522460938,\n", - " 312.7431640625,\n", - " 0.4303653836250305\n", - " ],\n", - " [\n", - " 302.67926025390625,\n", - " 209.3670196533203,\n", - " 0.3863958716392517\n", - " ],\n", - " [\n", - " 310.23931884765625,\n", - " 273.41265869140625,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.62945556640625,\n", - " 276.4300842285156,\n", - " 0.6256653070449829\n", - " ],\n", - " [\n", - " 363.4427185058594,\n", - " 281.51226806640625,\n", - " 0.4842453896999359\n", - " ],\n", - " [\n", - " 357.33184814453125,\n", - " 280.7920227050781,\n", - " 0.27625176310539246\n", - " ],\n", - " [\n", - " 194.59628295898438,\n", - " 306.9119873046875,\n", - " 0.41847968101501465\n", - " ],\n", - " [\n", - " 355.71160888671875,\n", - " 338.88134765625,\n", - " 0.44307053089141846\n", - " ],\n", - " [\n", - " 362.9523010253906,\n", - " 339.22442626953125,\n", - " 0.6132069230079651\n", - " ],\n", - " [\n", - " 317.2615051269531,\n", - " 345.85675048828125,\n", - " 0.35067668557167053\n", - " ],\n", - " [\n", - " 331.7022705078125,\n", - " 355.4823303222656,\n", - " 0.45521220564842224\n", - " ],\n", - " [\n", - " 363.580810546875,\n", - " 345.0674743652344,\n", - " 0.4948749542236328\n", - " ],\n", - " [\n", - " 269.8246765136719,\n", - " 356.2699279785156,\n", - " 0.5052472949028015\n", - " ],\n", - " [\n", - " 423.73480224609375,\n", - " 374.68438720703125,\n", - " 0.4521806836128235\n", - " ],\n", - " [\n", - " 266.7372131347656,\n", - " 357.82562255859375,\n", - " 0.44915807247161865\n", - " ],\n", - " [\n", - " 598.9044189453125,\n", - " 377.9191589355469,\n", - " 0.5668219923973083\n", - " ],\n", - " [\n", - " 185.02847290039062,\n", - " 209.2593536376953,\n", - " 0.29630619287490845\n", - " ],\n", - " [\n", - " 232.27960205078125,\n", - " 424.1368408203125,\n", - " 0.35069289803504944\n", - " ],\n", - " [\n", - " 516.3990478515625,\n", - " 429.19805908203125,\n", - " 0.6708835959434509\n", - " ],\n", - " [\n", - " 443.72869873046875,\n", - " 355.66790771484375,\n", - " 0.18464210629463196\n", - " ],\n", - " [\n", - " 509.17938232421875,\n", - " 388.7170104980469,\n", - " 0.4232410788536072\n", - " ],\n", - " [\n", - " 513.5040893554688,\n", - " 449.72918701171875,\n", - " 0.5325906872749329\n", - " ],\n", - " [\n", - " 583.7595825195312,\n", - " 413.1138916015625,\n", - " 0.4442688822746277\n", - " ],\n", - " [\n", - " 115.84835052490234,\n", - " 221.41104125976562,\n", - " 0.1638604700565338\n", - " ],\n", - " [\n", - " 116.47493743896484,\n", - " 283.7770690917969,\n", - " 0.2362251728773117\n", - " ],\n", - " [\n", - " 576.6185913085938,\n", - " 399.1842041015625,\n", - " 0.4103768765926361\n", - " ],\n", - " [\n", - " 144.26768493652344,\n", - " 343.4598693847656,\n", - " 0.2693617641925812\n", - " ],\n", - " [\n", - " 550.6482543945312,\n", - " 465.9261474609375,\n", - " 0.52559894323349\n", - " ],\n", - " [\n", - " 467.72491455078125,\n", - " 412.5487365722656,\n", - " 0.3213626742362976\n", - " ],\n", - " [\n", - " 466.04193115234375,\n", - " 410.77960205078125,\n", - " 0.32539424300193787\n", - " ],\n", - " [\n", - " 459.31622314453125,\n", - " 402.8010559082031,\n", - " 0.4295216500759125\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588106.1260552,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.1507206\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.1780674,\n", - " \"step\": 25,\n", - " \"pose\": [\n", - " [\n", - " 289.6149597167969,\n", - " 299.89166259765625,\n", - " 1.0\n", - " ],\n", - " [\n", - " 294.52008056640625,\n", - " 308.8827209472656,\n", - " 0.9231247305870056\n", - " ],\n", - " [\n", - " 298.28369140625,\n", - " 316.6026916503906,\n", - " 0.6108699440956116\n", - " ],\n", - " [\n", - " 292.314697265625,\n", - " 312.0814514160156,\n", - " 0.5867233276367188\n", - " ],\n", - " [\n", - " 306.8653564453125,\n", - " 313.03192138671875,\n", - " 0.7602759599685669\n", - " ],\n", - " [\n", - " 284.97998046875,\n", - " 268.31109619140625,\n", - " 0.9025271534919739\n", - " ],\n", - " [\n", - " 285.36627197265625,\n", - " 239.4760284423828,\n", - " 0.45559558272361755\n", - " ],\n", - " [\n", - " 278.97967529296875,\n", - " 251.67454528808594,\n", - " 0.3040803074836731\n", - " ],\n", - " [\n", - " 182.5161590576172,\n", - " 314.0823974609375,\n", - " 0.487441748380661\n", - " ],\n", - " [\n", - " 303.5965881347656,\n", - " 209.8799591064453,\n", - " 0.39406585693359375\n", - " ],\n", - " [\n", - " 310.803466796875,\n", - " 273.83941650390625,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.4359130859375,\n", - " 276.5120849609375,\n", - " 0.5850675106048584\n", - " ],\n", - " [\n", - " 364.39306640625,\n", - " 281.8566589355469,\n", - " 0.43770086765289307\n", - " ],\n", - " [\n", - " 184.96409606933594,\n", - " 312.2420349121094,\n", - " 0.32801908254623413\n", - " ],\n", - " [\n", - " 188.65463256835938,\n", - " 306.26580810546875,\n", - " 0.44451019167900085\n", - " ],\n", - " [\n", - " 356.0030517578125,\n", - " 340.06390380859375,\n", - " 0.393046498298645\n", - " ],\n", - " [\n", - " 361.8909606933594,\n", - " 340.0927429199219,\n", - " 0.5161962509155273\n", - " ],\n", - " [\n", - " 315.6810607910156,\n", - " 343.1770935058594,\n", - " 0.37498360872268677\n", - " ],\n", - " [\n", - " 330.904052734375,\n", - " 355.0949401855469,\n", - " 0.5339792370796204\n", - " ],\n", - " [\n", - " 367.3807067871094,\n", - " 343.9208679199219,\n", - " 0.41494372487068176\n", - " ],\n", - " [\n", - " 270.63043212890625,\n", - " 356.5071716308594,\n", - " 0.39088016748428345\n", - " ],\n", - " [\n", - " 424.5980529785156,\n", - " 377.47625732421875,\n", - " 0.40465661883354187\n", - " ],\n", - " [\n", - " 267.8541259765625,\n", - " 358.0108642578125,\n", - " 0.33039915561676025\n", - " ],\n", - " [\n", - " 599.1412353515625,\n", - " 378.35345458984375,\n", - " 0.5644627213478088\n", - " ],\n", - " [\n", - " 184.03726196289062,\n", - " 209.42005920410156,\n", - " 0.459592342376709\n", - " ],\n", - " [\n", - " 511.7866516113281,\n", - " 408.0044860839844,\n", - " 0.3771219551563263\n", - " ],\n", - " [\n", - " 516.994873046875,\n", - " 430.9440612792969,\n", - " 0.5924205780029297\n", - " ],\n", - " [\n", - " 443.1100158691406,\n", - " 355.68621826171875,\n", - " 0.2457418441772461\n", - " ],\n", - " [\n", - " 499.76837158203125,\n", - " 422.5597839355469,\n", - " 0.24084235727787018\n", - " ],\n", - " [\n", - " 511.1026611328125,\n", - " 445.35107421875,\n", - " 0.5383387804031372\n", - " ],\n", - " [\n", - " 589.8642578125,\n", - " 408.1378173828125,\n", - " 0.456050306558609\n", - " ],\n", - " [\n", - " 507.7047119140625,\n", - " 379.2082214355469,\n", - " 0.16193071007728577\n", - " ],\n", - " [\n", - " 112.13590240478516,\n", - " 276.6860656738281,\n", - " 0.282148540019989\n", - " ],\n", - " [\n", - " 573.1347045898438,\n", - " 393.6588439941406,\n", - " 0.4297298192977905\n", - " ],\n", - " [\n", - " 482.9963073730469,\n", - " 432.75811767578125,\n", - " 0.2714385986328125\n", - " ],\n", - " [\n", - " 550.9261474609375,\n", - " 467.3409118652344,\n", - " 0.5166769623756409\n", - " ],\n", - " [\n", - " 468.4465026855469,\n", - " 413.2013244628906,\n", - " 0.42883676290512085\n", - " ],\n", - " [\n", - " 133.96446228027344,\n", - " 171.50291442871094,\n", - " 0.35597431659698486\n", - " ],\n", - " [\n", - " 465.5216369628906,\n", - " 407.3671875,\n", - " 0.4744454324245453\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588106.1553848,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.1780674\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.2132318,\n", - " \"step\": 26,\n", - " \"pose\": [\n", - " [\n", - " 289.6811218261719,\n", - " 299.3060607910156,\n", - " 1.0\n", - " ],\n", - " [\n", - " 294.1546630859375,\n", - " 308.6109924316406,\n", - " 0.9218483567237854\n", - " ],\n", - " [\n", - " 297.23516845703125,\n", - " 316.22552490234375,\n", - " 0.5849137902259827\n", - " ],\n", - " [\n", - " 292.8453369140625,\n", - " 312.3377990722656,\n", - " 0.5977075695991516\n", - " ],\n", - " [\n", - " 306.42193603515625,\n", - " 313.16546630859375,\n", - " 0.73295658826828\n", - " ],\n", - " [\n", - " 285.2742919921875,\n", - " 268.0978698730469,\n", - " 0.8919780254364014\n", - " ],\n", - " [\n", - " 285.22930908203125,\n", - " 240.1131134033203,\n", - " 0.48513516783714294\n", - " ],\n", - " [\n", - " 278.72900390625,\n", - " 252.45925903320312,\n", - " 0.30949145555496216\n", - " ],\n", - " [\n", - " 182.2201690673828,\n", - " 313.8691711425781,\n", - " 0.4198264181613922\n", - " ],\n", - " [\n", - " 302.0194091796875,\n", - " 209.4342803955078,\n", - " 0.3796064555644989\n", - " ],\n", - " [\n", - " 311.20037841796875,\n", - " 273.72052001953125,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.5647888183594,\n", - " 275.8450622558594,\n", - " 0.551364004611969\n", - " ],\n", - " [\n", - " 361.8849792480469,\n", - " 280.7410583496094,\n", - " 0.4334920346736908\n", - " ],\n", - " [\n", - " 185.7280731201172,\n", - " 312.75677490234375,\n", - " 0.29518699645996094\n", - " ],\n", - " [\n", - " 188.90249633789062,\n", - " 306.63177490234375,\n", - " 0.43662378191947937\n", - " ],\n", - " [\n", - " 355.60833740234375,\n", - " 340.0720520019531,\n", - " 0.4073914885520935\n", - " ],\n", - " [\n", - " 362.4900817871094,\n", - " 339.8585205078125,\n", - " 0.5256603956222534\n", - " ],\n", - " [\n", - " 310.6485900878906,\n", - " 346.7264404296875,\n", - " 0.3758726119995117\n", - " ],\n", - " [\n", - " 331.7028503417969,\n", - " 357.2618408203125,\n", - " 0.5182921886444092\n", - " ],\n", - " [\n", - " 282.8375244140625,\n", - " 356.63385009765625,\n", - " 0.47025105357170105\n", - " ],\n", - " [\n", - " 271.1107482910156,\n", - " 356.7911376953125,\n", - " 0.49355366826057434\n", - " ],\n", - " [\n", - " 424.5859680175781,\n", - " 375.9132080078125,\n", - " 0.3878205716609955\n", - " ],\n", - " [\n", - " 267.3672180175781,\n", - " 358.4584655761719,\n", - " 0.4485253393650055\n", - " ],\n", - " [\n", - " 598.8714599609375,\n", - " 377.88165283203125,\n", - " 0.5623369216918945\n", - " ],\n", - " [\n", - " 184.80422973632812,\n", - " 207.90528869628906,\n", - " 0.4335136115550995\n", - " ],\n", - " [\n", - " 513.0913696289062,\n", - " 407.55938720703125,\n", - " 0.34691154956817627\n", - " ],\n", - " [\n", - " 517.757080078125,\n", - " 432.6747131347656,\n", - " 0.5728744864463806\n", - " ],\n", - " [\n", - " 163.63232421875,\n", - " 212.3854217529297,\n", - " 0.32033267617225647\n", - " ],\n", - " [\n", - " 570.7784423828125,\n", - " 193.9116973876953,\n", - " 0.40288540720939636\n", - " ],\n", - " [\n", - " 513.4071655273438,\n", - " 445.5836486816406,\n", - " 0.6099948883056641\n", - " ],\n", - " [\n", - " 590.9609985351562,\n", - " 408.857421875,\n", - " 0.4939699172973633\n", - " ],\n", - " [\n", - " 476.909423828125,\n", - " 420.9211120605469,\n", - " 0.17517627775669098\n", - " ],\n", - " [\n", - " 111.49516296386719,\n", - " 278.0699462890625,\n", - " 0.20062971115112305\n", - " ],\n", - " [\n", - " 572.727294921875,\n", - " 393.2834167480469,\n", - " 0.39421916007995605\n", - " ],\n", - " [\n", - " 481.32647705078125,\n", - " 431.9410400390625,\n", - " 0.2601860463619232\n", - " ],\n", - " [\n", - " 511.23797607421875,\n", - " 458.18768310546875,\n", - " 0.5182275772094727\n", - " ],\n", - " [\n", - " 468.54132080078125,\n", - " 413.6605529785156,\n", - " 0.40847253799438477\n", - " ],\n", - " [\n", - " 464.9053649902344,\n", - " 410.059326171875,\n", - " 0.35141322016716003\n", - " ],\n", - " [\n", - " 459.9055480957031,\n", - " 403.2681579589844,\n", - " 0.4966220259666443\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588106.1875935,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.2132318\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.2483606,\n", - " \"step\": 27,\n", - " \"pose\": [\n", - " [\n", - " 289.65423583984375,\n", - " 298.561767578125,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.9566955566406,\n", - " 308.3065490722656,\n", - " 0.9470576047897339\n", - " ],\n", - " [\n", - " 297.505859375,\n", - " 314.407958984375,\n", - " 0.6088659763336182\n", - " ],\n", - " [\n", - " 291.20574951171875,\n", - " 310.8966064453125,\n", - " 0.607652485370636\n", - " ],\n", - " [\n", - " 306.73492431640625,\n", - " 312.7744445800781,\n", - " 0.7566508054733276\n", - " ],\n", - " [\n", - " 285.1037902832031,\n", - " 267.3839416503906,\n", - " 0.906174898147583\n", - " ],\n", - " [\n", - " 185.3070526123047,\n", - " 311.79327392578125,\n", - " 0.5088176727294922\n", - " ],\n", - " [\n", - " 154.76300048828125,\n", - " 314.7828674316406,\n", - " 0.29141828417778015\n", - " ],\n", - " [\n", - " 182.6195068359375,\n", - " 312.42791748046875,\n", - " 0.5560340285301208\n", - " ],\n", - " [\n", - " 304.5721435546875,\n", - " 209.04302978515625,\n", - " 0.4600820243358612\n", - " ],\n", - " [\n", - " 310.9137878417969,\n", - " 273.2908935546875,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.1856994628906,\n", - " 276.47381591796875,\n", - " 0.5621199011802673\n", - " ],\n", - " [\n", - " 363.20672607421875,\n", - " 281.5467224121094,\n", - " 0.427944153547287\n", - " ],\n", - " [\n", - " 181.0316619873047,\n", - " 311.6895446777344,\n", - " 0.3624846041202545\n", - " ],\n", - " [\n", - " 188.46315002441406,\n", - " 304.592529296875,\n", - " 0.5054963231086731\n", - " ],\n", - " [\n", - " 353.033447265625,\n", - " 342.5238037109375,\n", - " 0.40231525897979736\n", - " ],\n", - " [\n", - " 362.5194091796875,\n", - " 339.60711669921875,\n", - " 0.6008350849151611\n", - " ],\n", - " [\n", - " 317.26544189453125,\n", - " 342.3359069824219,\n", - " 0.3960641920566559\n", - " ],\n", - " [\n", - " 331.4060974121094,\n", - " 356.75445556640625,\n", - " 0.5937938690185547\n", - " ],\n", - " [\n", - " 293.347412109375,\n", - " 357.84515380859375,\n", - " 0.4450031816959381\n", - " ],\n", - " [\n", - " 269.11407470703125,\n", - " 357.3741455078125,\n", - " 0.4057061970233917\n", - " ],\n", - " [\n", - " 378.8157653808594,\n", - " 355.3031921386719,\n", - " 0.39472222328186035\n", - " ],\n", - " [\n", - " 265.99078369140625,\n", - " 358.6902160644531,\n", - " 0.3461160957813263\n", - " ],\n", - " [\n", - " 598.9271850585938,\n", - " 377.77618408203125,\n", - " 0.5730097889900208\n", - " ],\n", - " [\n", - " 184.35238647460938,\n", - " 209.6236572265625,\n", - " 0.3743739724159241\n", - " ],\n", - " [\n", - " 513.5267333984375,\n", - " 407.8452453613281,\n", - " 0.394991397857666\n", - " ],\n", - " [\n", - " 517.43359375,\n", - " 432.714111328125,\n", - " 0.6186484694480896\n", - " ],\n", - " [\n", - " 566.1676635742188,\n", - " 200.06082153320312,\n", - " 0.36133381724357605\n", - " ],\n", - " [\n", - " 570.642333984375,\n", - " 203.29977416992188,\n", - " 0.2848742604255676\n", - " ],\n", - " [\n", - " 513.6658935546875,\n", - " 447.7688903808594,\n", - " 0.566241979598999\n", - " ],\n", - " [\n", - " 590.916259765625,\n", - " 409.0061950683594,\n", - " 0.5227742791175842\n", - " ],\n", - " [\n", - " 476.4045104980469,\n", - " 421.7107849121094,\n", - " 0.23629191517829895\n", - " ],\n", - " [\n", - " 109.81532287597656,\n", - " 272.0874938964844,\n", - " 0.2973298132419586\n", - " ],\n", - " [\n", - " 572.2440185546875,\n", - " 393.4117431640625,\n", - " 0.4230390191078186\n", - " ],\n", - " [\n", - " 481.6334228515625,\n", - " 434.35992431640625,\n", - " 0.20489251613616943\n", - " ],\n", - " [\n", - " 511.52740478515625,\n", - " 458.0361633300781,\n", - " 0.47707173228263855\n", - " ],\n", - " [\n", - " 468.145751953125,\n", - " 413.9035339355469,\n", - " 0.4124965965747833\n", - " ],\n", - " [\n", - " 465.691650390625,\n", - " 411.6900329589844,\n", - " 0.3242393732070923\n", - " ],\n", - " [\n", - " 459.133544921875,\n", - " 403.29656982421875,\n", - " 0.4457142651081085\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588106.2229764,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.2503667\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.2815137,\n", - " \"step\": 28,\n", - " \"pose\": [\n", - " [\n", - " 289.7947998046875,\n", - " 299.4747314453125,\n", - " 1.0\n", - " ],\n", - " [\n", - " 294.1377868652344,\n", - " 308.9644470214844,\n", - " 0.9098001718521118\n", - " ],\n", - " [\n", - " 297.0553894042969,\n", - " 316.2694396972656,\n", - " 0.5839970111846924\n", - " ],\n", - " [\n", - " 292.7187194824219,\n", - " 312.2520446777344,\n", - " 0.5812930464744568\n", - " ],\n", - " [\n", - " 306.51007080078125,\n", - " 313.4143981933594,\n", - " 0.7014763355255127\n", - " ],\n", - " [\n", - " 285.4446105957031,\n", - " 267.66400146484375,\n", - " 0.885840654373169\n", - " ],\n", - " [\n", - " 287.2601623535156,\n", - " 232.3909912109375,\n", - " 0.47544583678245544\n", - " ],\n", - " [\n", - " 279.16656494140625,\n", - " 250.76365661621094,\n", - " 0.29200607538223267\n", - " ],\n", - " [\n", - " 182.51055908203125,\n", - " 314.830322265625,\n", - " 0.4986189305782318\n", - " ],\n", - " [\n", - " 304.1475524902344,\n", - " 207.91217041015625,\n", - " 0.5152255892753601\n", - " ],\n", - " [\n", - " 311.350830078125,\n", - " 273.5013122558594,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.06219482421875,\n", - " 276.67120361328125,\n", - " 0.5790920853614807\n", - " ],\n", - " [\n", - " 362.5161437988281,\n", - " 283.22723388671875,\n", - " 0.4104999601840973\n", - " ],\n", - " [\n", - " 181.13710021972656,\n", - " 312.584716796875,\n", - " 0.29986241459846497\n", - " ],\n", - " [\n", - " 188.63394165039062,\n", - " 303.74395751953125,\n", - " 0.45568570494651794\n", - " ],\n", - " [\n", - " 357.4764099121094,\n", - " 339.7001953125,\n", - " 0.4089646339416504\n", - " ],\n", - " [\n", - " 362.9626770019531,\n", - " 339.128662109375,\n", - " 0.4946100115776062\n", - " ],\n", - " [\n", - " 311.04779052734375,\n", - " 346.371826171875,\n", - " 0.39371228218078613\n", - " ],\n", - " [\n", - " 339.0541687011719,\n", - " 359.549560546875,\n", - " 0.5586241483688354\n", - " ],\n", - " [\n", - " 283.3900146484375,\n", - " 356.6086120605469,\n", - " 0.4299786686897278\n", - " ],\n", - " [\n", - " 270.4642333984375,\n", - " 357.4127502441406,\n", - " 0.40563082695007324\n", - " ],\n", - " [\n", - " 423.99005126953125,\n", - " 375.4238586425781,\n", - " 0.43916311860084534\n", - " ],\n", - " [\n", - " 268.08233642578125,\n", - " 358.42919921875,\n", - " 0.3316787779331207\n", - " ],\n", - " [\n", - " 598.7156982421875,\n", - " 377.3929138183594,\n", - " 0.5510212182998657\n", - " ],\n", - " [\n", - " 182.943115234375,\n", - " 208.30332946777344,\n", - " 0.44296297430992126\n", - " ],\n", - " [\n", - " 564.0025024414062,\n", - " 386.8399353027344,\n", - " 0.39091676473617554\n", - " ],\n", - " [\n", - " 517.2132568359375,\n", - " 432.1662292480469,\n", - " 0.6276928186416626\n", - " ],\n", - " [\n", - " 570.1396484375,\n", - " 190.4938507080078,\n", - " 0.2375359982252121\n", - " ],\n", - " [\n", - " 492.12896728515625,\n", - " 413.6061096191406,\n", - " 0.3405477702617645\n", - " ],\n", - " [\n", - " 512.7205200195312,\n", - " 450.4252624511719,\n", - " 0.6235648393630981\n", - " ],\n", - " [\n", - " 590.1025390625,\n", - " 409.0594787597656,\n", - " 0.5204903483390808\n", - " ],\n", - " [\n", - " 476.5130310058594,\n", - " 422.0225524902344,\n", - " 0.14199496805667877\n", - " ],\n", - " [\n", - " 110.05477142333984,\n", - " 271.470703125,\n", - " 0.22186370193958282\n", - " ],\n", - " [\n", - " 571.6903686523438,\n", - " 393.01318359375,\n", - " 0.4487222135066986\n", - " ],\n", - " [\n", - " 142.70831298828125,\n", - " 346.6425476074219,\n", - " 0.2570325434207916\n", - " ],\n", - " [\n", - " 511.7834167480469,\n", - " 456.3761901855469,\n", - " 0.5804262161254883\n", - " ],\n", - " [\n", - " 468.8331298828125,\n", - " 413.937744140625,\n", - " 0.36150258779525757\n", - " ],\n", - " [\n", - " 465.01104736328125,\n", - " 410.2015380859375,\n", - " 0.3218684196472168\n", - " ],\n", - " [\n", - " 460.2878723144531,\n", - " 402.61614990234375,\n", - " 0.44286683201789856\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588106.2539957,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.2815137\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.313648,\n", - " \"step\": 29,\n", - " \"pose\": [\n", - " [\n", - " 290.2019958496094,\n", - " 299.82733154296875,\n", - " 1.0\n", - " ],\n", - " [\n", - " 294.038818359375,\n", - " 309.61590576171875,\n", - " 0.8994060754776001\n", - " ],\n", - " [\n", - " 297.1882019042969,\n", - " 316.2828369140625,\n", - " 0.580872118473053\n", - " ],\n", - " [\n", - " 292.24969482421875,\n", - " 311.94366455078125,\n", - " 0.553147554397583\n", - " ],\n", - " [\n", - " 305.8408508300781,\n", - " 312.88555908203125,\n", - " 0.7357133626937866\n", - " ],\n", - " [\n", - " 285.2379455566406,\n", - " 267.34576416015625,\n", - " 0.8884855508804321\n", - " ],\n", - " [\n", - " 288.9740905761719,\n", - " 231.48561096191406,\n", - " 0.5197221636772156\n", - " ],\n", - " [\n", - " 299.1871032714844,\n", - " 218.01211547851562,\n", - " 0.3429694175720215\n", - " ],\n", - " [\n", - " 181.70339965820312,\n", - " 317.9532165527344,\n", - " 0.46619749069213867\n", - " ],\n", - " [\n", - " 304.53472900390625,\n", - " 208.62942504882812,\n", - " 0.45269933342933655\n", - " ],\n", - " [\n", - " 311.17626953125,\n", - " 274.0134582519531,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.29833984375,\n", - " 278.41729736328125,\n", - " 0.5592008233070374\n", - " ],\n", - " [\n", - " 363.13763427734375,\n", - " 281.8921813964844,\n", - " 0.45556432008743286\n", - " ],\n", - " [\n", - " 357.6162414550781,\n", - " 281.98394775390625,\n", - " 0.30312928557395935\n", - " ],\n", - " [\n", - " 187.75889587402344,\n", - " 305.17547607421875,\n", - " 0.44207459688186646\n", - " ],\n", - " [\n", - " 355.2580261230469,\n", - " 341.6760559082031,\n", - " 0.38897281885147095\n", - " ],\n", - " [\n", - " 363.04852294921875,\n", - " 339.8902893066406,\n", - " 0.5060890316963196\n", - " ],\n", - " [\n", - " 314.8399963378906,\n", - " 341.91680908203125,\n", - " 0.40578165650367737\n", - " ],\n", - " [\n", - " 338.7541198730469,\n", - " 359.4593200683594,\n", - " 0.548823893070221\n", - " ],\n", - " [\n", - " 366.559814453125,\n", - " 343.24029541015625,\n", - " 0.4393633306026459\n", - " ],\n", - " [\n", - " 270.7618103027344,\n", - " 357.1903991699219,\n", - " 0.4302641451358795\n", - " ],\n", - " [\n", - " 379.385498046875,\n", - " 355.3668212890625,\n", - " 0.45166778564453125\n", - " ],\n", - " [\n", - " 268.61431884765625,\n", - " 357.9519348144531,\n", - " 0.36507532000541687\n", - " ],\n", - " [\n", - " 597.203125,\n", - " 379.3829345703125,\n", - " 0.587417721748352\n", - " ],\n", - " [\n", - " 182.3574676513672,\n", - " 208.2609100341797,\n", - " 0.39093875885009766\n", - " ],\n", - " [\n", - " 512.343994140625,\n", - " 407.104736328125,\n", - " 0.3616585433483124\n", - " ],\n", - " [\n", - " 517.728759765625,\n", - " 431.97174072265625,\n", - " 0.5530462265014648\n", - " ],\n", - " [\n", - " 567.7471313476562,\n", - " 195.76194763183594,\n", - " 0.41424036026000977\n", - " ],\n", - " [\n", - " 568.5385131835938,\n", - " 196.24961853027344,\n", - " 0.3196858763694763\n", - " ],\n", - " [\n", - " 512.130615234375,\n", - " 450.2174072265625,\n", - " 0.6944491863250732\n", - " ],\n", - " [\n", - " 154.75747680664062,\n", - " 365.33026123046875,\n", - " 0.46970120072364807\n", - " ],\n", - " [\n", - " 476.37847900390625,\n", - " 421.13104248046875,\n", - " 0.2024911344051361\n", - " ],\n", - " [\n", - " 109.27305603027344,\n", - " 276.56787109375,\n", - " 0.22318890690803528\n", - " ],\n", - " [\n", - " 573.2276611328125,\n", - " 393.43096923828125,\n", - " 0.45069870352745056\n", - " ],\n", - " [\n", - " 144.93081665039062,\n", - " 343.8260803222656,\n", - " 0.26525822281837463\n", - " ],\n", - " [\n", - " 510.0262145996094,\n", - " 457.258544921875,\n", - " 0.6254387497901917\n", - " ],\n", - " [\n", - " 468.2686767578125,\n", - " 412.5702209472656,\n", - " 0.40957126021385193\n", - " ],\n", - " [\n", - " 464.2906188964844,\n", - " 410.6726989746094,\n", - " 0.32647469639778137\n", - " ],\n", - " [\n", - " 460.0201721191406,\n", - " 403.037841796875,\n", - " 0.41243088245391846\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588106.285935,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.313648\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.3619356,\n", - " \"step\": 30,\n", - " \"pose\": [\n", - " [\n", - " 290.1323547363281,\n", - " 299.7147521972656,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.99139404296875,\n", - " 309.4753112792969,\n", - " 0.8872848749160767\n", - " ],\n", - " [\n", - " 297.3484802246094,\n", - " 315.24835205078125,\n", - " 0.5460883975028992\n", - " ],\n", - " [\n", - " 291.71209716796875,\n", - " 310.8031311035156,\n", - " 0.5654347538948059\n", - " ],\n", - " [\n", - " 305.3967590332031,\n", - " 312.7275085449219,\n", - " 0.7120907306671143\n", - " ],\n", - " [\n", - " 285.19940185546875,\n", - " 267.3405456542969,\n", - " 0.8942697048187256\n", - " ],\n", - " [\n", - " 289.3546447753906,\n", - " 231.18653869628906,\n", - " 0.483479768037796\n", - " ],\n", - " [\n", - " 299.21112060546875,\n", - " 218.4276580810547,\n", - " 0.4001926779747009\n", - " ],\n", - " [\n", - " 181.76585388183594,\n", - " 313.5556945800781,\n", - " 0.46045035123825073\n", - " ],\n", - " [\n", - " 309.8026428222656,\n", - " 210.481689453125,\n", - " 0.5369957089424133\n", - " ],\n", - " [\n", - " 310.97064208984375,\n", - " 273.8341369628906,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.9340515136719,\n", - " 277.03912353515625,\n", - " 0.6095738410949707\n", - " ],\n", - " [\n", - " 362.3292541503906,\n", - " 282.6873474121094,\n", - " 0.5387850999832153\n", - " ],\n", - " [\n", - " 183.47048950195312,\n", - " 311.634521484375,\n", - " 0.30759158730506897\n", - " ],\n", - " [\n", - " 193.97259521484375,\n", - " 306.5625,\n", - " 0.4332640469074249\n", - " ],\n", - " [\n", - " 355.8443908691406,\n", - " 341.6749267578125,\n", - " 0.3820899724960327\n", - " ],\n", - " [\n", - " 362.45452880859375,\n", - " 340.09088134765625,\n", - " 0.493086040019989\n", - " ],\n", - " [\n", - " 315.4454650878906,\n", - " 343.0971984863281,\n", - " 0.3856670558452606\n", - " ],\n", - " [\n", - " 339.348388671875,\n", - " 359.60870361328125,\n", - " 0.55845707654953\n", - " ],\n", - " [\n", - " 284.3851623535156,\n", - " 356.5133972167969,\n", - " 0.4924909174442291\n", - " ],\n", - " [\n", - " 271.4208679199219,\n", - " 357.4637451171875,\n", - " 0.3702590763568878\n", - " ],\n", - " [\n", - " 423.9642028808594,\n", - " 375.77801513671875,\n", - " 0.42082488536834717\n", - " ],\n", - " [\n", - " 269.3265075683594,\n", - " 358.4918212890625,\n", - " 0.3108918368816376\n", - " ],\n", - " [\n", - " 595.7271728515625,\n", - " 378.74505615234375,\n", - " 0.6383695006370544\n", - " ],\n", - " [\n", - " 182.5079803466797,\n", - " 209.4701690673828,\n", - " 0.3769323527812958\n", - " ],\n", - " [\n", - " 563.5107421875,\n", - " 388.12860107421875,\n", - " 0.34879517555236816\n", - " ],\n", - " [\n", - " 516.7620239257812,\n", - " 429.2193908691406,\n", - " 0.6218969225883484\n", - " ],\n", - " [\n", - " 443.3829650878906,\n", - " 357.2695007324219,\n", - " 0.2294222116470337\n", - " ],\n", - " [\n", - " 508.9411926269531,\n", - " 390.8059387207031,\n", - " 0.27742016315460205\n", - " ],\n", - " [\n", - " 513.4525756835938,\n", - " 451.084228515625,\n", - " 0.588313102722168\n", - " ],\n", - " [\n", - " 590.62939453125,\n", - " 409.5919494628906,\n", - " 0.49015718698501587\n", - " ],\n", - " [\n", - " 505.6385498046875,\n", - " 379.8114929199219,\n", - " 0.16522188484668732\n", - " ],\n", - " [\n", - " 108.85206604003906,\n", - " 271.24273681640625,\n", - " 0.2543381452560425\n", - " ],\n", - " [\n", - " 573.4502563476562,\n", - " 393.5893249511719,\n", - " 0.49820849299430847\n", - " ],\n", - " [\n", - " 142.85484313964844,\n", - " 314.2268981933594,\n", - " 0.19713541865348816\n", - " ],\n", - " [\n", - " 549.9910278320312,\n", - " 465.56768798828125,\n", - " 0.5062677264213562\n", - " ],\n", - " [\n", - " 469.6101379394531,\n", - " 412.96905517578125,\n", - " 0.3788171112537384\n", - " ],\n", - " [\n", - " 465.2295837402344,\n", - " 411.72161865234375,\n", - " 0.343596875667572\n", - " ],\n", - " [\n", - " 460.37811279296875,\n", - " 402.3249206542969,\n", - " 0.46701255440711975\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588106.3327684,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.3619356\n", - "}\n", - "{\n", - " \"kind\": \"pose\",\n", - " \"raw\": {\n", - " \"type\": \"pose\",\n", - " \"timestamp\": 1783588106.3911848,\n", - " \"step\": 31,\n", - " \"pose\": [\n", - " [\n", - " 289.88616943359375,\n", - " 299.245849609375,\n", - " 1.0\n", - " ],\n", - " [\n", - " 293.7210693359375,\n", - " 308.9605407714844,\n", - " 0.8916722536087036\n", - " ],\n", - " [\n", - " 297.2877502441406,\n", - " 315.5369873046875,\n", - " 0.5746937394142151\n", - " ],\n", - " [\n", - " 292.2024230957031,\n", - " 311.0600891113281,\n", - " 0.5595524907112122\n", - " ],\n", - " [\n", - " 305.3230285644531,\n", - " 313.09588623046875,\n", - " 0.7339632511138916\n", - " ],\n", - " [\n", - " 285.2215576171875,\n", - " 266.96453857421875,\n", - " 0.894293487071991\n", - " ],\n", - " [\n", - " 297.3047180175781,\n", - " 221.42364501953125,\n", - " 0.5297285914421082\n", - " ],\n", - " [\n", - " 295.8182067871094,\n", - " 216.30775451660156,\n", - " 0.38408026099205017\n", - " ],\n", - " [\n", - " 183.77606201171875,\n", - " 316.7649841308594,\n", - " 0.4275979697704315\n", - " ],\n", - " [\n", - " 308.4361572265625,\n", - " 210.48973083496094,\n", - " 0.5560570359230042\n", - " ],\n", - " [\n", - " 311.0546875,\n", - " 273.9168395996094,\n", - " 1.0\n", - " ],\n", - " [\n", - " 363.8443298339844,\n", - " 277.56488037109375,\n", - " 0.632280170917511\n", - " ],\n", - " [\n", - " 363.19598388671875,\n", - " 283.2601013183594,\n", - " 0.5504708290100098\n", - " ],\n", - " [\n", - " 360.5338439941406,\n", - " 282.9017028808594,\n", - " 0.3014270067214966\n", - " ],\n", - " [\n", - " 187.5973663330078,\n", - " 304.839111328125,\n", - " 0.4563285708427429\n", - " ],\n", - " [\n", - " 355.62762451171875,\n", - " 341.2684020996094,\n", - " 0.4316878914833069\n", - " ],\n", - " [\n", - " 362.1797180175781,\n", - " 340.63848876953125,\n", - " 0.523749828338623\n", - " ],\n", - " [\n", - " 310.7569580078125,\n", - " 347.2984313964844,\n", - " 0.3826305568218231\n", - " ],\n", - " [\n", - " 338.3757629394531,\n", - " 360.44317626953125,\n", - " 0.5659022927284241\n", - " ],\n", - " [\n", - " 367.23004150390625,\n", - " 344.0614929199219,\n", - " 0.43424880504608154\n", - " ],\n", - " [\n", - " 272.1240234375,\n", - " 355.71612548828125,\n", - " 0.5028250217437744\n", - " ],\n", - " [\n", - " 379.81280517578125,\n", - " 355.9754943847656,\n", - " 0.38737767934799194\n", - " ],\n", - " [\n", - " 268.998046875,\n", - " 357.2021789550781,\n", - " 0.44784843921661377\n", - " ],\n", - " [\n", - " 596.336181640625,\n", - " 379.1740417480469,\n", - " 0.545729398727417\n", - " ],\n", - " [\n", - " 201.0037841796875,\n", - " 228.0155029296875,\n", - " 0.42291054129600525\n", - " ],\n", - " [\n", - " 562.3812866210938,\n", - " 386.2914123535156,\n", - " 0.36510559916496277\n", - " ],\n", - " [\n", - " 517.466796875,\n", - " 433.1764831542969,\n", - " 0.5716246962547302\n", - " ],\n", - " [\n", - " 570.5004272460938,\n", - " 194.27825927734375,\n", - " 0.31970250606536865\n", - " ],\n", - " [\n", - " 572.74462890625,\n", - " 201.8236541748047,\n", - " 0.39004698395729065\n", - " ],\n", - " [\n", - " 513.2064208984375,\n", - " 451.0941162109375,\n", - " 0.6408159732818604\n", - " ],\n", - " [\n", - " 590.6119995117188,\n", - " 408.5152282714844,\n", - " 0.5167672634124756\n", - " ],\n", - " [\n", - " 114.4150619506836,\n", - " 235.0469207763672,\n", - " 0.13695929944515228\n", - " ],\n", - " [\n", - " 112.1463623046875,\n", - " 279.28265380859375,\n", - " 0.23755685985088348\n", - " ],\n", - " [\n", - " 571.991943359375,\n", - " 393.113525390625,\n", - " 0.4135165214538574\n", - " ],\n", - " [\n", - " 144.2115478515625,\n", - " 313.7943115234375,\n", - " 0.23972396552562714\n", - " ],\n", - " [\n", - " 510.1828918457031,\n", - " 456.5469055175781,\n", - " 0.503180205821991\n", - " ],\n", - " [\n", - " 468.22821044921875,\n", - " 413.3734436035156,\n", - " 0.37297725677490234\n", - " ],\n", - " [\n", - " 465.0234069824219,\n", - " 410.9199523925781,\n", - " 0.36548110842704773\n", - " ],\n", - " [\n", - " 465.41192626953125,\n", - " 406.6351623535156,\n", - " 0.4397272765636444\n", - " ]\n", - " ],\n", - " \"frame_time\": 1783588106.3645778,\n", - " \"pose_time\": null,\n", - " \"recording\": false\n", - " },\n", - " \"received_at\": 1783588106.3911848\n", - "}\n", - "Received 20 packet(s).\n" - ] - } - ], - "source": [ - "def receive_packets(conn, duration_s: float = 30.0, max_packets: int | None = None):\n", - " start = time.time()\n", - " packets: list[dict[str, Any]] = []\n", - "\n", - " while time.time() - start < duration_s:\n", - " if conn.poll(POLL_INTERVAL_S):\n", - " payload = conn.recv()\n", - " decoded = decode_payload(payload)\n", - " decoded[\"received_at\"] = time.time()\n", - " packets.append(decoded)\n", - " print(json.dumps(decoded, default=str, indent=2))\n", - "\n", - " if max_packets is not None and len(packets) >= max_packets:\n", - " break\n", - "\n", - " print(f\"Received {len(packets)} packet(s).\")\n", - " return packets\n", - "\n", - "\n", - "packets = receive_packets(conn, duration_s=30.0, max_packets=20)" - ] - }, - { - "cell_type": "markdown", - "id": "504fb2a444614c0babb325280ed9130a", - "metadata": {}, - "source": [ - "## Save captured packets\n", - "\n", - "This is useful if you want to inspect the stream shape after a test run." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "59bbdb311c014d738909a11f9e486628", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Saved 20 packet(s) to C:\\Users\\Cyril A\\Desktop\\Code\\DeepLabCut-live-GUI\\dlclivegui\\processors\\custom\\mock_unity_captured_packets.json\n" - ] - } - ], - "source": [ - "out_path = Path(\"mock_unity_captured_packets.json\")\n", - "out_path.write_text(json.dumps(packets, default=str, indent=2), encoding=\"utf-8\")\n", - "print(\"Saved\", len(packets), \"packet(s) to\", out_path.resolve())" - ] - }, - { - "cell_type": "markdown", - "id": "b43b363d81ae4b689946ece5c682cd59", - "metadata": {}, - "source": [ - "## Close the client connection" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "8a65eabff63a45729fe45fb5ade58bdc", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Connection closed\n" - ] - } - ], - "source": [ - "try:\n", - " conn.close()\n", - " print(\"Connection closed\")\n", - "except Exception as exc:\n", - " print(\"Close failed:\", exc)" - ] - }, - { - "cell_type": "markdown", - "id": "c3933fab20d04ec698c2621248eb3be0", - "metadata": {}, - "source": [ - "## Optional local smoke test with a standalone mock server\n", - "\n", - "Use this only if you want to validate the notebook client logic without running DLCLiveGUI.\n", - "\n", - "This requires `mock_socket_processor.py` to be importable, for example by placing it next to this notebook or adding its directory to `sys.path`." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "4dd4641cc4064e0191573fe9c69df29b", - "metadata": {}, - "outputs": [], - "source": [ - "# Optional smoke test. Leave commented unless you have mock_socket_processor.py available.\n", - "#\n", - "# import socket\n", - "# import sys\n", - "# from multiprocessing.connection import Client\n", - "#\n", - "# from mock_socket_processor import MockSocketProcessor\n", - "#\n", - "# def free_port():\n", - "# s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n", - "# s.bind((\"127.0.0.1\", 0))\n", - "# port = s.getsockname()[1]\n", - "# s.close()\n", - "# return port\n", - "#\n", - "# port = free_port()\n", - "# mock = MockSocketProcessor(bind=(\"127.0.0.1\", port), authkey=AUTHKEY)\n", - "# test_conn = Client(mock.address, authkey=AUTHKEY)\n", - "# test_conn.send({\"cmd\": \"ping\"})\n", - "# print(test_conn.recv())\n", - "# mock.process([[1, 2, 0.9]], frame_time=123.456)\n", - "# print(decode_payload(test_conn.recv()))\n", - "# test_conn.close()\n", - "# mock.stop()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "deeplabcut-live-gui (3.12.12)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}