scan, split, OCR, prepare for LLM
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

paddle_engine.py 2.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. from __future__ import annotations
  2. import functools
  3. import importlib.resources
  4. from typing import Any
  5. import cv2
  6. import yaml
  7. from paddlex import create_pipeline
  8. from src.models import OcrPageResult, ParsedBlock
  9. def _load_config() -> dict[str, Any]:
  10. ref = importlib.resources.files("paddlex") / "configs" / "pipelines" / "PP-StructureV3.yaml"
  11. with importlib.resources.as_file(ref) as path, open(path) as f:
  12. config: dict[str, Any] = yaml.safe_load(f)
  13. config["SubPipelines"]["GeneralOCR"]["SubModules"]["TextRecognition"]["model_name"] = (
  14. "eslav_PP-OCRv5_mobile_rec"
  15. )
  16. table_ocr = config["SubPipelines"]["TableRecognition"]["SubPipelines"]["GeneralOCR"]
  17. table_ocr["SubModules"]["TextRecognition"]["model_name"] = "eslav_PP-OCRv5_mobile_rec"
  18. return config
  19. class OcrEngine:
  20. def __init__(self) -> None:
  21. self._config: dict[str, Any] = _load_config()
  22. self._pipeline: Any = None
  23. def process(self, image_path: str) -> OcrPageResult:
  24. image = cv2.imread(image_path)
  25. if image is None:
  26. raise FileNotFoundError(f"Не удалось загрузить изображение: {image_path}")
  27. h, w = image.shape[:2]
  28. if self._pipeline is None:
  29. self._pipeline = create_pipeline(
  30. config=self._config,
  31. use_doc_orientation_classify=False,
  32. use_doc_unwarping=False,
  33. )
  34. raw_results = list(self._pipeline.predict(image))
  35. blocks, raw_json = self._parse_results(raw_results)
  36. return OcrPageResult(blocks=blocks, raw_json=raw_json, width=w, height=h)
  37. @staticmethod
  38. def _parse_results(raw_results: list[Any]) -> tuple[list[ParsedBlock], dict[str, Any]]:
  39. if not raw_results:
  40. return [], {}
  41. raw_json: dict[str, Any] = raw_results[0].json["res"]
  42. blocks: list[ParsedBlock] = []
  43. for item in raw_json.get("parsing_res_list", []):
  44. text = item.get("block_content", "")
  45. label = item.get("block_label", "text")
  46. bbox = item.get("block_bbox", [0, 0, 0, 0])
  47. if not text.strip():
  48. continue
  49. blocks.append(ParsedBlock(
  50. label=label, content=text,
  51. bbox=(int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3])),
  52. ))
  53. return blocks, raw_json
  54. @functools.lru_cache(maxsize=1)
  55. def _get_engine() -> OcrEngine:
  56. return OcrEngine()
  57. def ocr_image(image_path: str) -> OcrPageResult:
  58. return _get_engine().process(image_path)