| |
| """ |
| WHAT: This module defines the Depth Estimator, the third stage of the Multiverse AI pipeline. |
| WHY: Video generation models often need a sense of 3D space to create realistic camera movements |
| (like panning or zooming). By extracting a grayscale depth map from our flat 2D generated image, |
| we give the downstream video model the geometry it needs to distinguish foreground from background. |
| HOW: It implements the `BaseModel` interface. It uses the Hugging Face `transformers` library to load |
| the 'Depth-Anything' model, processes the PIL Image, and returns a new depth-mapped PIL Image. |
| """ |
|
|
| import gc |
| import torch |
| from PIL import Image |
|
|
| |
| from transformers import pipeline |
|
|
| |
| from .base import BaseModel |
| from ..config import MODEL_IDS, DEVICE, MOCK_INFERENCE |
|
|
| class DepthEstimator(BaseModel): |
| """ |
| Wrapper for the Depth Estimation model (Depth-Anything). |
| Takes a 2D PIL Image and generates a grayscale depth map (also a PIL Image). |
| """ |
|
|
| def __init__(self): |
| self.pipe = None |
| self.model_id = MODEL_IDS["depth_estimation"] |
|
|
| def initialize(self) -> None: |
| """ |
| WHAT: Loads the Depth-Anything model weights into memory. |
| WHY: We delay loading until this exact stage in the pipeline to conserve VRAM. |
| If we loaded it at application startup alongside SDXL, we would crash immediately. |
| HOW: Uses `transformers.pipeline` for the 'depth-estimation' task. We map the global |
| config.DEVICE string to ensure it runs on the GPU if available. |
| Falls back to Mock Mode if weights are missing or fails to load. |
| """ |
| if MOCK_INFERENCE: |
| print("[DepthEstimator] Running in MOCK mode. Bypassing depth model load.") |
| return |
|
|
| try: |
| self.pipe = pipeline( |
| task="depth-estimation", |
| model=self.model_id, |
| device=DEVICE |
| ) |
| except Exception as e: |
| print(f"[DepthEstimator Warning] Failed to load local weights: {e}. Degrading gracefully to MOCK fallback.") |
| self.pipe = None |
|
|
| def generate(self, **kwargs) -> Image.Image: |
| """ |
| WHAT: Analyzes the provided 2D image and calculates the distance of objects from the camera. |
| WHY: To create the 3D geometry asset required by the upcoming video generation stage. |
| HOW: Extracts the 'image' argument passed from the orchestration layer (pipeline.py), |
| runs it through the depth pipeline, and extracts the resulting PIL Image. |
| """ |
| |
| input_image = kwargs.get("image") |
| |
| if not input_image: |
| raise ValueError("DepthEstimator requires a valid PIL Image passed as 'image' in kwargs.") |
| if not isinstance(input_image, Image.Image): |
| raise ValueError("The provided 'image' must be a PIL Image object.") |
|
|
| if MOCK_INFERENCE or self.pipe is None: |
| print("[DepthEstimator] Generating mock depth map image...") |
| w, h = input_image.size |
| |
| depth_img = Image.new("L", (w, h)) |
| for y in range(h): |
| |
| val = int((y / h) * 255) |
| |
| depth_img.paste(val, (0, y, w, y + 1)) |
| return depth_img |
|
|
| try: |
| |
| |
| result = self.pipe(input_image) |
| |
| |
| depth_map = result["depth"] |
| |
| return depth_map |
| except Exception as e: |
| raise RuntimeError(f"Depth estimation failed: {str(e)}") from e |
|
|
| def cleanup(self) -> None: |
| """ |
| WHAT: Completely removes the depth model from memory and clears the hardware cache. |
| WHY: VRAM MANAGEMENT IS CRITICAL. Even though Depth-Anything-Small is lighter than SDXL, |
| it still consumes precious gigabytes of VRAM. Because the pipeline runs sequentially, |
| this model is no longer needed once the depth map is created. By deleting it now, |
| we ensure the GPU is completely empty and ready to load the heavy Video/Audio models next. |
| HOW: We delete the Python reference to the pipeline, force Python's garbage collector to run, |
| and strictly call `torch.cuda.empty_cache()` to free the allocated blocks on the GPU. |
| """ |
| if MOCK_INFERENCE: |
| return |
|
|
| if self.pipe is not None: |
| |
| del self.pipe |
| self.pipe = None |
| |
| |
| gc.collect() |
| |
| |
| if DEVICE == "cuda": |
| torch.cuda.empty_cache() |