Spaces:
Sleeping
Sleeping
| """ | |
| ============================================================= | |
| EasyOCR API - Reconocimiento de Texto Rápido | |
| ============================================================= | |
| API REST para reconocer texto usando EasyOCR | |
| Optimizado para velocidad - Ideal para páginas completas | |
| ============================================================= | |
| """ | |
| import io | |
| import easyocr | |
| import numpy as np | |
| from fastapi import FastAPI, UploadFile, File, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import FileResponse | |
| from pydantic import BaseModel | |
| from PIL import Image | |
| from typing import Optional, List | |
| import os | |
| # ============================================================= | |
| # CONFIGURACION | |
| # ============================================================= | |
| app = FastAPI( | |
| title="EasyOCR API", | |
| description="Reconocimiento de texto rápido con EasyOCR", | |
| version="1.0.0", | |
| docs_url="/docs", | |
| redoc_url="/redoc" | |
| ) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # ============================================================= | |
| # MODELOS DE RESPUESTA | |
| # ============================================================= | |
| class LineDetail(BaseModel): | |
| texto: str | |
| confianza: float | |
| class OCRResponse(BaseModel): | |
| exito: bool | |
| texto: str | |
| confianza: float | |
| lineas: List[LineDetail] | |
| mensaje: Optional[str] = None | |
| # ============================================================= | |
| # GESTOR DE EASYOCR | |
| # ============================================================= | |
| class EasyOCRManager: | |
| def __init__(self): | |
| self._reader = None | |
| def reader(self): | |
| if self._reader is None: | |
| print("=" * 50) | |
| print("Cargando EasyOCR (español + inglés)...") | |
| print("=" * 50) | |
| self._reader = easyocr.Reader( | |
| ['es', 'en'], | |
| gpu=False, | |
| verbose=False | |
| ) | |
| print("EasyOCR listo!") | |
| return self._reader | |
| def recognize(self, image: Image.Image) -> tuple: | |
| """Reconoce texto en una imagen""" | |
| # Convertir a RGB | |
| if image.mode != 'RGB': | |
| image = image.convert('RGB') | |
| # Redimensionar si es muy grande | |
| max_size = 2048 | |
| 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) | |
| # Convertir a numpy array | |
| image_np = np.array(image) | |
| # Reconocer | |
| resultados = self.reader.readtext(image_np) | |
| if not resultados: | |
| return "", 0.0, [] | |
| textos = [] | |
| confianzas = [] | |
| lineas = [] | |
| for bbox, texto, conf in resultados: | |
| texto = texto.strip() | |
| if texto and conf >= 0.3: # Filtrar baja confianza | |
| textos.append(texto) | |
| confianzas.append(conf) | |
| lineas.append({ | |
| "texto": texto, | |
| "confianza": round(conf, 3) | |
| }) | |
| texto_completo = ' '.join(textos) | |
| confianza_promedio = sum(confianzas) / len(confianzas) if confianzas else 0.0 | |
| return texto_completo, confianza_promedio, lineas | |
| # Instancia global | |
| ocr = EasyOCRManager() | |
| # ============================================================= | |
| # ENDPOINTS | |
| # ============================================================= | |
| async def inicio(): | |
| return { | |
| "nombre": "EasyOCR API", | |
| "version": "1.0.0", | |
| "descripcion": "Reconocimiento de texto rápido", | |
| "modelo": "EasyOCR (español + inglés)", | |
| "velocidad": "~2-5 segundos por imagen", | |
| "endpoints": { | |
| "GET /": "Esta información", | |
| "GET /health": "Estado del servicio", | |
| "GET /docs": "Documentación Swagger", | |
| "POST /recognize": "Reconocer texto en imagen" | |
| } | |
| } | |
| async def health(): | |
| return {"status": "ok", "modelo": "EasyOCR"} | |
| # Servir index.html si existe | |
| async def serve_app(): | |
| if os.path.exists("index.html"): | |
| return FileResponse("index.html") | |
| return {"error": "index.html no encontrado"} | |
| async def recognize(file: UploadFile = File(...)): | |
| """ | |
| Reconoce texto en una imagen usando EasyOCR. | |
| Rápido: ~2-5 segundos por imagen | |
| Soporta: Español e Inglés | |
| """ | |
| if not file.content_type or not file.content_type.startswith('image/'): | |
| raise HTTPException(status_code=400, detail="Debe ser una imagen") | |
| try: | |
| contents = await file.read() | |
| image = Image.open(io.BytesIO(contents)) | |
| texto, confianza, lineas = ocr.recognize(image) | |
| 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)} elementos" | |
| ) | |
| else: | |
| return OCRResponse( | |
| exito=False, | |
| texto="", | |
| confianza=0.0, | |
| lineas=[], | |
| mensaje="No se detectó texto" | |
| ) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # ============================================================= | |
| # MAIN | |
| # ============================================================= | |
| if __name__ == "__main__": | |
| import uvicorn | |
| print("\n" + "=" * 50) | |
| print(" EasyOCR API") | |
| print(" http://localhost:7860") | |
| print(" Docs: http://localhost:7860/docs") | |
| print("=" * 50 + "\n") | |
| uvicorn.run(app, host="0.0.0.0", port=7860) | |