Spaces:
Paused
Paused
Fix: use @spaces.GPU for ZeroGPU - load models on CPU, move to GPU per-request
Browse files
app.py
CHANGED
|
@@ -2,43 +2,36 @@
|
|
| 2 |
|
| 3 |
import json
|
| 4 |
import tempfile
|
| 5 |
-
from pathlib import Path
|
| 6 |
|
| 7 |
import cv2
|
| 8 |
import gradio as gr
|
| 9 |
import numpy as np
|
|
|
|
| 10 |
import torch
|
| 11 |
from huggingface_hub import hf_hub_download
|
| 12 |
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
|
| 13 |
from ultralytics import YOLO
|
| 14 |
|
| 15 |
-
# --- Model loading (
|
| 16 |
|
| 17 |
YOLO_REPO = "prasanthmj/yolov8-braille"
|
| 18 |
BYT5_REPO = "prasanthmj/braille-byt5-v3"
|
| 19 |
|
| 20 |
-
|
| 21 |
-
"""Download and load both models."""
|
| 22 |
-
# YOLOv8 braille detector
|
| 23 |
-
weights_path = hf_hub_download(YOLO_REPO, "yolov8_braille.pt")
|
| 24 |
-
braille_map_path = hf_hub_download(YOLO_REPO, "braille_map.json")
|
| 25 |
-
|
| 26 |
-
yolo_model = YOLO(weights_path)
|
| 27 |
-
with open(braille_map_path) as f:
|
| 28 |
-
dot_to_unicode = json.load(f)
|
| 29 |
-
|
| 30 |
-
# ByT5 Grade 2 interpreter
|
| 31 |
-
tokenizer = AutoTokenizer.from_pretrained(BYT5_REPO)
|
| 32 |
-
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 33 |
-
byt5_model = AutoModelForSeq2SeqLM.from_pretrained(BYT5_REPO).to(device)
|
| 34 |
-
byt5_model.eval()
|
| 35 |
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
-
print("
|
| 40 |
-
yolo_model, dot_to_unicode, tokenizer, byt5_model, device = load_models()
|
| 41 |
-
print(f"Models loaded. Device: {device}")
|
| 42 |
|
| 43 |
# --- CLAHE Preprocessing ---
|
| 44 |
|
|
@@ -102,30 +95,9 @@ def detect_braille(image_path: str, confidence: float = 0.15) -> list[list[dict]
|
|
| 102 |
|
| 103 |
return lines
|
| 104 |
|
| 105 |
-
# ---
|
| 106 |
-
|
| 107 |
-
def interpret_braille(braille_lines: list[str]) -> list[str]:
|
| 108 |
-
"""Translate braille Unicode lines to English using ByT5."""
|
| 109 |
-
results = []
|
| 110 |
-
for line in braille_lines:
|
| 111 |
-
if not line.strip():
|
| 112 |
-
results.append("")
|
| 113 |
-
continue
|
| 114 |
-
|
| 115 |
-
input_text = f"translate Braille to English: {line}"
|
| 116 |
-
inputs = tokenizer(input_text, return_tensors="pt", max_length=1024, truncation=True)
|
| 117 |
-
inputs = {k: v.to(device) for k, v in inputs.items()}
|
| 118 |
-
|
| 119 |
-
with torch.no_grad():
|
| 120 |
-
outputs = byt5_model.generate(**inputs, max_length=512)
|
| 121 |
-
|
| 122 |
-
decoded = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 123 |
-
results.append(decoded)
|
| 124 |
-
|
| 125 |
-
return results
|
| 126 |
-
|
| 127 |
-
# --- Main pipeline ---
|
| 128 |
|
|
|
|
| 129 |
def transcribe(image) -> str:
|
| 130 |
"""Full pipeline: image -> detection -> interpretation -> English text."""
|
| 131 |
if image is None:
|
|
@@ -155,8 +127,25 @@ def transcribe(image) -> str:
|
|
| 155 |
total_cells = sum(len(line) for line in lines)
|
| 156 |
avg_conf = np.mean([cell["confidence"] for line in lines for cell in line])
|
| 157 |
|
| 158 |
-
# Stage 2: Interpret with ByT5
|
| 159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
|
| 161 |
# Format output
|
| 162 |
braille_text = "\n".join(braille_lines)
|
|
|
|
| 2 |
|
| 3 |
import json
|
| 4 |
import tempfile
|
|
|
|
| 5 |
|
| 6 |
import cv2
|
| 7 |
import gradio as gr
|
| 8 |
import numpy as np
|
| 9 |
+
import spaces
|
| 10 |
import torch
|
| 11 |
from huggingface_hub import hf_hub_download
|
| 12 |
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
|
| 13 |
from ultralytics import YOLO
|
| 14 |
|
| 15 |
+
# --- Model loading (on CPU at startup, GPU allocated per-request) ---
|
| 16 |
|
| 17 |
YOLO_REPO = "prasanthmj/yolov8-braille"
|
| 18 |
BYT5_REPO = "prasanthmj/braille-byt5-v3"
|
| 19 |
|
| 20 |
+
print("Loading models...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
+
# YOLOv8 braille detector
|
| 23 |
+
weights_path = hf_hub_download(YOLO_REPO, "yolov8_braille.pt")
|
| 24 |
+
braille_map_path = hf_hub_download(YOLO_REPO, "braille_map.json")
|
| 25 |
+
yolo_model = YOLO(weights_path)
|
| 26 |
+
with open(braille_map_path) as f:
|
| 27 |
+
dot_to_unicode = json.load(f)
|
| 28 |
|
| 29 |
+
# ByT5 Grade 2 interpreter (load on CPU, moved to GPU per-request)
|
| 30 |
+
tokenizer = AutoTokenizer.from_pretrained(BYT5_REPO)
|
| 31 |
+
byt5_model = AutoModelForSeq2SeqLM.from_pretrained(BYT5_REPO)
|
| 32 |
+
byt5_model.eval()
|
| 33 |
|
| 34 |
+
print("Models loaded (CPU). GPU allocated per-request via ZeroGPU.")
|
|
|
|
|
|
|
| 35 |
|
| 36 |
# --- CLAHE Preprocessing ---
|
| 37 |
|
|
|
|
| 95 |
|
| 96 |
return lines
|
| 97 |
|
| 98 |
+
# --- Main pipeline (GPU allocated here) ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
+
@spaces.GPU
|
| 101 |
def transcribe(image) -> str:
|
| 102 |
"""Full pipeline: image -> detection -> interpretation -> English text."""
|
| 103 |
if image is None:
|
|
|
|
| 127 |
total_cells = sum(len(line) for line in lines)
|
| 128 |
avg_conf = np.mean([cell["confidence"] for line in lines for cell in line])
|
| 129 |
|
| 130 |
+
# Stage 2: Interpret each line with ByT5 on GPU
|
| 131 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 132 |
+
byt5_model.to(device)
|
| 133 |
+
|
| 134 |
+
english_lines = []
|
| 135 |
+
for line in braille_lines:
|
| 136 |
+
if not line.strip():
|
| 137 |
+
english_lines.append("")
|
| 138 |
+
continue
|
| 139 |
+
|
| 140 |
+
input_text = f"translate Braille to English: {line}"
|
| 141 |
+
inputs = tokenizer(input_text, return_tensors="pt", max_length=1024, truncation=True)
|
| 142 |
+
inputs = {k: v.to(device) for k, v in inputs.items()}
|
| 143 |
+
|
| 144 |
+
with torch.no_grad():
|
| 145 |
+
outputs = byt5_model.generate(**inputs, max_length=512)
|
| 146 |
+
|
| 147 |
+
decoded = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 148 |
+
english_lines.append(decoded)
|
| 149 |
|
| 150 |
# Format output
|
| 151 |
braille_text = "\n".join(braille_lines)
|