scan, split, OCR, prepare for LLM
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

surya_engine.py 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. from __future__ import annotations
  2. import logging
  3. import os
  4. import shutil
  5. from pathlib import Path
  6. from src.ocr.paddle_engine import OcrPageResult, ParsedBlock
  7. logger = logging.getLogger(__name__)
  8. _ENV_LOADED: bool = False
  9. def _load_env() -> None:
  10. global _ENV_LOADED
  11. if _ENV_LOADED:
  12. return
  13. env_path = Path(__file__).resolve().parent.parent.parent / ".env"
  14. if env_path.exists():
  15. with open(env_path) as f:
  16. for line in f:
  17. line = line.strip()
  18. if line and not line.startswith("#") and "=" in line:
  19. key, _, value = line.partition("=")
  20. key = key.strip()
  21. if key not in os.environ:
  22. os.environ[key] = value.strip().strip("\"'")
  23. _ENV_LOADED = True
  24. def _ensure_env() -> None:
  25. _load_env()
  26. os.environ.setdefault("SURYA_INFERENCE_BACKEND", "llamacpp")
  27. os.environ.setdefault("SURYA_GUIDED_LAYOUT", "false")
  28. if "LLAMA_CPP_BINARY" not in os.environ:
  29. binary = shutil.which("llama-server") or os.path.expanduser(
  30. "~/.local/bin/llama-server"
  31. )
  32. if Path(binary).exists():
  33. os.environ["LLAMA_CPP_BINARY"] = binary
  34. class SuryaEngine:
  35. """OCR engine using Surya 2 VLM via llama.cpp."""
  36. def __init__(self) -> None:
  37. _ensure_env()
  38. try:
  39. from surya.inference import SuryaInferenceManager
  40. from surya.recognition import RecognitionPredictor
  41. self._manager = SuryaInferenceManager()
  42. self._predictor = RecognitionPredictor(self._manager)
  43. except ImportError as e:
  44. msg = (
  45. "Surya engine not available. Install: pip install surya-ocr torch"
  46. )
  47. raise ImportError(msg) from e
  48. def process(self, image_path: str) -> OcrPageResult:
  49. from PIL import Image
  50. image = Image.open(image_path)
  51. results = self._predictor([image])
  52. blocks: list[ParsedBlock] = []
  53. raw_json: dict = {}
  54. if results:
  55. page = results[0]
  56. for blk in getattr(page, "blocks", []):
  57. html = getattr(blk, "html", "") or ""
  58. label = getattr(blk, "label", "text")
  59. bbox = getattr(blk, "bbox", [0, 0, 0, 0])
  60. confidence = float(getattr(blk, "confidence", 0.9))
  61. blocks.append(
  62. ParsedBlock(
  63. label=label,
  64. content=html,
  65. bbox=(int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3])),
  66. confidence=confidence,
  67. )
  68. )
  69. return OcrPageResult(
  70. blocks=blocks,
  71. raw_json=raw_json,
  72. width=image.width,
  73. height=image.height,
  74. )
  75. _surya_engine: SuryaEngine | None = None
  76. def surya_ocr_image(image_path: str) -> OcrPageResult:
  77. global _surya_engine
  78. if _surya_engine is None:
  79. _surya_engine = SuryaEngine()
  80. return _surya_engine.process(image_path)