""" ============================================================= 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 @property 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 # ============================================================= @app.get("/") 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" } } @app.get("/health") async def health(): return {"status": "ok", "modelo": "EasyOCR"} # Servir index.html si existe @app.get("/app") async def serve_app(): if os.path.exists("index.html"): return FileResponse("index.html") return {"error": "index.html no encontrado"} @app.post("/recognize", response_model=OCRResponse) 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)