doc-page-extractor 0.2.4__cp310-cp310-macosx_15_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of doc-page-extractor might be problematic. Click here for more details.
- doc_page_extractor/__init__.py +16 -0
- doc_page_extractor/clipper.py +119 -0
- doc_page_extractor/downloader.py +16 -0
- doc_page_extractor/extractor.py +218 -0
- doc_page_extractor/latex.py +33 -0
- doc_page_extractor/layout_order.py +239 -0
- doc_page_extractor/layoutreader.py +126 -0
- doc_page_extractor/model.py +133 -0
- doc_page_extractor/ocr.py +196 -0
- doc_page_extractor/ocr_corrector.py +126 -0
- doc_page_extractor/onnxocr/__init__.py +1 -0
- doc_page_extractor/onnxocr/cls_postprocess.py +26 -0
- doc_page_extractor/onnxocr/db_postprocess.py +246 -0
- doc_page_extractor/onnxocr/imaug.py +32 -0
- doc_page_extractor/onnxocr/operators.py +187 -0
- doc_page_extractor/onnxocr/predict_base.py +57 -0
- doc_page_extractor/onnxocr/predict_cls.py +109 -0
- doc_page_extractor/onnxocr/predict_det.py +139 -0
- doc_page_extractor/onnxocr/predict_rec.py +344 -0
- doc_page_extractor/onnxocr/predict_system.py +97 -0
- doc_page_extractor/onnxocr/rec_postprocess.py +896 -0
- doc_page_extractor/onnxocr/utils.py +71 -0
- doc_page_extractor/overlap.py +167 -0
- doc_page_extractor/plot.py +93 -0
- doc_page_extractor/raw_optimizer.py +104 -0
- doc_page_extractor/rectangle.py +72 -0
- doc_page_extractor/rotation.py +158 -0
- doc_page_extractor/table.py +60 -0
- doc_page_extractor/types.py +68 -0
- doc_page_extractor/utils.py +32 -0
- doc_page_extractor-0.2.4.dist-info/LICENSE +661 -0
- doc_page_extractor-0.2.4.dist-info/METADATA +88 -0
- doc_page_extractor-0.2.4.dist-info/RECORD +34 -0
- doc_page_extractor-0.2.4.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
|
|
3
|
+
from typing import Literal, Any
|
|
4
|
+
from PIL.Image import Image
|
|
5
|
+
from .types import TableLayoutParsedFormat
|
|
6
|
+
from .model import Model
|
|
7
|
+
from .utils import expand_image
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
OutputFormat = Literal["latex", "markdown", "html"]
|
|
11
|
+
|
|
12
|
+
class Table:
|
|
13
|
+
def __init__(self, device: Literal["cpu", "cuda"], model: Model) -> None:
|
|
14
|
+
self._model: Model = model
|
|
15
|
+
self._table_model: Any | None = None
|
|
16
|
+
self._ban: bool = False
|
|
17
|
+
if device == "cpu":
|
|
18
|
+
self._ban = True
|
|
19
|
+
|
|
20
|
+
def predict(self, image: Image, format: TableLayoutParsedFormat) -> str | None:
|
|
21
|
+
if self._ban:
|
|
22
|
+
print("CUDA is not available. You cannot parse table from image.")
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
output_format: str
|
|
26
|
+
if format == TableLayoutParsedFormat.LATEX:
|
|
27
|
+
output_format = "latex"
|
|
28
|
+
elif format == TableLayoutParsedFormat.MARKDOWN:
|
|
29
|
+
output_format = "markdown"
|
|
30
|
+
elif format == TableLayoutParsedFormat.HTML:
|
|
31
|
+
output_format = "html"
|
|
32
|
+
else:
|
|
33
|
+
raise ValueError(f"Table format {format} is not supported.")
|
|
34
|
+
|
|
35
|
+
image = expand_image(image, 0.1)
|
|
36
|
+
model = self._get_model()
|
|
37
|
+
|
|
38
|
+
with torch.no_grad():
|
|
39
|
+
results = model([image], output_format=output_format)
|
|
40
|
+
|
|
41
|
+
if len(results) == 0:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
return results[0]
|
|
45
|
+
|
|
46
|
+
def _get_model(self) -> Any:
|
|
47
|
+
if self._table_model is None:
|
|
48
|
+
from .struct_eqtable import build_model
|
|
49
|
+
model_path = self._model.get_struct_eqtable_path()
|
|
50
|
+
table_model = build_model(
|
|
51
|
+
model_ckpt=str(model_path),
|
|
52
|
+
max_new_tokens=1024,
|
|
53
|
+
max_time=30,
|
|
54
|
+
lmdeploy=False,
|
|
55
|
+
flash_attn=True,
|
|
56
|
+
batch_size=1,
|
|
57
|
+
local_files_only=False,
|
|
58
|
+
)
|
|
59
|
+
self._table_model = table_model.cuda()
|
|
60
|
+
return self._table_model
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Literal
|
|
3
|
+
from enum import auto, Enum
|
|
4
|
+
from PIL.Image import Image
|
|
5
|
+
from .rectangle import Rectangle
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class OCRFragment:
|
|
10
|
+
order: int
|
|
11
|
+
text: str
|
|
12
|
+
rank: float
|
|
13
|
+
rect: Rectangle
|
|
14
|
+
|
|
15
|
+
class LayoutClass(Enum):
|
|
16
|
+
TITLE = 0
|
|
17
|
+
PLAIN_TEXT = 1
|
|
18
|
+
ABANDON = 2
|
|
19
|
+
FIGURE = 3
|
|
20
|
+
FIGURE_CAPTION = 4
|
|
21
|
+
TABLE = 5
|
|
22
|
+
TABLE_CAPTION = 6
|
|
23
|
+
TABLE_FOOTNOTE = 7
|
|
24
|
+
ISOLATE_FORMULA = 8
|
|
25
|
+
FORMULA_CAPTION = 9
|
|
26
|
+
|
|
27
|
+
class TableLayoutParsedFormat(Enum):
|
|
28
|
+
LATEX = auto()
|
|
29
|
+
MARKDOWN = auto()
|
|
30
|
+
HTML = auto()
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class BaseLayout:
|
|
34
|
+
rect: Rectangle
|
|
35
|
+
fragments: list[OCRFragment]
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class PlainLayout(BaseLayout):
|
|
39
|
+
cls: Literal[
|
|
40
|
+
LayoutClass.TITLE,
|
|
41
|
+
LayoutClass.PLAIN_TEXT,
|
|
42
|
+
LayoutClass.ABANDON,
|
|
43
|
+
LayoutClass.FIGURE,
|
|
44
|
+
LayoutClass.FIGURE_CAPTION,
|
|
45
|
+
LayoutClass.TABLE_CAPTION,
|
|
46
|
+
LayoutClass.TABLE_FOOTNOTE,
|
|
47
|
+
LayoutClass.FORMULA_CAPTION,
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class TableLayout(BaseLayout):
|
|
52
|
+
parsed: tuple[str, TableLayoutParsedFormat] | None
|
|
53
|
+
cls: Literal[LayoutClass.TABLE]
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class FormulaLayout(BaseLayout):
|
|
57
|
+
latex: str | None
|
|
58
|
+
cls: Literal[LayoutClass.ISOLATE_FORMULA]
|
|
59
|
+
|
|
60
|
+
Layout = PlainLayout | TableLayout | FormulaLayout
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class ExtractedResult:
|
|
65
|
+
rotation: float
|
|
66
|
+
layouts: list[Layout]
|
|
67
|
+
extracted_image: Image | None
|
|
68
|
+
adjusted_image: Image | None
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
from math import ceil
|
|
5
|
+
from PIL.Image import Image
|
|
6
|
+
from PIL.ImageOps import expand
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def ensure_dir(path: str) -> str:
|
|
10
|
+
path = os.path.abspath(path)
|
|
11
|
+
os.makedirs(path, exist_ok=True)
|
|
12
|
+
return path
|
|
13
|
+
|
|
14
|
+
def is_space_text(text: str) -> bool:
|
|
15
|
+
return bool(re.match(r"^\s*$", text))
|
|
16
|
+
|
|
17
|
+
def expand_image(image: Image, percent: float):
|
|
18
|
+
width, height = image.size
|
|
19
|
+
border_width = ceil(width * percent)
|
|
20
|
+
border_height = ceil(height * percent)
|
|
21
|
+
fill_color: tuple[int, ...]
|
|
22
|
+
|
|
23
|
+
if image.mode == "RGBA":
|
|
24
|
+
fill_color = (255, 255, 255, 255)
|
|
25
|
+
else:
|
|
26
|
+
fill_color = (255, 255, 255)
|
|
27
|
+
|
|
28
|
+
return expand(
|
|
29
|
+
image=image,
|
|
30
|
+
border=(border_width, border_height),
|
|
31
|
+
fill=fill_color,
|
|
32
|
+
)
|