BonanDing commited on
Commit
1d38bfa
·
1 Parent(s): c8308b9

Remove stale DeMemWM raw-video helpers

Browse files
.exp_artifact/dememwm_remaining_fix_plan.md CHANGED
@@ -119,7 +119,7 @@ Fix DeMemWM nested split filtering
119
 
120
  ## Substep 2: Remove Stale Raw-Video Helper Path From DeMemWM Algorithm
121
 
122
- Status: `[ ]`
123
 
124
  Bug:
125
 
 
119
 
120
  ## Substep 2: Remove Stale Raw-Video Helper Path From DeMemWM Algorithm
121
 
122
+ Status: `[x]`
123
 
124
  Bug:
125
 
algorithms/dememwm/df_video.py CHANGED
@@ -1,7 +1,6 @@
1
  from collections.abc import Mapping
2
  import os
3
  import random
4
- import math
5
  import numpy as np
6
  import torch
7
  import torch.distributed as dist
@@ -9,7 +8,6 @@ import torch.nn.functional as F
9
  import torchvision.transforms.functional as TF
10
  from torchvision.transforms import InterpolationMode
11
  from PIL import Image
12
- from packaging import version as pver
13
  from einops import rearrange
14
  from tqdm import tqdm
15
  from omegaconf import DictConfig, open_dict
@@ -317,247 +315,6 @@ def _apply_memory_route_masks(frame_memory_masks, query_noise_levels, cfg, diffu
317
  return routed
318
 
319
 
320
- def euler_to_rotation_matrix(pitch, yaw):
321
- """
322
- Convert pitch and yaw angles (in radians) to a 3x3 rotation matrix.
323
- Supports batch input.
324
-
325
- Args:
326
- pitch (torch.Tensor): Pitch angles in radians.
327
- yaw (torch.Tensor): Yaw angles in radians.
328
-
329
- Returns:
330
- torch.Tensor: Rotation matrix of shape (batch_size, 3, 3).
331
- """
332
- cos_pitch, sin_pitch = torch.cos(pitch), torch.sin(pitch)
333
- cos_yaw, sin_yaw = torch.cos(yaw), torch.sin(yaw)
334
-
335
- R_pitch = torch.stack([
336
- torch.ones_like(pitch), torch.zeros_like(pitch), torch.zeros_like(pitch),
337
- torch.zeros_like(pitch), cos_pitch, -sin_pitch,
338
- torch.zeros_like(pitch), sin_pitch, cos_pitch
339
- ], dim=-1).reshape(-1, 3, 3)
340
-
341
- R_yaw = torch.stack([
342
- cos_yaw, torch.zeros_like(yaw), sin_yaw,
343
- torch.zeros_like(yaw), torch.ones_like(yaw), torch.zeros_like(yaw),
344
- -sin_yaw, torch.zeros_like(yaw), cos_yaw
345
- ], dim=-1).reshape(-1, 3, 3)
346
-
347
- return torch.matmul(R_yaw, R_pitch)
348
-
349
-
350
- def euler_to_camera_to_world_matrix(pose):
351
- """
352
- Convert (x, y, z, pitch, yaw) to a 4x4 camera-to-world transformation matrix using torch.
353
- Supports both (5,) and (f, b, 5) shaped inputs.
354
-
355
- Args:
356
- pose (torch.Tensor): Pose tensor of shape (5,) or (f, b, 5).
357
-
358
- Returns:
359
- torch.Tensor: Camera-to-world transformation matrix of shape (4, 4).
360
- """
361
-
362
- origin_dim = pose.ndim
363
- if origin_dim == 1:
364
- pose = pose.unsqueeze(0).unsqueeze(0) # Convert (5,) -> (1, 1, 5)
365
- elif origin_dim == 2:
366
- pose = pose.unsqueeze(0)
367
-
368
- x, y, z, pitch, yaw = pose[..., 0], pose[..., 1], pose[..., 2], pose[..., 3], pose[..., 4]
369
- pitch, yaw = torch.deg2rad(pitch), torch.deg2rad(yaw)
370
-
371
- # Compute rotation matrix (batch mode)
372
- R = euler_to_rotation_matrix(pitch, yaw) # Shape (f*b, 3, 3)
373
-
374
- # Create the 4x4 transformation matrix
375
- eye = torch.eye(4, dtype=torch.float32, device=pose.device)
376
- camera_to_world = eye.repeat(R.shape[0], 1, 1) # Shape (f*b, 4, 4)
377
-
378
- # Assign rotation
379
- camera_to_world[:, :3, :3] = R
380
-
381
- # Assign translation
382
- camera_to_world[:, :3, 3] = torch.stack([x.reshape(-1), y.reshape(-1), z.reshape(-1)], dim=-1)
383
-
384
- # Reshape back to (f, b, 4, 4) if needed
385
- if origin_dim == 3:
386
- return camera_to_world.view(pose.shape[0], pose.shape[1], 4, 4)
387
- elif origin_dim == 2:
388
- return camera_to_world.view(pose.shape[0], 4, 4)
389
- else:
390
- return camera_to_world.squeeze(0).squeeze(0) # Convert (1,1,4,4) -> (4,4)
391
-
392
- def is_inside_fov_3d_hv(points, center, center_pitch, center_yaw, fov_half_h, fov_half_v):
393
- """
394
- Check whether points are within a given 3D field of view (FOV)
395
- with separately defined horizontal and vertical ranges.
396
-
397
- The center view direction is specified by pitch and yaw (in degrees).
398
-
399
- :param points: (N, B, 3) Sample point coordinates
400
- :param center: (3,) Center coordinates of the FOV
401
- :param center_pitch: Pitch angle of the center view (in degrees)
402
- :param center_yaw: Yaw angle of the center view (in degrees)
403
- :param fov_half_h: Horizontal half-FOV angle (in degrees)
404
- :param fov_half_v: Vertical half-FOV angle (in degrees)
405
- :return: Boolean tensor (N, B), indicating whether each point is inside the FOV
406
- """
407
- # Compute vectors relative to the center
408
- vectors = points - center # shape (N, B, 3)
409
- x = vectors[..., 0]
410
- y = vectors[..., 1]
411
- z = vectors[..., 2]
412
-
413
- # Compute horizontal angle (yaw): measured with respect to the z-axis as the forward direction,
414
- # and the x-axis as left-right, resulting in a range of -180 to 180 degrees.
415
- azimuth = torch.atan2(x, z) * (180 / math.pi)
416
-
417
- # Compute vertical angle (pitch): measured with respect to the horizontal plane,
418
- # resulting in a range of -90 to 90 degrees.
419
- elevation = torch.atan2(y, torch.sqrt(x**2 + z**2)) * (180 / math.pi)
420
-
421
- # Compute the angular difference from the center view (handling circular angle wrap-around)
422
- diff_azimuth = (azimuth - center_yaw).abs() % 360
423
- diff_elevation = (elevation - center_pitch).abs() % 360
424
-
425
- # Adjust values greater than 180 degrees to the shorter angular difference
426
- diff_azimuth = torch.where(diff_azimuth > 180, 360 - diff_azimuth, diff_azimuth)
427
- diff_elevation = torch.where(diff_elevation > 180, 360 - diff_elevation, diff_elevation)
428
-
429
- # Check if both horizontal and vertical angles are within their respective FOV limits
430
- return (diff_azimuth < fov_half_h) & (diff_elevation < fov_half_v)
431
-
432
- def generate_points_in_sphere(n_points, radius):
433
- # Sample three independent uniform distributions
434
- samples_r = torch.rand(n_points) # For radius distribution
435
- samples_phi = torch.rand(n_points) # For azimuthal angle phi
436
- samples_u = torch.rand(n_points) # For polar angle theta
437
-
438
- # Apply cube root to ensure uniform volumetric distribution
439
- r = radius * torch.pow(samples_r, 1/3)
440
- # Azimuthal angle phi uniformly distributed in [0, 2π]
441
- phi = 2 * math.pi * samples_phi
442
- # Convert u to theta to ensure cos(theta) is uniformly distributed
443
- theta = torch.acos(1 - 2 * samples_u)
444
-
445
- # Convert spherical coordinates to Cartesian coordinates
446
- x = r * torch.sin(theta) * torch.cos(phi)
447
- y = r * torch.sin(theta) * torch.sin(phi)
448
- z = r * torch.cos(theta)
449
-
450
- points = torch.stack((x, y, z), dim=1)
451
- return points
452
-
453
- def tensor_max_with_number(tensor, number):
454
- number_tensor = torch.tensor(number, dtype=tensor.dtype, device=tensor.device)
455
- result = torch.max(tensor, number_tensor)
456
- return result
457
-
458
- def custom_meshgrid(*args):
459
- # ref: https://pytorch.org/docs/stable/generated/torch.meshgrid.html?highlight=meshgrid#torch.meshgrid
460
- if pver.parse(torch.__version__) < pver.parse('1.10'):
461
- return torch.meshgrid(*args)
462
- else:
463
- return torch.meshgrid(*args, indexing='ij')
464
-
465
- def camera_to_world_to_world_to_camera(camera_to_world: torch.Tensor) -> torch.Tensor:
466
- """
467
- Convert Camera-to-World matrices to World-to-Camera matrices for a tensor with shape (f, b, 4, 4).
468
-
469
- Args:
470
- camera_to_world (torch.Tensor): A tensor of shape (f, b, 4, 4), where:
471
- f = number of frames,
472
- b = batch size.
473
-
474
- Returns:
475
- torch.Tensor: A tensor of shape (f, b, 4, 4) representing the World-to-Camera matrices.
476
- """
477
- # Ensure input is a 4D tensor
478
- assert camera_to_world.ndim == 4 and camera_to_world.shape[2:] == (4, 4), \
479
- "Input must be of shape (f, b, 4, 4)"
480
-
481
- # Extract the rotation (R) and translation (T) parts
482
- R = camera_to_world[:, :, :3, :3] # Shape: (f, b, 3, 3)
483
- T = camera_to_world[:, :, :3, 3] # Shape: (f, b, 3)
484
-
485
- # Initialize an identity matrix for the output
486
- world_to_camera = torch.eye(4, device=camera_to_world.device).unsqueeze(0).unsqueeze(0)
487
- world_to_camera = world_to_camera.repeat(camera_to_world.size(0), camera_to_world.size(1), 1, 1) # Shape: (f, b, 4, 4)
488
-
489
- # Compute the rotation (transpose of R)
490
- world_to_camera[:, :, :3, :3] = R.transpose(2, 3)
491
-
492
- # Compute the translation (-R^T * T)
493
- world_to_camera[:, :, :3, 3] = -torch.matmul(R.transpose(2, 3), T.unsqueeze(-1)).squeeze(-1)
494
-
495
- return world_to_camera.to(camera_to_world.dtype)
496
-
497
- def convert_to_plucker(poses, curr_frame, focal_length, image_width, image_height):
498
-
499
- intrinsic = np.asarray([focal_length * image_width,
500
- focal_length * image_height,
501
- 0.5 * image_width,
502
- 0.5 * image_height], dtype=np.float32)
503
-
504
- c2ws = get_relative_pose(poses, zero_first_frame_scale=curr_frame)
505
- c2ws = rearrange(c2ws, "t b m n -> b t m n")
506
-
507
- K = torch.as_tensor(intrinsic, device=poses.device, dtype=poses.dtype).repeat(c2ws.shape[0],c2ws.shape[1],1) # [B, F, 4]
508
- plucker_embedding = ray_condition(K, c2ws, image_height, image_width, device=c2ws.device)
509
- plucker_embedding = rearrange(plucker_embedding, "b t h w d -> t b h w d").contiguous()
510
-
511
- return plucker_embedding
512
-
513
-
514
- def get_relative_pose(abs_c2ws, zero_first_frame_scale):
515
- abs_w2cs = camera_to_world_to_world_to_camera(abs_c2ws)
516
- target_cam_c2w = torch.tensor([
517
- [1, 0, 0, 0],
518
- [0, 1, 0, 0],
519
- [0, 0, 1, 0],
520
- [0, 0, 0, 1]
521
- ]).to(abs_c2ws.device).to(abs_c2ws.dtype)
522
- abs2rel = target_cam_c2w @ abs_w2cs[zero_first_frame_scale]
523
- ret_poses = [abs2rel @ abs_c2w for abs_c2w in abs_c2ws]
524
- ret_poses = torch.stack(ret_poses)
525
- return ret_poses
526
-
527
- def ray_condition(K, c2w, H, W, device):
528
- # c2w: B, V, 4, 4
529
- # K: B, V, 4
530
-
531
- B = K.shape[0]
532
-
533
- j, i = custom_meshgrid(
534
- torch.linspace(0, H - 1, H, device=device, dtype=c2w.dtype),
535
- torch.linspace(0, W - 1, W, device=device, dtype=c2w.dtype),
536
- )
537
- i = i.reshape([1, 1, H * W]).expand([B, 1, H * W]) + 0.5 # [B, HxW]
538
- j = j.reshape([1, 1, H * W]).expand([B, 1, H * W]) + 0.5 # [B, HxW]
539
-
540
- fx, fy, cx, cy = K.chunk(4, dim=-1) # B,V, 1
541
-
542
- zs = torch.ones_like(i, device=device, dtype=c2w.dtype) # [B, HxW]
543
- xs = -(i - cx) / fx * zs
544
- ys = -(j - cy) / fy * zs
545
-
546
- zs = zs.expand_as(ys)
547
-
548
- directions = torch.stack((xs, ys, zs), dim=-1) # B, V, HW, 3
549
- directions = directions / directions.norm(dim=-1, keepdim=True) # B, V, HW, 3
550
-
551
- rays_d = directions @ c2w[..., :3, :3].transpose(-1, -2) # B, V, 3, HW
552
- rays_o = c2w[..., :3, 3] # B, V, 3
553
- rays_o = rays_o[:, :, None].expand_as(rays_d) # B, V, 3, HW
554
- # c2w @ dirctions
555
- rays_dxo = torch.linalg.cross(rays_o, rays_d)
556
- plucker = torch.cat([rays_dxo, rays_d], dim=-1)
557
- plucker = plucker.reshape(B, c2w.shape[1], H, W, 6) # B, V, H, W, 6
558
-
559
- return plucker
560
-
561
  def random_transform(tensor):
562
  """
563
  Apply the same random translation, rotation, and scaling to all frames in the batch.
@@ -891,35 +648,12 @@ class DeMemWMMinecraft(DiffusionForcingBase):
891
  self.validation_step_outputs.clear()
892
 
893
  def _preprocess_batch(self, batch):
894
- if isinstance(batch, Mapping):
895
- return _preprocess_dememwm_latent_batch(batch)
896
-
897
- xs, conditions, pose_conditions, frame_index = batch
898
-
899
- if self.action_cond_dim:
900
- conditions = torch.cat([torch.zeros_like(conditions[:, :1]), conditions[:, 1:]], 1)
901
- conditions = rearrange(conditions, "b t d -> t b d").contiguous()
902
- else:
903
- raise NotImplementedError("Only support external cond.")
904
-
905
- pose_conditions = rearrange(pose_conditions, "b t d -> t b d").contiguous()
906
- c2w_mat = euler_to_camera_to_world_matrix(pose_conditions)
907
- xs = rearrange(xs, "b t c ... -> t b c ...").contiguous()
908
- frame_index = rearrange(frame_index, "b t -> t b").contiguous()
909
-
910
- return xs, conditions, pose_conditions, c2w_mat, frame_index
911
-
912
- def encode(self, x):
913
- # vae encoding
914
- T = x.shape[0]
915
- H, W = x.shape[-2:]
916
- scaling_factor = 0.07843137255
917
-
918
- x = rearrange(x, "t b c h w -> (t b) c h w")
919
- with torch.no_grad():
920
- x = self.vae.encode(x * 2 - 1).mean * scaling_factor
921
- x = rearrange(x, "(t b) (h w) c -> t b c h w", t=T, h=H // self.vae.patch_size, w=W // self.vae.patch_size)
922
- return x
923
 
924
  def decode(self, x):
925
  total_frames = x.shape[0]
@@ -930,116 +664,6 @@ class DeMemWMMinecraft(DiffusionForcingBase):
930
  x = rearrange(x, "(t b) c h w-> t b c h w", t=total_frames)
931
  return x
932
 
933
- def _generate_condition_indices(self, curr_frame, memory_condition_length, xs_pred, pose_conditions, frame_idx, horizon):
934
- """
935
- Generate indices for condition similarity based on the current frame and pose conditions.
936
- """
937
- if curr_frame < memory_condition_length:
938
- random_idx = [i for i in range(curr_frame)] + [0] * (memory_condition_length - curr_frame)
939
- random_idx = np.repeat(np.array(random_idx)[:, None], xs_pred.shape[1], -1)
940
- else:
941
- # Generate points in a sphere and filter based on field of view
942
- num_samples = 10000
943
- radius = 30
944
- points = generate_points_in_sphere(num_samples, radius).to(pose_conditions.device)
945
- points = points[:, None].repeat(1, pose_conditions.shape[1], 1)
946
- points += pose_conditions[curr_frame, :, :3][None]
947
- fov_half_h = torch.tensor(105 / 2, device=pose_conditions.device)
948
- fov_half_v = torch.tensor(75 / 2, device=pose_conditions.device)
949
-
950
- # in_fov1 = is_inside_fov_3d_hv(
951
- # points, pose_conditions[curr_frame, :, :3],
952
- # pose_conditions[curr_frame, :, -2], pose_conditions[curr_frame, :, -1],
953
- # fov_half_h, fov_half_v
954
- # )
955
-
956
- in_fov1 = torch.stack([
957
- is_inside_fov_3d_hv(points, pc[:, :3], pc[:, -2], pc[:, -1], fov_half_h, fov_half_v)
958
- for pc in pose_conditions[curr_frame:curr_frame+horizon]
959
- ])
960
-
961
- in_fov1 = torch.sum(in_fov1, 0) > 0
962
-
963
- # Compute overlap ratios and select indices
964
- in_fov_list = torch.stack([
965
- is_inside_fov_3d_hv(points, pc[:, :3], pc[:, -2], pc[:, -1], fov_half_h, fov_half_v)
966
- for pc in pose_conditions[:curr_frame]
967
- ])
968
-
969
- random_idx = []
970
- for _ in range(memory_condition_length):
971
- overlap_ratio = ((in_fov1.bool() & in_fov_list).sum(1)) / in_fov1.sum()
972
-
973
- confidence = overlap_ratio + (curr_frame - frame_idx[:curr_frame]) / curr_frame * (-0.2)
974
-
975
- if len(random_idx) > 0:
976
- confidence[torch.cat(random_idx)] = -1e10
977
- _, r_idx = torch.topk(confidence, k=1, dim=0)
978
- random_idx.append(r_idx[0])
979
-
980
- # choice 1: directly remove overlapping region
981
- occupied_mask = in_fov_list[r_idx[0, range(in_fov1.shape[-1])], :, range(in_fov1.shape[-1])].permute(1,0)
982
- in_fov1 = in_fov1 & ~occupied_mask
983
-
984
- # choice 2: apply similarity filter
985
- # cos_sim = F.cosine_similarity(xs_pred.to(r_idx.device)[r_idx[:, range(in_fov1.shape[1])],
986
- # range(in_fov1.shape[1])], xs_pred.to(r_idx.device)[:curr_frame], dim=2)
987
- # cos_sim = cos_sim.mean((-2,-1))
988
-
989
- # mask_sim = cos_sim>0.9
990
- # in_fov_list = in_fov_list & ~mask_sim[:,None].to(in_fov_list.device)
991
-
992
- random_idx = torch.stack(random_idx).cpu()
993
-
994
- return random_idx
995
-
996
- def _prepare_conditions(self,
997
- start_frame, curr_frame, horizon, conditions,
998
- pose_conditions, c2w_mat, frame_idx, random_idx,
999
- image_width, image_height):
1000
- """
1001
- Prepare input conditions and pose conditions for sampling.
1002
- """
1003
-
1004
- padding = torch.zeros((len(random_idx),) + conditions.shape[1:], device=conditions.device, dtype=conditions.dtype)
1005
- input_condition = torch.cat([conditions[start_frame:curr_frame + horizon], padding], dim=0)
1006
-
1007
- batch_size = conditions.shape[1]
1008
-
1009
- if self.use_plucker:
1010
- if self.relative_embedding:
1011
- frame_idx_list = []
1012
- input_pose_condition = []
1013
- for i in range(start_frame, curr_frame + horizon):
1014
- input_pose_condition.append(convert_to_plucker(torch.cat([c2w_mat[i:i+1],c2w_mat[random_idx[:,range(batch_size)], range(batch_size)]]).clone(), 0, focal_length=self.focal_length,
1015
- image_width=image_width, image_height=image_height).to(conditions.dtype))
1016
- frame_idx_list.append(torch.cat([frame_idx[i:i+1]-frame_idx[i:i+1], frame_idx[random_idx[:,range(batch_size)], range(batch_size)]-frame_idx[i:i+1]]))
1017
- input_pose_condition = torch.cat(input_pose_condition)
1018
- frame_idx_list = torch.cat(frame_idx_list)
1019
-
1020
- else:
1021
- input_pose_condition = torch.cat([c2w_mat[start_frame : curr_frame + horizon], c2w_mat[random_idx[:,range(batch_size)], range(batch_size)]], dim=0).clone()
1022
- input_pose_condition = convert_to_plucker(input_pose_condition, 0, focal_length=self.focal_length)
1023
- frame_idx_list = None
1024
- else:
1025
- input_pose_condition = torch.cat([pose_conditions[start_frame : curr_frame + horizon], pose_conditions[random_idx[:,range(batch_size)], range(batch_size)]], dim=0).clone()
1026
- frame_idx_list = None
1027
-
1028
- return input_condition, input_pose_condition, frame_idx_list
1029
-
1030
- def _prepare_noise_levels(self, scheduling_matrix, m, curr_frame, batch_size, memory_condition_length):
1031
- """
1032
- Prepare noise levels for the current sampling step.
1033
- """
1034
- from_noise_levels = np.concatenate((np.zeros((curr_frame,), dtype=np.int64), scheduling_matrix[m]))[:, None].repeat(batch_size, axis=1)
1035
- to_noise_levels = np.concatenate((np.zeros((curr_frame,), dtype=np.int64), scheduling_matrix[m + 1]))[:, None].repeat(batch_size, axis=1)
1036
- if memory_condition_length:
1037
- from_noise_levels = np.concatenate([from_noise_levels, np.zeros((memory_condition_length, from_noise_levels.shape[-1]), dtype=np.int32)], axis=0)
1038
- to_noise_levels = np.concatenate([to_noise_levels, np.zeros((memory_condition_length, from_noise_levels.shape[-1]), dtype=np.int32)], axis=0)
1039
- from_noise_levels = torch.from_numpy(from_noise_levels).to(self.device)
1040
- to_noise_levels = torch.from_numpy(to_noise_levels).to(self.device)
1041
- return from_noise_levels, to_noise_levels
1042
-
1043
  def validation_step(self, batch, batch_idx, namespace="validation") -> STEP_OUTPUT:
1044
  """
1045
  Perform a single validation step.
 
1
  from collections.abc import Mapping
2
  import os
3
  import random
 
4
  import numpy as np
5
  import torch
6
  import torch.distributed as dist
 
8
  import torchvision.transforms.functional as TF
9
  from torchvision.transforms import InterpolationMode
10
  from PIL import Image
 
11
  from einops import rearrange
12
  from tqdm import tqdm
13
  from omegaconf import DictConfig, open_dict
 
315
  return routed
316
 
317
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  def random_transform(tensor):
319
  """
320
  Apply the same random translation, rotation, and scaling to all frames in the batch.
 
648
  self.validation_step_outputs.clear()
649
 
650
  def _preprocess_batch(self, batch):
651
+ if not isinstance(batch, Mapping):
652
+ raise TypeError(
653
+ "DeMemWM requires the latent dict batch contract "
654
+ "from video_minecraft_dememwm_latent; raw tuple batches are unsupported."
655
+ )
656
+ return _preprocess_dememwm_latent_batch(batch)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
657
 
658
  def decode(self, x):
659
  total_frames = x.shape[0]
 
664
  x = rearrange(x, "(t b) c h w-> t b c h w", t=total_frames)
665
  return x
666
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
667
  def validation_step(self, batch, batch_idx, namespace="validation") -> STEP_OUTPUT:
668
  """
669
  Perform a single validation step.
tests/test_dememwm_latent_dataset.py CHANGED
@@ -572,9 +572,6 @@ class DeMemWMLatentDatasetTests(unittest.TestCase):
572
  self.horizons.append(horizon)
573
  return np.stack([np.full(horizon, level, dtype=np.int64) for level in (2, 1, 0)])
574
 
575
- def _generate_condition_indices(self, *args, **kwargs):
576
- raise AssertionError("latent dict validation must not use the legacy memory_condition_length path")
577
-
578
  def encode(self, xs):
579
  raise AssertionError("latent dict validation must not VAE-encode inputs")
580
 
 
572
  self.horizons.append(horizon)
573
  return np.stack([np.full(horizon, level, dtype=np.int64) for level in (2, 1, 0)])
574
 
 
 
 
575
  def encode(self, xs):
576
  raise AssertionError("latent dict validation must not VAE-encode inputs")
577