surya-ocr / app.py
roque99cheroA's picture
Upload 4 files
1732887 verified
Raw
History Blame Contribute Delete
8.21 kB
"""
=============================================================
SURYA OCR API - Reconocimiento de Manuscritos
=============================================================
API REST para reconocer texto manuscrito usando Surya OCR
Listo para desplegar en Hugging Face Spaces
=============================================================
"""
import io
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from PIL import Image
from typing import Optional, List
# Surya OCR
from surya.detection import DetectionPredictor
from surya.recognition import RecognitionPredictor, FoundationPredictor
# =============================================================
# CONFIGURACION DE LA API
# =============================================================
app = FastAPI(
title="Surya OCR API",
description="Reconocimiento de texto manuscrito con Surya OCR - Modelo de alta precision",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc"
)
# Permitir CORS (acceso desde cualquier origen)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# =============================================================
# MODELOS DE RESPUESTA
# =============================================================
class LineDetail(BaseModel):
"""Detalle de cada linea reconocida"""
texto: str
confianza: float
class OCRResponse(BaseModel):
"""Respuesta del reconocimiento OCR"""
exito: bool
texto: str
confianza: float
lineas: List[LineDetail]
mensaje: Optional[str] = None
# =============================================================
# GESTOR DE MODELOS SURYA (Carga diferida)
# =============================================================
class SuryaOCR:
"""
Gestor de modelos Surya con carga diferida.
Los modelos se cargan solo cuando se necesitan.
"""
def __init__(self):
self._detector = None
self._recognizer = None
self._foundation = None
self._loaded = False
def _load_models(self):
"""Carga los modelos de Surya"""
if not self._loaded:
print("=" * 50)
print("Cargando modelos Surya OCR...")
print("=" * 50)
print("[1/3] Cargando detector de texto...")
self._detector = DetectionPredictor()
print("[2/3] Cargando modelo foundation...")
self._foundation = FoundationPredictor()
print("[3/3] Cargando reconocedor de texto...")
self._recognizer = RecognitionPredictor(self._foundation)
self._loaded = True
print("=" * 50)
print("Modelos cargados correctamente!")
print("=" * 50)
@property
def detector(self):
self._load_models()
return self._detector
@property
def recognizer(self):
self._load_models()
return self._recognizer
def recognize(self, image: Image.Image) -> tuple:
"""
Reconoce texto en una imagen.
Args:
image: Imagen PIL
Returns:
tuple: (texto_completo, confianza_promedio, lista_lineas)
"""
# Asegurar RGB
if image.mode != 'RGB':
image = image.convert('RGB')
# Redimensionar si es muy grande (max 1024px)
max_size = 1024
if max(image.size) > max_size:
ratio = max_size / max(image.size)
new_size = (int(image.size[0] * ratio), int(image.size[1] * ratio))
image = image.resize(new_size, Image.Resampling.LANCZOS)
# Procesar con Surya
images = [image]
predictions = self.recognizer(
images,
det_predictor=self.detector
)
# Extraer resultados
if not predictions or not predictions[0].text_lines:
return "", 0.0, []
textos = []
confianzas = []
lineas = []
for line in predictions[0].text_lines:
texto = line.text.strip()
confianza = line.confidence
# FILTROS para eliminar ruido:
# 1. Solo texto con confianza >= 50%
if confianza < 0.50:
continue
# 2. Ignorar texto vacío
if not texto:
continue
# 3. Ignorar patrones repetitivos (ruido común)
if "the state" in texto.lower():
continue
if "and the" in texto.lower() and len(texto) > 50:
continue
# 4. Ignorar líneas muy cortas (1-2 caracteres) con baja confianza
if len(texto) <= 2 and confianza < 0.80:
continue
textos.append(texto)
confianzas.append(confianza)
lineas.append({
"texto": texto,
"confianza": round(confianza, 3)
})
texto_completo = ' '.join(textos)
confianza_promedio = sum(confianzas) / len(confianzas) if confianzas else 0.0
return texto_completo, confianza_promedio, lineas
# Instancia global
surya = SuryaOCR()
# =============================================================
# ENDPOINTS DE LA API
# =============================================================
@app.get("/")
async def inicio():
"""
Pagina de inicio con informacion de la API
"""
return {
"nombre": "Surya OCR API",
"version": "1.0.0",
"descripcion": "API para reconocimiento de texto manuscrito",
"modelo": "Surya OCR (2024)",
"endpoints": {
"GET /": "Esta informacion",
"GET /health": "Estado del servicio",
"GET /docs": "Documentacion interactiva (Swagger)",
"POST /recognize": "Reconocer texto en imagen"
},
"uso": "Sube una imagen a /recognize para obtener el texto"
}
@app.get("/health")
async def health():
"""
Verifica el estado del servicio
"""
return {
"status": "ok",
"modelo": "Surya OCR",
"listo": True
}
@app.post("/recognize", response_model=OCRResponse)
async def recognize(file: UploadFile = File(...)):
"""
Reconoce texto manuscrito en una imagen.
Sube una imagen (JPG, PNG, etc.) y obtendras el texto reconocido.
- **file**: Archivo de imagen a procesar
Retorna:
- **exito**: Si el reconocimiento fue exitoso
- **texto**: Texto completo reconocido
- **confianza**: Nivel de confianza (0-1)
- **lineas**: Detalle de cada linea detectada
"""
# Validar que sea imagen
if not file.content_type or not file.content_type.startswith('image/'):
raise HTTPException(
status_code=400,
detail="El archivo debe ser una imagen (JPG, PNG, etc.)"
)
try:
# Leer imagen
contents = await file.read()
image = Image.open(io.BytesIO(contents))
# Reconocer texto
texto, confianza, lineas = surya.recognize(image)
# Respuesta
if texto:
return OCRResponse(
exito=True,
texto=texto,
confianza=round(confianza, 3),
lineas=[LineDetail(**l) for l in lineas],
mensaje=f"Se reconocieron {len(lineas)} lineas de texto"
)
else:
return OCRResponse(
exito=False,
texto="",
confianza=0.0,
lineas=[],
mensaje="No se detecto texto en la imagen"
)
except Exception as e:
raise HTTPException(
status_code=500,
detail=f"Error procesando imagen: {str(e)}"
)
# =============================================================
# PUNTO DE ENTRADA
# =============================================================
if __name__ == "__main__":
import uvicorn
print("\n" + "=" * 50)
print(" SURYA OCR API")
print(" http://localhost:7860")
print(" Docs: http://localhost:7860/docs")
print("=" * 50 + "\n")
uvicorn.run(app, host="0.0.0.0", port=7860)