| """ |
| Loader for HAND_paired_data dataset containing paired robot and human demonstrations. |
| """ |
|
|
| import os |
| from pathlib import Path |
| from typing import Any |
|
|
| import cv2 |
| import numpy as np |
|
|
| from dataset_upload.helpers import generate_unique_id |
|
|
|
|
| CAMERA_VIEWS = ["external_imgs", "over_shoulder_imgs"] |
|
|
|
|
| class HandPairedFrameLoader: |
| """Pickle-able loader that reads a list of JPG image paths on demand (RGB, uint8).""" |
|
|
| def __init__(self, image_paths: list[str]) -> None: |
| if not image_paths: |
| raise ValueError("image_paths must be non-empty") |
| self.image_paths = image_paths |
|
|
| def __call__(self) -> np.ndarray: |
| frames: list[np.ndarray] = [] |
| for p in self.image_paths: |
| img_bgr = cv2.imread(p, cv2.IMREAD_COLOR) |
| if img_bgr is None: |
| continue |
| img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) |
| frames.append(img_rgb) |
| if not frames: |
| return np.empty((0, 0, 0, 3), dtype=np.uint8) |
| frames_np = np.asarray(frames, dtype=np.uint8) |
| return frames_np |
|
|
|
|
| def _sorted_jpgs(dir_path: Path) -> list[str]: |
| """Return sorted list of JPG file paths from a directory.""" |
| paths = [p for p in dir_path.glob("*.jpg")] |
|
|
| def _key(p: Path): |
| |
| name = p.stem |
| try: |
| |
| if "_" in name: |
| return int(name.split("_")[-1]) |
| return int(name) |
| except Exception: |
| return 0 |
|
|
| paths.sort(key=_key) |
| return [str(p) for p in paths] |
|
|
|
|
| def _parse_task_name(folder_name: str) -> str: |
| """Convert folder name to human-readable task instruction. |
| |
| Examples: |
| blend_carrot -> blend carrot |
| close_microwave_hand -> close microwave |
| """ |
| |
| task = folder_name.replace("_hand", "") |
| |
| task = task.replace("_", " ") |
| return task |
|
|
|
|
| def _is_human_task(folder_name: str) -> bool: |
| """Check if this is a human demonstration task.""" |
| return folder_name.endswith("_hand") |
|
|
|
|
| def _make_traj(image_paths: list[str], task_text: str, is_robot: bool) -> dict[str, Any]: |
| """Create a trajectory dictionary.""" |
| traj: dict[str, Any] = {} |
| traj["id"] = generate_unique_id() |
| traj["task"] = task_text |
| traj["frames"] = HandPairedFrameLoader(image_paths) |
| traj["is_robot"] = is_robot |
| traj["quality_label"] = "successful" |
| traj["data_source"] = "hand_paired" |
| traj["preference_group_id"] = None |
| traj["preference_rank"] = None |
| return traj |
|
|
|
|
| def load_hand_paired_dataset(dataset_path: str, dataset_name: str) -> dict[str, list[dict]]: |
| """Load HAND_paired_data dataset from local folders. |
| |
| Args: |
| dataset_path: Root directory containing task folders (e.g., blend_carrot, blend_carrot_hand, etc.) |
| |
| Structure: |
| dataset_path/ |
| blend_carrot/ |
| traj0/ |
| external_imgs/ |
| im_0.jpg, im_1.jpg, ... |
| over_shoulder_imgs/ |
| im_0.jpg, im_1.jpg, ... |
| traj1/ |
| ... |
| blend_carrot_hand/ |
| traj0/ |
| ... |
| close_microwave/ |
| ... |
| close_microwave_hand/ |
| ... |
| |
| Returns: |
| Mapping: task instruction -> list of trajectory dicts |
| """ |
| root = Path(os.path.expanduser(dataset_path)) |
| if not root.exists(): |
| raise FileNotFoundError(f"HAND_paired dataset path not found: {root}") |
|
|
| |
| task_dirs = [p for p in root.iterdir() if p.is_dir()] |
|
|
| task_data: dict[str, list[dict]] = {} |
|
|
| dataset_name = dataset_name.replace("hand_paired_", "") |
|
|
| for task_dir in task_dirs: |
| print(f"Processing task: {task_dir.name}") |
| task_name = _parse_task_name(task_dir.name) |
| is_robot = not _is_human_task(task_dir.name) |
| if dataset_name == "robot": |
| if not is_robot: |
| continue |
| elif dataset_name == "human": |
| if is_robot: |
| continue |
|
|
| |
| traj_dirs = [p for p in task_dir.iterdir() if p.is_dir() and p.name.startswith("traj")] |
| print(f"Found {len(traj_dirs)} trajectory directories") |
| for traj_dir in traj_dirs: |
| print(f"Processing trajectory: {traj_dir.name}") |
| |
| for camera_view in CAMERA_VIEWS: |
| print(f"Processing camera view: {camera_view}") |
| camera_dir = traj_dir / camera_view |
| if not camera_dir.exists(): |
| continue |
|
|
| |
| image_paths = _sorted_jpgs(camera_dir) |
| if not image_paths: |
| continue |
|
|
| |
| traj = _make_traj(image_paths, task_name, is_robot) |
| task_data.setdefault(task_name, []).append(traj) |
|
|
| print(f"Loaded {len(task_data)} unique tasks from HAND paired {dataset_name} dataset") |
| for task, trajs in task_data.items(): |
| robot_count = sum(1 for t in trajs if t["is_robot"]) |
| human_count = sum(1 for t in trajs if not t["is_robot"]) |
| print(f" {task}: {robot_count} robot, {human_count} human trajectories") |
|
|
| return task_data |
|
|