Align DeMemWM latent dataset loading
Browse files
datasets/video/minecraft_video_dememwm_latent_dataset.py
CHANGED
|
@@ -10,6 +10,26 @@ from omegaconf import DictConfig
|
|
| 10 |
from .memory_selection import SEGMENT_KEYS, cfg_get, memory_segment_lengths, select_memory_indices
|
| 11 |
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
class MinecraftVideoDeMemWMLatentDataset(torch.utils.data.Dataset):
|
| 14 |
"""Load precomputed VAE latents and append causal memory frames.
|
| 15 |
|
|
@@ -23,6 +43,8 @@ class MinecraftVideoDeMemWMLatentDataset(torch.utils.data.Dataset):
|
|
| 23 |
self.cfg = cfg
|
| 24 |
self.split = split
|
| 25 |
self.save_dir = Path(cfg.save_dir)
|
|
|
|
|
|
|
| 26 |
self.n_frames = int(cfg_get(cfg, "n_frames_valid", cfg.n_frames)) if split in {"validation", "test"} else int(cfg.n_frames)
|
| 27 |
self.frame_skip = int(cfg_get(cfg, "frame_skip", 1))
|
| 28 |
self.target_span = max(1, (self.n_frames - 1) * self.frame_skip + 1)
|
|
@@ -32,12 +54,23 @@ class MinecraftVideoDeMemWMLatentDataset(torch.utils.data.Dataset):
|
|
| 32 |
self._target_offsets = np.arange(self.n_frames, dtype=np.int64) * self.frame_skip
|
| 33 |
self._memory_segments = memory_segment_lengths(self.n_frames, self.memory_selection)
|
| 34 |
self.memory_condition_length = sum(self._memory_segments[key] for key in SEGMENT_KEYS)
|
|
|
|
|
|
|
| 35 |
self.data_paths = self.get_data_paths(split)
|
| 36 |
if not self.data_paths:
|
| 37 |
-
raise FileNotFoundError(f"No latent .npz files found for split '{split}' under {self.save_dir}")
|
| 38 |
|
| 39 |
self.lengths = np.asarray([self._file_length(path) for path in self.data_paths], dtype=np.int64)
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
if self.single_eval_clip:
|
| 42 |
clips = np.ones_like(clips)
|
| 43 |
self.clips_per_file = clips
|
|
@@ -49,8 +82,22 @@ class MinecraftVideoDeMemWMLatentDataset(torch.utils.data.Dataset):
|
|
| 49 |
|
| 50 |
def get_data_paths(self, split: str):
|
| 51 |
split_dir = self.save_dir / split
|
| 52 |
-
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
def __len__(self):
|
| 56 |
return int(self.cum_clips_per_file[-1])
|
|
@@ -66,6 +113,7 @@ class MinecraftVideoDeMemWMLatentDataset(torch.utils.data.Dataset):
|
|
| 66 |
def load_data(self, idx: int):
|
| 67 |
# === 1. Resolve clip and load arrays ===
|
| 68 |
file_idx, frame_idx = self.split_idx(int(idx))
|
|
|
|
| 69 |
arrays = self._load_arrays(self.data_paths[file_idx])
|
| 70 |
latents = arrays["latents"]
|
| 71 |
actions = arrays["actions"]
|
|
@@ -116,10 +164,13 @@ class MinecraftVideoDeMemWMLatentDataset(torch.utils.data.Dataset):
|
|
| 116 |
return int(data["latents"].shape[0])
|
| 117 |
|
| 118 |
def _load_arrays(self, path: Path):
|
|
|
|
|
|
|
|
|
|
| 119 |
with np.load(path, allow_pickle=False) as data:
|
| 120 |
latents = np.asarray(data["latents"])
|
| 121 |
-
actions = np.asarray(data["actions"])
|
| 122 |
-
poses = np.asarray(data["poses"], dtype=np.float32)
|
| 123 |
if "frame_indices" in data:
|
| 124 |
frame_indices = np.asarray(data["frame_indices"], dtype=np.int64)
|
| 125 |
else:
|
|
@@ -131,13 +182,40 @@ class MinecraftVideoDeMemWMLatentDataset(torch.utils.data.Dataset):
|
|
| 131 |
image_hw = np.asarray(cfg_get(self.cfg, "image_hw", [cfg_get(self.cfg, "resolution", 0), cfg_get(self.cfg, "resolution", 0)]), dtype=np.int64)
|
| 132 |
|
| 133 |
self._validate_arrays(path, latents, actions, poses, frame_indices)
|
| 134 |
-
|
| 135 |
"latents": latents,
|
| 136 |
"actions": actions,
|
| 137 |
"poses": poses,
|
| 138 |
"frame_indices": frame_indices,
|
| 139 |
"image_hw": image_hw,
|
| 140 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
|
| 142 |
def _validate_arrays(self, path: Path, latents, actions, poses, frame_indices) -> None:
|
| 143 |
length = len(latents)
|
|
|
|
| 10 |
from .memory_selection import SEGMENT_KEYS, cfg_get, memory_segment_lengths, select_memory_indices
|
| 11 |
|
| 12 |
|
| 13 |
+
_INITIAL_FRAME_OFFSET = 100
|
| 14 |
+
_ACTION_DIM = 25
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _convert_action_space(actions: np.ndarray) -> np.ndarray:
|
| 18 |
+
vec_25 = np.zeros((len(actions), _ACTION_DIM), dtype=np.float32)
|
| 19 |
+
vec_25[actions[:, 0] == 1, 11] = 1.0
|
| 20 |
+
vec_25[actions[:, 0] == 2, 12] = 1.0
|
| 21 |
+
vec_25[actions[:, 4] == 11, 16] = -1.0
|
| 22 |
+
vec_25[actions[:, 4] == 13, 16] = 1.0
|
| 23 |
+
vec_25[actions[:, 3] == 11, 15] = -1.0
|
| 24 |
+
vec_25[actions[:, 3] == 13, 15] = 1.0
|
| 25 |
+
vec_25[actions[:, 5] == 6, 24] = 1.0
|
| 26 |
+
vec_25[actions[:, 5] == 1, 24] = 1.0
|
| 27 |
+
vec_25[actions[:, 1] == 1, 13] = 1.0
|
| 28 |
+
vec_25[actions[:, 1] == 2, 14] = 1.0
|
| 29 |
+
vec_25[actions[:, 7] == 1, 2] = 1.0
|
| 30 |
+
return vec_25
|
| 31 |
+
|
| 32 |
+
|
| 33 |
class MinecraftVideoDeMemWMLatentDataset(torch.utils.data.Dataset):
|
| 34 |
"""Load precomputed VAE latents and append causal memory frames.
|
| 35 |
|
|
|
|
| 43 |
self.cfg = cfg
|
| 44 |
self.split = split
|
| 45 |
self.save_dir = Path(cfg.save_dir)
|
| 46 |
+
self.wo_updown = bool(cfg_get(cfg, "wo_updown", False))
|
| 47 |
+
self.initial_frame_offset = _INITIAL_FRAME_OFFSET
|
| 48 |
self.n_frames = int(cfg_get(cfg, "n_frames_valid", cfg.n_frames)) if split in {"validation", "test"} else int(cfg.n_frames)
|
| 49 |
self.frame_skip = int(cfg_get(cfg, "frame_skip", 1))
|
| 50 |
self.target_span = max(1, (self.n_frames - 1) * self.frame_skip + 1)
|
|
|
|
| 54 |
self._target_offsets = np.arange(self.n_frames, dtype=np.int64) * self.frame_skip
|
| 55 |
self._memory_segments = memory_segment_lengths(self.n_frames, self.memory_selection)
|
| 56 |
self.memory_condition_length = sum(self._memory_segments[key] for key in SEGMENT_KEYS)
|
| 57 |
+
self._cached_arrays_path = None
|
| 58 |
+
self._cached_arrays = None
|
| 59 |
self.data_paths = self.get_data_paths(split)
|
| 60 |
if not self.data_paths:
|
| 61 |
+
raise FileNotFoundError(f"No latent .npz files found for split '{split}' under {self.save_dir / split}")
|
| 62 |
|
| 63 |
self.lengths = np.asarray([self._file_length(path) for path in self.data_paths], dtype=np.int64)
|
| 64 |
+
required_length = self.initial_frame_offset + self.target_span
|
| 65 |
+
too_short = self.lengths < required_length
|
| 66 |
+
if bool(too_short.any()):
|
| 67 |
+
bad_idx = int(np.nonzero(too_short)[0][0])
|
| 68 |
+
raise ValueError(
|
| 69 |
+
f"Latent file {self.data_paths[bad_idx]} has {int(self.lengths[bad_idx])} frames, "
|
| 70 |
+
f"but DeMemWM requires at least {required_length} for split '{split}'"
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
clips = (self.lengths - required_length + 1).astype(np.int64)
|
| 74 |
if self.single_eval_clip:
|
| 75 |
clips = np.ones_like(clips)
|
| 76 |
self.clips_per_file = clips
|
|
|
|
| 82 |
|
| 83 |
def get_data_paths(self, split: str):
|
| 84 |
split_dir = self.save_dir / split
|
| 85 |
+
if not split_dir.exists():
|
| 86 |
+
return []
|
| 87 |
+
paths = sorted((path for path in split_dir.glob("**/*.npz") if path.is_file()), key=lambda path: path.name)
|
| 88 |
+
|
| 89 |
+
if self.wo_updown:
|
| 90 |
+
paths = [path for path in paths if "w_updown" not in str(path)]
|
| 91 |
+
|
| 92 |
+
if split in {"validation", "test"} and self.wo_updown:
|
| 93 |
+
paths = [path for path in paths if "w_updown" not in str(path)]
|
| 94 |
+
elif split in {"validation", "test"}:
|
| 95 |
+
paths = [path for path in paths if "w_updown" in str(path)]
|
| 96 |
+
|
| 97 |
+
if not paths:
|
| 98 |
+
for sub_path in sorted(split_dir.iterdir(), key=lambda path: path.name):
|
| 99 |
+
paths += sorted((path for path in sub_path.glob("**/*.npz") if path.is_file()), key=lambda path: path.name)
|
| 100 |
+
return paths
|
| 101 |
|
| 102 |
def __len__(self):
|
| 103 |
return int(self.cum_clips_per_file[-1])
|
|
|
|
| 113 |
def load_data(self, idx: int):
|
| 114 |
# === 1. Resolve clip and load arrays ===
|
| 115 |
file_idx, frame_idx = self.split_idx(int(idx))
|
| 116 |
+
frame_idx += self.initial_frame_offset
|
| 117 |
arrays = self._load_arrays(self.data_paths[file_idx])
|
| 118 |
latents = arrays["latents"]
|
| 119 |
actions = arrays["actions"]
|
|
|
|
| 164 |
return int(data["latents"].shape[0])
|
| 165 |
|
| 166 |
def _load_arrays(self, path: Path):
|
| 167 |
+
if self._cached_arrays_path == path:
|
| 168 |
+
return self._cached_arrays
|
| 169 |
+
|
| 170 |
with np.load(path, allow_pickle=False) as data:
|
| 171 |
latents = np.asarray(data["latents"])
|
| 172 |
+
actions = self._normalize_actions(path, np.asarray(data["actions"]))
|
| 173 |
+
poses = self._sanitize_poses(path, np.asarray(data["poses"], dtype=np.float32), len(actions))
|
| 174 |
if "frame_indices" in data:
|
| 175 |
frame_indices = np.asarray(data["frame_indices"], dtype=np.int64)
|
| 176 |
else:
|
|
|
|
| 182 |
image_hw = np.asarray(cfg_get(self.cfg, "image_hw", [cfg_get(self.cfg, "resolution", 0), cfg_get(self.cfg, "resolution", 0)]), dtype=np.int64)
|
| 183 |
|
| 184 |
self._validate_arrays(path, latents, actions, poses, frame_indices)
|
| 185 |
+
arrays = {
|
| 186 |
"latents": latents,
|
| 187 |
"actions": actions,
|
| 188 |
"poses": poses,
|
| 189 |
"frame_indices": frame_indices,
|
| 190 |
"image_hw": image_hw,
|
| 191 |
}
|
| 192 |
+
self._cached_arrays_path = path
|
| 193 |
+
self._cached_arrays = arrays
|
| 194 |
+
return arrays
|
| 195 |
+
|
| 196 |
+
def _normalize_actions(self, path: Path, actions: np.ndarray) -> np.ndarray:
|
| 197 |
+
if actions.ndim != 2:
|
| 198 |
+
raise ValueError(f"actions in {path} must have shape [num_frames, action_dim]")
|
| 199 |
+
if actions.shape[1] == _ACTION_DIM:
|
| 200 |
+
return actions.astype(np.float32, copy=False)
|
| 201 |
+
if actions.shape[1] < 8:
|
| 202 |
+
raise ValueError(f"raw actions in {path} must have at least 8 columns to convert to 25-D actions")
|
| 203 |
+
return _convert_action_space(actions)
|
| 204 |
+
|
| 205 |
+
@staticmethod
|
| 206 |
+
def _sanitize_poses(path: Path, poses: np.ndarray, action_length: int) -> np.ndarray:
|
| 207 |
+
if poses.ndim != 2 or poses.shape[1] < 5:
|
| 208 |
+
raise ValueError(f"poses in {path} must have shape [num_frames, >=5]")
|
| 209 |
+
|
| 210 |
+
poses = poses.astype(np.float32, copy=True)
|
| 211 |
+
if len(poses) > 1:
|
| 212 |
+
poses[0, 1] = poses[1, 1]
|
| 213 |
+
height_range = float(np.ptp(poses[:, 1])) if len(poses) else 0.0
|
| 214 |
+
if height_range >= 2:
|
| 215 |
+
raise ValueError(f"Pose height variation too large for {path}: {height_range}")
|
| 216 |
+
if len(poses) < action_length:
|
| 217 |
+
poses = np.pad(poses, ((1, 0), (0, 0)))
|
| 218 |
+
return poses
|
| 219 |
|
| 220 |
def _validate_arrays(self, path: Path, latents, actions, poses, frame_indices) -> None:
|
| 221 |
length = len(latents)
|
tests/test_dememwm_latent_dataset.py
CHANGED
|
@@ -42,6 +42,41 @@ def _poses(num_frames):
|
|
| 42 |
return poses
|
| 43 |
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
class MemorySelectionTests(unittest.TestCase):
|
| 46 |
def test_worldmem_backend_is_explicitly_rejected_until_implemented(self):
|
| 47 |
poses = _poses(8)
|
|
@@ -149,6 +184,75 @@ class MemorySelectionTests(unittest.TestCase):
|
|
| 149 |
|
| 150 |
class DeMemWMLatentDatasetTests(unittest.TestCase):
|
| 151 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
def test_preprocess_splits_sequence_tensors_from_memory_segments(self):
|
| 153 |
from algorithms.dememwm.df_video import _preprocess_dememwm_latent_batch
|
| 154 |
|
|
@@ -497,13 +601,13 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
|
|
| 497 |
root = Path(tmp)
|
| 498 |
split_dir = root / "training"
|
| 499 |
split_dir.mkdir()
|
| 500 |
-
num_frames =
|
| 501 |
np.savez(
|
| 502 |
split_dir / "sample.npz",
|
| 503 |
latents=np.arange(num_frames * 2, dtype=np.float32).reshape(num_frames, 1, 1, 2),
|
| 504 |
actions=np.ones((num_frames, 25), dtype=np.float32),
|
| 505 |
poses=_poses(num_frames),
|
| 506 |
-
frame_indices=np.arange(
|
| 507 |
image_hw=np.array([360, 640], dtype=np.int64),
|
| 508 |
)
|
| 509 |
cfg = OmegaConf.create(
|
|
@@ -526,7 +630,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
|
|
| 526 |
self.assertEqual(sample["memory_segments"], {"target": 3, "anchor": 2, "dynamic": 2, "revisit": 2})
|
| 527 |
self.assertEqual(tuple(sample["latents"].shape), (9, 1, 1, 2))
|
| 528 |
self.assertEqual(sample["frame_indices"][:3].tolist(), [106, 107, 108])
|
| 529 |
-
self.assertEqual(sample["frame_indices"][3:5].tolist(), [
|
| 530 |
self.assertEqual(sample["frame_indices"][5:7].tolist(), [104, 105])
|
| 531 |
self.assertTrue(all(idx < 104 for idx in sample["frame_indices"][7:9].tolist() if idx >= 0))
|
| 532 |
self.assertEqual(len(sample["frame_indices"][7:9]), 2)
|
|
@@ -549,7 +653,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
|
|
| 549 |
self.assertTrue(torch.equal(preprocessed["actions"][3:], torch.ones_like(preprocessed["actions"][3:])))
|
| 550 |
self.assertEqual(tuple(preprocessed["poses"].shape), (9, 1, 5))
|
| 551 |
self.assertIs(preprocessed["frame_memory_pose"], preprocessed["poses"])
|
| 552 |
-
self.assertEqual(preprocessed["frame_memory_pose"][:7, 0, 0].tolist(), [0.0, 1.0, 2.0, -
|
| 553 |
self.assertEqual(tuple(preprocessed["frame_indices"].shape), (9, 1))
|
| 554 |
self.assertEqual(preprocessed["memory_segments"], sample["memory_segments"])
|
| 555 |
self.assertEqual(preprocessed["segment_lengths"], sample["memory_segments"])
|
|
@@ -566,7 +670,7 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
|
|
| 566 |
{"anchor": (3, 5), "dynamic": (5, 7), "revisit": (7, 9)},
|
| 567 |
)
|
| 568 |
self.assertEqual(preprocessed["target_tensors"]["frame_indices"][:, 0].tolist(), [106, 107, 108])
|
| 569 |
-
self.assertEqual(preprocessed["stream_tensors"]["anchor"]["frame_indices"][:, 0].tolist(), [
|
| 570 |
self.assertEqual(preprocessed["stream_tensors"]["dynamic"]["frame_indices"][:, 0].tolist(), [104, 105])
|
| 571 |
self.assertEqual(preprocessed["stream_tensors"]["revisit"]["frame_indices"].shape[0], 2)
|
| 572 |
self.assertEqual(tuple(preprocessed["memory_masks"]["target"].shape), (1, 3))
|
|
|
|
| 42 |
return poses
|
| 43 |
|
| 44 |
|
| 45 |
+
def _dataset_cfg(root, **overrides):
|
| 46 |
+
cfg = {
|
| 47 |
+
"save_dir": str(root),
|
| 48 |
+
"n_frames": 3,
|
| 49 |
+
"n_frames_valid": 3,
|
| 50 |
+
"frame_skip": 1,
|
| 51 |
+
"action_cond_dim": 25,
|
| 52 |
+
"resolution": 128,
|
| 53 |
+
"image_hw": [360, 640],
|
| 54 |
+
"shuffle_clips": False,
|
| 55 |
+
"single_eval_clip": True,
|
| 56 |
+
"memory_selection": _selection_cfg(max_anchor_frames=0, max_dynamic_frames=0, max_revisit_frames=0),
|
| 57 |
+
}
|
| 58 |
+
cfg.update(overrides)
|
| 59 |
+
return OmegaConf.create(cfg)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def _write_latent_npz(path, num_frames, actions=None, poses=None, frame_indices=None):
|
| 63 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 64 |
+
if actions is None:
|
| 65 |
+
actions = np.ones((num_frames, 25), dtype=np.float32)
|
| 66 |
+
if poses is None:
|
| 67 |
+
poses = _poses(num_frames)
|
| 68 |
+
if frame_indices is None:
|
| 69 |
+
frame_indices = np.arange(num_frames, dtype=np.int64)
|
| 70 |
+
np.savez(
|
| 71 |
+
path,
|
| 72 |
+
latents=np.arange(num_frames * 2, dtype=np.float32).reshape(num_frames, 1, 1, 2),
|
| 73 |
+
actions=actions,
|
| 74 |
+
poses=poses,
|
| 75 |
+
frame_indices=frame_indices,
|
| 76 |
+
image_hw=np.array([360, 640], dtype=np.int64),
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
class MemorySelectionTests(unittest.TestCase):
|
| 81 |
def test_worldmem_backend_is_explicitly_rejected_until_implemented(self):
|
| 82 |
poses = _poses(8)
|
|
|
|
| 184 |
|
| 185 |
class DeMemWMLatentDatasetTests(unittest.TestCase):
|
| 186 |
|
| 187 |
+
def test_dataset_requires_split_directory_without_root_fallback(self):
|
| 188 |
+
with tempfile.TemporaryDirectory() as tmp:
|
| 189 |
+
root = Path(tmp)
|
| 190 |
+
_write_latent_npz(root / "sample.npz", 104)
|
| 191 |
+
|
| 192 |
+
with self.assertRaisesRegex(FileNotFoundError, "validation"):
|
| 193 |
+
MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root), split="validation")
|
| 194 |
+
|
| 195 |
+
def test_dataset_matches_original_w_updown_split_filtering(self):
|
| 196 |
+
with tempfile.TemporaryDirectory() as tmp:
|
| 197 |
+
root = Path(tmp)
|
| 198 |
+
_write_latent_npz(root / "validation" / "plain.npz", 104)
|
| 199 |
+
_write_latent_npz(root / "validation" / "clip_w_updown.npz", 104)
|
| 200 |
+
|
| 201 |
+
dataset = MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root), split="validation")
|
| 202 |
+
dataset_wo = MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root, wo_updown=True), split="validation")
|
| 203 |
+
|
| 204 |
+
self.assertEqual([path.name for path in dataset.data_paths], ["clip_w_updown.npz"])
|
| 205 |
+
self.assertEqual([path.name for path in dataset_wo.data_paths], ["plain.npz"])
|
| 206 |
+
|
| 207 |
+
def test_dataset_rejects_files_shorter_than_original_frame_skip_window(self):
|
| 208 |
+
with tempfile.TemporaryDirectory() as tmp:
|
| 209 |
+
root = Path(tmp)
|
| 210 |
+
_write_latent_npz(root / "training" / "short.npz", 102)
|
| 211 |
+
|
| 212 |
+
with self.assertRaisesRegex(ValueError, "requires at least 103"):
|
| 213 |
+
MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root), split="training")
|
| 214 |
+
|
| 215 |
+
def test_dataset_applies_original_pose_height_sanity_check(self):
|
| 216 |
+
with tempfile.TemporaryDirectory() as tmp:
|
| 217 |
+
root = Path(tmp)
|
| 218 |
+
poses = _poses(104)
|
| 219 |
+
poses[:, 1] = np.linspace(0.0, 3.0, 104, dtype=np.float32)
|
| 220 |
+
_write_latent_npz(root / "training" / "sample.npz", 104, poses=poses)
|
| 221 |
+
dataset = MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root), split="training")
|
| 222 |
+
|
| 223 |
+
with self.assertRaisesRegex(ValueError, "Pose height variation"):
|
| 224 |
+
dataset[0]
|
| 225 |
+
|
| 226 |
+
def test_dataset_converts_raw_actions_to_original_25d_contract(self):
|
| 227 |
+
with tempfile.TemporaryDirectory() as tmp:
|
| 228 |
+
root = Path(tmp)
|
| 229 |
+
raw_actions = np.zeros((104, 8), dtype=np.int64)
|
| 230 |
+
raw_actions[:, 0] = 1
|
| 231 |
+
raw_actions[:, 3] = 11
|
| 232 |
+
raw_actions[:, 4] = 13
|
| 233 |
+
raw_actions[:, 7] = 1
|
| 234 |
+
_write_latent_npz(root / "training" / "sample.npz", 104, actions=raw_actions)
|
| 235 |
+
sample = MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root), split="training")[0]
|
| 236 |
+
|
| 237 |
+
self.assertEqual(tuple(sample["actions"].shape), (3, 25))
|
| 238 |
+
self.assertTrue(torch.equal(sample["actions"][:, 11], torch.ones(3)))
|
| 239 |
+
self.assertTrue(torch.equal(sample["actions"][:, 16], torch.ones(3)))
|
| 240 |
+
self.assertTrue(torch.equal(sample["actions"][:, 15], -torch.ones(3)))
|
| 241 |
+
self.assertTrue(torch.equal(sample["actions"][:, 2], torch.ones(3)))
|
| 242 |
+
|
| 243 |
+
def test_dataset_reuses_cached_npz_arrays_for_same_file(self):
|
| 244 |
+
with tempfile.TemporaryDirectory() as tmp:
|
| 245 |
+
root = Path(tmp)
|
| 246 |
+
_write_latent_npz(root / "training" / "sample.npz", 104)
|
| 247 |
+
dataset = MinecraftVideoDeMemWMLatentDataset(_dataset_cfg(root), split="training")
|
| 248 |
+
first = dataset[0]
|
| 249 |
+
|
| 250 |
+
with mock.patch("datasets.video.minecraft_video_dememwm_latent_dataset.np.load", side_effect=AssertionError("cache miss")):
|
| 251 |
+
second = dataset[1]
|
| 252 |
+
|
| 253 |
+
self.assertEqual(first["frame_indices"][:3].tolist(), [100, 101, 102])
|
| 254 |
+
self.assertEqual(second["frame_indices"][:3].tolist(), [101, 102, 103])
|
| 255 |
+
|
| 256 |
def test_preprocess_splits_sequence_tensors_from_memory_segments(self):
|
| 257 |
from algorithms.dememwm.df_video import _preprocess_dememwm_latent_batch
|
| 258 |
|
|
|
|
| 601 |
root = Path(tmp)
|
| 602 |
split_dir = root / "training"
|
| 603 |
split_dir.mkdir()
|
| 604 |
+
num_frames = 112
|
| 605 |
np.savez(
|
| 606 |
split_dir / "sample.npz",
|
| 607 |
latents=np.arange(num_frames * 2, dtype=np.float32).reshape(num_frames, 1, 1, 2),
|
| 608 |
actions=np.ones((num_frames, 25), dtype=np.float32),
|
| 609 |
poses=_poses(num_frames),
|
| 610 |
+
frame_indices=np.arange(num_frames, dtype=np.int64),
|
| 611 |
image_hw=np.array([360, 640], dtype=np.int64),
|
| 612 |
)
|
| 613 |
cfg = OmegaConf.create(
|
|
|
|
| 630 |
self.assertEqual(sample["memory_segments"], {"target": 3, "anchor": 2, "dynamic": 2, "revisit": 2})
|
| 631 |
self.assertEqual(tuple(sample["latents"].shape), (9, 1, 1, 2))
|
| 632 |
self.assertEqual(sample["frame_indices"][:3].tolist(), [106, 107, 108])
|
| 633 |
+
self.assertEqual(sample["frame_indices"][3:5].tolist(), [0, 1])
|
| 634 |
self.assertEqual(sample["frame_indices"][5:7].tolist(), [104, 105])
|
| 635 |
self.assertTrue(all(idx < 104 for idx in sample["frame_indices"][7:9].tolist() if idx >= 0))
|
| 636 |
self.assertEqual(len(sample["frame_indices"][7:9]), 2)
|
|
|
|
| 653 |
self.assertTrue(torch.equal(preprocessed["actions"][3:], torch.ones_like(preprocessed["actions"][3:])))
|
| 654 |
self.assertEqual(tuple(preprocessed["poses"].shape), (9, 1, 5))
|
| 655 |
self.assertIs(preprocessed["frame_memory_pose"], preprocessed["poses"])
|
| 656 |
+
self.assertEqual(preprocessed["frame_memory_pose"][:7, 0, 0].tolist(), [0.0, 1.0, 2.0, -106.0, -105.0, -2.0, -1.0])
|
| 657 |
self.assertEqual(tuple(preprocessed["frame_indices"].shape), (9, 1))
|
| 658 |
self.assertEqual(preprocessed["memory_segments"], sample["memory_segments"])
|
| 659 |
self.assertEqual(preprocessed["segment_lengths"], sample["memory_segments"])
|
|
|
|
| 670 |
{"anchor": (3, 5), "dynamic": (5, 7), "revisit": (7, 9)},
|
| 671 |
)
|
| 672 |
self.assertEqual(preprocessed["target_tensors"]["frame_indices"][:, 0].tolist(), [106, 107, 108])
|
| 673 |
+
self.assertEqual(preprocessed["stream_tensors"]["anchor"]["frame_indices"][:, 0].tolist(), [0, 1])
|
| 674 |
self.assertEqual(preprocessed["stream_tensors"]["dynamic"]["frame_indices"][:, 0].tolist(), [104, 105])
|
| 675 |
self.assertEqual(preprocessed["stream_tensors"]["revisit"]["frame_indices"].shape[0], 2)
|
| 676 |
self.assertEqual(tuple(preprocessed["memory_masks"]["target"].shape), (1, 3))
|