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 |
-
|
| 896 |
-
|
| 897 |
-
|
| 898 |
-
|
| 899 |
-
|
| 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 |
|