ColabWan / models /wan /vista4d /preprocess.py
1ripon1's picture
Upload folder using huggingface_hub
7344bef verified
Raw
History Blame Contribute Delete
33.2 kB
import hashlib
import os
import shutil
import time
import zipfile
from contextlib import nullcontext
from pathlib import Path
import cv2
import imageio
import numpy as np
import torch
import torch.nn.functional as F
from einops import rearrange
from PIL import Image
from .camera import get_plucker_embedding
VISTA4D_REUSE_GENERATED_MAP_IN_MEMORY = True
VISTA4D_DA3_PROCESS_RES = 896
VISTA4D_DA3_CHUNK_SIZE = -1
VISTA4D_DA3_CHUNK_OVERLAP = 8
_VISTA4D_MAP_CACHE = {}
_VISTA4D_MAP_CACHE_MAX_ITEMS = 2
def _center_slice(length, frame_num):
if length < frame_num:
raise ValueError(f"Vista4D input needs at least {frame_num} frames, got {length}.")
start = (length - frame_num) // 2
return slice(start, start + frame_num)
def _tensor_to_video_np(frames):
video = frames.detach().cpu().float().permute(1, 2, 3, 0).numpy()
return ((video + 1.0) * 127.5).clip(0, 255).astype(np.uint8)
def _video_np_to_tensor(video, device, dtype):
video = torch.from_numpy(video).permute(3, 0, 1, 2).float().div_(127.5).sub_(1.0)
return video.to(device=device, dtype=dtype)
def _crop_resize_video(video, height, width, resample=Image.Resampling.LANCZOS):
if video.shape[1:3] == (height, width):
return video.copy()
frames = []
for frame in video:
image = Image.fromarray(frame)
in_w, in_h = image.size
if in_h / in_w > height / width:
crop_h = int(in_w * height / width)
top = (in_h - crop_h) // 2
image = image.crop((0, top, in_w, top + crop_h))
else:
crop_w = int(in_h * width / height)
left = (in_w - crop_w) // 2
image = image.crop((left, 0, left + crop_w, in_h))
frames.append(np.asarray(image.resize((width, height), resample)))
return np.stack(frames, axis=0)
def _resize_intrinsics(intrinsics, height, width, height_input, width_input):
intrinsics = intrinsics.copy()
if height_input / width_input > height / width:
crop_h = int(width_input * height / width)
intrinsics[..., 3] -= (height_input - crop_h) / 2
height_input = crop_h
else:
crop_w = int(height_input * width / height)
intrinsics[..., 2] -= (width_input - crop_w) / 2
width_input = crop_w
intrinsics[..., 0] *= width / width_input
intrinsics[..., 1] *= height / height_input
intrinsics[..., 2] *= width / width_input
intrinsics[..., 3] *= height / height_input
return intrinsics
def _infer_intrinsics_size(intrinsics, fallback_height, fallback_width):
cx = float(np.nanmedian(intrinsics[..., 2]))
cy = float(np.nanmedian(intrinsics[..., 3]))
if np.isfinite(cx) and np.isfinite(cy) and cx > 0 and cy > 0:
return max(1, int(round(cy * 2))), max(1, int(round(cx * 2)))
return fallback_height, fallback_width
def _read_video(path):
cap = cv2.VideoCapture(os.fspath(path))
if not cap.isOpened():
raise ValueError(f"Could not open Vista4D video: {path}")
frames = []
while True:
ok, frame = cap.read()
if not ok:
break
frames.append(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
cap.release()
if not frames:
raise ValueError(f"Vista4D video has no frames: {path}")
return np.stack(frames, axis=0)
def _save_video(path, video, fps):
os.makedirs(os.path.dirname(os.fspath(path)), exist_ok=True)
writer = imageio.get_writer(os.fspath(path), fps=fps, quality=9, macro_block_size=1)
try:
for frame in video:
writer.append_data(frame)
finally:
writer.close()
def _load_masks(folder, frame_num, height, width, default):
folder = Path(folder)
if not folder.is_dir():
fill = np.ones if default else np.zeros
return fill((frame_num, height, width), dtype=np.bool_)
files = sorted(folder.glob("*.png"))
if len(files) == 0:
raise ValueError(f"Vista4D mask folder is empty: {folder}")
masks = []
for file in files:
frame = cv2.imread(os.fspath(file), cv2.IMREAD_GRAYSCALE)
if frame is None:
raise ValueError(f"Could not load Vista4D mask: {file}")
masks.append(frame > 127)
masks = np.stack(masks, axis=0)
masks = masks[_center_slice(masks.shape[0], frame_num)]
if masks.shape[1:3] != (height, width):
masks = F.interpolate(torch.from_numpy(masks[:, None].astype(np.float32)), size=(height, width), mode="nearest")[:, 0].numpy() > 0.5
return masks
def _save_masks(folder, masks):
folder = Path(folder)
folder.mkdir(parents=True, exist_ok=True)
for idx, mask in enumerate(masks):
cv2.imwrite(os.fspath(folder / f"{idx:05d}.png"), mask.astype(np.uint8) * 255)
def _pixel_grid(height, width):
ys, xs = np.meshgrid(np.arange(height, dtype=np.float32), np.arange(width, dtype=np.float32), indexing="ij")
return xs.reshape(-1), ys.reshape(-1)
def _frame_points(video, depths, sky_mask, dynamic_mask, cam_c2w, intrinsics, frame_idx, xs, ys):
depth = depths[frame_idx].reshape(-1).astype(np.float32)
valid = np.isfinite(depth) & (depth > 1e-5)
if sky_mask is not None:
valid &= ~sky_mask[frame_idx].reshape(-1).astype(np.bool_)
if not np.any(valid):
return np.empty((0, 3), dtype=np.float32), np.empty((0, 3), dtype=np.uint8), np.empty((0,), dtype=np.bool_)
fx, fy, cx, cy = intrinsics[frame_idx]
z = depth[valid]
cam_points = np.stack(((xs[valid] - cx) * z / fx, (ys[valid] - cy) * z / fy, z), axis=1)
world_points = cam_points @ cam_c2w[frame_idx, :3, :3].T + cam_c2w[frame_idx, :3, 3]
colors = video[frame_idx].reshape(-1, 3)[valid]
point_dynamic = dynamic_mask[frame_idx].reshape(-1)[valid] if dynamic_mask is not None else np.zeros(colors.shape[0], dtype=np.bool_)
return world_points.astype(np.float32), colors, point_dynamic.astype(np.bool_)
def _project_points(points, colors, cam_w2c, intrinsics, height, width):
if points.shape[0] == 0:
return None
cam_points = points @ cam_w2c[:3, :3].T + cam_w2c[:3, 3]
z = cam_points[:, 2]
valid = z > 1e-5
if not np.any(valid):
return None
fx, fy, cx, cy = intrinsics
valid_idx = np.nonzero(valid)[0]
x = np.rint(cam_points[valid, 0] * fx / z[valid] + cx).astype(np.int32)
y = np.rint(cam_points[valid, 1] * fy / z[valid] + cy).astype(np.int32)
inside = (x >= 0) & (x < width) & (y >= 0) & (y < height)
if not np.any(inside):
return None
return y[inside] * width + x[inside], z[valid][inside], colors[valid][inside], valid_idx[inside]
def _render_point_chunks(point_chunks, cam_w2c, intrinsics, height, width):
empty_frame = np.zeros((height, width, 3), dtype=np.uint8)
empty_mask = np.zeros((height, width), dtype=np.bool_)
zbuf = np.full(height * width, np.inf, dtype=np.float32)
projected = []
for points, colors, is_dynamic in point_chunks:
item = _project_points(points, colors, cam_w2c, intrinsics, height, width)
if item is None:
continue
pix, z, colors, _ = item
np.minimum.at(zbuf, pix, z)
projected.append((pix, z, colors, is_dynamic))
if len(projected) == 0:
return empty_frame, empty_mask, empty_mask.copy()
output = np.zeros((height * width, 3), dtype=np.uint8)
dynamic = np.zeros(height * width, dtype=np.bool_)
for pix, z, colors, is_dynamic in projected:
keep = z <= zbuf[pix] + 1e-4
output[pix[keep]] = colors[keep]
if is_dynamic:
dynamic[pix[keep]] = True
mask = np.isfinite(zbuf).reshape(height, width)
return output.reshape(height, width, 3), mask, dynamic.reshape(height, width) & mask
def _to_torch_point_cache(point_cache, device):
return [(torch.from_numpy(points).to(device=device, dtype=torch.float32), torch.from_numpy(colors).to(device=device)) for points, colors in point_cache]
def _project_points_torch(points, colors, cam_w2c, intrinsics, height, width):
if points.shape[0] == 0:
return None
cam_points = points @ cam_w2c[:3, :3].T + cam_w2c[:3, 3]
z_all = cam_points[:, 2]
valid = z_all > 1e-5
if not bool(valid.any()):
return None
fx, fy, cx, cy = intrinsics.unbind(0)
valid_idx = torch.nonzero(valid, as_tuple=False).squeeze(1)
z = z_all[valid_idx]
x = torch.round(cam_points[valid_idx, 0] * fx / z + cx).to(torch.int64)
y = torch.round(cam_points[valid_idx, 1] * fy / z + cy).to(torch.int64)
inside = (x >= 0) & (x < width) & (y >= 0) & (y < height)
if not bool(inside.any()):
return None
return y[inside] * width + x[inside], z[inside], colors[valid_idx[inside]]
def _render_point_chunks_torch(point_chunks, cam_w2c, intrinsics, height, width):
device = cam_w2c.device
zbuf = torch.full((height * width,), float("inf"), dtype=torch.float32, device=device)
projected = []
order_start = 0
for points, colors, is_dynamic in point_chunks:
item = _project_points_torch(points, colors, cam_w2c, intrinsics, height, width)
if item is None:
order_start += points.shape[0]
continue
pix, z, colors = item
order = torch.arange(order_start, order_start + pix.shape[0], dtype=torch.int32, device=device)
zbuf.scatter_reduce_(0, pix, z, reduce="amin", include_self=True)
projected.append((pix, z, colors, order, is_dynamic))
order_start += points.shape[0]
frame = torch.zeros((height * width, 3), dtype=torch.uint8, device=device)
dynamic = torch.zeros((height * width,), dtype=torch.bool, device=device)
if len(projected) > 0:
winner_order = torch.full((height * width,), -1, dtype=torch.int32, device=device)
for pix, z, _, order, is_dynamic in projected:
keep = z <= zbuf[pix] + 1e-4
if keep.any():
winner_order.scatter_reduce_(0, pix[keep], order[keep], reduce="amax", include_self=True)
if is_dynamic:
dynamic[pix[keep]] = True
for pix, _, colors, order, _ in projected:
selected = order == winner_order[pix]
if selected.any():
frame[pix[selected]] = colors[selected]
mask = torch.isfinite(zbuf)
return frame.reshape(height, width, 3).cpu().numpy(), mask.reshape(height, width).cpu().numpy(), (dynamic & mask).reshape(height, width).cpu().numpy()
def _render_point_cloud_video_torch(static_cache, dynamic_cache, target_cam_w2c, target_intrinsics, height, width, window_radius):
device = torch.device("cuda")
static_cache = _to_torch_point_cache(static_cache, device)
dynamic_cache = _to_torch_point_cache(dynamic_cache, device)
target_cam_w2c = torch.from_numpy(target_cam_w2c).to(device=device, dtype=torch.float32)
target_intrinsics = torch.from_numpy(target_intrinsics).to(device=device, dtype=torch.float32)
rendered = []
alpha_masks = []
dynamic_masks = []
context = torch.autocast(device_type="cuda", enabled=False) if device.type == "cuda" else nullcontext()
with torch.no_grad(), context:
for target_idx in range(len(dynamic_cache)):
if window_radius < 0:
point_chunks = [(points, colors, False) for points, colors in static_cache]
else:
start = max(0, target_idx - window_radius)
end = min(len(dynamic_cache), target_idx + window_radius + 1)
point_chunks = [(static_cache[idx][0], static_cache[idx][1], False) for idx in range(start, end) if static_cache[idx][0].shape[0] > 0]
dynamic_points, dynamic_colors = dynamic_cache[target_idx]
if dynamic_points.shape[0] > 0:
point_chunks.append((dynamic_points, dynamic_colors, True))
frame, mask, rendered_dynamic = _render_point_chunks_torch(point_chunks, target_cam_w2c[target_idx], target_intrinsics[target_idx], height, width)
rendered.append(frame)
alpha_masks.append(mask)
dynamic_masks.append(rendered_dynamic)
return np.stack(rendered, axis=0), np.stack(alpha_masks, axis=0), np.stack(dynamic_masks, axis=0)
def _render_point_cloud_video(video, depths, sky_mask, dynamic_mask, cam_c2w, intrinsics, target_cam_c2w=None, target_intrinsics=None, window_radius=-1):
frame_num, height, width = video.shape[:3]
if target_cam_c2w is None:
target_cam_c2w = cam_c2w
if target_intrinsics is None:
target_intrinsics = intrinsics
xs, ys = _pixel_grid(height, width)
static_cache = []
dynamic_cache = []
for idx in range(frame_num):
points, colors, point_dynamic = _frame_points(video, depths, sky_mask, dynamic_mask, cam_c2w, intrinsics, idx, xs, ys)
static = ~point_dynamic
static_cache.append((points[static], colors[static]))
dynamic_cache.append((points[point_dynamic], colors[point_dynamic]))
target_cam_w2c = np.linalg.inv(target_cam_c2w).astype(np.float32)
rendered = []
alpha_masks = []
dynamic_masks = []
window_radius = int(window_radius)
if window_radius < 0:
static_points = [points for points, _ in static_cache if points.shape[0] > 0]
static_colors = [colors for points, colors in static_cache if points.shape[0] > 0]
static_cache = [(np.concatenate(static_points, axis=0), np.concatenate(static_colors, axis=0))] if static_points else []
if torch.cuda.is_available():
return _render_point_cloud_video_torch(static_cache, dynamic_cache, target_cam_w2c, target_intrinsics, height, width, window_radius)
for target_idx in range(frame_num):
if window_radius < 0:
point_chunks = [(points, colors, False) for points, colors in static_cache]
else:
start = max(0, target_idx - window_radius)
end = min(frame_num, target_idx + window_radius + 1)
point_chunks = [(static_cache[idx][0], static_cache[idx][1], False) for idx in range(start, end) if static_cache[idx][0].shape[0] > 0]
dynamic_points, dynamic_colors = dynamic_cache[target_idx]
if dynamic_points.shape[0] > 0:
point_chunks.append((dynamic_points, dynamic_colors, True))
frame, mask, rendered_dynamic = _render_point_chunks(point_chunks, target_cam_w2c[target_idx], target_intrinsics[target_idx], height, width)
rendered.append(frame)
alpha_masks.append(mask)
dynamic_masks.append(rendered_dynamic)
return np.stack(rendered, axis=0), np.stack(alpha_masks, axis=0), np.stack(dynamic_masks, axis=0)
def _load_cameras(path, frame_num):
data = np.load(path)
cam_c2w = data["cam_c2w"][_center_slice(data["cam_c2w"].shape[0], frame_num)]
intrinsics = data["intrinsics"][_center_slice(data["intrinsics"].shape[0], frame_num)]
return cam_c2w.astype(np.float32), intrinsics.astype(np.float32)
def _save_cameras(path, cam_c2w, intrinsics):
os.makedirs(os.path.dirname(os.fspath(path)), exist_ok=True)
np.savez(path, cam_c2w=cam_c2w.astype(np.float32), intrinsics=intrinsics.astype(np.float32))
def _is_camera_npz(path):
if Path(path).suffix.lower() != ".npz":
return False
try:
with np.load(path) as data:
return "cam_c2w" in data.files and "intrinsics" in data.files
except Exception:
return False
def _extract_zip(path):
digest = hashlib.sha1(os.path.abspath(path).encode("utf-8")).hexdigest()[:12]
target = Path("ckpts") / "temp" / "vista4d_maps" / digest
if target.is_dir():
return target
target.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(path) as archive:
archive.extractall(target)
roots = [item for item in target.iterdir() if item.is_dir()]
if len(roots) == 1 and (roots[0] / "video_src.mp4").is_file():
return roots[0]
return target
def _resolve_custom_input(input_custom):
if input_custom is None or str(input_custom).strip() == "":
return None, None
path = Path(input_custom)
if path.is_file() and _is_camera_npz(path):
return None, path
if path.is_file() and path.suffix.lower() == ".zip":
return _extract_zip(path), None
if path.is_file():
path = path.parent
if not path.is_dir():
raise ValueError(f"Vista4D custom_guide must be a preprocessed map folder/zip or target camera .npz, got: {input_custom}")
return path, None
def _default_output_dir():
from wgp import save_path
return save_path
def _hash_array(hasher, array):
array = np.ascontiguousarray(array)
hasher.update(str(array.shape).encode("utf-8"))
hasher.update(str(array.dtype).encode("utf-8"))
hasher.update(array.view(np.uint8))
def _hash_file(path):
hasher = hashlib.sha1()
with open(path, "rb") as reader:
for chunk in iter(lambda: reader.read(1024 * 1024), b""):
hasher.update(chunk)
return hasher.hexdigest()
def _make_generated_map_cache_key(video, frame_num, height, width, fps, process_res, chunk_size, chunk_overlap, custom_settings, target_camera_path, model_mode):
hasher = hashlib.sha1()
hasher.update(b"vista4d-memory-map-v2")
hasher.update(f"{frame_num}|{height}|{width}|{fps}|{process_res}|{chunk_size}|{chunk_overlap}".encode("utf-8"))
if target_camera_path is None:
hasher.update(str(model_mode or "").encode("utf-8"))
if isinstance(custom_settings, dict):
hasher.update(str(custom_settings.get("vista4d_seg_keywords", "_all_")).encode("utf-8"))
hasher.update(str(custom_settings.get("vista4d_scene_scale", 1.0)).encode("utf-8"))
if target_camera_path is None:
hasher.update(str(custom_settings.get("vista4d_camera_strength", 100.0)).encode("utf-8"))
if target_camera_path is not None:
hasher.update(os.path.abspath(os.fspath(target_camera_path)).encode("utf-8"))
hasher.update(_hash_file(target_camera_path).encode("utf-8"))
_hash_array(hasher, video)
return hasher.hexdigest()
def _cache_generated_map(cache_key, data):
_VISTA4D_MAP_CACHE[cache_key] = data
while len(_VISTA4D_MAP_CACHE) > _VISTA4D_MAP_CACHE_MAX_ITEMS:
_VISTA4D_MAP_CACHE.pop(next(iter(_VISTA4D_MAP_CACHE)))
def _parse_keywords(custom_settings):
raw = "_all_"
if isinstance(custom_settings, dict):
raw = str(custom_settings.get("vista4d_seg_keywords", raw))
return [item.strip() for item in raw.replace(";", ",").split(",") if item.strip()]
def _camera_strength(custom_settings):
if not isinstance(custom_settings, dict):
return 1.0
return float(custom_settings.get("vista4d_camera_strength", 100.0) or 0.0) / 100.0
def _get_da3_settings():
return VISTA4D_DA3_PROCESS_RES, VISTA4D_DA3_CHUNK_SIZE, VISTA4D_DA3_CHUNK_OVERLAP
def _save_generated_map_to_disk(data, source_cam_c2w, source_intrinsics, depths, sky_mask, fps):
map_dir = Path(_default_output_dir()) / f"vista4d_map_{time.strftime('%Y%m%d_%H%M%S')}"
map_dir.mkdir(parents=True, exist_ok=True)
_save_video(map_dir / "video_src.mp4", data["source_video"], fps=fps)
_save_video(map_dir / "video_pc.mp4", data["point_cloud_video"], fps=fps)
_save_masks(map_dir / "alpha_mask_src", data["source_alpha_mask"])
_save_masks(map_dir / "dynamic_mask_src", data["source_motion_mask"])
_save_masks(map_dir / "static_mask_src", data["source_alpha_mask"] & ~data["source_motion_mask"])
_save_masks(map_dir / "alpha_mask_pc", data["point_cloud_alpha_mask"])
_save_masks(map_dir / "dynamic_mask_pc", data["point_cloud_motion_mask"])
_save_masks(map_dir / "static_mask_pc", data["point_cloud_alpha_mask"] & ~data["point_cloud_motion_mask"])
_save_masks(map_dir / "sky_mask_src", sky_mask.astype(np.bool_))
_save_cameras(map_dir / "cameras_src.npz", source_cam_c2w, source_intrinsics)
_save_cameras(map_dir / "cameras_tgt.npz", data["cam_c2w"], data["intrinsics"])
np.save(map_dir / "depths_da3.npy", depths.astype(np.float16))
shutil.make_archive(os.fspath(map_dir), "zip", root_dir=map_dir)
print(f"Vista4D map saved to: {map_dir}")
return map_dir
def _rotation_x(angle):
c, s = np.cos(angle), np.sin(angle)
return np.array([[1.0, 0.0, 0.0], [0.0, c, -s], [0.0, s, c]], dtype=np.float32)
def _rotation_y(angle):
c, s = np.cos(angle), np.sin(angle)
return np.array([[c, 0.0, s], [0.0, 1.0, 0.0], [-s, 0.0, c]], dtype=np.float32)
def _estimate_focus_depth(depths, sky_mask):
valid = np.isfinite(depths) & (depths > 1e-5)
if sky_mask is not None:
valid &= ~sky_mask.astype(np.bool_)
if not np.any(valid):
return 1.0
samples = depths[valid]
if samples.size > 200000:
samples = samples[np.linspace(0, samples.size - 1, 200000, dtype=np.int64)]
return max(float(np.median(samples)), 1e-3)
def _apply_local_camera_delta(cam_c2w, translations, rotations):
target = cam_c2w.copy()
for idx in range(cam_c2w.shape[0]):
delta = np.eye(4, dtype=np.float32)
delta[:3, :3] = rotations[idx]
delta[:3, 3] = translations[idx]
target[idx] = cam_c2w[idx] @ delta
return target
def _generate_target_camera_trajectory(cam_c2w, intrinsics, depths, sky_mask, model_mode, strength):
mode = str(model_mode or "dolly_zoom")
if mode not in {
"dolly_zoom", "left_front_zoom", "right_front_zoom", "close_crane_above", "close_crane_below", "arc_right_45", "arc_left_45",
"push_in", "pull_back", "truck_right", "truck_left", "pedestal_up", "pedestal_down",
"pan_right_45", "pan_left_45", "tilt_up_45", "tilt_down_45", "zoom_in", "zoom_out", "bird_view",
"crane_above_right", "crane_above_left", "crane_below_right", "crane_below_left",
}:
mode = "dolly_zoom"
focus = _estimate_focus_depth(depths, sky_mask)
frame_num = cam_c2w.shape[0]
progress = np.linspace(0.0, 1.0, frame_num, dtype=np.float32) * np.float32(strength)
translations = np.zeros((frame_num, 3), dtype=np.float32)
rotations = np.repeat(np.eye(3, dtype=np.float32)[None], frame_num, axis=0)
target_intrinsics = intrinsics.copy()
focal_scale = np.ones(frame_num, dtype=np.float32)
if mode == "dolly_zoom":
translations[:, 2] = 0.35 * focus * progress
focal_scale = np.clip((focus - translations[:, 2]) / focus, 0.25, 4.0).astype(np.float32)
elif mode in ("left_front_zoom", "right_front_zoom"):
translations[:, 0] = (-0.22 if mode == "left_front_zoom" else 0.22) * focus * progress
translations[:, 2] = 0.20 * focus * progress
focal_scale = np.clip(1.0 + 0.25 * progress, 0.25, 4.0).astype(np.float32)
elif mode in ("close_crane_above", "close_crane_below"):
translations[:, 1] = (-0.22 if mode == "close_crane_above" else 0.22) * focus * progress
translations[:, 2] = 0.18 * focus * progress
for idx, amount in enumerate(progress):
rotations[idx] = _rotation_x(np.deg2rad(-15.0 if mode == "close_crane_above" else 15.0) * amount)
elif mode in ("arc_right_45", "arc_left_45"):
sign = -1.0 if mode == "arc_right_45" else 1.0
angles = np.deg2rad(45.0) * progress * sign
translations[:, 0] = -focus * np.sin(angles)
translations[:, 2] = focus * (1.0 - np.cos(angles))
for idx, angle in enumerate(angles):
rotations[idx] = _rotation_y(angle)
elif mode == "push_in":
translations[:, 2] = 0.25 * focus * progress
elif mode == "pull_back":
translations[:, 2] = -0.25 * focus * progress
elif mode == "truck_right":
translations[:, 0] = 0.25 * focus * progress
elif mode == "truck_left":
translations[:, 0] = -0.25 * focus * progress
elif mode == "pedestal_up":
translations[:, 1] = -0.25 * focus * progress
elif mode == "pedestal_down":
translations[:, 1] = 0.25 * focus * progress
elif mode == "pan_right_45":
for idx, amount in enumerate(progress):
rotations[idx] = _rotation_y(np.deg2rad(45.0) * amount)
elif mode == "pan_left_45":
for idx, amount in enumerate(progress):
rotations[idx] = _rotation_y(np.deg2rad(-45.0) * amount)
elif mode == "tilt_up_45":
for idx, amount in enumerate(progress):
rotations[idx] = _rotation_x(np.deg2rad(45.0) * amount)
elif mode == "tilt_down_45":
for idx, amount in enumerate(progress):
rotations[idx] = _rotation_x(np.deg2rad(-45.0) * amount)
elif mode == "zoom_in":
focal_scale = np.clip(1.0 + 0.60 * progress, 0.25, 4.0).astype(np.float32)
elif mode == "zoom_out":
focal_scale = np.clip(1.0 - 0.35 * progress, 0.25, 4.0).astype(np.float32)
elif mode == "bird_view":
translations[:, 1] = -0.70 * focus * progress
translations[:, 2] = -0.12 * focus * progress
for idx, amount in enumerate(progress):
rotations[idx] = _rotation_x(np.deg2rad(-60.0) * amount)
elif mode in ("crane_above_right", "crane_above_left", "crane_below_right", "crane_below_left"):
above = "above" in mode
right = "right" in mode
translations[:, 0] = (0.18 if right else -0.18) * focus * progress
translations[:, 1] = (-0.24 if above else 0.24) * focus * progress
translations[:, 2] = 0.16 * focus * progress
yaw = np.deg2rad(-12.0 if right else 12.0)
pitch = np.deg2rad(-18.0 if above else 18.0)
for idx, amount in enumerate(progress):
rotations[idx] = _rotation_y(yaw * amount) @ _rotation_x(pitch * amount)
target_intrinsics[:, 0:2] *= focal_scale[:, None]
return _apply_local_camera_delta(cam_c2w, translations, rotations), target_intrinsics
def _create_map_from_control_video(input_frames, frame_num, height, width, fps, custom_settings, target_camera_path=None, model_mode=None):
video = _tensor_to_video_np(input_frames)
video = video[_center_slice(video.shape[0], frame_num)]
height_input, width_input = video.shape[1:3]
# video = _crop_resize_video(video, height, width)
scene_scale = 1.0
if isinstance(custom_settings, dict):
scene_scale = float(custom_settings.get("vista4d_scene_scale", 1.0) or 1.0)
process_res, chunk_size, chunk_overlap = _get_da3_settings()
if process_res <= 0:
process_res = width
from preprocessing.depth_anything_v3.depth import resolve_da3_chunk_size, run_da3_reconstruction
chunk_size = resolve_da3_chunk_size(chunk_size)
cache_key = None
if VISTA4D_REUSE_GENERATED_MAP_IN_MEMORY:
cache_key = _make_generated_map_cache_key(video, frame_num, height, width, fps, process_res, chunk_size, chunk_overlap, custom_settings, target_camera_path, model_mode)
cached = _VISTA4D_MAP_CACHE.get(cache_key)
if cached is not None:
print("Vista4D map reused from memory cache.")
return cached
depths, sky_mask, cam_c2w, intrinsics = run_da3_reconstruction(video, process_res=process_res, chunk_size=chunk_size, chunk_overlap=chunk_overlap)
if scene_scale != 1.0:
depths *= scene_scale
cam_c2w[:, :3, 3] *= scene_scale
source_cam_c2w = cam_c2w
source_intrinsics = intrinsics
target_cam_c2w = cam_c2w
target_intrinsics = intrinsics
if target_camera_path is not None:
target_cam_c2w, target_intrinsics = _load_cameras(target_camera_path, frame_num)
camera_height, camera_width = _infer_intrinsics_size(target_intrinsics, height_input, width_input)
if (camera_height, camera_width) != (height, width):
target_intrinsics = _resize_intrinsics(target_intrinsics, height, width, camera_height, camera_width)
if scene_scale != 1.0:
target_cam_c2w[:, :3, 3] *= scene_scale
else:
target_cam_c2w, target_intrinsics = _generate_target_camera_trajectory(cam_c2w, intrinsics, depths, sky_mask, model_mode, _camera_strength(custom_settings))
keywords = _parse_keywords(custom_settings)
if len(keywords) == 0:
dynamic_mask = np.zeros((frame_num, height, width), dtype=np.bool_)
elif "_all_" in keywords:
dynamic_mask = np.ones((frame_num, height, width), dtype=np.bool_)
else:
from preprocessing.sam3.preprocessor import run_sam3_video
dynamic_mask = run_sam3_video(video, keywords)
source_alpha_mask = np.ones((frame_num, height, width), dtype=np.bool_)
point_cloud_video, point_cloud_alpha_mask, point_cloud_dynamic_mask = _render_point_cloud_video(video, depths, sky_mask, dynamic_mask, cam_c2w, intrinsics, target_cam_c2w, target_intrinsics)
data = {
"source_video": video,
"point_cloud_video": point_cloud_video,
"source_alpha_mask": source_alpha_mask,
"source_motion_mask": dynamic_mask,
"point_cloud_alpha_mask": point_cloud_alpha_mask,
"point_cloud_motion_mask": point_cloud_dynamic_mask,
"cam_c2w": target_cam_c2w,
"intrinsics": target_intrinsics,
}
if VISTA4D_REUSE_GENERATED_MAP_IN_MEMORY:
_cache_generated_map(cache_key, data)
print("Vista4D map kept in memory cache.")
return data
return _save_generated_map_to_disk(data, source_cam_c2w, source_intrinsics, depths, sky_mask, fps)
def _load_map(map_dir, frame_num, height, width):
video_src = _read_video(map_dir / "video_src.mp4")
video_pc = _read_video(map_dir / "video_pc.mp4")
video_src = video_src[_center_slice(video_src.shape[0], frame_num)]
video_pc = video_pc[_center_slice(video_pc.shape[0], frame_num)]
src_h, src_w = video_src.shape[1:3]
video_src = _crop_resize_video(video_src, height, width)
video_pc = _crop_resize_video(video_pc, height, width)
cam_c2w, intrinsics = _load_cameras(map_dir / "cameras_tgt.npz", frame_num)
if (src_h, src_w) != (height, width):
intrinsics = _resize_intrinsics(intrinsics, height, width, src_h, src_w)
return {
"source_video": video_src,
"point_cloud_video": video_pc,
"source_alpha_mask": _load_masks(map_dir / "alpha_mask_src", frame_num, height, width, True),
"source_motion_mask": _load_masks(map_dir / "dynamic_mask_src", frame_num, height, width, False),
"point_cloud_alpha_mask": _load_masks(map_dir / "alpha_mask_pc", frame_num, height, width, True),
"point_cloud_motion_mask": _load_masks(map_dir / "dynamic_mask_pc", frame_num, height, width, False),
"cam_c2w": cam_c2w,
"intrinsics": intrinsics,
}
def _pack_masks(alpha_mask, motion_mask, device, dtype):
masks = np.stack((alpha_mask, motion_mask), axis=0)[None].astype(np.float32)
masks = torch.from_numpy(masks).to(device=device, dtype=dtype)
b, c, f, h, w = masks.shape
masks = torch.cat((torch.repeat_interleave(masks[:, :, 0:1], repeats=4, dim=2), masks[:, :, 1:]), dim=2)
masks = rearrange(masks, "b c (f sf) (h sh) (w sw) -> b (c sf sh sw) f h w", sf=4, sh=8, sw=8)
return masks
def prepare_vista4d_condition(pipeline, input_frames, input_custom, frame_num, height, width, tile_size, fps=16, custom_settings=None, model_mode=None):
map_dir, target_camera_path = _resolve_custom_input(input_custom)
data = None
if map_dir is None:
if input_frames is None:
raise ValueError("Vista4D needs a control video or a preprocessed map in custom_guide.")
map_ref = _create_map_from_control_video(input_frames, frame_num, height, width, fps, custom_settings, target_camera_path, model_mode=model_mode)
if isinstance(map_ref, dict):
data = map_ref
else:
map_dir = map_ref
if data is None:
data = _load_map(map_dir, frame_num, height, width)
device = pipeline.device
dtype = pipeline.dtype
vae_dtype = pipeline.VAE_dtype
source_video = _video_np_to_tensor(data["source_video"], device, vae_dtype)
point_video = _video_np_to_tensor(data["point_cloud_video"], device, vae_dtype)
source_latents = pipeline.vae.encode([source_video], tile_size=tile_size)[0].unsqueeze(0).to(device=device, dtype=dtype)
point_latents = pipeline.vae.encode([point_video], tile_size=tile_size)[0].unsqueeze(0).to(device=device, dtype=dtype)
source_masks = _pack_masks(data["source_alpha_mask"], data["source_motion_mask"], device, dtype)
point_masks = _pack_masks(data["point_cloud_alpha_mask"], data["point_cloud_motion_mask"], device, dtype)
lat_h = height // pipeline.vae_stride[1]
lat_w = width // pipeline.vae_stride[2]
cam_c2w = torch.from_numpy(data["cam_c2w"][None]).to(device=device, dtype=dtype)
intrinsics = torch.from_numpy(data["intrinsics"][None]).to(device=device, dtype=dtype)
cam_emb = get_plucker_embedding(intrinsics, cam_c2w, height, width, height_dit=lat_h // 2, width_dit=lat_w // 2)
cam_emb = cam_emb[:, ::pipeline.vae_stride[0]].to(dtype=dtype)
return {
"vista": {
"source_latents": source_latents,
"point_latents": point_latents,
"source_mask_latents": source_masks,
"point_mask_latents": point_masks,
"cam_emb": cam_emb,
}
}