natural-pdf 0.1.4__py3-none-any.whl → 0.1.6__py3-none-any.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.
- docs/api/index.md +386 -0
- docs/assets/favicon.png +3 -0
- docs/assets/favicon.svg +3 -0
- docs/assets/javascripts/custom.js +17 -0
- docs/assets/logo.svg +3 -0
- docs/assets/sample-screen.png +0 -0
- docs/assets/social-preview.png +17 -0
- docs/assets/social-preview.svg +17 -0
- docs/assets/stylesheets/custom.css +65 -0
- docs/document-qa/index.ipynb +435 -0
- docs/document-qa/index.md +79 -0
- docs/element-selection/index.ipynb +915 -0
- docs/element-selection/index.md +229 -0
- docs/index.md +170 -0
- docs/installation/index.md +69 -0
- docs/interactive-widget/index.ipynb +962 -0
- docs/interactive-widget/index.md +12 -0
- docs/layout-analysis/index.ipynb +818 -0
- docs/layout-analysis/index.md +185 -0
- docs/ocr/index.md +209 -0
- docs/pdf-navigation/index.ipynb +314 -0
- docs/pdf-navigation/index.md +97 -0
- docs/regions/index.ipynb +816 -0
- docs/regions/index.md +294 -0
- docs/tables/index.ipynb +658 -0
- docs/tables/index.md +144 -0
- docs/text-analysis/index.ipynb +370 -0
- docs/text-analysis/index.md +105 -0
- docs/text-extraction/index.ipynb +1478 -0
- docs/text-extraction/index.md +292 -0
- docs/tutorials/01-loading-and-extraction.ipynb +1710 -0
- docs/tutorials/01-loading-and-extraction.md +95 -0
- docs/tutorials/02-finding-elements.ipynb +340 -0
- docs/tutorials/02-finding-elements.md +149 -0
- docs/tutorials/03-extracting-blocks.ipynb +147 -0
- docs/tutorials/03-extracting-blocks.md +48 -0
- docs/tutorials/04-table-extraction.ipynb +114 -0
- docs/tutorials/04-table-extraction.md +50 -0
- docs/tutorials/05-excluding-content.ipynb +270 -0
- docs/tutorials/05-excluding-content.md +109 -0
- docs/tutorials/06-document-qa.ipynb +332 -0
- docs/tutorials/06-document-qa.md +91 -0
- docs/tutorials/07-layout-analysis.ipynb +288 -0
- docs/tutorials/07-layout-analysis.md +66 -0
- docs/tutorials/07-working-with-regions.ipynb +413 -0
- docs/tutorials/07-working-with-regions.md +151 -0
- docs/tutorials/08-spatial-navigation.ipynb +508 -0
- docs/tutorials/08-spatial-navigation.md +190 -0
- docs/tutorials/09-section-extraction.ipynb +2434 -0
- docs/tutorials/09-section-extraction.md +256 -0
- docs/tutorials/10-form-field-extraction.ipynb +512 -0
- docs/tutorials/10-form-field-extraction.md +201 -0
- docs/tutorials/11-enhanced-table-processing.ipynb +54 -0
- docs/tutorials/11-enhanced-table-processing.md +9 -0
- docs/tutorials/12-ocr-integration.ipynb +604 -0
- docs/tutorials/12-ocr-integration.md +175 -0
- docs/tutorials/13-semantic-search.ipynb +1328 -0
- docs/tutorials/13-semantic-search.md +77 -0
- docs/visual-debugging/index.ipynb +2970 -0
- docs/visual-debugging/index.md +157 -0
- docs/visual-debugging/region.png +0 -0
- natural_pdf/__init__.py +50 -33
- natural_pdf/analyzers/__init__.py +2 -1
- natural_pdf/analyzers/layout/base.py +32 -24
- natural_pdf/analyzers/layout/docling.py +131 -72
- natural_pdf/analyzers/layout/gemini.py +264 -0
- natural_pdf/analyzers/layout/layout_analyzer.py +156 -113
- natural_pdf/analyzers/layout/layout_manager.py +125 -58
- natural_pdf/analyzers/layout/layout_options.py +43 -17
- natural_pdf/analyzers/layout/paddle.py +152 -95
- natural_pdf/analyzers/layout/surya.py +164 -92
- natural_pdf/analyzers/layout/tatr.py +149 -84
- natural_pdf/analyzers/layout/yolo.py +89 -45
- natural_pdf/analyzers/text_options.py +22 -15
- natural_pdf/analyzers/text_structure.py +131 -85
- natural_pdf/analyzers/utils.py +30 -23
- natural_pdf/collections/pdf_collection.py +146 -97
- natural_pdf/core/__init__.py +1 -1
- natural_pdf/core/element_manager.py +419 -337
- natural_pdf/core/highlighting_service.py +268 -196
- natural_pdf/core/page.py +1044 -521
- natural_pdf/core/pdf.py +516 -313
- natural_pdf/elements/__init__.py +1 -1
- natural_pdf/elements/base.py +307 -225
- natural_pdf/elements/collections.py +805 -543
- natural_pdf/elements/line.py +39 -36
- natural_pdf/elements/rect.py +32 -30
- natural_pdf/elements/region.py +889 -879
- natural_pdf/elements/text.py +127 -99
- natural_pdf/exporters/__init__.py +0 -1
- natural_pdf/exporters/searchable_pdf.py +261 -102
- natural_pdf/ocr/__init__.py +57 -35
- natural_pdf/ocr/engine.py +150 -46
- natural_pdf/ocr/engine_easyocr.py +146 -150
- natural_pdf/ocr/engine_paddle.py +118 -175
- natural_pdf/ocr/engine_surya.py +78 -141
- natural_pdf/ocr/ocr_factory.py +114 -0
- natural_pdf/ocr/ocr_manager.py +122 -124
- natural_pdf/ocr/ocr_options.py +16 -20
- natural_pdf/ocr/utils.py +98 -0
- natural_pdf/qa/__init__.py +1 -1
- natural_pdf/qa/document_qa.py +119 -111
- natural_pdf/search/__init__.py +37 -31
- natural_pdf/search/haystack_search_service.py +312 -189
- natural_pdf/search/haystack_utils.py +186 -122
- natural_pdf/search/search_options.py +25 -14
- natural_pdf/search/search_service_protocol.py +12 -6
- natural_pdf/search/searchable_mixin.py +261 -176
- natural_pdf/selectors/__init__.py +2 -1
- natural_pdf/selectors/parser.py +159 -316
- natural_pdf/templates/__init__.py +1 -1
- natural_pdf/templates/spa/css/style.css +334 -0
- natural_pdf/templates/spa/index.html +31 -0
- natural_pdf/templates/spa/js/app.js +472 -0
- natural_pdf/templates/spa/words.txt +235976 -0
- natural_pdf/utils/debug.py +32 -0
- natural_pdf/utils/highlighting.py +8 -2
- natural_pdf/utils/identifiers.py +29 -0
- natural_pdf/utils/packaging.py +418 -0
- natural_pdf/utils/reading_order.py +65 -63
- natural_pdf/utils/text_extraction.py +195 -0
- natural_pdf/utils/visualization.py +70 -61
- natural_pdf/widgets/__init__.py +2 -3
- natural_pdf/widgets/viewer.py +749 -718
- {natural_pdf-0.1.4.dist-info → natural_pdf-0.1.6.dist-info}/METADATA +53 -17
- natural_pdf-0.1.6.dist-info/RECORD +141 -0
- {natural_pdf-0.1.4.dist-info → natural_pdf-0.1.6.dist-info}/WHEEL +1 -1
- natural_pdf-0.1.6.dist-info/top_level.txt +4 -0
- notebooks/Examples.ipynb +1293 -0
- pdfs/.gitkeep +0 -0
- pdfs/01-practice.pdf +543 -0
- pdfs/0500000US42001.pdf +0 -0
- pdfs/0500000US42007.pdf +0 -0
- pdfs/2014 Statistics.pdf +0 -0
- pdfs/2019 Statistics.pdf +0 -0
- pdfs/Atlanta_Public_Schools_GA_sample.pdf +0 -0
- pdfs/needs-ocr.pdf +0 -0
- natural_pdf/templates/ocr_debug.html +0 -517
- natural_pdf-0.1.4.dist-info/RECORD +0 -61
- natural_pdf-0.1.4.dist-info/top_level.txt +0 -1
- {natural_pdf-0.1.4.dist-info → natural_pdf-0.1.6.dist-info}/licenses/LICENSE +0 -0
@@ -1,14 +1,15 @@
|
|
1
1
|
# layout_detector_surya.py
|
2
|
-
import
|
2
|
+
import copy
|
3
3
|
import importlib.util
|
4
|
+
import logging
|
4
5
|
import os
|
5
6
|
import tempfile
|
6
|
-
import
|
7
|
-
|
7
|
+
from typing import Any, Dict, List, Optional, Tuple
|
8
|
+
|
8
9
|
from PIL import Image
|
9
10
|
|
10
11
|
from .base import LayoutDetector
|
11
|
-
from .layout_options import
|
12
|
+
from .layout_options import BaseLayoutOptions, SuryaLayoutOptions
|
12
13
|
|
13
14
|
logger = logging.getLogger(__name__)
|
14
15
|
|
@@ -33,42 +34,58 @@ class SuryaLayoutDetector(LayoutDetector):
|
|
33
34
|
def __init__(self):
|
34
35
|
super().__init__()
|
35
36
|
self.supported_classes = {
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
"text",
|
38
|
+
"pageheader",
|
39
|
+
"pagefooter",
|
40
|
+
"sectionheader",
|
41
|
+
"table",
|
42
|
+
"tableofcontents",
|
43
|
+
"picture",
|
44
|
+
"caption",
|
45
|
+
"heading",
|
46
|
+
"title",
|
47
|
+
"list",
|
48
|
+
"listitem",
|
49
|
+
"code",
|
50
|
+
"textinlinemath",
|
51
|
+
"mathformula",
|
52
|
+
"form",
|
53
|
+
"table-row",
|
54
|
+
"table-column",
|
41
55
|
}
|
42
|
-
self._page_ref = None
|
56
|
+
self._page_ref = None # To store page reference from options
|
43
57
|
|
44
58
|
def is_available(self) -> bool:
|
45
59
|
return LayoutPredictor is not None and TableRecPredictor is not None
|
46
60
|
|
47
61
|
def _get_cache_key(self, options: BaseLayoutOptions) -> str:
|
48
62
|
if not isinstance(options, SuryaLayoutOptions):
|
49
|
-
|
50
|
-
device_key = str(options.device).lower() if options.device else
|
63
|
+
options = SuryaLayoutOptions(device=options.device)
|
64
|
+
device_key = str(options.device).lower() if options.device else "default_device"
|
51
65
|
model_key = options.model_name
|
52
66
|
return f"{self.__class__.__name__}_{device_key}_{model_key}"
|
53
67
|
|
54
68
|
def _load_model_from_options(self, options: BaseLayoutOptions) -> Dict[str, Any]:
|
55
69
|
if not self.is_available():
|
56
|
-
raise RuntimeError(
|
70
|
+
raise RuntimeError(
|
71
|
+
"Surya dependencies (surya.layout and surya.table_rec) not installed."
|
72
|
+
)
|
57
73
|
if not isinstance(options, SuryaLayoutOptions):
|
58
74
|
raise TypeError("Incorrect options type provided for Surya model loading.")
|
59
75
|
self.logger.info(f"Loading Surya models (device={options.device})...")
|
60
76
|
models = {}
|
61
|
-
try:
|
62
|
-
models[
|
63
|
-
models[
|
77
|
+
try:
|
78
|
+
models["layout"] = LayoutPredictor()
|
79
|
+
models["table_rec"] = TableRecPredictor()
|
64
80
|
self.logger.info("Surya LayoutPredictor and TableRecPredictor loaded.")
|
65
81
|
return models
|
66
82
|
except Exception as e:
|
67
83
|
self.logger.error(f"Failed to load Surya models: {e}", exc_info=True)
|
68
84
|
raise
|
69
|
-
|
70
|
-
def _expand_bbox(
|
71
|
-
|
85
|
+
|
86
|
+
def _expand_bbox(
|
87
|
+
self, bbox: Tuple[float, float, float, float], padding: int, max_width: int, max_height: int
|
88
|
+
) -> Tuple[int, int, int, int]:
|
72
89
|
"""Expand bbox by padding, clamping to max dimensions."""
|
73
90
|
x0, y0, x1, y1 = bbox
|
74
91
|
x0 = max(0, int(x0 - padding))
|
@@ -83,97 +100,139 @@ class SuryaLayoutDetector(LayoutDetector):
|
|
83
100
|
raise RuntimeError("Surya dependencies (layout and table_rec) not installed.")
|
84
101
|
|
85
102
|
if not isinstance(options, SuryaLayoutOptions):
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
103
|
+
self.logger.warning(
|
104
|
+
"Received BaseLayoutOptions, expected SuryaLayoutOptions. Using defaults."
|
105
|
+
)
|
106
|
+
options = SuryaLayoutOptions(
|
107
|
+
confidence=options.confidence,
|
108
|
+
classes=options.classes,
|
109
|
+
exclude_classes=options.exclude_classes,
|
110
|
+
device=options.device,
|
111
|
+
extra_args=options.extra_args,
|
112
|
+
recognize_table_structure=True,
|
113
|
+
)
|
114
|
+
|
94
115
|
# Extract page reference and scaling factors from extra_args (passed by LayoutAnalyzer)
|
95
|
-
self._page_ref = options.extra_args.get(
|
96
|
-
img_scale_x = options.extra_args.get(
|
97
|
-
img_scale_y = options.extra_args.get(
|
98
|
-
|
116
|
+
self._page_ref = options.extra_args.get("_page_ref")
|
117
|
+
img_scale_x = options.extra_args.get("_img_scale_x")
|
118
|
+
img_scale_y = options.extra_args.get("_img_scale_y")
|
119
|
+
|
99
120
|
# We still need this check, otherwise later steps that need these vars will fail
|
100
|
-
can_do_table_rec =
|
121
|
+
can_do_table_rec = (
|
122
|
+
options.recognize_table_structure
|
123
|
+
and self._page_ref
|
124
|
+
and img_scale_x is not None
|
125
|
+
and img_scale_y is not None
|
126
|
+
)
|
101
127
|
if options.recognize_table_structure and not can_do_table_rec:
|
102
|
-
|
103
|
-
|
128
|
+
logger.warning(
|
129
|
+
"Surya table recognition cannot proceed without page reference and scaling factors. Disabling."
|
130
|
+
)
|
131
|
+
options.recognize_table_structure = False
|
104
132
|
|
105
133
|
# Validate classes
|
106
|
-
if options.classes:
|
107
|
-
|
134
|
+
if options.classes:
|
135
|
+
self.validate_classes(options.classes)
|
136
|
+
if options.exclude_classes:
|
137
|
+
self.validate_classes(options.exclude_classes)
|
108
138
|
|
109
139
|
models = self._get_model(options)
|
110
|
-
layout_predictor = models[
|
111
|
-
table_rec_predictor = models[
|
140
|
+
layout_predictor = models["layout"]
|
141
|
+
table_rec_predictor = models["table_rec"]
|
112
142
|
|
113
143
|
input_image = image.convert("RGB")
|
114
144
|
input_image_list = [input_image]
|
115
145
|
|
116
|
-
initial_layout_detections = []
|
146
|
+
initial_layout_detections = [] # Detections relative to input_image
|
117
147
|
tables_to_process = []
|
118
|
-
|
119
|
-
# --- Initial Layout Detection ---
|
148
|
+
|
149
|
+
# --- Initial Layout Detection ---
|
120
150
|
self.logger.debug("Running Surya layout prediction...")
|
121
151
|
layout_predictions = layout_predictor(input_image_list)
|
122
152
|
self.logger.debug(f"Surya prediction returned {len(layout_predictions)} results.")
|
123
|
-
if not layout_predictions:
|
153
|
+
if not layout_predictions:
|
154
|
+
return []
|
124
155
|
prediction = layout_predictions[0]
|
125
156
|
|
126
|
-
normalized_classes_req =
|
127
|
-
|
157
|
+
normalized_classes_req = (
|
158
|
+
{self._normalize_class_name(c) for c in options.classes} if options.classes else None
|
159
|
+
)
|
160
|
+
normalized_classes_excl = (
|
161
|
+
{self._normalize_class_name(c) for c in options.exclude_classes}
|
162
|
+
if options.exclude_classes
|
163
|
+
else set()
|
164
|
+
)
|
128
165
|
|
129
166
|
for layout_box in prediction.bboxes:
|
130
167
|
class_name_orig = layout_box.label
|
131
168
|
normalized_class = self._normalize_class_name(class_name_orig)
|
132
169
|
score = float(layout_box.confidence)
|
133
170
|
|
134
|
-
if score < options.confidence:
|
135
|
-
|
136
|
-
if normalized_class in
|
171
|
+
if score < options.confidence:
|
172
|
+
continue
|
173
|
+
if normalized_classes_req and normalized_class not in normalized_classes_req:
|
174
|
+
continue
|
175
|
+
if normalized_class in normalized_classes_excl:
|
176
|
+
continue
|
137
177
|
|
138
178
|
x_min, y_min, x_max, y_max = map(float, layout_box.bbox)
|
139
179
|
detection_data = {
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
180
|
+
"bbox": (x_min, y_min, x_max, y_max),
|
181
|
+
"class": class_name_orig,
|
182
|
+
"confidence": score,
|
183
|
+
"normalized_class": normalized_class,
|
184
|
+
"source": "layout",
|
185
|
+
"model": "surya",
|
146
186
|
}
|
147
187
|
initial_layout_detections.append(detection_data)
|
148
188
|
|
149
|
-
if options.recognize_table_structure and normalized_class in (
|
189
|
+
if options.recognize_table_structure and normalized_class in (
|
190
|
+
"table",
|
191
|
+
"tableofcontents",
|
192
|
+
):
|
150
193
|
tables_to_process.append(detection_data)
|
151
194
|
|
152
|
-
self.logger.info(
|
195
|
+
self.logger.info(
|
196
|
+
f"Surya initially detected {len(initial_layout_detections)} layout elements matching criteria."
|
197
|
+
)
|
153
198
|
|
154
199
|
# --- Table Structure Recognition (Optional) ---
|
155
200
|
if not options.recognize_table_structure or not tables_to_process:
|
156
|
-
self.logger.debug(
|
201
|
+
self.logger.debug(
|
202
|
+
"Skipping Surya table structure recognition (disabled or no tables found)."
|
203
|
+
)
|
157
204
|
return initial_layout_detections
|
158
205
|
|
159
|
-
self.logger.info(
|
206
|
+
self.logger.info(
|
207
|
+
f"Attempting Surya table structure recognition for {len(tables_to_process)} tables..."
|
208
|
+
)
|
160
209
|
high_res_crops = []
|
161
|
-
pdf_offsets = []
|
162
|
-
|
163
|
-
high_res_dpi = getattr(self._page_ref._parent,
|
164
|
-
|
210
|
+
pdf_offsets = [] # Store (pdf_x0, pdf_y0) for each crop
|
211
|
+
|
212
|
+
high_res_dpi = getattr(self._page_ref._parent, "_config", {}).get(
|
213
|
+
"surya_table_rec_dpi", 192
|
214
|
+
)
|
215
|
+
bbox_padding = getattr(self._page_ref._parent, "_config", {}).get(
|
216
|
+
"surya_table_bbox_padding", 10
|
217
|
+
)
|
165
218
|
pdf_to_highres_scale = high_res_dpi / 72.0
|
166
|
-
|
219
|
+
|
167
220
|
# Render high-res page ONCE
|
168
|
-
self.logger.debug(
|
169
|
-
|
221
|
+
self.logger.debug(
|
222
|
+
f"Rendering page {self._page_ref.number} at {high_res_dpi} DPI for table recognition..."
|
223
|
+
)
|
224
|
+
high_res_page_image = self._page_ref.to_image(
|
225
|
+
resolution=high_res_dpi, include_highlights=False
|
226
|
+
)
|
170
227
|
if not high_res_page_image:
|
171
228
|
raise RuntimeError(f"Failed to render page {self._page_ref.number} at high resolution.")
|
172
|
-
self.logger.debug(
|
229
|
+
self.logger.debug(
|
230
|
+
f" High-res image size: {high_res_page_image.width}x{high_res_page_image.height}"
|
231
|
+
)
|
173
232
|
|
174
233
|
for i, table_detection in enumerate(tables_to_process):
|
175
|
-
img_x0, img_y0, img_x1, img_y1 = table_detection[
|
176
|
-
|
234
|
+
img_x0, img_y0, img_x1, img_y1 = table_detection["bbox"]
|
235
|
+
|
177
236
|
# PDF coords
|
178
237
|
pdf_x0 = img_x0 * img_scale_x
|
179
238
|
pdf_y0 = img_y0 * img_scale_y
|
@@ -183,40 +242,44 @@ class SuryaLayoutDetector(LayoutDetector):
|
|
183
242
|
pdf_y0 = max(0, pdf_y0)
|
184
243
|
pdf_x1 = min(self._page_ref.width, pdf_x1)
|
185
244
|
pdf_y1 = min(self._page_ref.height, pdf_y1)
|
186
|
-
|
245
|
+
|
187
246
|
# High-res image coords
|
188
247
|
hr_x0 = pdf_x0 * pdf_to_highres_scale
|
189
248
|
hr_y0 = pdf_y0 * pdf_to_highres_scale
|
190
249
|
hr_x1 = pdf_x1 * pdf_to_highres_scale
|
191
250
|
hr_y1 = pdf_y1 * pdf_to_highres_scale
|
192
|
-
|
251
|
+
|
193
252
|
# Expand high-res bbox
|
194
253
|
hr_x0_exp, hr_y0_exp, hr_x1_exp, hr_y1_exp = self._expand_bbox(
|
195
|
-
(hr_x0, hr_y0, hr_x1, hr_y1),
|
196
|
-
padding=bbox_padding,
|
197
|
-
max_width=high_res_page_image.width,
|
198
|
-
max_height=high_res_page_image.height
|
254
|
+
(hr_x0, hr_y0, hr_x1, hr_y1),
|
255
|
+
padding=bbox_padding,
|
256
|
+
max_width=high_res_page_image.width,
|
257
|
+
max_height=high_res_page_image.height,
|
199
258
|
)
|
200
259
|
|
201
260
|
crop = high_res_page_image.crop((hr_x0_exp, hr_y0_exp, hr_x1_exp, hr_y1_exp))
|
202
261
|
high_res_crops.append(crop)
|
203
|
-
pdf_offsets.append((pdf_x0, pdf_y0))
|
204
|
-
|
262
|
+
pdf_offsets.append((pdf_x0, pdf_y0))
|
263
|
+
|
205
264
|
if not high_res_crops:
|
206
265
|
self.logger.info("No valid high-resolution table crops generated.")
|
207
266
|
return initial_layout_detections
|
208
267
|
|
209
|
-
structure_detections = []
|
268
|
+
structure_detections = [] # Detections relative to std_res input_image
|
210
269
|
|
211
|
-
# --- Run Table Recognition (will raise error on failure) ---
|
212
|
-
self.logger.debug(
|
270
|
+
# --- Run Table Recognition (will raise error on failure) ---
|
271
|
+
self.logger.debug(
|
272
|
+
f"Running Surya table recognition on {len(high_res_crops)} high-res images..."
|
273
|
+
)
|
213
274
|
table_predictions = table_rec_predictor(high_res_crops)
|
214
275
|
self.logger.debug(f"Surya table recognition returned {len(table_predictions)} results.")
|
215
276
|
|
216
|
-
# --- Process Results ---
|
277
|
+
# --- Process Results ---
|
217
278
|
if len(table_predictions) != len(pdf_offsets):
|
218
|
-
|
219
|
-
|
279
|
+
# This case is less likely if predictor didn't error, but good sanity check
|
280
|
+
raise RuntimeError(
|
281
|
+
f"Mismatch between table inputs ({len(pdf_offsets)}) and predictions ({len(table_predictions)})."
|
282
|
+
)
|
220
283
|
|
221
284
|
for table_pred, (offset_pdf_x0, offset_pdf_y0) in zip(table_predictions, pdf_offsets):
|
222
285
|
# Process Rows
|
@@ -230,11 +293,16 @@ class SuryaLayoutDetector(LayoutDetector):
|
|
230
293
|
img_row_y0 = pdf_row_y0 / img_scale_y
|
231
294
|
img_row_x1 = pdf_row_x1 / img_scale_x
|
232
295
|
img_row_y1 = pdf_row_y1 / img_scale_y
|
233
|
-
structure_detections.append(
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
296
|
+
structure_detections.append(
|
297
|
+
{
|
298
|
+
"bbox": (img_row_x0, img_row_y0, img_row_x1, img_row_y1),
|
299
|
+
"class": "table-row",
|
300
|
+
"confidence": 1.0,
|
301
|
+
"normalized_class": "table-row",
|
302
|
+
"source": "layout",
|
303
|
+
"model": "surya",
|
304
|
+
}
|
305
|
+
)
|
238
306
|
|
239
307
|
# Process Columns
|
240
308
|
for col_box in table_pred.cols:
|
@@ -247,13 +315,17 @@ class SuryaLayoutDetector(LayoutDetector):
|
|
247
315
|
img_col_y0 = pdf_col_y0 / img_scale_y
|
248
316
|
img_col_x1 = pdf_col_x1 / img_scale_x
|
249
317
|
img_col_y1 = pdf_col_y1 / img_scale_y
|
250
|
-
structure_detections.append(
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
318
|
+
structure_detections.append(
|
319
|
+
{
|
320
|
+
"bbox": (img_col_x0, img_col_y0, img_col_x1, img_col_y1),
|
321
|
+
"class": "table-column",
|
322
|
+
"confidence": 1.0,
|
323
|
+
"normalized_class": "table-column",
|
324
|
+
"source": "layout",
|
325
|
+
"model": "surya",
|
326
|
+
}
|
327
|
+
)
|
255
328
|
|
256
329
|
self.logger.info(f"Added {len(structure_detections)} table structure elements.")
|
257
330
|
|
258
331
|
return initial_layout_detections + structure_detections
|
259
|
-
|