Spaces:
Running
Running
| """Gradio Web UI for V-JEPA / I-JEPA mask prediction demo. | |
| Demonstrates the Joint Embedding Predictive Architecture (JEPA) concept: | |
| - Upload an image | |
| - Randomly mask a portion of its patches | |
| - Visualize how the model attends to the masked regions | |
| """ | |
| import logging | |
| import os | |
| import gradio as gr | |
| from PIL import Image | |
| from src.predictor import Predictor | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", | |
| ) | |
| logger = logging.getLogger(__name__) | |
| TITLE = "V-JEPA / I-JEPA - Joint Embedding Predictive Architecture Demo" | |
| DESCRIPTION = """ | |
| ## What is JEPA? | |
| **Joint Embedding Predictive Architecture (JEPA)** is a self-supervised learning | |
| framework by Meta AI that learns image/video representations by predicting abstract | |
| patch representations rather than pixel values. | |
| ### How this demo works | |
| 1. **Upload** an image | |
| 2. **Adjust** the mask ratio (fraction of patches to hide) | |
| 3. The model (I-JEPA ViT-H/14) processes the image: | |
| - **Left**: Shows which patches are masked (grey regions with red borders) | |
| - **Right**: Attention rollout heatmap showing how the encoder distributes | |
| attention across patches. White borders mark the masked regions. | |
| The attention map reveals the model's learned spatial reasoning: how it relates | |
| visible context to predict missing regions. | |
| **Model**: `facebook/ijepa_vith14_1k` (ViT-Huge, 632M params, ImageNet-1K pretrained) | |
| """ | |
| def run_prediction( | |
| image: Image.Image | None, | |
| mask_ratio: float, | |
| ) -> tuple[Image.Image | None, Image.Image | None]: | |
| """Execute mask prediction and return visualization. | |
| Args: | |
| image: Uploaded PIL image (or None if not provided). | |
| mask_ratio: Fraction of patches to mask. | |
| Returns: | |
| Tuple of (masked_image, attention_map), or (None, None) on error. | |
| """ | |
| if image is None: | |
| gr.Warning("Please upload an image first.") | |
| return None, None | |
| predictor = Predictor() | |
| if not predictor.is_initialized: | |
| gr.Info("Loading model for the first time, this may take a moment...") | |
| predictor.initialize() | |
| try: | |
| masked_image, attention_map = predictor.predict(image, mask_ratio) | |
| except Exception as e: | |
| logger.exception("Prediction failed") | |
| gr.Warning(f"Prediction failed: {e}") | |
| return None, None | |
| return masked_image, attention_map | |
| def build_ui() -> gr.Blocks: | |
| """Construct the Gradio Blocks interface. | |
| Returns: | |
| Configured Gradio Blocks app. | |
| """ | |
| with gr.Blocks( | |
| title=TITLE, | |
| theme=gr.themes.Soft(), | |
| ) as demo: | |
| gr.Markdown(f"# {TITLE}") | |
| gr.Markdown(DESCRIPTION) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| input_image = gr.Image( | |
| label="Upload Image", | |
| type="pil", | |
| height=300, | |
| ) | |
| mask_slider = gr.Slider( | |
| minimum=0.1, | |
| maximum=0.9, | |
| value=0.5, | |
| step=0.05, | |
| label="Mask Ratio", | |
| info="Fraction of image patches to mask", | |
| ) | |
| run_button = gr.Button("Run Prediction", variant="primary") | |
| with gr.Column(scale=2): | |
| with gr.Row(): | |
| masked_output = gr.Image( | |
| label="Masked Input (grey = masked patches)", | |
| height=300, | |
| ) | |
| attention_output = gr.Image( | |
| label="Attention Rollout Map", | |
| height=300, | |
| ) | |
| run_button.click( | |
| fn=run_prediction, | |
| inputs=[input_image, mask_slider], | |
| outputs=[masked_output, attention_output], | |
| ) | |
| gr.Markdown( | |
| "---\n" | |
| "**References**: " | |
| "[I-JEPA (Assran et al., 2023)](https://arxiv.org/abs/2301.08243) | " | |
| "[V-JEPA (Bardes et al., 2024)](https://arxiv.org/abs/2404.08471) | " | |
| "[V-JEPA 2 (Meta, 2025)](https://arxiv.org/abs/2506.09985)" | |
| ) | |
| return demo | |
| # Module-level demo for HF Spaces | |
| demo = build_ui() | |
| if __name__ == "__main__": | |
| port = int(os.environ.get("GRADIO_SERVER_PORT", "7860")) | |
| demo.launch(server_port=port) | |