461bf6fd40
CI / lint (3.11) (push) Has been cancelled
CI / lint (3.12) (push) Has been cancelled
CI / lint (3.13) (push) Has been cancelled
CI / shellcheck (push) Has been cancelled
CI / shfmt (push) Has been cancelled
CI / setup (3.11) (push) Has been cancelled
CI / setup (3.12) (push) Has been cancelled
CI / setup (3.13) (push) Has been cancelled
CI / check-licenses (3.12) (push) Has been cancelled
CI / test_unit (3.11) (push) Has been cancelled
CI / test_unit (3.12) (push) Has been cancelled
CI / test_unit (3.13) (push) Has been cancelled
CI / test_unit_no_extras (3.11) (push) Has been cancelled
CI / test_unit_no_extras (3.12) (push) Has been cancelled
CI / test_json_to_html (3.12) (push) Has been cancelled
CI / test_unit_no_extras (3.13) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.12, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.11, --extra xlsx) (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.12, --extra xlsx) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.11, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (csv, 3.13, --extra csv) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.11, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.12, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (docx, 3.13, --extra docx) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.11, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.12, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (markdown, 3.13, --extra md) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.11, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.12, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (odt, 3.13, --extra odt) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.11, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.12, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pdf-image, 3.13, --extra pdf --extra image --extra paddleocr) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.11, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.12, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pptx, 3.13, --extra pptx) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.11, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.12, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
CI / test_unit_dependency_extras (pypandoc, 3.13, --extra epub --extra org --extra rtf --extra rst) (push) Has been cancelled
Build And Push Docker Image / set-short-sha (push) Has been cancelled
Partition Benchmark / setup (push) Has been cancelled
Partition Benchmark / Measure and compare partition() runtime (push) Has been cancelled
CI / test_unit_dependency_extras (xlsx, 3.13, --extra xlsx) (push) Has been cancelled
CI / test_ingest_src (3.12) (push) Has been cancelled
CI / test_json_to_markdown (3.12) (push) Has been cancelled
CI / changelog (push) Has been cancelled
CI / test_dockerfile (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Build And Push Docker Image / build-images (linux/amd64, opensource-linux-8core) (push) Has been cancelled
Build And Push Docker Image / build-images (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Build And Push Docker Image / publish-images (push) Has been cancelled
147 lines
5.7 KiB
Python
147 lines
5.7 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
import numpy as np
|
|
from PIL import Image as PILImage
|
|
|
|
from unstructured.documents.elements import ElementType
|
|
from unstructured.logger import logger, trace_logger
|
|
from unstructured.partition.utils.constants import Source
|
|
from unstructured.partition.utils.ocr_models.ocr_interface import OCRAgent
|
|
from unstructured.utils import requires_dependencies
|
|
|
|
if TYPE_CHECKING:
|
|
from unstructured_inference.inference.elements import TextRegion, TextRegions
|
|
from unstructured_inference.inference.layoutelement import LayoutElements
|
|
|
|
|
|
class OCRAgentPaddle(OCRAgent):
|
|
"""OCR service implementation for PaddleOCR."""
|
|
|
|
def __init__(self, language: str = "en"):
|
|
self.agent = self.load_agent(language)
|
|
|
|
def load_agent(self, language: str):
|
|
"""Loads the PaddleOCR agent as a global variable to ensure that we only load it once."""
|
|
|
|
import paddle
|
|
from unstructured_paddleocr import PaddleOCR
|
|
|
|
# Disable signal handlers at C++ level upon failing
|
|
# ref: https://www.paddlepaddle.org.cn/documentation/docs/en/api/paddle/
|
|
# disable_signal_handler_en.html#disable-signal-handler
|
|
paddle.disable_signal_handler()
|
|
# Use paddlepaddle-gpu if there is gpu device available
|
|
gpu_available = paddle.device.cuda.device_count() > 0
|
|
if gpu_available:
|
|
logger.info(f"Loading paddle with GPU on language={language}...")
|
|
else:
|
|
logger.info(f"Loading paddle with CPU on language={language}...")
|
|
try:
|
|
# Enable MKL-DNN for paddle to speed up OCR if OS supports it
|
|
# ref: https://paddle-inference.readthedocs.io/en/master/
|
|
# api_reference/cxx_api_doc/Config/CPUConfig.html
|
|
paddle_ocr = PaddleOCR(
|
|
use_angle_cls=True,
|
|
use_gpu=gpu_available,
|
|
lang=language,
|
|
enable_mkldnn=True,
|
|
show_log=False,
|
|
rec_batch_num=1,
|
|
)
|
|
except AttributeError:
|
|
paddle_ocr = PaddleOCR(
|
|
use_angle_cls=True,
|
|
use_gpu=gpu_available,
|
|
lang=language,
|
|
enable_mkldnn=False,
|
|
show_log=False,
|
|
rec_batch_num=1,
|
|
)
|
|
return paddle_ocr
|
|
|
|
def get_text_from_image(self, image: PILImage.Image) -> str:
|
|
ocr_regions = self.get_layout_from_image(image)
|
|
return "\n\n".join(ocr_regions.texts)
|
|
|
|
def is_text_sorted(self):
|
|
return False
|
|
|
|
def get_layout_from_image(self, image: PILImage.Image) -> TextRegions:
|
|
"""Get the OCR regions from image as a list of text regions with paddle."""
|
|
|
|
trace_logger.detail("Processing entire page OCR with paddle...")
|
|
|
|
# TODO(yuming): pass in language parameter once we
|
|
# have the mapping for paddle lang code
|
|
# see CORE-2034
|
|
ocr_data = self.agent.ocr(np.array(image), cls=True)
|
|
ocr_regions = self.parse_data(ocr_data)
|
|
|
|
return ocr_regions
|
|
|
|
@requires_dependencies("unstructured_inference")
|
|
def get_layout_elements_from_image(self, image: PILImage.Image) -> LayoutElements:
|
|
ocr_regions = self.get_layout_from_image(image)
|
|
|
|
# NOTE(christine): For paddle, there is no difference in `ocr_layout` and `ocr_text` in
|
|
# terms of grouping because we get ocr_text from `ocr_layout, so the first two grouping
|
|
# and merging steps are not necessary.
|
|
return LayoutElements(
|
|
element_coords=ocr_regions.element_coords,
|
|
texts=ocr_regions.texts,
|
|
element_class_ids=np.zeros(ocr_regions.texts.shape),
|
|
element_class_id_map={0: ElementType.UNCATEGORIZED_TEXT},
|
|
)
|
|
|
|
@requires_dependencies("unstructured_inference")
|
|
def parse_data(self, ocr_data: list[Any]) -> TextRegions:
|
|
"""Parse the OCR result data to extract a list of TextRegion objects from paddle.
|
|
|
|
The function processes the OCR result dictionary, looking for bounding
|
|
box information and associated text to create instances of the TextRegion
|
|
class, which are then appended to a list.
|
|
|
|
Parameters:
|
|
- ocr_data (list): A list containing the OCR result data
|
|
|
|
Returns:
|
|
- TextRegions:
|
|
TextRegions object, containing data from all text regions in numpy arrays; each row
|
|
represents a detected text region within the OCR-ed image.
|
|
|
|
Note:
|
|
- An empty string or a None value for the 'text' key in the input
|
|
dictionary will result in its associated bounding box being ignored.
|
|
"""
|
|
|
|
from unstructured_inference.inference.elements import TextRegions
|
|
|
|
from unstructured.partition.pdf_image.inference_utils import build_text_region_from_coords
|
|
|
|
text_regions: list[TextRegion] = []
|
|
for idx in range(len(ocr_data)):
|
|
res = ocr_data[idx]
|
|
if not res:
|
|
continue
|
|
|
|
for line in res:
|
|
x1 = min([i[0] for i in line[0]])
|
|
y1 = min([i[1] for i in line[0]])
|
|
x2 = max([i[0] for i in line[0]])
|
|
y2 = max([i[1] for i in line[0]])
|
|
text = line[1][0]
|
|
if not text:
|
|
continue
|
|
cleaned_text = text.strip()
|
|
if cleaned_text:
|
|
text_region = build_text_region_from_coords(
|
|
x1, y1, x2, y2, text=cleaned_text, source=Source.OCR_PADDLE
|
|
)
|
|
text_regions.append(text_region)
|
|
|
|
# FIXME (yao): find out if paddle supports a vectorized output format so we can skip the
|
|
# step of parsing a list
|
|
return TextRegions.from_list(text_regions)
|