Spaces:
Running
on
Zero
Running
on
Zero
carpelan
commited on
Commit
·
d3fc046
1
Parent(s):
688de79
Fixed some texts and added PDF upload functionality
Browse files- app/content/sidebar.md +8 -8
- app/pipelines.py +5 -5
- app/tabs/submit.py +61 -21
- pyproject.toml +1 -0
- requirements.txt +2 -1
- uv.lock +42 -0
app/content/sidebar.md
CHANGED
@@ -1,19 +1,19 @@
|
|
1 |
## HTRflow Demo
|
2 |
-
|
3 |
-
Welcome to the **HTRflow Demo** – a web application developed by the **National Archives of Sweden** in collaboration with [Huminfra](https://www.huminfra.se/). This demo lets you explore how AI transforms historical manuscripts into digital text using [HTRflow](https://ai-riksarkivet.github.io/htrflow/latest).
|
4 |
|
5 |
> Note: This demo application is for demonstration purposes only and is not intended for production use.
|
6 |
> The application is hosted on Hugging Face 🤗 using shared infrastructure, which means there is a daily quota limit on how much you can use the app each day.
|
7 |
|
8 |
-
### Contact
|
9 |
-
|
10 |
-
✉️ [[email protected]](mailto:[email protected])
|
11 |
-
|
12 |
-
### Projects
|
13 |
-
|
14 |
Both the App and HTRflow's code are completely open source. Explore and contribute on GitHub:
|
15 |
|
16 |
- [APP](https://github.com/AI-Riksarkivet/htrflow_app).
|
17 |
- [HTRflow](https://ai-riksarkivet.github.io/htrflow/latest).
|
18 |
|
19 |
If you find our projects useful, please consider giving us a star ⭐!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
## HTRflow Demo
|
2 |
+
Developed by the **National Archives of Sweden** with [Huminfra](https://www.huminfra.se/) that demonstrates AI-powered conversion of historical manuscripts to digital text using [HTRflow](https://ai-riksarkivet.github.io/htrflow/latest).
|
|
|
3 |
|
4 |
> Note: This demo application is for demonstration purposes only and is not intended for production use.
|
5 |
> The application is hosted on Hugging Face 🤗 using shared infrastructure, which means there is a daily quota limit on how much you can use the app each day.
|
6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
Both the App and HTRflow's code are completely open source. Explore and contribute on GitHub:
|
8 |
|
9 |
- [APP](https://github.com/AI-Riksarkivet/htrflow_app).
|
10 |
- [HTRflow](https://ai-riksarkivet.github.io/htrflow/latest).
|
11 |
|
12 |
If you find our projects useful, please consider giving us a star ⭐!
|
13 |
+
|
14 |
+
|
15 |
+
### Contact
|
16 |
+
|
17 |
+
✉️ [[email protected]](mailto:[email protected])
|
18 |
+
|
19 |
+
|
app/pipelines.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
PIPELINES = {
|
2 |
"Swedish - Spreads": {
|
3 |
"file": "app/assets/templates/nested_swe_ra.yaml",
|
4 |
-
"description": "This pipeline works well on handwritten historic documents written in Swedish with multiple text regions. The model is
|
5 |
"examples": [
|
6 |
"R0003364_00005.jpg",
|
7 |
"30002027_00008.jpg",
|
@@ -10,7 +10,7 @@ PIPELINES = {
|
|
10 |
},
|
11 |
"Swedish - Single page and snippets": {
|
12 |
"file": "app/assets/templates/simple_swe_ra.yaml",
|
13 |
-
"description": "This pipeline works well on handwritten historic letters and other documents written in Swedish with only one text region. The model is
|
14 |
"examples": [
|
15 |
"451511_1512_01.jpg",
|
16 |
"A0062408_00006.jpg",
|
@@ -23,14 +23,14 @@ PIPELINES = {
|
|
23 |
"description": "This pipeline works well on handwritten historic letters and other documents written in Norwegian with only one text region. The model is developed by the <a href='https://huggingface.co/Sprakbanken/TrOCR-norhand-v3'>Language Bank</a> at The National Library of Norway.",
|
24 |
"examples": ["norhand_fmgh040_4.jpg"],
|
25 |
},
|
26 |
-
"
|
27 |
"file": "app/assets/templates/simple_medival.yaml",
|
28 |
-
"description": "This pipeline works well for medieval scripts written in single-page running text.
|
29 |
"examples": ["manusscript_kb.png"],
|
30 |
},
|
31 |
"English - Single page and snippets": {
|
32 |
"file": "app/assets/templates/simple_eng_modern.yaml",
|
33 |
-
"description": "This pipeline works well for English text in single page running text.
|
34 |
"examples": ["iam.png"],
|
35 |
},
|
36 |
}
|
|
|
1 |
PIPELINES = {
|
2 |
"Swedish - Spreads": {
|
3 |
"file": "app/assets/templates/nested_swe_ra.yaml",
|
4 |
+
"description": "This pipeline works well on handwritten historic documents written in Swedish with multiple text regions. The HTR model used in the pipeline is <b>Swedish Lion Libre</b> from <a href='https://huggingface.co/Riksarkivet'>the National Archives of Sweden</a>.",
|
5 |
"examples": [
|
6 |
"R0003364_00005.jpg",
|
7 |
"30002027_00008.jpg",
|
|
|
10 |
},
|
11 |
"Swedish - Single page and snippets": {
|
12 |
"file": "app/assets/templates/simple_swe_ra.yaml",
|
13 |
+
"description": "This pipeline works well on handwritten historic letters and other documents written in Swedish with only one text region. The HTR model used in the pipeline is <b>Swedish Lion Libre</b> from <a href='https://huggingface.co/Riksarkivet'>the National Archives of Sweden</a>.",
|
14 |
"examples": [
|
15 |
"451511_1512_01.jpg",
|
16 |
"A0062408_00006.jpg",
|
|
|
23 |
"description": "This pipeline works well on handwritten historic letters and other documents written in Norwegian with only one text region. The model is developed by the <a href='https://huggingface.co/Sprakbanken/TrOCR-norhand-v3'>Language Bank</a> at The National Library of Norway.",
|
24 |
"examples": ["norhand_fmgh040_4.jpg"],
|
25 |
},
|
26 |
+
"Medieval - Single page and snippets": {
|
27 |
"file": "app/assets/templates/simple_medival.yaml",
|
28 |
+
"description": "This pipeline works well for medieval scripts written in single-page running text. The HTR model is from <a href='https://huggingface.co/medieval-data'>Medieval Data</a>, but other models can be selected from here: <a href='https://huggingface.co/collections/medieval-data/trocr-medieval-htr-66871faba03abfbb1b66ab69'>Medieval Models</a>.",
|
29 |
"examples": ["manusscript_kb.png"],
|
30 |
},
|
31 |
"English - Single page and snippets": {
|
32 |
"file": "app/assets/templates/simple_eng_modern.yaml",
|
33 |
+
"description": "This pipeline works well for English text in single page running text. The HTR model is from <a href='https://huggingface.co/microsoft/trocr-base-handwritten'>Microsoft</a>.",
|
34 |
"examples": ["iam.png"],
|
35 |
},
|
36 |
}
|
app/tabs/submit.py
CHANGED
@@ -13,6 +13,7 @@ from gradio_modal import Modal
|
|
13 |
from htrflow.pipeline.pipeline import Pipeline
|
14 |
from htrflow.pipeline.steps import init_step
|
15 |
from htrflow.volume.volume import Collection
|
|
|
16 |
|
17 |
from app.pipelines import PIPELINES
|
18 |
|
@@ -36,7 +37,12 @@ class PipelineWithProgress(Pipeline):
|
|
36 |
@classmethod
|
37 |
def from_config(cls, config: dict[str, str]):
|
38 |
"""Init pipeline from config, ensuring the correct subclass is instantiated."""
|
39 |
-
return cls(
|
|
|
|
|
|
|
|
|
|
|
40 |
|
41 |
def run(self, collection, start=0, progress=None):
|
42 |
"""
|
@@ -95,7 +101,9 @@ def run_htrflow(custom_template_yaml, batch_image_gallery, progress=gr.Progress(
|
|
95 |
|
96 |
pipe = PipelineWithProgress.from_config(config)
|
97 |
|
98 |
-
gr.Info(
|
|
|
|
|
99 |
progress(0.1, desc="HTRflow: Processing")
|
100 |
|
101 |
collection.label = "demo_output"
|
@@ -192,7 +200,7 @@ def get_image_from_image_id(image_id):
|
|
192 |
def get_images_from_iiif_manifest(iiif_manifest_url, max_images=20, height=1200):
|
193 |
"""
|
194 |
Read images from a v2/v3 IIIF manifest, limited to max_images.
|
195 |
-
|
196 |
Arguments:
|
197 |
iiif_manifest_url: URL to IIIF manifest
|
198 |
height: Max height of returned images
|
@@ -201,7 +209,7 @@ def get_images_from_iiif_manifest(iiif_manifest_url, max_images=20, height=1200)
|
|
201 |
try:
|
202 |
buffer = io.BytesIO()
|
203 |
c = pycurl.Curl()
|
204 |
-
|
205 |
c.setopt(c.URL, iiif_manifest_url)
|
206 |
c.setopt(c.WRITEDATA, buffer)
|
207 |
c.setopt(c.CAINFO, certifi.where())
|
@@ -211,42 +219,48 @@ def get_images_from_iiif_manifest(iiif_manifest_url, max_images=20, height=1200)
|
|
211 |
c.setopt(c.TIMEOUT, 10)
|
212 |
c.setopt(c.NOSIGNAL, 1)
|
213 |
c.setopt(c.USERAGENT, "curl/7.68.0")
|
214 |
-
|
215 |
c.perform()
|
216 |
-
|
217 |
http_code = c.getinfo(c.RESPONSE_CODE)
|
218 |
if http_code != 200:
|
219 |
raise Exception(f"HTTP Error: {http_code}")
|
220 |
-
|
221 |
manifest = buffer.getvalue().decode("utf-8")
|
222 |
c.close()
|
223 |
-
|
224 |
except pycurl.error as e:
|
225 |
error_code, error_msg = e.args
|
226 |
-
raise Exception(
|
227 |
-
|
|
|
|
|
228 |
# Hacky solution to get all images regardless of API version - treat
|
229 |
# the manifest as a string and match everything that looks like an IIIF
|
230 |
# image URL.
|
231 |
pattern = r'(?P<identifier>https?://[^"\s]*)/(?P<region>[^"\s]*?)/(?P<size>[^"\s]*?)/(?P<rotation>!?\d*?)/(?P<quality>[^"\s]*?)\.(?P<format>jpg|tif|png|gif|jp2|pdf|webp)'
|
232 |
-
|
233 |
-
images =
|
234 |
-
|
|
|
|
|
235 |
for match in re.findall(pattern, manifest):
|
236 |
identifier, _, _, _, _, format_ = match
|
237 |
images.add(f"{identifier}/full/{height},/0/default.{format_}")
|
238 |
-
|
239 |
# Stop adding images if we've reached the maximum
|
240 |
if len(images) >= max_images:
|
241 |
break
|
242 |
-
|
243 |
# Sort and limit the results to max_images
|
244 |
return sorted(images)[:max_images], gr.update(visible=True)
|
245 |
|
246 |
|
247 |
with gr.Blocks() as submit:
|
248 |
gr.Markdown("# Upload")
|
249 |
-
gr.Markdown(
|
|
|
|
|
250 |
|
251 |
collection_submit_state = gr.State()
|
252 |
|
@@ -293,11 +307,16 @@ with gr.Blocks() as submit:
|
|
293 |
"Use an image from a IIIF manifest by pasting a IIIF manifest URL. Press enter to submit."
|
294 |
),
|
295 |
placeholder="",
|
296 |
-
scale=0
|
297 |
)
|
298 |
-
max_images_iiif_manifest= gr.Number(
|
|
|
|
|
|
|
299 |
label="Number of image to return from IIIF manifest",
|
300 |
-
minimum=1,
|
|
|
|
|
301 |
iiif_gallery = gr.Gallery(
|
302 |
interactive=False,
|
303 |
columns=4,
|
@@ -314,6 +333,18 @@ with gr.Blocks() as submit:
|
|
314 |
placeholder="https://example.com/image.jpg",
|
315 |
)
|
316 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
317 |
with gr.Column(variant="panel", elem_classes="panel-with-border"):
|
318 |
gr.Markdown("## Settings")
|
319 |
gr.Markdown(
|
@@ -370,7 +401,7 @@ with gr.Blocks() as submit:
|
|
370 |
)
|
371 |
|
372 |
with gr.Row():
|
373 |
-
run_button = gr.Button("
|
374 |
|
375 |
@batch_image_gallery.upload(
|
376 |
inputs=batch_image_gallery,
|
@@ -385,9 +416,17 @@ with gr.Blocks() as submit:
|
|
385 |
image_id.submit(get_image_from_image_id, image_id, batch_image_gallery).then(
|
386 |
fn=lambda: "Swedish - Spreads", outputs=pipeline_dropdown
|
387 |
)
|
388 |
-
iiif_manifest_url.submit(
|
|
|
|
|
|
|
|
|
389 |
image_url.submit(lambda url: [url], image_url, batch_image_gallery)
|
390 |
|
|
|
|
|
|
|
|
|
391 |
run_button.click(
|
392 |
fn=run_htrflow,
|
393 |
inputs=[custom_template_yaml, batch_image_gallery],
|
@@ -398,5 +437,6 @@ with gr.Blocks() as submit:
|
|
398 |
examples.select(get_selected_example_pipeline, None, pipeline_dropdown)
|
399 |
|
400 |
iiif_gallery.select(get_selected_example_image, None, batch_image_gallery)
|
|
|
401 |
|
402 |
edit_pipeline_button.click(lambda: Modal(visible=True), None, edit_pipeline_modal)
|
|
|
13 |
from htrflow.pipeline.pipeline import Pipeline
|
14 |
from htrflow.pipeline.steps import init_step
|
15 |
from htrflow.volume.volume import Collection
|
16 |
+
from pdf2image import convert_from_path
|
17 |
|
18 |
from app.pipelines import PIPELINES
|
19 |
|
|
|
37 |
@classmethod
|
38 |
def from_config(cls, config: dict[str, str]):
|
39 |
"""Init pipeline from config, ensuring the correct subclass is instantiated."""
|
40 |
+
return cls(
|
41 |
+
[
|
42 |
+
init_step(step["step"], step.get("settings", {}))
|
43 |
+
for step in config["steps"]
|
44 |
+
]
|
45 |
+
)
|
46 |
|
47 |
def run(self, collection, start=0, progress=None):
|
48 |
"""
|
|
|
101 |
|
102 |
pipe = PipelineWithProgress.from_config(config)
|
103 |
|
104 |
+
gr.Info(
|
105 |
+
f"HTRflow: processing {len(images)} {'image' if len(images) == 1 else 'images'}."
|
106 |
+
)
|
107 |
progress(0.1, desc="HTRflow: Processing")
|
108 |
|
109 |
collection.label = "demo_output"
|
|
|
200 |
def get_images_from_iiif_manifest(iiif_manifest_url, max_images=20, height=1200):
|
201 |
"""
|
202 |
Read images from a v2/v3 IIIF manifest, limited to max_images.
|
203 |
+
|
204 |
Arguments:
|
205 |
iiif_manifest_url: URL to IIIF manifest
|
206 |
height: Max height of returned images
|
|
|
209 |
try:
|
210 |
buffer = io.BytesIO()
|
211 |
c = pycurl.Curl()
|
212 |
+
|
213 |
c.setopt(c.URL, iiif_manifest_url)
|
214 |
c.setopt(c.WRITEDATA, buffer)
|
215 |
c.setopt(c.CAINFO, certifi.where())
|
|
|
219 |
c.setopt(c.TIMEOUT, 10)
|
220 |
c.setopt(c.NOSIGNAL, 1)
|
221 |
c.setopt(c.USERAGENT, "curl/7.68.0")
|
222 |
+
|
223 |
c.perform()
|
224 |
+
|
225 |
http_code = c.getinfo(c.RESPONSE_CODE)
|
226 |
if http_code != 200:
|
227 |
raise Exception(f"HTTP Error: {http_code}")
|
228 |
+
|
229 |
manifest = buffer.getvalue().decode("utf-8")
|
230 |
c.close()
|
231 |
+
|
232 |
except pycurl.error as e:
|
233 |
error_code, error_msg = e.args
|
234 |
+
raise Exception(
|
235 |
+
f"Could not fetch IIIF manifest from {iiif_manifest_url} ({error_msg})"
|
236 |
+
)
|
237 |
+
|
238 |
# Hacky solution to get all images regardless of API version - treat
|
239 |
# the manifest as a string and match everything that looks like an IIIF
|
240 |
# image URL.
|
241 |
pattern = r'(?P<identifier>https?://[^"\s]*)/(?P<region>[^"\s]*?)/(?P<size>[^"\s]*?)/(?P<rotation>!?\d*?)/(?P<quality>[^"\s]*?)\.(?P<format>jpg|tif|png|gif|jp2|pdf|webp)'
|
242 |
+
|
243 |
+
images = (
|
244 |
+
set()
|
245 |
+
) # create a set to eliminate duplicates (e.g. thumbnails and fullsize images)
|
246 |
+
|
247 |
for match in re.findall(pattern, manifest):
|
248 |
identifier, _, _, _, _, format_ = match
|
249 |
images.add(f"{identifier}/full/{height},/0/default.{format_}")
|
250 |
+
|
251 |
# Stop adding images if we've reached the maximum
|
252 |
if len(images) >= max_images:
|
253 |
break
|
254 |
+
|
255 |
# Sort and limit the results to max_images
|
256 |
return sorted(images)[:max_images], gr.update(visible=True)
|
257 |
|
258 |
|
259 |
with gr.Blocks() as submit:
|
260 |
gr.Markdown("# Upload")
|
261 |
+
gr.Markdown(
|
262 |
+
"Select or upload the image you want to transcribe. Most common image formats are supported and you can upload max 5 images at a time in this hosted demo."
|
263 |
+
)
|
264 |
|
265 |
collection_submit_state = gr.State()
|
266 |
|
|
|
307 |
"Use an image from a IIIF manifest by pasting a IIIF manifest URL. Press enter to submit."
|
308 |
),
|
309 |
placeholder="",
|
310 |
+
scale=0,
|
311 |
)
|
312 |
+
max_images_iiif_manifest = gr.Number(
|
313 |
+
value=20,
|
314 |
+
min_width=50,
|
315 |
+
scale=0,
|
316 |
label="Number of image to return from IIIF manifest",
|
317 |
+
minimum=1,
|
318 |
+
visible=False,
|
319 |
+
)
|
320 |
iiif_gallery = gr.Gallery(
|
321 |
interactive=False,
|
322 |
columns=4,
|
|
|
333 |
placeholder="https://example.com/image.jpg",
|
334 |
)
|
335 |
|
336 |
+
with gr.Tab("PDF"):
|
337 |
+
pdf_file = gr.File(label="PDF", file_types=[".pdf"])
|
338 |
+
|
339 |
+
pdf_gallery = gr.Gallery(
|
340 |
+
interactive=False,
|
341 |
+
columns=4,
|
342 |
+
allow_preview=False,
|
343 |
+
container=False,
|
344 |
+
show_label=False,
|
345 |
+
object_fit="scale-down",
|
346 |
+
)
|
347 |
+
|
348 |
with gr.Column(variant="panel", elem_classes="panel-with-border"):
|
349 |
gr.Markdown("## Settings")
|
350 |
gr.Markdown(
|
|
|
401 |
)
|
402 |
|
403 |
with gr.Row():
|
404 |
+
run_button = gr.Button("Run HTR", variant="primary", scale=0, min_width=200)
|
405 |
|
406 |
@batch_image_gallery.upload(
|
407 |
inputs=batch_image_gallery,
|
|
|
416 |
image_id.submit(get_image_from_image_id, image_id, batch_image_gallery).then(
|
417 |
fn=lambda: "Swedish - Spreads", outputs=pipeline_dropdown
|
418 |
)
|
419 |
+
iiif_manifest_url.submit(
|
420 |
+
get_images_from_iiif_manifest,
|
421 |
+
[iiif_manifest_url, max_images_iiif_manifest],
|
422 |
+
[iiif_gallery, max_images_iiif_manifest],
|
423 |
+
)
|
424 |
image_url.submit(lambda url: [url], image_url, batch_image_gallery)
|
425 |
|
426 |
+
pdf_file.upload(
|
427 |
+
lambda imgs: convert_from_path(imgs), inputs=pdf_file, outputs=pdf_gallery
|
428 |
+
)
|
429 |
+
|
430 |
run_button.click(
|
431 |
fn=run_htrflow,
|
432 |
inputs=[custom_template_yaml, batch_image_gallery],
|
|
|
437 |
examples.select(get_selected_example_pipeline, None, pipeline_dropdown)
|
438 |
|
439 |
iiif_gallery.select(get_selected_example_image, None, batch_image_gallery)
|
440 |
+
pdf_gallery.select(get_selected_example_image, None, batch_image_gallery)
|
441 |
|
442 |
edit_pipeline_button.click(lambda: Modal(visible=True), None, edit_pipeline_modal)
|
pyproject.toml
CHANGED
@@ -24,6 +24,7 @@ dependencies = [
|
|
24 |
"dill>=0.3.9",
|
25 |
"spaces>=0.32.0",
|
26 |
"pycurl",
|
|
|
27 |
]
|
28 |
|
29 |
[project.urls]
|
|
|
24 |
"dill>=0.3.9",
|
25 |
"spaces>=0.32.0",
|
26 |
"pycurl",
|
27 |
+
"pdf2image>=1.17.0",
|
28 |
]
|
29 |
|
30 |
[project.urls]
|
requirements.txt
CHANGED
@@ -3,4 +3,5 @@ gradio>=5.20.1
|
|
3 |
tqdm>=4.67.1
|
4 |
gradio-modal>=0.0.4
|
5 |
dill>=0.3.9
|
6 |
-
pycurl>=7.45.6
|
|
|
|
3 |
tqdm>=4.67.1
|
4 |
gradio-modal>=0.0.4
|
5 |
dill>=0.3.9
|
6 |
+
pycurl>=7.45.6
|
7 |
+
pdf2image>=1.17.0
|
uv.lock
CHANGED
@@ -543,6 +543,8 @@ dependencies = [
|
|
543 |
{ name = "gradio" },
|
544 |
{ name = "gradio-modal" },
|
545 |
{ name = "htrflow" },
|
|
|
|
|
546 |
{ name = "spaces" },
|
547 |
{ name = "tqdm" },
|
548 |
]
|
@@ -560,6 +562,8 @@ requires-dist = [
|
|
560 |
{ name = "gradio", specifier = ">=5.17.0" },
|
561 |
{ name = "gradio-modal", specifier = ">=0.0.4" },
|
562 |
{ name = "htrflow", specifier = "==0.2.5" },
|
|
|
|
|
563 |
{ name = "spaces", specifier = ">=0.32.0" },
|
564 |
{ name = "tqdm", specifier = ">=4.67.1" },
|
565 |
]
|
@@ -1181,6 +1185,18 @@ wheels = [
|
|
1181 |
{ url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 },
|
1182 |
]
|
1183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1184 |
[[package]]
|
1185 |
name = "pfzy"
|
1186 |
version = "0.3.4"
|
@@ -1357,6 +1373,32 @@ wheels = [
|
|
1357 |
{ url = "https://files.pythonhosted.org/packages/2d/c7/a0d3356f3074ac548afefa515ff46f3bea011deca607faf1c09b26dd5330/pycryptodomex-3.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:27e84eeff24250ffec32722334749ac2a57a5fd60332cd6a0680090e7c42877e", size = 1792099 },
|
1358 |
]
|
1359 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1360 |
[[package]]
|
1361 |
name = "pydantic"
|
1362 |
version = "2.10.6"
|
|
|
543 |
{ name = "gradio" },
|
544 |
{ name = "gradio-modal" },
|
545 |
{ name = "htrflow" },
|
546 |
+
{ name = "pdf2image" },
|
547 |
+
{ name = "pycurl" },
|
548 |
{ name = "spaces" },
|
549 |
{ name = "tqdm" },
|
550 |
]
|
|
|
562 |
{ name = "gradio", specifier = ">=5.17.0" },
|
563 |
{ name = "gradio-modal", specifier = ">=0.0.4" },
|
564 |
{ name = "htrflow", specifier = "==0.2.5" },
|
565 |
+
{ name = "pdf2image", specifier = ">=1.17.0" },
|
566 |
+
{ name = "pycurl" },
|
567 |
{ name = "spaces", specifier = ">=0.32.0" },
|
568 |
{ name = "tqdm", specifier = ">=4.67.1" },
|
569 |
]
|
|
|
1185 |
{ url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 },
|
1186 |
]
|
1187 |
|
1188 |
+
[[package]]
|
1189 |
+
name = "pdf2image"
|
1190 |
+
version = "1.17.0"
|
1191 |
+
source = { registry = "https://pypi.org/simple" }
|
1192 |
+
dependencies = [
|
1193 |
+
{ name = "pillow" },
|
1194 |
+
]
|
1195 |
+
sdist = { url = "https://files.pythonhosted.org/packages/00/d8/b280f01045555dc257b8153c00dee3bc75830f91a744cd5f84ef3a0a64b1/pdf2image-1.17.0.tar.gz", hash = "sha256:eaa959bc116b420dd7ec415fcae49b98100dda3dd18cd2fdfa86d09f112f6d57", size = 12811 }
|
1196 |
+
wheels = [
|
1197 |
+
{ url = "https://files.pythonhosted.org/packages/62/33/61766ae033518957f877ab246f87ca30a85b778ebaad65b7f74fa7e52988/pdf2image-1.17.0-py3-none-any.whl", hash = "sha256:ecdd58d7afb810dffe21ef2b1bbc057ef434dabbac6c33778a38a3f7744a27e2", size = 11618 },
|
1198 |
+
]
|
1199 |
+
|
1200 |
[[package]]
|
1201 |
name = "pfzy"
|
1202 |
version = "0.3.4"
|
|
|
1373 |
{ url = "https://files.pythonhosted.org/packages/2d/c7/a0d3356f3074ac548afefa515ff46f3bea011deca607faf1c09b26dd5330/pycryptodomex-3.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:27e84eeff24250ffec32722334749ac2a57a5fd60332cd6a0680090e7c42877e", size = 1792099 },
|
1374 |
]
|
1375 |
|
1376 |
+
[[package]]
|
1377 |
+
name = "pycurl"
|
1378 |
+
version = "7.45.6"
|
1379 |
+
source = { registry = "https://pypi.org/simple" }
|
1380 |
+
sdist = { url = "https://files.pythonhosted.org/packages/71/35/fe5088d914905391ef2995102cf5e1892cf32cab1fa6ef8130631c89ec01/pycurl-7.45.6.tar.gz", hash = "sha256:2b73e66b22719ea48ac08a93fc88e57ef36d46d03cb09d972063c9aa86bb74e6", size = 239470 }
|
1381 |
+
wheels = [
|
1382 |
+
{ url = "https://files.pythonhosted.org/packages/31/11/a491677a902053e2720c29e9bc743313e156579da71fcf281b0883e36674/pycurl-7.45.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c31b390f1e2cd4525828f1bb78c1f825c0aab5d1588228ed71b22c4784bdb593", size = 3499748 },
|
1383 |
+
{ url = "https://files.pythonhosted.org/packages/22/b0/c5d90c97741e9896c76490edead6056adb87d7083e3bf4761218072cf10d/pycurl-7.45.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:942b352b69184cb26920db48e0c5cb95af39874b57dbe27318e60f1e68564e37", size = 3640625 },
|
1384 |
+
{ url = "https://files.pythonhosted.org/packages/09/63/c7ec2bfedabd71cd71acaed094f3a831ea06198e3b078a199fc353178728/pycurl-7.45.6-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3441ee77e830267aa6e2bb43b29fd5f8a6bd6122010c76a6f0bf84462e9ea9c7", size = 4879310 },
|
1385 |
+
{ url = "https://files.pythonhosted.org/packages/ac/b4/1682fe9d8df5223f6dffa34fe99886ec1a02b136d2cea1f5ec4ad46f1548/pycurl-7.45.6-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:2a21e13278d7553a04b421676c458449f6c10509bebf04993f35154b06ee2b20", size = 4561745 },
|
1386 |
+
{ url = "https://files.pythonhosted.org/packages/be/57/0039590b322ef00a1e5664fab888f26bc18b891d0675340d151a106101ca/pycurl-7.45.6-cp310-cp310-win32.whl", hash = "sha256:d0b5501d527901369aba307354530050f56cd102410f2a3bacd192dc12c645e3", size = 2607483 },
|
1387 |
+
{ url = "https://files.pythonhosted.org/packages/23/c1/7e6c29a814de032d21fe40e679855a6b359f3b2099ab48f9bef346d88b41/pycurl-7.45.6-cp310-cp310-win_amd64.whl", hash = "sha256:abe1b204a2f96f2eebeaf93411f03505b46d151ef6d9d89326e6dece7b3a008a", size = 3135985 },
|
1388 |
+
{ url = "https://files.pythonhosted.org/packages/85/45/0cc7060dd0a69dbe4806c71460e7a388e2b19097298eb2b4ac4acddea2a1/pycurl-7.45.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6f57ad26d6ab390391ad5030790e3f1a831c1ee54ad3bf969eb378f5957eeb0a", size = 3498594 },
|
1389 |
+
{ url = "https://files.pythonhosted.org/packages/f4/a9/07a54c81b3fc6d5634e7298f657f9b8fd3d907b1245ef0d16f3b60f67835/pycurl-7.45.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6fd295f03c928da33a00f56c91765195155d2ac6f12878f6e467830b5dce5f5", size = 3640087 },
|
1390 |
+
{ url = "https://files.pythonhosted.org/packages/59/bc/fb422d2f7568e5ebdbbf34ecfb3e80303d4fa4679319f7d068fb769c521e/pycurl-7.45.6-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:334721ce1ccd71ff8e405470768b3d221b4393570ccc493fcbdbef4cd62e91ed", size = 4884164 },
|
1391 |
+
{ url = "https://files.pythonhosted.org/packages/9e/5a/d6708fbf2727a256983513828250c097aff6a99fc222071a030f108f41ba/pycurl-7.45.6-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:0cd6b7794268c17f3c660162ed6381769ce0ad260331ef49191418dfc3a2d61a", size = 4566286 },
|
1392 |
+
{ url = "https://files.pythonhosted.org/packages/e2/c6/ab4e44ac30853b53239af03eeb4e798851c4bfbe182f3aeec29dc7f11e41/pycurl-7.45.6-cp311-cp311-win32.whl", hash = "sha256:357ea634395310085b9d5116226ac5ec218a6ceebf367c2451ebc8d63a6e9939", size = 2607279 },
|
1393 |
+
{ url = "https://files.pythonhosted.org/packages/c4/90/b26b72fc8e10cd3ab9fb2cfc995106348e2916779de7feb629772f67d8d2/pycurl-7.45.6-cp311-cp311-win_amd64.whl", hash = "sha256:878ae64484db18f8f10ba99bffc83fefb4fe8f5686448754f93ec32fa4e4ee93", size = 3135613 },
|
1394 |
+
{ url = "https://files.pythonhosted.org/packages/0a/31/01e3ae56940c9aff2886f06a660641ec779d7265aef8b3bd981add67f3a9/pycurl-7.45.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c872d4074360964697c39c1544fe8c91bfecbff27c1cdda1fee5498e5fdadcda", size = 3499118 },
|
1395 |
+
{ url = "https://files.pythonhosted.org/packages/02/b2/365423ac0f8d13afffb28e6f3d6bf1aae242934f16ae3eda5f3615c0e978/pycurl-7.45.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56d1197eadd5774582b259cde4364357da71542758d8e917f91cc6ed7ed5b262", size = 3640501 },
|
1396 |
+
{ url = "https://files.pythonhosted.org/packages/67/d6/7d24b60ee878167e3cef6d37fad7d15cf660c0d510539d1417fd59665d1b/pycurl-7.45.6-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8a99e56d2575aa74c48c0cd08852a65d5fc952798f76a34236256d5589bf5aa0", size = 4893499 },
|
1397 |
+
{ url = "https://files.pythonhosted.org/packages/24/8c/c84df085310f2489e293adc7880f82339f50dcd6cf9dba2750b81eaf11d4/pycurl-7.45.6-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c04230b9e9cfdca9cf3eb09a0bec6cf2f084640f1f1ca1929cca51411af85de2", size = 4576761 },
|
1398 |
+
{ url = "https://files.pythonhosted.org/packages/bc/a2/682c909d93c27b6c6cd369c5427fee87c525e02c56e1a985c150a2d451fd/pycurl-7.45.6-cp312-cp312-win32.whl", hash = "sha256:ae893144b82d72d95c932ebdeb81fc7e9fde758e5ecd5dd10ad5b67f34a8b8ee", size = 2605115 },
|
1399 |
+
{ url = "https://files.pythonhosted.org/packages/71/42/b1189c859cec1bdb00c93177e8bdee7c75b23a0e30eba41da637b40444c1/pycurl-7.45.6-cp312-cp312-win_amd64.whl", hash = "sha256:56f841b6f2f7a8b2d3051b9ceebd478599dbea3c8d1de8fb9333c895d0c1eea5", size = 3132702 },
|
1400 |
+
]
|
1401 |
+
|
1402 |
[[package]]
|
1403 |
name = "pydantic"
|
1404 |
version = "2.10.6"
|