Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:33:56 +08:00

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)