Priya Soni
Deploy DepthWizard backend
ad38b1a
Raw History Blame Contribute Delete
5.32 kB
"""
M1 Perception Module - Monocular Depth Inference Engine
"""
import cv2
import logging
import numpy as np
from typing import Optional, Tuple
from PIL import Image, ImageOps
from transformers import pipeline
class DepthEstimator:
def __init__(self):
self.pipe = None
self.is_initialized = False
def initialize(self, model_size: str = "Base") -> bool:
"""Loads the pretrained model weights into memory."""
# Map size strings to HuggingFace repo names
self.model_size = model_size
model_map = {
"Small": "depth-anything/Depth-Anything-V2-Small-hf",
"Base": "depth-anything/Depth-Anything-V2-Base-hf",
"Large": "depth-anything/Depth-Anything-V2-Large-hf"
}
repo_id = model_map.get(model_size, model_map["Base"])
logging.info(f"[M1 MODEL] Initializing Depth Anything V2 {model_size} ({repo_id})...")
try:
self.pipe = pipeline(task="depth-estimation", model=repo_id)
self.is_initialized = True
return True
except Exception as e:
logging.error(f"[M1 MODEL] Failed to initialize model: {str(e)}")
return False
def predict_with_confidence(self, image: Image.Image, tta_mode: str = "2-view") -> Optional[Tuple[np.ndarray, np.ndarray]]:
"""
Executes depth inference with configurable Rotational Test-Time Augmentation (TTA).
tta_mode: 'none' (1x), '2-view' (0° + 180°), or '4-view' (0°, 90°, 180°, 270°)
Returns: (final_depth_array, confidence_map_array)
"""
if not self.is_initialized:
logging.error("[M1 MODEL] Cannot predict: Model not initialized.")
return None
try:
import time
start_time = time.time()
img_np = np.array(image)
# Determine rotation steps based on mode
if tta_mode == "4-view":
rotations = [0, 1, 2, 3]
elif tta_mode == "2-view":
rotations = [0, 2]
else:
rotations = [0]
preds = []
for k in rotations:
# Rotate input image (k * 90 degrees)
rotated_input = np.rot90(img_np, k)
pil_rot = Image.fromarray(rotated_input)
# Predict and extract the tensor
res = self.pipe(pil_rot)["predicted_depth"]
depth_arr = res.squeeze().cpu().numpy().astype(np.float32)
# Rotate predicted depth back to canonical orientation
canonical_depth = np.rot90(depth_arr, -k)
preds.append(canonical_depth)
# Consensus and variance mapping
stacked = np.stack(preds, axis=0)
final_depth = np.median(stacked, axis=0)
# The standard deviation across rotations represents structural uncertainty
if len(preds) > 1:
uncertainty_map = np.std(stacked, axis=0)
u_max = np.max(uncertainty_map) if np.max(uncertainty_map) > 0 else 1.0
confidence_map = 1.0 - (uncertainty_map / u_max)
else:
# Default to 1.0 confidence for single-pass inference
confidence_map = np.ones_like(final_depth)
# ---------------------------------------------------------
# Calculate Terrain Bias B(D) using Morphological Opening
# ---------------------------------------------------------
# Calculate kernel size based on physical scale, not raw pixels
gsd_meters_per_pixel = 1.0 # Default fallback, should be passed in from metadata
max_building_width_meters = 60.0 # Assumes largest target building is 60m wide
calculated_size = int(max_building_width_meters / gsd_meters_per_pixel)
# OpenCV kernels must be odd numbers
kernel_size = calculated_size if calculated_size % 2 != 0 else calculated_size + 1
kernel_size = max(3, kernel_size) # Ensure it doesn't collapse to 0
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size))
terrain_bias = cv2.morphologyEx(final_depth, cv2.MORPH_OPEN, kernel)
height_map = np.maximum(final_depth - terrain_bias, 0.0)
# ---------------------------------------------------------
# Calculate latency in milliseconds
inference_ms = round((time.time() - start_time) * 1000, 2)
# Determine execution device from HuggingFace pipeline
device_used = str(self.pipe.device)
return {
"relative_depth": final_depth,
"relative_height": height_map,
"quality_map": confidence_map,
"metadata": {
"model": f"Depth-Anything-V2-{self.model_size}",
"tta_mode": tta_mode,
"device": device_used,
"inference_ms": inference_ms,
"input_size": (img_np.shape[0], img_np.shape[1]),
"dtype": str(final_depth.dtype)
}
}
except Exception as e:
logging.error(f"[M1 MODEL] Inference failed: {str(e)}")
return None