doc-page-extractor 0.2.0__tar.gz → 0.2.2__tar.gz
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.
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/PKG-INFO +2 -2
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/__init__.py +1 -1
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/extractor.py +30 -27
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/latex.py +10 -8
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/layout_order.py +12 -10
- doc_page_extractor-0.2.2/doc_page_extractor/model.py +114 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/ocr.py +13 -20
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/table.py +14 -24
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/types.py +4 -27
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor.egg-info/PKG-INFO +2 -2
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor.egg-info/SOURCES.txt +1 -1
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor.egg-info/requires.txt +1 -1
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/setup.py +2 -2
- doc_page_extractor-0.2.0/doc_page_extractor/models.py +0 -92
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/LICENSE +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/README.md +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/clipper.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/downloader.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/layoutreader.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/ocr_corrector.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/__init__.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/cls_postprocess.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/db_postprocess.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/imaug.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/operators.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/predict_base.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/predict_cls.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/predict_det.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/predict_rec.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/predict_system.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/rec_postprocess.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/utils.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/overlap.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/plot.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/raw_optimizer.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/rectangle.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/rotation.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/struct_eqtable/__init__.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/struct_eqtable/internvl/__init__.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/struct_eqtable/internvl/conversation.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/struct_eqtable/internvl/internvl.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/struct_eqtable/internvl/internvl_lmdeploy.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/struct_eqtable/pix2s/__init__.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/struct_eqtable/pix2s/pix2s.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/struct_eqtable/pix2s/pix2s_trt.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/utils.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor.egg-info/dependency_links.txt +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor.egg-info/top_level.txt +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/setup.cfg +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/tests/__init__.py +0 -0
- {doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/tests/test_history_bus.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: doc-page-extractor
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: doc page extractor can identify text and format in images and return structured data.
|
|
5
5
|
Home-page: https://github.com/Moskize91/doc-page-extractor
|
|
6
6
|
Author: Tao Zeyu
|
|
@@ -16,7 +16,7 @@ Requires-Dist: transformers<=4.47,>=4.42.4
|
|
|
16
16
|
Requires-Dist: doclayout_yolo>=0.0.3
|
|
17
17
|
Requires-Dist: pix2tex<=0.2.0,>=0.1.4
|
|
18
18
|
Requires-Dist: accelerate<2.0,>=1.6.0
|
|
19
|
-
Requires-Dist: huggingface_hub
|
|
19
|
+
Requires-Dist: huggingface_hub<1.0,>=0.30.2
|
|
20
20
|
Dynamic: author
|
|
21
21
|
Dynamic: author-email
|
|
22
22
|
Dynamic: description
|
|
@@ -2,6 +2,7 @@ from .extractor import DocExtractor
|
|
|
2
2
|
from .clipper import clip, clip_from_image
|
|
3
3
|
from .plot import plot
|
|
4
4
|
from .rectangle import Point, Rectangle
|
|
5
|
+
from .model import Model, HuggingfaceModel
|
|
5
6
|
from .types import (
|
|
6
7
|
ExtractedResult,
|
|
7
8
|
OCRFragment,
|
|
@@ -12,5 +13,4 @@ from .types import (
|
|
|
12
13
|
PlainLayout,
|
|
13
14
|
FormulaLayout,
|
|
14
15
|
TableLayout,
|
|
15
|
-
ModelsDownloader
|
|
16
16
|
)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import torch
|
|
1
2
|
|
|
3
|
+
from os import PathLike
|
|
2
4
|
from typing import Literal, Generator
|
|
3
5
|
from PIL.Image import Image
|
|
4
6
|
from doclayout_yolo import YOLOv10
|
|
5
|
-
from logging import Logger, getLogger
|
|
6
7
|
|
|
7
|
-
from .
|
|
8
|
+
from .model import Model, HuggingfaceModel
|
|
8
9
|
from .ocr import OCR
|
|
9
10
|
from .ocr_corrector import correct_fragments
|
|
10
11
|
from .raw_optimizer import RawOptimizer
|
|
@@ -16,7 +17,6 @@ from .overlap import merge_fragments_as_line, remove_overlap_layouts
|
|
|
16
17
|
from .clipper import clip_from_image
|
|
17
18
|
from .types import (
|
|
18
19
|
ExtractedResult,
|
|
19
|
-
ModelsDownloader,
|
|
20
20
|
OCRFragment,
|
|
21
21
|
Layout,
|
|
22
22
|
LayoutClass,
|
|
@@ -29,32 +29,35 @@ from .types import (
|
|
|
29
29
|
|
|
30
30
|
class DocExtractor:
|
|
31
31
|
def __init__(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
self,
|
|
33
|
+
model_cache_dir: PathLike | None = None,
|
|
34
|
+
device: Literal["cpu", "cuda"] = "cpu",
|
|
35
|
+
model: Model | None = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
|
|
38
|
+
if model is None:
|
|
39
|
+
if model_cache_dir is None:
|
|
40
|
+
raise ValueError("You must provide a model_cache_dir or a model instance.")
|
|
41
|
+
model = HuggingfaceModel(model_cache_dir)
|
|
42
|
+
|
|
43
|
+
if device == "cuda" and not torch.cuda.is_available():
|
|
44
|
+
device = "cpu"
|
|
45
|
+
print("CUDA is not available. Using CPU instead.")
|
|
40
46
|
|
|
41
47
|
self._device: Literal["cpu", "cuda"] = device
|
|
48
|
+
self._model: Model = model
|
|
42
49
|
self._yolo: YOLOv10 | None = None
|
|
43
|
-
self._ocr: OCR = OCR(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
)
|
|
51
|
-
self.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
)
|
|
55
|
-
self._layout_order: LayoutOrder = LayoutOrder(
|
|
56
|
-
get_model_dir=self._models_downloader.layoutreader,
|
|
57
|
-
)
|
|
50
|
+
self._ocr: OCR = OCR(device, model)
|
|
51
|
+
self._table: Table = Table(device, model)
|
|
52
|
+
self._latex: LaTeX = LaTeX(device, model)
|
|
53
|
+
self._layout_order: LayoutOrder = LayoutOrder(device, model)
|
|
54
|
+
|
|
55
|
+
def prepare_models(self):
|
|
56
|
+
self._model.get_onnx_ocr_path()
|
|
57
|
+
self._model.get_yolo_path()
|
|
58
|
+
self._model.get_layoutreader_path()
|
|
59
|
+
self._model.get_struct_eqtable_path()
|
|
60
|
+
self._model.get_latex_path()
|
|
58
61
|
|
|
59
62
|
def extract(
|
|
60
63
|
self,
|
|
@@ -199,7 +202,7 @@ class DocExtractor:
|
|
|
199
202
|
|
|
200
203
|
def _get_yolo(self) -> YOLOv10:
|
|
201
204
|
if self._yolo is None:
|
|
202
|
-
model_path = self.
|
|
205
|
+
model_path = self._model.get_yolo_path()
|
|
203
206
|
self._yolo = YOLOv10(str(model_path))
|
|
204
207
|
return self._yolo
|
|
205
208
|
|
|
@@ -6,12 +6,13 @@ from pix2tex.cli import LatexOCR
|
|
|
6
6
|
from PIL.Image import Image
|
|
7
7
|
from typing import Literal
|
|
8
8
|
from .utils import expand_image
|
|
9
|
-
from .
|
|
9
|
+
from .model import Model
|
|
10
|
+
|
|
10
11
|
|
|
11
12
|
class LaTeX:
|
|
12
|
-
def __init__(self, device: Literal["cpu", "cuda"],
|
|
13
|
-
self.
|
|
14
|
-
self.
|
|
13
|
+
def __init__(self, device: Literal["cpu", "cuda"], model: Model) -> None:
|
|
14
|
+
self._model: Model = model
|
|
15
|
+
self._latex_model: LatexOCR | None = None
|
|
15
16
|
self._device: Literal["cpu", "cuda"] = device
|
|
16
17
|
|
|
17
18
|
def extract(self, image: Image) -> str | None:
|
|
@@ -21,11 +22,12 @@ class LaTeX:
|
|
|
21
22
|
return model(image)
|
|
22
23
|
|
|
23
24
|
def _get_model(self) -> LatexOCR:
|
|
24
|
-
if self.
|
|
25
|
-
self._model
|
|
25
|
+
if self._latex_model is None:
|
|
26
|
+
model_path = self._model.get_latex_path()
|
|
27
|
+
self._latex_model = LatexOCR(Munch({
|
|
26
28
|
"config": os.path.join("settings", "config.yaml"),
|
|
27
|
-
"checkpoint": os.path.join(
|
|
29
|
+
"checkpoint": os.path.join(model_path, "checkpoints", "weights.pth"),
|
|
28
30
|
"no_cuda": self._device == "cpu",
|
|
29
31
|
"no_resize": False,
|
|
30
32
|
}))
|
|
31
|
-
return self.
|
|
33
|
+
return self._latex_model
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import torch
|
|
2
2
|
|
|
3
|
-
from typing import Generator
|
|
3
|
+
from typing import Generator, Literal
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from transformers import LayoutLMv3ForTokenClassification
|
|
6
6
|
|
|
7
|
-
from .types import Layout, LayoutClass
|
|
7
|
+
from .types import Layout, LayoutClass
|
|
8
|
+
from .model import Model
|
|
8
9
|
from .layoutreader import prepare_inputs, boxes2inputs, parse_logits
|
|
9
10
|
|
|
10
11
|
|
|
@@ -17,18 +18,19 @@ class _BBox:
|
|
|
17
18
|
value: tuple[float, float, float, float]
|
|
18
19
|
|
|
19
20
|
class LayoutOrder:
|
|
20
|
-
def __init__(self,
|
|
21
|
-
self.
|
|
22
|
-
self.
|
|
23
|
-
self._device
|
|
21
|
+
def __init__(self, device: Literal["cpu", "cuda"], model: Model):
|
|
22
|
+
self._model: Model = model
|
|
23
|
+
self._order_model: LayoutLMv3ForTokenClassification | None = None
|
|
24
|
+
self._device: Literal["cpu", "cuda"] = device
|
|
24
25
|
|
|
25
26
|
def _get_model(self) -> LayoutLMv3ForTokenClassification:
|
|
26
|
-
if self.
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
if self._order_model is None:
|
|
28
|
+
model_path = self._model.get_layoutreader_path()
|
|
29
|
+
self._order_model = LayoutLMv3ForTokenClassification.from_pretrained(
|
|
30
|
+
pretrained_model_name_or_path=model_path,
|
|
29
31
|
local_files_only=True,
|
|
30
32
|
).to(device=self._device)
|
|
31
|
-
return self.
|
|
33
|
+
return self._order_model
|
|
32
34
|
|
|
33
35
|
def sort(self, layouts: list[Layout], size: tuple[int, int]) -> list[Layout]:
|
|
34
36
|
width, height = size
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from os import PathLike
|
|
2
|
+
from typing import runtime_checkable, Protocol
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from threading import Lock
|
|
5
|
+
from huggingface_hub import hf_hub_download, snapshot_download, try_to_load_from_cache
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@runtime_checkable
|
|
9
|
+
class Model(Protocol):
|
|
10
|
+
def get_onnx_ocr_path(self) -> Path:
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
def get_yolo_path(self) -> Path:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
def get_layoutreader_path(self) -> Path:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
def get_struct_eqtable_path(self) -> Path:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def get_latex_path(self) -> Path:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
class HuggingfaceModel(Model):
|
|
26
|
+
def __init__(self, model_cache_dir: PathLike):
|
|
27
|
+
super().__init__()
|
|
28
|
+
self._lock: Lock = Lock()
|
|
29
|
+
self._model_cache_dir: Path = Path(model_cache_dir)
|
|
30
|
+
|
|
31
|
+
def get_onnx_ocr_path(self) -> Path:
|
|
32
|
+
return self._get_model_path(
|
|
33
|
+
repo_id="moskize/OnnxOCR",
|
|
34
|
+
filename="README.md",
|
|
35
|
+
repo_type=None,
|
|
36
|
+
is_snapshot=True,
|
|
37
|
+
wanna_dir_path=True,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def get_yolo_path(self) -> Path:
|
|
41
|
+
return self._get_model_path(
|
|
42
|
+
repo_id="opendatalab/PDF-Extract-Kit-1.0",
|
|
43
|
+
filename="models/Layout/YOLO/doclayout_yolo_ft.pt",
|
|
44
|
+
repo_type=None,
|
|
45
|
+
is_snapshot=False,
|
|
46
|
+
wanna_dir_path=False,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def get_layoutreader_path(self) -> Path:
|
|
50
|
+
return self._get_model_path(
|
|
51
|
+
repo_id="hantian/layoutreader",
|
|
52
|
+
filename="model.safetensors",
|
|
53
|
+
repo_type=None,
|
|
54
|
+
is_snapshot=True,
|
|
55
|
+
wanna_dir_path=True,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def get_struct_eqtable_path(self) -> Path:
|
|
59
|
+
return self._get_model_path(
|
|
60
|
+
repo_id="U4R/StructTable-InternVL2-1B",
|
|
61
|
+
filename="model.safetensors",
|
|
62
|
+
repo_type=None,
|
|
63
|
+
is_snapshot=True,
|
|
64
|
+
wanna_dir_path=True,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def get_latex_path(self) -> Path:
|
|
68
|
+
return self._get_model_path(
|
|
69
|
+
repo_id="lukbl/LaTeX-OCR",
|
|
70
|
+
filename="checkpoints/weights.pth",
|
|
71
|
+
repo_type="space",
|
|
72
|
+
is_snapshot=True,
|
|
73
|
+
wanna_dir_path=True,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def _get_model_path(
|
|
77
|
+
self,
|
|
78
|
+
repo_id: str,
|
|
79
|
+
filename: str,
|
|
80
|
+
repo_type: str | None,
|
|
81
|
+
is_snapshot: bool,
|
|
82
|
+
wanna_dir_path: bool,
|
|
83
|
+
) -> Path:
|
|
84
|
+
|
|
85
|
+
with self._lock:
|
|
86
|
+
model_path = try_to_load_from_cache(
|
|
87
|
+
repo_id=repo_id,
|
|
88
|
+
filename=filename,
|
|
89
|
+
repo_type=repo_type,
|
|
90
|
+
cache_dir=self._model_cache_dir
|
|
91
|
+
)
|
|
92
|
+
if isinstance(model_path, str):
|
|
93
|
+
model_path = Path(model_path)
|
|
94
|
+
if wanna_dir_path:
|
|
95
|
+
for _ in Path(filename).parts:
|
|
96
|
+
model_path = model_path.parent
|
|
97
|
+
|
|
98
|
+
else:
|
|
99
|
+
if is_snapshot:
|
|
100
|
+
model_path = snapshot_download(
|
|
101
|
+
cache_dir=self._model_cache_dir,
|
|
102
|
+
repo_id=repo_id,
|
|
103
|
+
repo_type=repo_type,
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
model_path = hf_hub_download(
|
|
107
|
+
cache_dir=self._model_cache_dir,
|
|
108
|
+
repo_id=repo_id,
|
|
109
|
+
repo_type=repo_type,
|
|
110
|
+
filename=filename,
|
|
111
|
+
)
|
|
112
|
+
model_path = Path(model_path)
|
|
113
|
+
|
|
114
|
+
return model_path
|
|
@@ -5,7 +5,8 @@ import os
|
|
|
5
5
|
from typing import Literal, Generator
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from .onnxocr import TextSystem
|
|
8
|
-
from .types import
|
|
8
|
+
from .types import OCRFragment
|
|
9
|
+
from .model import Model
|
|
9
10
|
from .rectangle import Rectangle
|
|
10
11
|
from .utils import is_space_text
|
|
11
12
|
|
|
@@ -46,17 +47,10 @@ class _OONXParams:
|
|
|
46
47
|
det_model_dir: str
|
|
47
48
|
rec_char_dict_path: str
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
50
|
class OCR:
|
|
53
|
-
def __init__(
|
|
54
|
-
self,
|
|
55
|
-
device: Literal["cpu", "cuda"],
|
|
56
|
-
get_model_dir: GetModelDir,
|
|
57
|
-
):
|
|
51
|
+
def __init__(self, device: Literal["cpu", "cuda"], model: Model):
|
|
58
52
|
self._device: Literal["cpu", "cuda"] = device
|
|
59
|
-
self.
|
|
53
|
+
self._model: Model = model
|
|
60
54
|
self._text_system: TextSystem | None = None
|
|
61
55
|
|
|
62
56
|
def search_fragments(self, image: np.ndarray) -> Generator[OCRFragment, None, None]:
|
|
@@ -89,17 +83,9 @@ class OCR:
|
|
|
89
83
|
for box, res in zip(dt_boxes, rec_res):
|
|
90
84
|
yield box.tolist(), res
|
|
91
85
|
|
|
92
|
-
def make_model_paths(self) -> list[str]:
|
|
93
|
-
model_paths = []
|
|
94
|
-
model_dir = self._get_model_dir()
|
|
95
|
-
for model_path in _MODELS:
|
|
96
|
-
file_name = os.path.join(*model_path)
|
|
97
|
-
model_paths.append(os.path.join(model_dir, file_name))
|
|
98
|
-
return model_paths
|
|
99
|
-
|
|
100
86
|
def _get_text_system(self) -> TextSystem:
|
|
101
87
|
if self._text_system is None:
|
|
102
|
-
model_paths = self.
|
|
88
|
+
model_paths = self._make_model_paths()
|
|
103
89
|
self._text_system = TextSystem(_OONXParams(
|
|
104
90
|
use_angle_cls=True,
|
|
105
91
|
use_gpu=(self._device != "cpu"),
|
|
@@ -127,9 +113,16 @@ class OCR:
|
|
|
127
113
|
det_model_dir=model_paths[2],
|
|
128
114
|
rec_char_dict_path=model_paths[3],
|
|
129
115
|
))
|
|
130
|
-
|
|
131
116
|
return self._text_system
|
|
132
117
|
|
|
118
|
+
def _make_model_paths(self) -> list[str]:
|
|
119
|
+
model_paths: list[str] = []
|
|
120
|
+
model_dir = self._model.get_onnx_ocr_path()
|
|
121
|
+
for model_path in _MODELS:
|
|
122
|
+
file_name = os.path.join(*model_path)
|
|
123
|
+
model_paths.append(str(model_dir / file_name))
|
|
124
|
+
return model_paths
|
|
125
|
+
|
|
133
126
|
def _preprocess_image(self, image: np.ndarray) -> np.ndarray:
|
|
134
127
|
image = self._alpha_to_color(image, (255, 255, 255))
|
|
135
128
|
# image = cv2.bitwise_not(image) # inv
|
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import torch
|
|
3
2
|
|
|
4
3
|
from typing import Literal, Any
|
|
5
4
|
from PIL.Image import Image
|
|
6
|
-
from .types import TableLayoutParsedFormat
|
|
5
|
+
from .types import TableLayoutParsedFormat
|
|
6
|
+
from .model import Model
|
|
7
7
|
from .utils import expand_image
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
OutputFormat = Literal["latex", "markdown", "html"]
|
|
11
11
|
|
|
12
12
|
class Table:
|
|
13
|
-
def __init__(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
get_model_dir: GetModelDir,
|
|
17
|
-
):
|
|
18
|
-
self._model: Any | None = None
|
|
19
|
-
self._model_path: str = get_model_dir()
|
|
13
|
+
def __init__(self, device: Literal["cpu", "cuda"], model: Model) -> None:
|
|
14
|
+
self._model: Model = model
|
|
15
|
+
self._table_model: Any | None = None
|
|
20
16
|
self._ban: bool = False
|
|
21
|
-
if device == "cpu"
|
|
17
|
+
if device == "cpu":
|
|
22
18
|
self._ban = True
|
|
23
19
|
|
|
24
20
|
def predict(self, image: Image, format: TableLayoutParsedFormat) -> str | None:
|
|
@@ -47,24 +43,18 @@ class Table:
|
|
|
47
43
|
|
|
48
44
|
return results[0]
|
|
49
45
|
|
|
50
|
-
def _get_model(self):
|
|
51
|
-
if self.
|
|
52
|
-
local_files_only: bool
|
|
53
|
-
if os.path.exists(self._model_path):
|
|
54
|
-
local_files_only = True
|
|
55
|
-
else:
|
|
56
|
-
local_files_only = False
|
|
57
|
-
os.makedirs(self._model_path)
|
|
58
|
-
|
|
46
|
+
def _get_model(self) -> Any:
|
|
47
|
+
if self._table_model is None:
|
|
59
48
|
from .struct_eqtable import build_model
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
model_path = self._model.get_struct_eqtable_path()
|
|
50
|
+
table_model = build_model(
|
|
51
|
+
model_ckpt=model_path,
|
|
62
52
|
max_new_tokens=1024,
|
|
63
53
|
max_time=30,
|
|
64
54
|
lmdeploy=False,
|
|
65
55
|
flash_attn=True,
|
|
66
56
|
batch_size=1,
|
|
67
|
-
local_files_only=
|
|
57
|
+
local_files_only=False,
|
|
68
58
|
)
|
|
69
|
-
self.
|
|
70
|
-
return self.
|
|
59
|
+
self._table_model = table_model.cuda()
|
|
60
|
+
return self._table_model
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Literal
|
|
2
|
+
from typing import Literal
|
|
3
3
|
from enum import auto, Enum
|
|
4
4
|
from PIL.Image import Image
|
|
5
5
|
from .rectangle import Rectangle
|
|
@@ -32,7 +32,7 @@ class TableLayoutParsedFormat(Enum):
|
|
|
32
32
|
@dataclass
|
|
33
33
|
class BaseLayout:
|
|
34
34
|
rect: Rectangle
|
|
35
|
-
fragments:
|
|
35
|
+
fragments: list[OCRFragment]
|
|
36
36
|
|
|
37
37
|
@dataclass
|
|
38
38
|
class PlainLayout(BaseLayout):
|
|
@@ -63,29 +63,6 @@ Layout = PlainLayout | TableLayout | FormulaLayout
|
|
|
63
63
|
@dataclass
|
|
64
64
|
class ExtractedResult:
|
|
65
65
|
rotation: float
|
|
66
|
-
layouts:
|
|
66
|
+
layouts: list[Layout]
|
|
67
67
|
extracted_image: Image | None
|
|
68
|
-
adjusted_image: Image | None
|
|
69
|
-
|
|
70
|
-
GetModelDir = Callable[[], str]
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
@runtime_checkable
|
|
74
|
-
class ModelsDownloader(Protocol):
|
|
75
|
-
|
|
76
|
-
def onnx_ocr(self) -> str:
|
|
77
|
-
pass
|
|
78
|
-
|
|
79
|
-
def yolo(self) -> str:
|
|
80
|
-
pass
|
|
81
|
-
|
|
82
|
-
def layoutreader(self) -> str:
|
|
83
|
-
pass
|
|
84
|
-
|
|
85
|
-
def struct_eqtable(self) -> str:
|
|
86
|
-
pass
|
|
87
|
-
|
|
88
|
-
def latex(self) -> str:
|
|
89
|
-
pass
|
|
90
|
-
|
|
91
|
-
|
|
68
|
+
adjusted_image: Image | None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: doc-page-extractor
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: doc page extractor can identify text and format in images and return structured data.
|
|
5
5
|
Home-page: https://github.com/Moskize91/doc-page-extractor
|
|
6
6
|
Author: Tao Zeyu
|
|
@@ -16,7 +16,7 @@ Requires-Dist: transformers<=4.47,>=4.42.4
|
|
|
16
16
|
Requires-Dist: doclayout_yolo>=0.0.3
|
|
17
17
|
Requires-Dist: pix2tex<=0.2.0,>=0.1.4
|
|
18
18
|
Requires-Dist: accelerate<2.0,>=1.6.0
|
|
19
|
-
Requires-Dist: huggingface_hub
|
|
19
|
+
Requires-Dist: huggingface_hub<1.0,>=0.30.2
|
|
20
20
|
Dynamic: author
|
|
21
21
|
Dynamic: author-email
|
|
22
22
|
Dynamic: description
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor.egg-info/SOURCES.txt
RENAMED
|
@@ -8,7 +8,7 @@ doc_page_extractor/extractor.py
|
|
|
8
8
|
doc_page_extractor/latex.py
|
|
9
9
|
doc_page_extractor/layout_order.py
|
|
10
10
|
doc_page_extractor/layoutreader.py
|
|
11
|
-
doc_page_extractor/
|
|
11
|
+
doc_page_extractor/model.py
|
|
12
12
|
doc_page_extractor/ocr.py
|
|
13
13
|
doc_page_extractor/ocr_corrector.py
|
|
14
14
|
doc_page_extractor/overlap.py
|
|
@@ -5,7 +5,7 @@ if "doc_page_extractor.struct_eqtable" not in find_packages():
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="doc-page-extractor",
|
|
8
|
-
version="0.2.
|
|
8
|
+
version="0.2.2",
|
|
9
9
|
author="Tao Zeyu",
|
|
10
10
|
author_email="i@taozeyu.com",
|
|
11
11
|
url="https://github.com/Moskize91/doc-page-extractor",
|
|
@@ -23,6 +23,6 @@ setup(
|
|
|
23
23
|
"doclayout_yolo>=0.0.3",
|
|
24
24
|
"pix2tex>=0.1.4,<=0.2.0",
|
|
25
25
|
"accelerate>=1.6.0,<2.0",
|
|
26
|
-
"huggingface_hub>=0.30.2",
|
|
26
|
+
"huggingface_hub>=0.30.2,<1.0",
|
|
27
27
|
],
|
|
28
28
|
)
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from logging import Logger
|
|
4
|
-
from huggingface_hub import hf_hub_download, snapshot_download, try_to_load_from_cache
|
|
5
|
-
from .types import ModelsDownloader
|
|
6
|
-
|
|
7
|
-
class HuggingfaceModelsDownloader(ModelsDownloader):
|
|
8
|
-
def __init__(
|
|
9
|
-
self,
|
|
10
|
-
logger: Logger,
|
|
11
|
-
model_dir_path: str | None
|
|
12
|
-
):
|
|
13
|
-
self._logger = logger
|
|
14
|
-
self._model_dir_path: str | None = model_dir_path
|
|
15
|
-
|
|
16
|
-
def onnx_ocr(self) -> str:
|
|
17
|
-
repo_path = try_to_load_from_cache(
|
|
18
|
-
repo_id="moskize/OnnxOCR",
|
|
19
|
-
filename="README.md",
|
|
20
|
-
cache_dir=self._model_dir_path
|
|
21
|
-
)
|
|
22
|
-
if isinstance(repo_path, str):
|
|
23
|
-
return os.path.dirname(repo_path)
|
|
24
|
-
else:
|
|
25
|
-
self._logger.info("Downloading OCR model...")
|
|
26
|
-
return snapshot_download(
|
|
27
|
-
cache_dir=self._model_dir_path,
|
|
28
|
-
repo_id="moskize/OnnxOCR",
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
def yolo(self) -> str:
|
|
32
|
-
yolo_file_path = try_to_load_from_cache(
|
|
33
|
-
repo_id="opendatalab/PDF-Extract-Kit-1.0",
|
|
34
|
-
filename="models/Layout/YOLO/doclayout_yolo_ft.pt",
|
|
35
|
-
cache_dir=self._model_dir_path
|
|
36
|
-
)
|
|
37
|
-
if isinstance(yolo_file_path, str):
|
|
38
|
-
return yolo_file_path
|
|
39
|
-
else:
|
|
40
|
-
self._logger.info("Downloading YOLO model...")
|
|
41
|
-
return hf_hub_download(
|
|
42
|
-
cache_dir=self._model_dir_path,
|
|
43
|
-
repo_id="opendatalab/PDF-Extract-Kit-1.0",
|
|
44
|
-
filename="models/Layout/YOLO/doclayout_yolo_ft.pt",
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
def layoutreader(self) -> str:
|
|
48
|
-
repo_path = try_to_load_from_cache(
|
|
49
|
-
repo_id="hantian/layoutreader",
|
|
50
|
-
filename="model.safetensors",
|
|
51
|
-
cache_dir=self._model_dir_path
|
|
52
|
-
)
|
|
53
|
-
if isinstance(repo_path, str):
|
|
54
|
-
return os.path.dirname(repo_path)
|
|
55
|
-
else:
|
|
56
|
-
self._logger.info("Downloading LayoutReader model...")
|
|
57
|
-
return snapshot_download(
|
|
58
|
-
cache_dir=self._model_dir_path,
|
|
59
|
-
repo_id="hantian/layoutreader",
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
def struct_eqtable(self) -> str:
|
|
63
|
-
repo_path = try_to_load_from_cache(
|
|
64
|
-
repo_id="U4R/StructTable-InternVL2-1B",
|
|
65
|
-
filename="model.safetensors",
|
|
66
|
-
cache_dir=self._model_dir_path
|
|
67
|
-
)
|
|
68
|
-
if isinstance(repo_path, str):
|
|
69
|
-
return os.path.dirname(repo_path)
|
|
70
|
-
else:
|
|
71
|
-
self._logger.info("Downloading StructEqTable model...")
|
|
72
|
-
return snapshot_download(
|
|
73
|
-
cache_dir=self._model_dir_path,
|
|
74
|
-
repo_id="U4R/StructTable-InternVL2-1B",
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
def latex(self):
|
|
78
|
-
repo_path = try_to_load_from_cache(
|
|
79
|
-
repo_id="lukbl/LaTeX-OCR",
|
|
80
|
-
filename="checkpoints/weights.pth",
|
|
81
|
-
repo_type="space",
|
|
82
|
-
cache_dir=self._model_dir_path
|
|
83
|
-
)
|
|
84
|
-
if isinstance(repo_path, str):
|
|
85
|
-
return os.path.dirname(os.path.dirname(repo_path))
|
|
86
|
-
else:
|
|
87
|
-
self._logger.info("Downloading LaTeX model...")
|
|
88
|
-
return snapshot_download(
|
|
89
|
-
cache_dir=self._model_dir_path,
|
|
90
|
-
repo_type="space",
|
|
91
|
-
repo_id="lukbl/LaTeX-OCR",
|
|
92
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/__init__.py
RENAMED
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/cls_postprocess.py
RENAMED
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/db_postprocess.py
RENAMED
|
File without changes
|
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/operators.py
RENAMED
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/predict_base.py
RENAMED
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/predict_cls.py
RENAMED
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/predict_det.py
RENAMED
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/predict_rec.py
RENAMED
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/predict_system.py
RENAMED
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/onnxocr/rec_postprocess.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor/struct_eqtable/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{doc_page_extractor-0.2.0 → doc_page_extractor-0.2.2}/doc_page_extractor.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|