| import io |
| import os |
| import tempfile |
| from fastapi import FastAPI, File, UploadFile, HTTPException, Query |
| from PIL import Image |
| import pytesseract |
| import pypdfium2 as pdfium |
|
|
| app = FastAPI(title="Scholarform OCR Service") |
|
|
| @app.get("/") |
| def root(): |
| return {"ok": True, "service": "ocr"} |
|
|
| @app.get("/health") |
| def health(): |
| return {"ok": True, "service": "ocr"} |
|
|
| def ocr_image_bytes(data: bytes, lang: str) -> str: |
| image = Image.open(io.BytesIO(data)).convert("RGB") |
| return pytesseract.image_to_string(image, lang=lang) |
|
|
| def ocr_pdf_file(path: str, lang: str, max_pages: int) -> str: |
| pdf = pdfium.PdfDocument(path) |
| pages = min(len(pdf), max_pages) |
| texts = [] |
| for i in range(pages): |
| page = pdf[i] |
| pil_img = page.render(scale=2).to_pil() |
| texts.append(pytesseract.image_to_string(pil_img, lang=lang)) |
| return "\n".join(texts) |
|
|
| @app.post("/ocr") |
| async def ocr( |
| file: UploadFile = File(...), |
| lang: str = Query("eng"), |
| max_pages: int = Query(10, ge=1, le=200), |
| ): |
| name = (file.filename or "").lower() |
| content = await file.read() |
|
|
| if not content: |
| raise HTTPException(status_code=400, detail="Empty file") |
|
|
| try: |
| if name.endswith(".pdf"): |
| with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp: |
| tmp.write(content) |
| path = tmp.name |
| try: |
| text = ocr_pdf_file(path, lang=lang, max_pages=max_pages) |
| finally: |
| try: |
| os.remove(path) |
| except Exception: |
| pass |
| return {"ok": True, "type": "pdf", "text": text} |
|
|
| if name.endswith((".png", ".jpg", ".jpeg", ".webp", ".bmp", ".tiff")): |
| text = ocr_image_bytes(content, lang=lang) |
| return {"ok": True, "type": "image", "text": text} |
|
|
| raise HTTPException(status_code=400, detail="Supported: PDF or image") |
| except HTTPException: |
| raise |
| except Exception as e: |
| raise HTTPException(status_code=500, detail=f"OCR failed: {e}") |
|
|