depthwizard-backend / depth /tiled_inference.py
Priya Soni
Deploy DepthWizard backend
ad38b1a
Raw History Blame Contribute Delete
5.3 kB
"""
Module: depth.tiled_inference
Description: High-resolution overlapping tiled monocular depth inference using Depth Anything V2.
"""
import os
import time
import torch
import numpy as np
from PIL import Image
from typing import Union, Optional, Tuple, Dict, Any
from depth.depth_model import load_model, get_device
def _create_2d_window(height: int, width: int, window_type: str = "hann") -> np.ndarray:
if window_type == "hann":
wy = np.sin(np.linspace(np.pi / (2 * height), np.pi - np.pi / (2 * height), height)) ** 2
wx = np.sin(np.linspace(np.pi / (2 * width), np.pi - np.pi / (2 * width), width)) ** 2
w2d = np.outer(wy, wx)
elif window_type == "pyramid":
wy = 1.0 - np.abs(np.linspace(-1, 1, height))
wx = 1.0 - np.abs(np.linspace(-1, 1, width))
w2d = np.outer(wy, wx)
else:
w2d = np.ones((height, width), dtype=np.float32)
w2d = np.clip(w2d, 1e-4, 1.0).astype(np.float32)
return w2d
def estimate_depth_tiled(
image: Union[str, np.ndarray, Image.Image],
tile_size: int = 512,
overlap: float = 0.25,
batch_size: int = 8,
window_type: str = "hann",
device: Optional[Union[str, torch.device]] = None
) -> Tuple[np.ndarray, Dict[str, Any]]:
start_time = time.time()
if isinstance(image, str):
if not os.path.exists(image):
raise FileNotFoundError(f"Image file not found: {image}")
pil_img = Image.open(image).convert("RGB")
rgb_arr = np.array(pil_img, dtype=np.uint8)
elif isinstance(image, np.ndarray):
arr = np.squeeze(image)
if arr.dtype != np.uint8:
if arr.max() <= 1.0:
arr = (arr * 255.0).astype(np.uint8)
else:
arr = np.clip(arr, 0, 255).astype(np.uint8)
if arr.ndim == 2:
arr = np.stack([arr] * 3, axis=-1)
rgb_arr = arr
elif isinstance(image, Image.Image):
rgb_arr = np.array(image.convert("RGB"), dtype=np.uint8)
else:
raise TypeError(f"Unsupported input image type: {type(image)}")
H, W = rgb_arr.shape[:2]
stride = max(1, int(round(tile_size * (1.0 - overlap))))
y_starts = list(range(0, max(1, H - tile_size + 1), stride))
if len(y_starts) == 0 or (y_starts[-1] + tile_size < H):
y_starts.append(max(0, H - tile_size))
x_starts = list(range(0, max(1, W - tile_size + 1), stride))
if len(x_starts) == 0 or (x_starts[-1] + tile_size < W):
x_starts.append(max(0, W - tile_size))
y_starts = sorted(list(set(y_starts)))
x_starts = sorted(list(set(x_starts)))
tiles = []
for y0 in y_starts:
y1 = min(H, y0 + tile_size)
y0_eff = max(0, y1 - tile_size)
for x0 in x_starts:
x1 = min(W, x0 + tile_size)
x0_eff = max(0, x1 - tile_size)
tiles.append((y0_eff, y1, x0_eff, x1))
num_tiles = len(tiles)
model, processor, target_device = load_model(device=device)
base_window = _create_2d_window(tile_size, tile_size, window_type=window_type)
accum_depth = np.zeros((H, W), dtype=np.float64)
accum_weight = np.zeros((H, W), dtype=np.float64)
if torch.cuda.is_available() and target_device.type == "cuda":
torch.cuda.reset_peak_memory_stats(target_device)
for b_idx in range(0, num_tiles, batch_size):
batch_coords = tiles[b_idx : b_idx + batch_size]
batch_pil = [
Image.fromarray(rgb_arr[y0:y1, x0:x1])
for (y0, y1, x0, x1) in batch_coords
]
inputs = processor(images=batch_pil, return_tensors="pt").to(target_device)
with torch.no_grad():
outputs = model(**inputs)
post_processed = processor.post_process_depth_estimation(
outputs,
target_sizes=[(y1 - y0, x1 - x0) for (y0, y1, x0, x1) in batch_coords]
)
for (y0, y1, x0, x1), pred in zip(batch_coords, post_processed):
t_depth = pred["predicted_depth"].cpu().numpy().astype(np.float64)
th, tw = y1 - y0, x1 - x0
if (th, tw) == (tile_size, tile_size):
w = base_window
else:
w = _create_2d_window(th, tw, window_type=window_type)
accum_depth[y0:y1, x0:x1] += t_depth * w
accum_weight[y0:y1, x0:x1] += w
valid_weight = accum_weight > 0
final_depth = np.zeros((H, W), dtype=np.float32)
final_depth[valid_weight] = (accum_depth[valid_weight] / accum_weight[valid_weight]).astype(np.float32)
elapsed = time.time() - start_time
peak_gpu_mb = 0.0
if torch.cuda.is_available() and target_device.type == "cuda":
peak_gpu_mb = float(torch.cuda.max_memory_allocated(target_device) / (1024 * 1024))
info = {
"image_shape": [H, W, 3],
"tile_size": tile_size,
"overlap": overlap,
"stride": stride,
"grid_y": len(y_starts),
"grid_x": len(x_starts),
"total_tiles": num_tiles,
"batch_size": batch_size,
"window_type": window_type,
"elapsed_seconds": elapsed,
"peak_gpu_memory_mb": peak_gpu_mb,
"device": str(target_device)
}
return final_depth, info
# Alias for backwards compatibility
run_tiled_inference = estimate_depth_tiled