| 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] |
| |
|
|
| 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, |
| } |
| } |
|
|