Spaces:
Running on L40S
Running on L40S
Add packaged camera dataset sample
Browse files- .gitattributes +1 -0
- assets/examples/camera_benchmark_v3/data/00000000.tar +3 -0
- assets/examples/camera_benchmark_v3/wdinfo.json +14 -0
- cosmos-framework/cosmos_framework/data/vfm/action/action_viz/app_test.py +32 -0
- cosmos-framework/cosmos_framework/data/vfm/action/action_viz/local_worker.py +21 -2
- cosmos-framework/cosmos_framework/data/vfm/action/action_viz/local_worker_test.py +24 -0
- cosmos-framework/cosmos_framework/data/vfm/action/action_viz/normalization_stats.json +6 -0
- cosmos-framework/cosmos_framework/data/vfm/action/camera_benchmark_dataset.py +195 -0
- cosmos-framework/cosmos_framework/data/vfm/action/urdf_visualizer/action_datasets.py +21 -0
- cosmos-framework/cosmos_framework/data/vfm/action/urdf_visualizer/viewer.py +9 -1
- start.sh +2 -0
.gitattributes
CHANGED
|
@@ -14,6 +14,7 @@ assets/examples/droid_plus_lerobot_640x360_20260412/success/videos/observation.i
|
|
| 14 |
assets/examples/droid_plus_lerobot_640x360_20260412/success/videos/observation.image.wrist_image_left/chunk-000/file-000.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 15 |
assets/examples/fractal20220817_data/videos/observation.images.image/chunk-000/file-000.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 16 |
assets/examples/av_v2_03292026_wdinfo/data/00000000.tar filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 17 |
assets/examples/fastumi/fastumi_single_arm/pour_coke/videos/observation.image.right_main_camera_rgb/chunk-000/file-000.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 18 |
assets/examples/AgiBotWorld-GEAR_20260208/agibot-offshelf/20251016_500h/gripper/task_1018/data/chunk-000/file_000.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
assets/examples/AgiBotWorld-GEAR_20260208/agibot-offshelf/20251016_500h/gripper/task_1018/videos/observation.images.hand_left/chunk-000/file-000.mp4 filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 14 |
assets/examples/droid_plus_lerobot_640x360_20260412/success/videos/observation.image.wrist_image_left/chunk-000/file-000.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 15 |
assets/examples/fractal20220817_data/videos/observation.images.image/chunk-000/file-000.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 16 |
assets/examples/av_v2_03292026_wdinfo/data/00000000.tar filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
assets/examples/camera_benchmark_v3/data/00000000.tar filter=lfs diff=lfs merge=lfs -text
|
| 18 |
assets/examples/fastumi/fastumi_single_arm/pour_coke/videos/observation.image.right_main_camera_rgb/chunk-000/file-000.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 19 |
assets/examples/AgiBotWorld-GEAR_20260208/agibot-offshelf/20251016_500h/gripper/task_1018/data/chunk-000/file_000.parquet filter=lfs diff=lfs merge=lfs -text
|
| 20 |
assets/examples/AgiBotWorld-GEAR_20260208/agibot-offshelf/20251016_500h/gripper/task_1018/videos/observation.images.hand_left/chunk-000/file-000.mp4 filter=lfs diff=lfs merge=lfs -text
|
assets/examples/camera_benchmark_v3/data/00000000.tar
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:816d31b913ba99e4b755efb1767e8ecf52fc5586451148416604eeec9a9c1743
|
| 3 |
+
size 5201920
|
assets/examples/camera_benchmark_v3/wdinfo.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"data_keys": [
|
| 3 |
+
"pkl"
|
| 4 |
+
],
|
| 5 |
+
"chunk_size": 1,
|
| 6 |
+
"root": "data",
|
| 7 |
+
"data_list": [
|
| 8 |
+
"00000000.tar"
|
| 9 |
+
],
|
| 10 |
+
"data_list_key_count": [
|
| 11 |
+
1
|
| 12 |
+
],
|
| 13 |
+
"total_key_count": 1
|
| 14 |
+
}
|
cosmos-framework/cosmos_framework/data/vfm/action/action_viz/app_test.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
|
|
|
|
|
|
| 3 |
import numpy as np
|
| 4 |
import pytest
|
| 5 |
|
|
@@ -12,6 +14,36 @@ from cosmos_framework.data.vfm.action.action_viz.state import (
|
|
| 12 |
GenerationResult,
|
| 13 |
write_generated_action,
|
| 14 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
|
| 17 |
@pytest.mark.L0
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
import numpy as np
|
| 6 |
import pytest
|
| 7 |
|
|
|
|
| 14 |
GenerationResult,
|
| 15 |
write_generated_action,
|
| 16 |
)
|
| 17 |
+
from cosmos_framework.data.vfm.action.camera_benchmark_dataset import CameraBenchmarkDataset
|
| 18 |
+
from cosmos_framework.data.vfm.action.urdf_visualizer.unified_action import ActionFormat
|
| 19 |
+
from cosmos_framework.data.vfm.action.urdf_visualizer.viewer import _build_datasets
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@pytest.mark.L0
|
| 23 |
+
def test_camera_dataset_registry_uses_validation_split() -> None:
|
| 24 |
+
entry = _build_datasets()["camera"]
|
| 25 |
+
|
| 26 |
+
assert entry.action_format is ActionFormat.EGO_9D
|
| 27 |
+
assert entry.dataset_class.endswith("camera_benchmark_dataset.CameraBenchmarkDataset")
|
| 28 |
+
assert entry.dataset_kwargs["root"].endswith("/camera_benchmark_v3")
|
| 29 |
+
assert entry.dataset_kwargs["split"] == "val"
|
| 30 |
+
assert entry.dataset_kwargs["shuffle"] is False
|
| 31 |
+
assert entry.dataset_kwargs["num_frames"] == 61
|
| 32 |
+
assert entry.dataset_kwargs["translation_scale"] == 10
|
| 33 |
+
assert "credential_path" not in entry.dataset_kwargs
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
@pytest.mark.L0
|
| 37 |
+
def test_camera_packaged_sample_loads() -> None:
|
| 38 |
+
repo_root = Path(__file__).resolve().parents[6]
|
| 39 |
+
dataset = CameraBenchmarkDataset(root=str(repo_root / "assets/examples/camera_benchmark_v3"), num_frames=61)
|
| 40 |
+
|
| 41 |
+
sample = dataset[0]
|
| 42 |
+
|
| 43 |
+
assert sample["video"].shape == (3, 61, 720, 1280)
|
| 44 |
+
assert sample["action"].shape == (60, 9)
|
| 45 |
+
assert sample["conditioning_fps"].item() == 30.0
|
| 46 |
+
assert isinstance(sample["ai_caption"], str)
|
| 47 |
|
| 48 |
|
| 49 |
@pytest.mark.L0
|
cosmos-framework/cosmos_framework/data/vfm/action/action_viz/local_worker.py
CHANGED
|
@@ -575,8 +575,26 @@ def _write_conditioning_video(request: GenerationRequest, job_dir: Path, action_
|
|
| 575 |
|
| 576 |
frame_count = min(len(frames), int(action_chunk_size) + 1)
|
| 577 |
vision_path = job_dir / "conditioning.mp4"
|
| 578 |
-
|
| 579 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 580 |
|
| 581 |
|
| 582 |
def _write_action_values(request: GenerationRequest, job_dir: Path) -> tuple[Path, int, dict[str, Any]]:
|
|
@@ -601,6 +619,7 @@ def _domain_name_for_dataset(dataset: str) -> str:
|
|
| 601 |
domains = {
|
| 602 |
"av": "av",
|
| 603 |
"bridge": "bridge_orig_lerobot",
|
|
|
|
| 604 |
"droid": "droid_lerobot",
|
| 605 |
"fractal": "fractal",
|
| 606 |
"robomind_franka": "robomind-franka",
|
|
|
|
| 575 |
|
| 576 |
frame_count = min(len(frames), int(action_chunk_size) + 1)
|
| 577 |
vision_path = job_dir / "conditioning.mp4"
|
| 578 |
+
fps = _sample_conditioning_fps(sample, default=entry.fps)
|
| 579 |
+
imageio.mimsave(vision_path, list(frames[:frame_count]), fps=fps, codec="libx264", macro_block_size=16)
|
| 580 |
+
return vision_path, fps
|
| 581 |
+
|
| 582 |
+
|
| 583 |
+
def _sample_conditioning_fps(sample: dict[str, Any], default: int) -> int:
|
| 584 |
+
raw = sample.get("conditioning_fps")
|
| 585 |
+
if raw is None:
|
| 586 |
+
return int(default)
|
| 587 |
+
try:
|
| 588 |
+
if hasattr(raw, "detach"):
|
| 589 |
+
raw = raw.detach().cpu().item()
|
| 590 |
+
elif hasattr(raw, "item"):
|
| 591 |
+
raw = raw.item()
|
| 592 |
+
fps = int(round(float(raw)))
|
| 593 |
+
except (TypeError, ValueError) as exc:
|
| 594 |
+
raise ValueError(f"Invalid sample conditioning_fps value {raw!r}") from exc
|
| 595 |
+
if fps <= 0:
|
| 596 |
+
raise ValueError(f"Invalid sample conditioning_fps value {raw!r}")
|
| 597 |
+
return fps
|
| 598 |
|
| 599 |
|
| 600 |
def _write_action_values(request: GenerationRequest, job_dir: Path) -> tuple[Path, int, dict[str, Any]]:
|
|
|
|
| 619 |
domains = {
|
| 620 |
"av": "av",
|
| 621 |
"bridge": "bridge_orig_lerobot",
|
| 622 |
+
"camera": "camera_pose",
|
| 623 |
"droid": "droid_lerobot",
|
| 624 |
"fractal": "fractal",
|
| 625 |
"robomind_franka": "robomind-franka",
|
cosmos-framework/cosmos_framework/data/vfm/action/action_viz/local_worker_test.py
CHANGED
|
@@ -9,7 +9,10 @@ import pytest
|
|
| 9 |
|
| 10 |
from cosmos_framework.data.vfm.action.action_viz.local_worker import (
|
| 11 |
_copy_generation_outputs,
|
|
|
|
| 12 |
_patched_unipc_progress,
|
|
|
|
|
|
|
| 13 |
)
|
| 14 |
from cosmos_framework.data.vfm.action.action_viz.state import (
|
| 15 |
BRIDGE_ACTION_DIM,
|
|
@@ -114,6 +117,27 @@ def test_copy_generation_outputs_requires_action_for_inverse_dynamics(tmp_path)
|
|
| 114 |
_copy_generation_outputs(request, inference_dir, Path(request.output_dir))
|
| 115 |
|
| 116 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
def _request(tmp_path, model_mode: str) -> GenerationRequest:
|
| 118 |
return GenerationRequest(
|
| 119 |
generation_id="gen",
|
|
|
|
| 9 |
|
| 10 |
from cosmos_framework.data.vfm.action.action_viz.local_worker import (
|
| 11 |
_copy_generation_outputs,
|
| 12 |
+
_domain_name_for_dataset,
|
| 13 |
_patched_unipc_progress,
|
| 14 |
+
_sample_conditioning_fps,
|
| 15 |
+
_transform_action_values,
|
| 16 |
)
|
| 17 |
from cosmos_framework.data.vfm.action.action_viz.state import (
|
| 18 |
BRIDGE_ACTION_DIM,
|
|
|
|
| 117 |
_copy_generation_outputs(request, inference_dir, Path(request.output_dir))
|
| 118 |
|
| 119 |
|
| 120 |
+
@pytest.mark.L0
|
| 121 |
+
def test_camera_domain_and_pose_scale_normalization() -> None:
|
| 122 |
+
assert _domain_name_for_dataset("camera") == "camera_pose"
|
| 123 |
+
|
| 124 |
+
model_rows, summary = _transform_action_values(
|
| 125 |
+
[[0.1, -0.2, 0.3, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0]],
|
| 126 |
+
"camera",
|
| 127 |
+
to_model_space=True,
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
assert summary["method"] == "pose_scale"
|
| 131 |
+
assert summary["action_dim"] == 9
|
| 132 |
+
np.testing.assert_allclose(model_rows[0], [1.0, -2.0, 3.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0])
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
@pytest.mark.L0
|
| 136 |
+
def test_sample_conditioning_fps_prefers_sample_metadata() -> None:
|
| 137 |
+
assert _sample_conditioning_fps({"conditioning_fps": np.array(29.97, dtype=np.float32)}, default=10) == 30
|
| 138 |
+
assert _sample_conditioning_fps({}, default=10) == 10
|
| 139 |
+
|
| 140 |
+
|
| 141 |
def _request(tmp_path, model_mode: str) -> GenerationRequest:
|
| 142 |
return GenerationRequest(
|
| 143 |
generation_id="gen",
|
cosmos-framework/cosmos_framework/data/vfm/action/action_viz/normalization_stats.json
CHANGED
|
@@ -11,6 +11,12 @@
|
|
| 11 |
"q01": [-0.038884, -0.028667, -0.03784, 0.976292, -0.163098, -0.081545, -0.160193, 0.976322, -0.078872, 0.0],
|
| 12 |
"q99": [0.039722, 0.029068, 0.026702, 1.0, 0.160195, 0.081655, 0.163227, 1.0, 0.095189, 1.0]
|
| 13 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
"droid": {
|
| 15 |
"action_dim": 10,
|
| 16 |
"method": "quantile_rot",
|
|
|
|
| 11 |
"q01": [-0.038884, -0.028667, -0.03784, 0.976292, -0.163098, -0.081545, -0.160193, 0.976322, -0.078872, 0.0],
|
| 12 |
"q99": [0.039722, 0.029068, 0.026702, 1.0, 0.160195, 0.081655, 0.163227, 1.0, 0.095189, 1.0]
|
| 13 |
},
|
| 14 |
+
"camera": {
|
| 15 |
+
"action_dim": 9,
|
| 16 |
+
"method": "pose_scale",
|
| 17 |
+
"rotation_scale": 1.0,
|
| 18 |
+
"translation_scale": 10.0
|
| 19 |
+
},
|
| 20 |
"droid": {
|
| 21 |
"action_dim": 10,
|
| 22 |
"method": "quantile_rot",
|
cosmos-framework/cosmos_framework/data/vfm/action/camera_benchmark_dataset.py
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
| 2 |
+
# SPDX-License-Identifier: OpenMDW-1.1
|
| 3 |
+
|
| 4 |
+
"""Packaged camera benchmark samples for the action viewer."""
|
| 5 |
+
|
| 6 |
+
from __future__ import annotations
|
| 7 |
+
|
| 8 |
+
import json
|
| 9 |
+
import pickle
|
| 10 |
+
import tarfile
|
| 11 |
+
from io import BytesIO
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
from typing import Literal
|
| 14 |
+
|
| 15 |
+
import av
|
| 16 |
+
import numpy as np
|
| 17 |
+
import torch
|
| 18 |
+
from scipy.spatial.transform import Rotation as R
|
| 19 |
+
from torch.utils.data import Dataset
|
| 20 |
+
|
| 21 |
+
from cosmos_framework.data.vfm.action.domain_utils import get_domain_id
|
| 22 |
+
from cosmos_framework.data.vfm.action.pose_utils import RotationConvention, pose_abs_to_rel
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class CameraBenchmarkDataset(Dataset):
|
| 26 |
+
"""Load camera-pose validation samples packaged with the viewer assets."""
|
| 27 |
+
|
| 28 |
+
def __init__(
|
| 29 |
+
self,
|
| 30 |
+
root: str = "/app/assets/examples/camera_benchmark_v3",
|
| 31 |
+
mode: str = "policy",
|
| 32 |
+
split: str = "val",
|
| 33 |
+
shuffle: bool = False,
|
| 34 |
+
rotation_format: RotationConvention = "rot6d",
|
| 35 |
+
pose_convention: Literal["backward_anchored", "backward_framewise"] = "backward_framewise",
|
| 36 |
+
fix_caption: bool = False,
|
| 37 |
+
fix_caption_text: str = "The camera moves in a scene.",
|
| 38 |
+
num_frames: int = 17,
|
| 39 |
+
embodiment_type: str = "camera_pose",
|
| 40 |
+
**_: object,
|
| 41 |
+
) -> None:
|
| 42 |
+
del shuffle
|
| 43 |
+
split = split.lower().strip()
|
| 44 |
+
if split not in {"val", "valid", "validation", "full"}:
|
| 45 |
+
raise ValueError("CameraBenchmarkDataset supports only the packaged validation split.")
|
| 46 |
+
if mode not in {"joint", "forward_dynamics", "inverse_dynamics", "policy", "image2video"}:
|
| 47 |
+
raise ValueError(f"Unsupported camera dataset mode {mode!r}")
|
| 48 |
+
|
| 49 |
+
self.root = Path(root)
|
| 50 |
+
if not self.root.exists():
|
| 51 |
+
raise ValueError(f"Camera benchmark root does not exist: {self.root}")
|
| 52 |
+
self.mode = mode
|
| 53 |
+
self.rotation_format = rotation_format
|
| 54 |
+
self.pose_convention = pose_convention
|
| 55 |
+
self.fix_caption = bool(fix_caption)
|
| 56 |
+
self.fix_caption_text = fix_caption_text
|
| 57 |
+
self.num_frames = int(num_frames)
|
| 58 |
+
self.domain_id = get_domain_id(embodiment_type)
|
| 59 |
+
self._tar_files, self._key_counts, self._total_key_count = _load_wdinfo(self.root)
|
| 60 |
+
|
| 61 |
+
def __len__(self) -> int:
|
| 62 |
+
return self._total_key_count
|
| 63 |
+
|
| 64 |
+
def __getitem__(self, index: int) -> dict[str, object]:
|
| 65 |
+
if index < 0:
|
| 66 |
+
index += len(self)
|
| 67 |
+
if index < 0 or index >= len(self):
|
| 68 |
+
raise IndexError(index)
|
| 69 |
+
|
| 70 |
+
tar_path, local_index = self._locate_record(index)
|
| 71 |
+
record = _load_record(tar_path, local_index)
|
| 72 |
+
metadata = record["metadata"]
|
| 73 |
+
video, fps = _decode_video(record["video"], self.num_frames)
|
| 74 |
+
camera_data = json.loads(record["camera"])
|
| 75 |
+
camera_to_world = _camera_to_world_poses(camera_data, len(video[0]))
|
| 76 |
+
action = pose_abs_to_rel(
|
| 77 |
+
camera_to_world,
|
| 78 |
+
rotation_format=self.rotation_format,
|
| 79 |
+
pose_convention=self.pose_convention,
|
| 80 |
+
translation_scale=1.0,
|
| 81 |
+
rotation_scale=1.0,
|
| 82 |
+
)
|
| 83 |
+
return {
|
| 84 |
+
"video": video,
|
| 85 |
+
"action": torch.from_numpy(action),
|
| 86 |
+
"conditioning_fps": torch.tensor(metadata.get("fps", fps), dtype=torch.float32),
|
| 87 |
+
"ai_caption": self.fix_caption_text if self.fix_caption else str(metadata["caption"]),
|
| 88 |
+
"mode": "policy" if self.mode == "joint" else self.mode,
|
| 89 |
+
"__key__": torch.tensor([index], dtype=torch.long),
|
| 90 |
+
"domain_id": torch.tensor(self.domain_id, dtype=torch.long),
|
| 91 |
+
"viewpoint": "ego_view",
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
def _locate_record(self, index: int) -> tuple[Path, int]:
|
| 95 |
+
offset = 0
|
| 96 |
+
for tar_path, key_count in zip(self._tar_files, self._key_counts, strict=True):
|
| 97 |
+
next_offset = offset + key_count
|
| 98 |
+
if index < next_offset:
|
| 99 |
+
return tar_path, index - offset
|
| 100 |
+
offset = next_offset
|
| 101 |
+
raise IndexError(index)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def _load_wdinfo(root: Path) -> tuple[list[Path], list[int], int]:
|
| 105 |
+
wdinfo = json.loads((root / "wdinfo.json").read_text())
|
| 106 |
+
data_root = wdinfo.get("root", "")
|
| 107 |
+
data_list = wdinfo.get("data_list", [])
|
| 108 |
+
if not data_list:
|
| 109 |
+
raise RuntimeError(f"No camera benchmark tar files listed in {root / 'wdinfo.json'}")
|
| 110 |
+
|
| 111 |
+
chunk_size = int(wdinfo.get("chunk_size", 1))
|
| 112 |
+
key_counts = [int(value) for value in wdinfo.get("data_list_key_count", [chunk_size] * len(data_list))]
|
| 113 |
+
if len(key_counts) != len(data_list):
|
| 114 |
+
raise ValueError("camera benchmark wdinfo data_list_key_count must match data_list length")
|
| 115 |
+
|
| 116 |
+
tar_root = root / data_root if data_root else root
|
| 117 |
+
tar_files = [tar_root / filename for filename in data_list]
|
| 118 |
+
total_key_count = int(wdinfo.get("total_key_count", sum(key_counts)))
|
| 119 |
+
if total_key_count <= 0:
|
| 120 |
+
raise RuntimeError(f"No packaged camera benchmark samples found under {root}")
|
| 121 |
+
return tar_files, key_counts, total_key_count
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def _load_record(tar_path: Path, local_index: int) -> dict[str, object]:
|
| 125 |
+
with tarfile.open(tar_path, mode="r:*") as tar:
|
| 126 |
+
members = sorted(
|
| 127 |
+
(member for member in tar.getmembers() if member.isfile() and member.name.endswith(".pkl")),
|
| 128 |
+
key=lambda member: member.name,
|
| 129 |
+
)
|
| 130 |
+
if local_index >= len(members):
|
| 131 |
+
raise IndexError(local_index)
|
| 132 |
+
extracted = tar.extractfile(members[local_index])
|
| 133 |
+
if extracted is None:
|
| 134 |
+
raise RuntimeError(f"Failed to extract {members[local_index].name} from {tar_path}")
|
| 135 |
+
try:
|
| 136 |
+
return pickle.load(extracted)
|
| 137 |
+
finally:
|
| 138 |
+
extracted.close()
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def _decode_video(video_bytes: bytes, num_frames: int) -> tuple[torch.Tensor, float]:
|
| 142 |
+
container = av.open(BytesIO(video_bytes), mode="r")
|
| 143 |
+
try:
|
| 144 |
+
stream = next((stream for stream in container.streams.video), None)
|
| 145 |
+
if stream is None:
|
| 146 |
+
raise ValueError("Camera benchmark video has no video stream")
|
| 147 |
+
fps = float(stream.average_rate or stream.base_rate or 30.0)
|
| 148 |
+
frames = [frame.to_rgb().to_ndarray() for frame in container.decode(stream)]
|
| 149 |
+
finally:
|
| 150 |
+
container.close()
|
| 151 |
+
|
| 152 |
+
if num_frames > 0:
|
| 153 |
+
target_count = 1 + 4 * ((num_frames - 1) // 4)
|
| 154 |
+
if len(frames) < target_count:
|
| 155 |
+
raise ValueError(
|
| 156 |
+
f"Camera benchmark video has {len(frames)} frames, but {target_count} are required"
|
| 157 |
+
)
|
| 158 |
+
frames = frames[:target_count]
|
| 159 |
+
if len(frames) < 2:
|
| 160 |
+
raise ValueError("Camera benchmark video must contain at least two frames")
|
| 161 |
+
|
| 162 |
+
video = torch.from_numpy(np.stack(frames, axis=0)).permute(3, 0, 1, 2).contiguous()
|
| 163 |
+
return video, fps
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def _camera_to_world_poses(camera_data: dict[str, object], frame_count: int) -> np.ndarray:
|
| 167 |
+
camera = camera_data["camera"]
|
| 168 |
+
if not isinstance(camera, dict):
|
| 169 |
+
raise ValueError("Camera benchmark camera.json must contain a camera object")
|
| 170 |
+
world_to_camera = np.asarray(camera["pose_world2cam"], dtype=np.float32).reshape(-1, 7)
|
| 171 |
+
if len(world_to_camera) < frame_count:
|
| 172 |
+
raise ValueError(
|
| 173 |
+
f"Camera benchmark camera.json has {len(world_to_camera)} poses, "
|
| 174 |
+
f"but {frame_count} are required"
|
| 175 |
+
)
|
| 176 |
+
world_to_camera = _extrinsic_params_to_matrices(world_to_camera[:frame_count])
|
| 177 |
+
homogeneous = np.repeat(np.eye(4, dtype=np.float32)[None], frame_count, axis=0)
|
| 178 |
+
homogeneous[:, :3, :] = world_to_camera
|
| 179 |
+
return np.linalg.inv(homogeneous).astype(np.float32, copy=False)
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def _extrinsic_params_to_matrices(qxyzw_t: np.ndarray) -> np.ndarray:
|
| 183 |
+
"""Convert ``[qx, qy, qz, qw, tx, ty, tz]`` rows to world-to-camera matrices."""
|
| 184 |
+
|
| 185 |
+
values = np.asarray(qxyzw_t, dtype=np.float32)
|
| 186 |
+
if values.ndim < 1 or values.shape[-1] != 7:
|
| 187 |
+
raise ValueError(f"Camera extrinsics must have trailing dim 7, got {values.shape}")
|
| 188 |
+
quat = values[..., :4]
|
| 189 |
+
norm = np.linalg.norm(quat, axis=-1, keepdims=True)
|
| 190 |
+
if np.any(norm < 1e-8):
|
| 191 |
+
raise ValueError("Camera extrinsics contain a zero-norm quaternion")
|
| 192 |
+
rotation = R.from_quat((quat / norm).reshape(-1, 4)).as_matrix().astype(np.float32)
|
| 193 |
+
rotation = rotation.reshape(*values.shape[:-1], 3, 3)
|
| 194 |
+
translation = values[..., 4:7, None]
|
| 195 |
+
return np.concatenate([rotation, translation], axis=-1).astype(np.float32, copy=False)
|
cosmos-framework/cosmos_framework/data/vfm/action/urdf_visualizer/action_datasets.py
CHANGED
|
@@ -11,6 +11,7 @@ from typing import Any, Callable
|
|
| 11 |
|
| 12 |
from cosmos_framework.data.vfm.action.av_dataset import AVDataset
|
| 13 |
from cosmos_framework.data.vfm.action.bridge_orig_lerobot_dataset import BridgeOrigLeRobotDataset
|
|
|
|
| 14 |
from cosmos_framework.data.vfm.action.droid_lerobot_dataset import DROIDLeRobotDataset
|
| 15 |
from cosmos_framework.data.vfm.action.fractal import FractalLeRobotDataset
|
| 16 |
from cosmos_framework.data.vfm.action.robomind_franka_dataset import RoboMINDFrankaDataset
|
|
@@ -42,6 +43,7 @@ def _env_path(*names: str, default: str) -> str:
|
|
| 42 |
|
| 43 |
|
| 44 |
BRIDGE_ROOT = _env_path("BRIDGE_LEROBOT_ROOT", "DATASET_PATH", default="/app/assets/examples/bridge_lerobot_v3")
|
|
|
|
| 45 |
|
| 46 |
DATASET_BRIDGE_480 = L(dataset_entry)(
|
| 47 |
name="bridge_20260501",
|
|
@@ -62,6 +64,25 @@ DATASET_BRIDGE_480 = L(dataset_entry)(
|
|
| 62 |
)
|
| 63 |
|
| 64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
DATASET_FRACTAL_256 = L(dataset_entry)(
|
| 66 |
name="fractal_20260501",
|
| 67 |
dataset=L(FractalLeRobotDataset)(
|
|
|
|
| 11 |
|
| 12 |
from cosmos_framework.data.vfm.action.av_dataset import AVDataset
|
| 13 |
from cosmos_framework.data.vfm.action.bridge_orig_lerobot_dataset import BridgeOrigLeRobotDataset
|
| 14 |
+
from cosmos_framework.data.vfm.action.camera_benchmark_dataset import CameraBenchmarkDataset
|
| 15 |
from cosmos_framework.data.vfm.action.droid_lerobot_dataset import DROIDLeRobotDataset
|
| 16 |
from cosmos_framework.data.vfm.action.fractal import FractalLeRobotDataset
|
| 17 |
from cosmos_framework.data.vfm.action.robomind_franka_dataset import RoboMINDFrankaDataset
|
|
|
|
| 43 |
|
| 44 |
|
| 45 |
BRIDGE_ROOT = _env_path("BRIDGE_LEROBOT_ROOT", "DATASET_PATH", default="/app/assets/examples/bridge_lerobot_v3")
|
| 46 |
+
CAMERA_ROOT = _env_path("CAMERA_ROOT", default="/app/assets/examples/camera_benchmark_v3")
|
| 47 |
|
| 48 |
DATASET_BRIDGE_480 = L(dataset_entry)(
|
| 49 |
name="bridge_20260501",
|
|
|
|
| 64 |
)
|
| 65 |
|
| 66 |
|
| 67 |
+
DATASET_CAMERA_480 = L(dataset_entry)(
|
| 68 |
+
name="camera_480_20260501",
|
| 69 |
+
dataset=L(CameraBenchmarkDataset)(
|
| 70 |
+
root=CAMERA_ROOT,
|
| 71 |
+
split="val",
|
| 72 |
+
shuffle=False,
|
| 73 |
+
fix_caption=False,
|
| 74 |
+
num_frames=61,
|
| 75 |
+
translation_scale=10,
|
| 76 |
+
max_action_translation_norm=10,
|
| 77 |
+
mode="policy",
|
| 78 |
+
rotation_format="rot6d",
|
| 79 |
+
pose_convention="backward_framewise",
|
| 80 |
+
),
|
| 81 |
+
ratio=1,
|
| 82 |
+
resolution="480",
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
DATASET_FRACTAL_256 = L(dataset_entry)(
|
| 87 |
name="fractal_20260501",
|
| 88 |
dataset=L(FractalLeRobotDataset)(
|
cosmos-framework/cosmos_framework/data/vfm/action/urdf_visualizer/viewer.py
CHANGED
|
@@ -121,6 +121,7 @@ def _build_datasets() -> dict[str, DatasetEntry]:
|
|
| 121 |
from cosmos_framework.data.vfm.action.urdf_visualizer.action_datasets import (
|
| 122 |
DATASET_AV_480,
|
| 123 |
DATASET_BRIDGE_480,
|
|
|
|
| 124 |
DATASET_DROID_480,
|
| 125 |
DATASET_FRACTAL_256,
|
| 126 |
DATASET_ROBOMIND_FRANKA_480,
|
|
@@ -161,6 +162,13 @@ def _build_datasets() -> dict[str, DatasetEntry]:
|
|
| 161 |
action_format=ActionFormat.SINGLE_ARM_10D,
|
| 162 |
to_opencv=_BRIDGE_TO_OPENCV,
|
| 163 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
"droid": _lazycfg_to_entry(
|
| 165 |
DATASET_DROID_480,
|
| 166 |
robot_name="franka_panda",
|
|
@@ -221,7 +229,7 @@ def _create_dataset(entry: DatasetEntry, chunk_length: int):
|
|
| 221 |
|
| 222 |
kwargs = dict(entry.dataset_kwargs)
|
| 223 |
kwargs["chunk_length"] = chunk_length
|
| 224 |
-
kwargs
|
| 225 |
kwargs["mode"] = "policy"
|
| 226 |
kwargs["enable_fast_init"] = True
|
| 227 |
|
|
|
|
| 121 |
from cosmos_framework.data.vfm.action.urdf_visualizer.action_datasets import (
|
| 122 |
DATASET_AV_480,
|
| 123 |
DATASET_BRIDGE_480,
|
| 124 |
+
DATASET_CAMERA_480,
|
| 125 |
DATASET_DROID_480,
|
| 126 |
DATASET_FRACTAL_256,
|
| 127 |
DATASET_ROBOMIND_FRANKA_480,
|
|
|
|
| 162 |
action_format=ActionFormat.SINGLE_ARM_10D,
|
| 163 |
to_opencv=_BRIDGE_TO_OPENCV,
|
| 164 |
),
|
| 165 |
+
"camera": _lazycfg_to_entry(
|
| 166 |
+
DATASET_CAMERA_480,
|
| 167 |
+
robot_name="",
|
| 168 |
+
max_finger_width=0.0,
|
| 169 |
+
fps=30,
|
| 170 |
+
action_format=ActionFormat.EGO_9D,
|
| 171 |
+
),
|
| 172 |
"droid": _lazycfg_to_entry(
|
| 173 |
DATASET_DROID_480,
|
| 174 |
robot_name="franka_panda",
|
|
|
|
| 229 |
|
| 230 |
kwargs = dict(entry.dataset_kwargs)
|
| 231 |
kwargs["chunk_length"] = chunk_length
|
| 232 |
+
kwargs.setdefault("split", "full")
|
| 233 |
kwargs["mode"] = "policy"
|
| 234 |
kwargs["enable_fast_init"] = True
|
| 235 |
|
start.sh
CHANGED
|
@@ -34,6 +34,7 @@ fi
|
|
| 34 |
export COSMOS_VIEWER_ON_DEMAND_VIDEO="${COSMOS_VIEWER_ON_DEMAND_VIDEO:-0}"
|
| 35 |
export COSMOS_VIEWER_DOWNLOAD_DATA="${COSMOS_VIEWER_DOWNLOAD_DATA:-0}"
|
| 36 |
export BRIDGE_LEROBOT_ROOT="${BRIDGE_LEROBOT_ROOT:-/app/assets/examples/bridge_lerobot_v3}"
|
|
|
|
| 37 |
export AV_ROOT="${AV_ROOT:-/app/assets/examples/av_v2_03292026_wdinfo}"
|
| 38 |
export UMI_ROOT="${UMI_ROOT:-/app/assets/examples/fastumi/fastumi_single_arm/pour_coke}"
|
| 39 |
export FRACTAL_ROOT="${FRACTAL_ROOT:-/app/assets/examples/fractal20220817_data}"
|
|
@@ -59,6 +60,7 @@ echo "ACTION_VIZ_PERSISTENT_RUNNER=${ACTION_VIZ_PERSISTENT_RUNNER}"
|
|
| 59 |
echo "ACTION_VIZ_USE_TORCH_COMPILE=${ACTION_VIZ_USE_TORCH_COMPILE}"
|
| 60 |
echo "PYTORCH_CUDA_ALLOC_CONF=${PYTORCH_CUDA_ALLOC_CONF}"
|
| 61 |
echo "BRIDGE_LEROBOT_ROOT=${BRIDGE_LEROBOT_ROOT}"
|
|
|
|
| 62 |
echo "COSMOS_VIEWER_ON_DEMAND_VIDEO=${COSMOS_VIEWER_ON_DEMAND_VIDEO}"
|
| 63 |
echo "COSMOS_VIEWER_SHARE=${COSMOS_VIEWER_SHARE}"
|
| 64 |
echo "AV_ROOT=${AV_ROOT}"
|
|
|
|
| 34 |
export COSMOS_VIEWER_ON_DEMAND_VIDEO="${COSMOS_VIEWER_ON_DEMAND_VIDEO:-0}"
|
| 35 |
export COSMOS_VIEWER_DOWNLOAD_DATA="${COSMOS_VIEWER_DOWNLOAD_DATA:-0}"
|
| 36 |
export BRIDGE_LEROBOT_ROOT="${BRIDGE_LEROBOT_ROOT:-/app/assets/examples/bridge_lerobot_v3}"
|
| 37 |
+
export CAMERA_ROOT="${CAMERA_ROOT:-/app/assets/examples/camera_benchmark_v3}"
|
| 38 |
export AV_ROOT="${AV_ROOT:-/app/assets/examples/av_v2_03292026_wdinfo}"
|
| 39 |
export UMI_ROOT="${UMI_ROOT:-/app/assets/examples/fastumi/fastumi_single_arm/pour_coke}"
|
| 40 |
export FRACTAL_ROOT="${FRACTAL_ROOT:-/app/assets/examples/fractal20220817_data}"
|
|
|
|
| 60 |
echo "ACTION_VIZ_USE_TORCH_COMPILE=${ACTION_VIZ_USE_TORCH_COMPILE}"
|
| 61 |
echo "PYTORCH_CUDA_ALLOC_CONF=${PYTORCH_CUDA_ALLOC_CONF}"
|
| 62 |
echo "BRIDGE_LEROBOT_ROOT=${BRIDGE_LEROBOT_ROOT}"
|
| 63 |
+
echo "CAMERA_ROOT=${CAMERA_ROOT}"
|
| 64 |
echo "COSMOS_VIEWER_ON_DEMAND_VIDEO=${COSMOS_VIEWER_ON_DEMAND_VIDEO}"
|
| 65 |
echo "COSMOS_VIEWER_SHARE=${COSMOS_VIEWER_SHARE}"
|
| 66 |
echo "AV_ROOT=${AV_ROOT}"
|