robocandywrapper 0.2.13__tar.gz → 0.2.15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {robocandywrapper-0.2.13/robocandywrapper.egg-info → robocandywrapper-0.2.15}/PKG-INFO +4 -3
  2. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/pyproject.toml +5 -3
  3. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/__init__.py +1 -1
  4. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/constants.py +1 -0
  5. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/plugins/__init__.py +6 -0
  6. robocandywrapper-0.2.15/robocandywrapper/plugins/molmopoint.py +163 -0
  7. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15/robocandywrapper.egg-info}/PKG-INFO +4 -3
  8. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper.egg-info/SOURCES.txt +1 -0
  9. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper.egg-info/requires.txt +3 -2
  10. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/setup.py +1 -1
  11. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/LICENSE +0 -0
  12. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/MANIFEST.in +0 -0
  13. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/README.md +0 -0
  14. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/dataformats/__init__.py +0 -0
  15. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/dataformats/lerobot_21/__init__.py +0 -0
  16. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/dataformats/lerobot_21/convert_v20_to_v21.py +0 -0
  17. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/dataformats/lerobot_21/dataset.py +0 -0
  18. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/dataformats/lerobot_21/utils.py +0 -0
  19. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/factory.py +0 -0
  20. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/metadata_view.py +0 -0
  21. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/plugin.py +0 -0
  22. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/plugins/affordance.py +0 -0
  23. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/plugins/control_mode.py +0 -0
  24. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/plugins/episode_outcome.py +0 -0
  25. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/plugins/subtask.py +0 -0
  26. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/samplers/__init__.py +0 -0
  27. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/samplers/config.py +0 -0
  28. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/samplers/factory.py +0 -0
  29. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/samplers/weighted.py +0 -0
  30. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/utils.py +0 -0
  31. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper/wrapper.py +0 -0
  32. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper.egg-info/dependency_links.txt +0 -0
  33. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/robocandywrapper.egg-info/top_level.txt +0 -0
  34. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/setup.cfg +0 -0
  35. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/tests/test_dataset_weights_integration.py +0 -0
  36. {robocandywrapper-0.2.13 → robocandywrapper-0.2.15}/tests/test_key_rename_stats.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robocandywrapper
3
- Version: 0.2.13
3
+ Version: 0.2.15
4
4
  Summary: Sweet wrappers for extending and remixing LeRobot Datasets
5
5
  Author: RoboCandyWrapper Contributors
6
6
  License: MIT License
@@ -42,14 +42,15 @@ Requires-Dist: numpy>=1.20.0
42
42
  Requires-Dist: torch>=2.0.0
43
43
  Requires-Dist: lerobot<0.5,>=0.4
44
44
  Requires-Dist: pandas>=1.3.0
45
- Requires-Dist: motion-primitives
46
45
  Requires-Dist: rewact_tools
46
+ Requires-Dist: motion-primitives
47
+ Requires-Dist: lerobot-policy-diffusion-motion-primitives
48
+ Requires-Dist: lerobot-policy-diffusion-pointing
47
49
  Provides-Extra: dev
48
50
  Requires-Dist: pytest>=7.0.0; extra == "dev"
49
51
  Requires-Dist: black>=22.0.0; extra == "dev"
50
52
  Requires-Dist: isort>=5.10.0; extra == "dev"
51
53
  Requires-Dist: flake8>=4.0.0; extra == "dev"
52
- Requires-Dist: lerobot-policy-diffusion-motion-primitives; extra == "dev"
53
54
  Dynamic: license-file
54
55
  Dynamic: requires-python
55
56
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "robocandywrapper"
7
- version = "0.2.13"
7
+ version = "0.2.15"
8
8
  description = "Sweet wrappers for extending and remixing LeRobot Datasets"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10,<3.11"
@@ -27,8 +27,10 @@ dependencies = [
27
27
  "torch>=2.0.0",
28
28
  "lerobot>=0.4,<0.5",
29
29
  "pandas>=1.3.0",
30
- "motion-primitives",
31
30
  "rewact_tools",
31
+ "motion-primitives",
32
+ "lerobot-policy-diffusion-motion-primitives",
33
+ "lerobot-policy-diffusion-pointing",
32
34
  ]
33
35
 
34
36
  [project.optional-dependencies]
@@ -37,7 +39,6 @@ dev = [
37
39
  "black>=22.0.0",
38
40
  "isort>=5.10.0",
39
41
  "flake8>=4.0.0",
40
- "lerobot-policy-diffusion-motion-primitives"
41
42
  ]
42
43
 
43
44
  [project.urls]
@@ -57,6 +58,7 @@ override-dependencies = [
57
58
 
58
59
  [tool.uv.sources]
59
60
  lerobot-policy-diffusion-motion-primitives = { path = "../../experimental/lerobot_policy_diffusion_motion_primitives", editable = true }
61
+ lerobot-policy-diffusion-pointing = { path = "../../experimental/lerobot_policy_diffusion_pointing", editable = true }
60
62
  motion-primitives = { path = "../../experimental/motion_primitives", editable = true }
61
63
  rewact-tools = { path = "../rewACT/rewact_tools", editable = true }
62
64
 
@@ -18,7 +18,7 @@ from robocandywrapper.constants import (
18
18
  MOTION_PRIMITIVE_PLUGIN_NAME,
19
19
  )
20
20
 
21
- __version__ = "0.2.13"
21
+ __version__ = "0.2.15"
22
22
 
23
23
  __all__ = [
24
24
  "DatasetPlugin",
@@ -10,5 +10,6 @@ AFFORDANCE_PLUGIN_NAME = "affordance"
10
10
  CONTROL_MODE_PLUGIN_NAME = "control_mode"
11
11
  EPISODE_OUTCOME_PLUGIN_NAME = "episode_outcome"
12
12
  SUBTASK_PLUGIN_NAME = "subtask"
13
+ MOLMOPOINT_PLUGIN_NAME = "molmopoint"
13
14
  MOTION_PRIMITIVE_PLUGIN_NAME = "motion_primitives"
14
15
 
@@ -12,6 +12,10 @@ from robocandywrapper.plugins.episode_outcome import (
12
12
  EpisodeOutcomePlugin,
13
13
  EpisodeOutcomeInstance,
14
14
  )
15
+ from robocandywrapper.plugins.molmopoint import (
16
+ MolmoPointPlugin,
17
+ MolmoPointInstance,
18
+ )
15
19
 
16
20
  __all__ = [
17
21
  "ControlModePlugin",
@@ -20,5 +24,7 @@ __all__ = [
20
24
  "AffordanceInstance",
21
25
  "EpisodeOutcomePlugin",
22
26
  "EpisodeOutcomeInstance",
27
+ "MolmoPointPlugin",
28
+ "MolmoPointInstance",
23
29
  ]
24
30
 
@@ -0,0 +1,163 @@
1
+ """
2
+ MolmoPoint Plugin for per-frame visual affordance point labels.
3
+
4
+ Storage: {dataset_root}/candywrapper_plugins/molmopoint/episode_{idx:06d}.parquet
5
+
6
+ Parquet schema per episode:
7
+ frame_index : int — episode-relative frame index (0-based)
8
+ camera : str — camera key, e.g. "observation.images.front"
9
+ point_x : float — normalised x coordinate (-1 to 1, left to right)
10
+ point_y : float — normalised y coordinate (-1 to 1, top to bottom)
11
+ is_keyframe : bool — True for raw MolmoPoint predictions, False for interpolated
12
+
13
+ Coordinates are in [-1, 1] where (-1,-1) is top-left and (1,1) is bottom-right.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import warnings
18
+ from pathlib import Path
19
+ from typing import Any, Dict, Optional
20
+
21
+ import torch
22
+ from lerobot.datasets.lerobot_dataset import LeRobotDataset
23
+
24
+ from robocandywrapper.constants import CANDYWRAPPER_PLUGINS_DIR, MOLMOPOINT_PLUGIN_NAME
25
+ from robocandywrapper.plugin import DatasetPlugin, PluginInstance
26
+
27
+
28
+ def _calculate_episode_data_index(hf_dataset) -> dict[str, torch.Tensor]:
29
+ episode_data_index: dict[str, list[int]] = {"from": [], "to": []}
30
+ if len(hf_dataset) == 0:
31
+ return {"from": torch.tensor([]), "to": torch.tensor([])}
32
+ current_episode = None
33
+ for idx, episode_idx in enumerate(hf_dataset["episode_index"]):
34
+ if episode_idx != current_episode:
35
+ episode_data_index["from"].append(idx)
36
+ if current_episode is not None:
37
+ episode_data_index["to"].append(idx)
38
+ current_episode = episode_idx
39
+ episode_data_index["to"].append(idx + 1)
40
+ return {k: torch.tensor(v) for k, v in episode_data_index.items()}
41
+
42
+
43
+ class MolmoPointPlugin(DatasetPlugin):
44
+ """Plugin that provides per-frame MolmoPoint affordance labels."""
45
+
46
+ def attach(self, dataset: "LeRobotDataset") -> "MolmoPointInstance":
47
+ return MolmoPointInstance(dataset, self)
48
+
49
+
50
+ class MolmoPointInstance(PluginInstance):
51
+ """Dataset-specific MolmoPoint label data."""
52
+
53
+ def __init__(self, dataset: "LeRobotDataset", config: MolmoPointPlugin):
54
+ super().__init__(dataset)
55
+ self.config = config
56
+ self._data: Dict[int, "pd.DataFrame"] = {}
57
+ self._cached_episode_data_index: dict[str, torch.Tensor] | None = None
58
+
59
+ self._camera_keys: list[str] = list(dataset.meta.video_keys)
60
+ self._camera_to_idx: dict[str, int] = {
61
+ k: i for i, k in enumerate(self._camera_keys)
62
+ }
63
+ self._load()
64
+
65
+ def _get_episode_data_index(self) -> dict[str, torch.Tensor]:
66
+ if hasattr(self.dataset, "episode_data_index"):
67
+ return self.dataset.episode_data_index
68
+ if self._cached_episode_data_index is None:
69
+ self._cached_episode_data_index = _calculate_episode_data_index(
70
+ self.dataset.hf_dataset
71
+ )
72
+ return self._cached_episode_data_index
73
+
74
+ def get_data_keys(self) -> list[str]:
75
+ return ["pointing", "pointing_mask"]
76
+
77
+ def get_item_data(
78
+ self,
79
+ idx: int,
80
+ episode_idx: int,
81
+ accumulated_data: Optional[Dict[str, Any]] = None,
82
+ ) -> dict[str, Any]:
83
+ """Return per-camera pointing vector and mask for this frame.
84
+
85
+ Returns:
86
+ pointing: tensor of shape (num_cameras * 2,) with [x0, y0, x1, y1, ...]
87
+ in [-1, 1]. One (x, y) pair per camera in dataset.meta.video_keys
88
+ order. Unlabelled cameras get (0, 0).
89
+ pointing_mask: bool tensor of shape (num_cameras,), True where
90
+ a real label exists for that camera.
91
+ """
92
+ num_cameras = len(self._camera_keys)
93
+ pointing = torch.zeros(num_cameras * 2)
94
+ pointing_mask = torch.zeros(num_cameras, dtype=torch.bool)
95
+
96
+ if episode_idx not in self._data:
97
+ return {"pointing": pointing, "pointing_mask": pointing_mask}
98
+
99
+ ep_data_index = self._get_episode_data_index()
100
+ ep_start = ep_data_index["from"][episode_idx].item()
101
+ frame_in_episode = idx - ep_start
102
+
103
+ df = self._data[episode_idx]
104
+ rows = df[df["frame_index"] == frame_in_episode]
105
+
106
+ for _, row in rows.iterrows():
107
+ cam_idx = self._camera_to_idx.get(row["camera"])
108
+ if cam_idx is None:
109
+ continue
110
+ pointing[cam_idx * 2] = float(row["point_x"])
111
+ pointing[cam_idx * 2 + 1] = float(row["point_y"])
112
+ pointing_mask[cam_idx] = True
113
+
114
+ return {"pointing": pointing, "pointing_mask": pointing_mask}
115
+
116
+ def _get_plugin_dir(self) -> Path:
117
+ plugin_dir = (
118
+ Path(self.dataset.root) / CANDYWRAPPER_PLUGINS_DIR / MOLMOPOINT_PLUGIN_NAME
119
+ )
120
+ plugin_dir.mkdir(parents=True, exist_ok=True)
121
+ return plugin_dir
122
+
123
+ def _episode_path(self, episode_idx: int) -> Path:
124
+ return self._get_plugin_dir() / f"episode_{episode_idx:06d}.parquet"
125
+
126
+ def _load(self) -> None:
127
+ plugin_dir = self._get_plugin_dir()
128
+ import pandas as pd
129
+
130
+ loaded = 0
131
+ for pq_file in sorted(plugin_dir.glob("episode_*.parquet")):
132
+ try:
133
+ ep_idx = int(pq_file.stem.split("_")[1])
134
+ self._data[ep_idx] = pd.read_parquet(pq_file)
135
+ loaded += 1
136
+ except Exception as e:
137
+ warnings.warn(f"Could not load {pq_file}: {e}")
138
+
139
+ if loaded:
140
+ print(f"Loaded MolmoPoint labels for {loaded} episodes from {plugin_dir}")
141
+
142
+ def save_episode(self, episode_idx: int, df: "pd.DataFrame") -> Path:
143
+ """Save MolmoPoint labels for one episode. Returns the saved path."""
144
+ self._data[episode_idx] = df
145
+ path = self._episode_path(episode_idx)
146
+ df.to_parquet(path, index=False)
147
+ return path
148
+
149
+ def get_episode_labels(self, episode_idx: int) -> "pd.DataFrame | None":
150
+ return self._data.get(episode_idx)
151
+
152
+ def get_episode_labels_by_camera(
153
+ self, episode_idx: int, camera: str,
154
+ ) -> "pd.DataFrame | None":
155
+ df = self._data.get(episode_idx)
156
+ if df is None:
157
+ return None
158
+ filtered = df[df["camera"] == camera]
159
+ return filtered if not filtered.empty else None
160
+
161
+ @property
162
+ def labeled_episodes(self) -> list[int]:
163
+ return sorted(self._data.keys())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robocandywrapper
3
- Version: 0.2.13
3
+ Version: 0.2.15
4
4
  Summary: Sweet wrappers for extending and remixing LeRobot Datasets
5
5
  Author: RoboCandyWrapper Contributors
6
6
  License: MIT License
@@ -42,14 +42,15 @@ Requires-Dist: numpy>=1.20.0
42
42
  Requires-Dist: torch>=2.0.0
43
43
  Requires-Dist: lerobot<0.5,>=0.4
44
44
  Requires-Dist: pandas>=1.3.0
45
- Requires-Dist: motion-primitives
46
45
  Requires-Dist: rewact_tools
46
+ Requires-Dist: motion-primitives
47
+ Requires-Dist: lerobot-policy-diffusion-motion-primitives
48
+ Requires-Dist: lerobot-policy-diffusion-pointing
47
49
  Provides-Extra: dev
48
50
  Requires-Dist: pytest>=7.0.0; extra == "dev"
49
51
  Requires-Dist: black>=22.0.0; extra == "dev"
50
52
  Requires-Dist: isort>=5.10.0; extra == "dev"
51
53
  Requires-Dist: flake8>=4.0.0; extra == "dev"
52
- Requires-Dist: lerobot-policy-diffusion-motion-primitives; extra == "dev"
53
54
  Dynamic: license-file
54
55
  Dynamic: requires-python
55
56
 
@@ -24,6 +24,7 @@ robocandywrapper/plugins/__init__.py
24
24
  robocandywrapper/plugins/affordance.py
25
25
  robocandywrapper/plugins/control_mode.py
26
26
  robocandywrapper/plugins/episode_outcome.py
27
+ robocandywrapper/plugins/molmopoint.py
27
28
  robocandywrapper/plugins/subtask.py
28
29
  robocandywrapper/samplers/__init__.py
29
30
  robocandywrapper/samplers/config.py
@@ -2,12 +2,13 @@ numpy>=1.20.0
2
2
  torch>=2.0.0
3
3
  lerobot<0.5,>=0.4
4
4
  pandas>=1.3.0
5
- motion-primitives
6
5
  rewact_tools
6
+ motion-primitives
7
+ lerobot-policy-diffusion-motion-primitives
8
+ lerobot-policy-diffusion-pointing
7
9
 
8
10
  [dev]
9
11
  pytest>=7.0.0
10
12
  black>=22.0.0
11
13
  isort>=5.10.0
12
14
  flake8>=4.0.0
13
- lerobot-policy-diffusion-motion-primitives
@@ -9,7 +9,7 @@ long_description = readme_file.read_text(encoding="utf-8") if readme_file.exists
9
9
 
10
10
  setup(
11
11
  name="robocandywrapper",
12
- version="0.2.13",
12
+ version="0.2.15",
13
13
  description="Sweet wrappers for extending and remixing LeRobot Datasets",
14
14
  long_description=long_description,
15
15
  long_description_content_type="text/markdown",