BabelDOC 0.1.23__tar.gz → 0.1.24__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.
Files changed (82) hide show
  1. {babeldoc-0.1.23 → babeldoc-0.1.24}/PKG-INFO +2 -1
  2. babeldoc-0.1.24/babeldoc/__init__.py +1 -0
  3. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/const.py +1 -1
  4. babeldoc-0.1.24/babeldoc/document_il/babeldoc_exception/BabelDOCException.py +3 -0
  5. babeldoc-0.1.24/babeldoc/document_il/midend/detect_scanned_file.py +124 -0
  6. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/midend/layout_parser.py +1 -1
  7. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/docvision/rpc_doclayout.py +18 -15
  8. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/high_level.py +24 -11
  9. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/main.py +3 -3
  10. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/progress_monitor.py +29 -8
  11. {babeldoc-0.1.23 → babeldoc-0.1.24}/pyproject.toml +3 -2
  12. {babeldoc-0.1.23 → babeldoc-0.1.24}/uv.lock +168 -1
  13. babeldoc-0.1.23/babeldoc/__init__.py +0 -1
  14. {babeldoc-0.1.23 → babeldoc-0.1.24}/.cursorignore +0 -0
  15. {babeldoc-0.1.23 → babeldoc-0.1.24}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
  16. {babeldoc-0.1.23 → babeldoc-0.1.24}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
  17. {babeldoc-0.1.23 → babeldoc-0.1.24}/.github/dependabot.yml +0 -0
  18. {babeldoc-0.1.23 → babeldoc-0.1.24}/.github/labels.yml +0 -0
  19. {babeldoc-0.1.23 → babeldoc-0.1.24}/.github/release-drafter.yml +0 -0
  20. {babeldoc-0.1.23 → babeldoc-0.1.24}/.github/workflows/codeql.yml +0 -0
  21. {babeldoc-0.1.23 → babeldoc-0.1.24}/.github/workflows/docs.yml +0 -0
  22. {babeldoc-0.1.23 → babeldoc-0.1.24}/.github/workflows/labeler.yml +0 -0
  23. {babeldoc-0.1.23 → babeldoc-0.1.24}/.github/workflows/publish-to-pypi.yml +0 -0
  24. {babeldoc-0.1.23 → babeldoc-0.1.24}/.github/workflows/test.yml +0 -0
  25. {babeldoc-0.1.23 → babeldoc-0.1.24}/.gitignore +0 -0
  26. {babeldoc-0.1.23 → babeldoc-0.1.24}/.pre-commit-config.yaml +0 -0
  27. {babeldoc-0.1.23 → babeldoc-0.1.24}/LICENSE +0 -0
  28. {babeldoc-0.1.23 → babeldoc-0.1.24}/README.md +0 -0
  29. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/assets/assets.py +0 -0
  30. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/assets/embedding_assets_metadata.py +0 -0
  31. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/asynchronize/__init__.py +0 -0
  32. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/converter.py +0 -0
  33. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/__init__.py +0 -0
  34. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/backend/__init__.py +0 -0
  35. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/backend/pdf_creater.py +0 -0
  36. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/frontend/__init__.py +0 -0
  37. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/frontend/il_creater.py +0 -0
  38. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/il_version_1.py +0 -0
  39. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/il_version_1.rnc +0 -0
  40. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/il_version_1.rng +0 -0
  41. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/il_version_1.xsd +0 -0
  42. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/midend/__init__.py +0 -0
  43. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/midend/add_debug_information.py +0 -0
  44. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/midend/il_translator.py +0 -0
  45. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/midend/paragraph_finder.py +0 -0
  46. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/midend/remove_descent.py +0 -0
  47. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/midend/styles_and_formulas.py +0 -0
  48. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/midend/typesetting.py +0 -0
  49. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/translator/__init__.py +0 -0
  50. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/translator/cache.py +0 -0
  51. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/translator/translator.py +0 -0
  52. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/utils/fontmap.py +0 -0
  53. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/utils/layout_helper.py +0 -0
  54. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/utils/style_helper.py +0 -0
  55. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/document_il/xml_converter.py +0 -0
  56. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/docvision/README.md +0 -0
  57. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/docvision/__init__.py +0 -0
  58. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/docvision/doclayout.py +0 -0
  59. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/pdfinterp.py +0 -0
  60. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/tools/generate_font_metadata.py +0 -0
  61. {babeldoc-0.1.23 → babeldoc-0.1.24}/babeldoc/translation_config.py +0 -0
  62. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/CODE_OF_CONDUCT.md +0 -0
  63. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/CONTRIBUTING.md +0 -0
  64. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/CONTRIBUTOR_REWARD.md +0 -0
  65. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md +0 -0
  66. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/ImplementationDetails/ILTranslator/ILTranslator.md +0 -0
  67. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/ImplementationDetails/PDFCreation/PDFCreation.md +0 -0
  68. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/ImplementationDetails/PDFParsing/PDFParsing.md +0 -0
  69. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md +0 -0
  70. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/ImplementationDetails/README.md +0 -0
  71. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/ImplementationDetails/StylesAndFormulas/StylesAndFormulas.md +0 -0
  72. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/ImplementationDetails/Typesetting/Typesetting.md +0 -0
  73. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/README.md +0 -0
  74. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/deploy.sh +0 -0
  75. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/images/babeldoc-banner.png +0 -0
  76. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/images/babeldoc-contributor_reward_example.png +0 -0
  77. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/images/babeldoc-preview.gif +0 -0
  78. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/index.md +0 -0
  79. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/intro-to-pdf-object.md +0 -0
  80. {babeldoc-0.1.23 → babeldoc-0.1.24}/docs/requirements.txt +0 -0
  81. {babeldoc-0.1.23 → babeldoc-0.1.24}/mkdocs.yml +0 -0
  82. {babeldoc-0.1.23 → babeldoc-0.1.24}/tests/test_translation_config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: BabelDOC
3
- Version: 0.1.23
3
+ Version: 0.1.24
4
4
  Summary: Yet Another Document Translator
5
5
  Project-URL: Homepage, https://github.com/funstory-ai/BabelDOC
6
6
  Project-URL: Issues, https://github.com/funstory-ai/BabelDOC/issues
@@ -26,6 +26,7 @@ Requires-Dist: peewee>=3.17.8
26
26
  Requires-Dist: pydantic>=2.10.6
27
27
  Requires-Dist: pymupdf>=1.25.1
28
28
  Requires-Dist: rich>=13.9.4
29
+ Requires-Dist: scikit-image>=0.25.2
29
30
  Requires-Dist: tenacity>=9.0.0
30
31
  Requires-Dist: toml>=0.10.2
31
32
  Requires-Dist: tqdm>=4.67.1
@@ -0,0 +1 @@
1
+ __version__ = "0.1.24"
@@ -2,7 +2,7 @@ import shutil
2
2
  import subprocess
3
3
  from pathlib import Path
4
4
 
5
- __version__ = "0.1.23"
5
+ __version__ = "0.1.24"
6
6
 
7
7
  CACHE_FOLDER = Path.home() / ".cache" / "babeldoc"
8
8
 
@@ -0,0 +1,3 @@
1
+ class ScannedPDFError(Exception):
2
+ def __init__(self, message):
3
+ super().__init__(message)
@@ -0,0 +1,124 @@
1
+ import logging
2
+
3
+ import cv2
4
+ import numpy as np
5
+ import pymupdf
6
+ from skimage.metrics import structural_similarity
7
+
8
+ from babeldoc.document_il import il_version_1
9
+ from babeldoc.document_il.babeldoc_exception.BabelDOCException import ScannedPDFError
10
+ from babeldoc.document_il.utils.style_helper import GREEN
11
+ from babeldoc.translation_config import TranslationConfig
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class DetectScannedFile:
17
+ stage_name = "DetectScannedFile"
18
+
19
+ def __init__(self, translation_config: TranslationConfig):
20
+ self.translation_config = translation_config
21
+
22
+ def _save_debug_box_to_page(self, page: il_version_1.Page, similarity: float):
23
+ """Save debug boxes and text labels to the PDF page."""
24
+ if not self.translation_config.debug:
25
+ return
26
+
27
+ color = GREEN
28
+
29
+ # Create text label at top-left corner
30
+ # Note: PDF coordinates are from bottom-left,
31
+ # so we use y2 for top position
32
+ style = il_version_1.PdfStyle(
33
+ font_id="china-ss",
34
+ font_size=4,
35
+ graphic_state=color,
36
+ )
37
+ page_width = page.cropbox.box.x2 - page.cropbox.box.x
38
+ page_height = page.cropbox.box.y2 - page.cropbox.box.y
39
+ unicode = f"scanned score: {similarity * 100:.2f} %"
40
+ page.pdf_paragraph.append(
41
+ il_version_1.PdfParagraph(
42
+ first_line_indent=False,
43
+ box=il_version_1.Box(
44
+ x=page.cropbox.box.x + page_width * 0.03,
45
+ y=page.cropbox.box.y,
46
+ x2=page.cropbox.box.x2,
47
+ y2=page.cropbox.box.y2 - page_height * 0.03,
48
+ ),
49
+ vertical=False,
50
+ pdf_style=style,
51
+ unicode=unicode,
52
+ pdf_paragraph_composition=[
53
+ il_version_1.PdfParagraphComposition(
54
+ pdf_same_style_unicode_characters=il_version_1.PdfSameStyleUnicodeCharacters(
55
+ unicode=unicode,
56
+ pdf_style=style,
57
+ debug_info=True,
58
+ ),
59
+ ),
60
+ ],
61
+ xobj_id=-1,
62
+ ),
63
+ )
64
+
65
+ def process(self, docs: il_version_1.Document):
66
+ """Generate layouts for all pages that need to be translated."""
67
+ # Get pages that need to be translated
68
+ pages_to_translate = [
69
+ page
70
+ for page in docs.page
71
+ if self.translation_config.should_translate_page(page.page_number + 1)
72
+ ]
73
+ mupdf = pymupdf.open(self.translation_config.get_working_file_path("input.pdf"))
74
+ total = len(pages_to_translate)
75
+ threshold = 0.8 * total
76
+ threshold = max(threshold, 1)
77
+ scanned = 0
78
+ non_scanned = 0
79
+ non_scanned_threshold = total - threshold
80
+ with self.translation_config.progress_monitor.stage_start(
81
+ self.stage_name,
82
+ total,
83
+ ) as progress:
84
+ for page in pages_to_translate:
85
+ if scanned < threshold and non_scanned < non_scanned_threshold:
86
+ # Only continue detection if both counts are below thresholds
87
+ is_scanned = self.detect_page_is_scanned(page, mupdf)
88
+ if is_scanned:
89
+ scanned += 1
90
+ else:
91
+ non_scanned += 1
92
+ else:
93
+ # We have enough information to determine document type
94
+ non_scanned += 1
95
+ progress.advance(1)
96
+
97
+ if scanned > threshold:
98
+ logger.warning(
99
+ f"Detected {scanned} scanned pages, which is more than 80% of the total pages. "
100
+ "Please check the input PDF file.",
101
+ )
102
+ raise ScannedPDFError("Scanned PDF detected.")
103
+
104
+ @staticmethod
105
+ def detect_page_is_scanned(page: il_version_1.Page, pdf: pymupdf.Document) -> bool:
106
+ before_page_image = pdf[page.page_number].get_pixmap()
107
+ before_page_image = np.frombuffer(before_page_image.samples, np.uint8).reshape(
108
+ before_page_image.height,
109
+ before_page_image.width,
110
+ 3,
111
+ )[:, :, ::-1]
112
+ new_xref = pdf.get_new_xref()
113
+ pdf.update_object(new_xref, "<<>>")
114
+ pdf.update_stream(new_xref, page.base_operations.value.encode("utf-8"))
115
+ pdf[page.page_number].set_contents(new_xref)
116
+ after_page_image = pdf[page.page_number].get_pixmap()
117
+ after_page_image = np.frombuffer(after_page_image.samples, np.uint8).reshape(
118
+ after_page_image.height,
119
+ after_page_image.width,
120
+ 3,
121
+ )[:, :, ::-1]
122
+ before_page_image = cv2.cvtColor(before_page_image, cv2.COLOR_RGB2GRAY)
123
+ after_page_image = cv2.cvtColor(after_page_image, cv2.COLOR_RGB2GRAY)
124
+ return structural_similarity(before_page_image, after_page_image) > 0.9
@@ -121,7 +121,7 @@ class LayoutParser:
121
121
  total,
122
122
  ) as progress:
123
123
  # Process pages in batches
124
- batch_size = 16
124
+ batch_size = 1
125
125
  for i in range(0, total, batch_size):
126
126
  self.translation_config.raise_if_cancelled()
127
127
  batch_pages = pages_to_translate[i : i + batch_size]
@@ -130,21 +130,24 @@ class RpcDocLayoutModel(DocLayoutModel):
130
130
  image = [image]
131
131
 
132
132
  results = []
133
- preds = predict_layout(image, host=self.host, imgsz=imgsz)
134
- if len(preds) > 0:
135
- for pred in preds:
136
- boxes = [
137
- YoloBox(None, np.array(x["xyxy"]), np.array(x["conf"]), x["cls"])
138
- for x in pred["boxes"]
139
- ]
140
- results.append(
141
- YoloResult(
142
- boxes=boxes,
143
- names={int(k): v for k, v in pred["names"].items()},
144
- ),
145
- )
146
- else:
147
- results.append(YoloResult(boxes_data=np.array([]), names=[]))
133
+ for img in image:
134
+ preds = predict_layout([img], host=self.host, imgsz=imgsz)
135
+ if len(preds) > 0:
136
+ for pred in preds:
137
+ boxes = [
138
+ YoloBox(
139
+ None, np.array(x["xyxy"]), np.array(x["conf"]), x["cls"]
140
+ )
141
+ for x in pred["boxes"]
142
+ ]
143
+ results.append(
144
+ YoloResult(
145
+ boxes=boxes,
146
+ names={int(k): v for k, v in pred["names"].items()},
147
+ ),
148
+ )
149
+ else:
150
+ results.append(YoloResult(boxes_data=np.array([]), names=[]))
148
151
 
149
152
  return results
150
153
 
@@ -24,6 +24,7 @@ from babeldoc.document_il.backend.pdf_creater import SUBSET_FONT_STAGE_NAME
24
24
  from babeldoc.document_il.backend.pdf_creater import PDFCreater
25
25
  from babeldoc.document_il.frontend.il_creater import ILCreater
26
26
  from babeldoc.document_il.midend.add_debug_information import AddDebugInformation
27
+ from babeldoc.document_il.midend.detect_scanned_file import DetectScannedFile
27
28
  from babeldoc.document_il.midend.il_translator import ILTranslator
28
29
  from babeldoc.document_il.midend.layout_parser import LayoutParser
29
30
  from babeldoc.document_il.midend.paragraph_finder import ParagraphFinder
@@ -40,17 +41,18 @@ from babeldoc.translation_config import TranslationConfig
40
41
  logger = logging.getLogger(__name__)
41
42
 
42
43
  TRANSLATE_STAGES = [
43
- ILCreater.stage_name,
44
- LayoutParser.stage_name,
45
- ParagraphFinder.stage_name,
46
- StylesAndFormulas.stage_name,
47
- RemoveDescent.stage_name,
48
- ILTranslator.stage_name,
49
- Typesetting.stage_name,
50
- FontMapper.stage_name,
51
- PDFCreater.stage_name,
52
- SUBSET_FONT_STAGE_NAME,
53
- SAVE_PDF_STAGE_NAME,
44
+ (ILCreater.stage_name, 5.35), # Parse PDF and Create IR
45
+ (DetectScannedFile.stage_name, 0.5), # No historical data, estimated
46
+ (LayoutParser.stage_name, 6.42), # Parse Page Layout
47
+ (ParagraphFinder.stage_name, 2.14), # Parse Paragraphs
48
+ (StylesAndFormulas.stage_name, 1.12), # Parse Formulas and Styles
49
+ (RemoveDescent.stage_name, 0.15), # Remove Char Descent
50
+ (ILTranslator.stage_name, 75.16), # Translate Paragraphs
51
+ (Typesetting.stage_name, 3.84), # Typesetting
52
+ (FontMapper.stage_name, 0.65), # Add Fonts
53
+ (PDFCreater.stage_name, 1.41), # Generate drawing instructions
54
+ (SUBSET_FONT_STAGE_NAME, 1.29), # Subset font
55
+ (SAVE_PDF_STAGE_NAME, 2.45), # Save PDF
54
56
  ]
55
57
 
56
58
  resfont_map = {
@@ -307,6 +309,17 @@ def do_translate(pm, translation_config):
307
309
  docs,
308
310
  translation_config.get_working_file_path("create_il.debug.json"),
309
311
  )
312
+
313
+ # 检测是否为扫描文件
314
+ logger.debug("start detect scanned file")
315
+ DetectScannedFile(translation_config).process(docs)
316
+ logger.debug("finish detect scanned file")
317
+ if translation_config.debug:
318
+ xml_converter.write_json(
319
+ docs,
320
+ translation_config.get_working_file_path("detect_scanned_file.json"),
321
+ )
322
+
310
323
  # Generate layouts for all pages
311
324
  logger.debug("start generating layouts")
312
325
  docs = LayoutParser(translation_config).process(docs, doc_input)
@@ -23,7 +23,7 @@ from babeldoc.docvision.rpc_doclayout import RpcDocLayoutModel
23
23
  from babeldoc.translation_config import TranslationConfig
24
24
 
25
25
  logger = logging.getLogger(__name__)
26
- __version__ = "0.1.23"
26
+ __version__ = "0.1.24"
27
27
 
28
28
 
29
29
  def create_parser():
@@ -392,7 +392,7 @@ def create_progress_handler(translation_config: TranslationConfig):
392
392
  stage_tasks[stage],
393
393
  completed=event["stage_current"],
394
394
  total=event["stage_total"],
395
- description=f"{event['stage']} ({event['stage_current']}/{event['stage_total']})",
395
+ description=f"{event['stage']}",
396
396
  refresh=True,
397
397
  )
398
398
  progress.update(
@@ -407,7 +407,7 @@ def create_progress_handler(translation_config: TranslationConfig):
407
407
  stage_tasks[stage],
408
408
  completed=event["stage_total"],
409
409
  total=event["stage_total"],
410
- description=f"{event['stage']} (Complete)",
410
+ description=f"{event['stage']}",
411
411
  refresh=True,
412
412
  )
413
413
  progress.update(
@@ -11,7 +11,7 @@ logger = logging.getLogger(__name__)
11
11
  class ProgressMonitor:
12
12
  def __init__(
13
13
  self,
14
- stages: list[str],
14
+ stages: list[tuple[str, float]],
15
15
  progress_change_callback: Callable | None = None,
16
16
  finish_callback: Callable | None = None,
17
17
  report_interval: float = 0.1,
@@ -19,7 +19,13 @@ class ProgressMonitor:
19
19
  cancel_event: threading.Event | None = None,
20
20
  loop: asyncio.AbstractEventLoop | None = None,
21
21
  ):
22
- self.stage = {k: TranslationStage(k, 0, self) for k in stages}
22
+ # Convert stages list to dict with name and weight
23
+ self.stage = {}
24
+ total_weight = sum(weight for _, weight in stages)
25
+ for name, weight in stages:
26
+ normalized_weight = weight / total_weight
27
+ self.stage[name] = TranslationStage(name, 0, self, normalized_weight)
28
+
23
29
  self.progress_change_callback = progress_change_callback
24
30
  self.finish_callback = finish_callback
25
31
  self.report_interval = report_interval
@@ -37,9 +43,9 @@ class ProgressMonitor:
37
43
  stages=[
38
44
  {
39
45
  "name": name,
40
- "percent": 1.0 / len(stages),
46
+ "percent": self.stage[name].weight,
41
47
  }
42
- for name in stages
48
+ for name, _ in stages
43
49
  ],
44
50
  )
45
51
  self.lock = threading.Lock()
@@ -102,9 +108,23 @@ class ProgressMonitor:
102
108
  )
103
109
 
104
110
  def calculate_current_progress(self, stage=None):
105
- progress = self.finish_stage_count * 100 / len(self.stage)
106
- if stage is not None:
107
- progress += stage.current * 100 / stage.total / len(self.stage)
111
+ # Count completed stages
112
+ completed_stages = sum(
113
+ 1 for s in self.stage.values() if s.run_time > 0 and s.current == s.total
114
+ )
115
+
116
+ # If all stages are complete, return exactly 100
117
+ if completed_stages == len(self.stage):
118
+ return 100
119
+
120
+ # Calculate progress based on weights
121
+ progress = sum(
122
+ s.weight * 100
123
+ for s in self.stage.values()
124
+ if s.run_time > 0 and s.current == s.total
125
+ )
126
+ if stage is not None and stage.total > 0:
127
+ progress += stage.weight * stage.current * 100 / stage.total
108
128
  return progress
109
129
 
110
130
  def stage_update(self, stage, n: int):
@@ -142,13 +162,14 @@ class ProgressMonitor:
142
162
 
143
163
 
144
164
  class TranslationStage:
145
- def __init__(self, name: str, total: int, pm: ProgressMonitor):
165
+ def __init__(self, name: str, total: int, pm: ProgressMonitor, weight: float):
146
166
  self.name = name
147
167
  self.display_name = name
148
168
  self.current = 0
149
169
  self.total = total
150
170
  self.pm = pm
151
171
  self.run_time = 0
172
+ self.weight = weight
152
173
 
153
174
  def __enter__(self):
154
175
  return self
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "BabelDOC"
3
- version = "0.1.23"
3
+ version = "0.1.24"
4
4
  description = "Yet Another Document Translator"
5
5
  license = "AGPL-3.0"
6
6
  readme = "README.md"
@@ -31,6 +31,7 @@ dependencies = [
31
31
  "msgpack>=1.1.0",
32
32
  "pydantic>=2.10.6",
33
33
  "tenacity>=9.0.0",
34
+ "scikit-image>=0.25.2",
34
35
  ]
35
36
 
36
37
  [project.urls]
@@ -132,7 +133,7 @@ pythonpath = [".", "src"]
132
133
  testpaths = ["tests"]
133
134
 
134
135
  [bumpver]
135
- current_version = "0.1.23"
136
+ current_version = "0.1.24"
136
137
  version_pattern = "MAJOR.MINOR.PATCH[.PYTAGNUM]"
137
138
 
138
139
  [bumpver.file_patterns]
@@ -48,7 +48,7 @@ wheels = [
48
48
 
49
49
  [[package]]
50
50
  name = "babeldoc"
51
- version = "0.1.23"
51
+ version = "0.1.24"
52
52
  source = { editable = "." }
53
53
  dependencies = [
54
54
  { name = "bitstring" },
@@ -67,6 +67,7 @@ dependencies = [
67
67
  { name = "pydantic" },
68
68
  { name = "pymupdf" },
69
69
  { name = "rich" },
70
+ { name = "scikit-image" },
70
71
  { name = "tenacity" },
71
72
  { name = "toml" },
72
73
  { name = "tqdm" },
@@ -106,6 +107,7 @@ requires-dist = [
106
107
  { name = "pydantic", specifier = ">=2.10.6" },
107
108
  { name = "pymupdf", specifier = ">=1.25.1" },
108
109
  { name = "rich", specifier = ">=13.9.4" },
110
+ { name = "scikit-image", specifier = ">=0.25.2" },
109
111
  { name = "tenacity", specifier = ">=9.0.0" },
110
112
  { name = "toml", specifier = ">=0.10.2" },
111
113
  { name = "tqdm", specifier = ">=4.67.1" },
@@ -660,6 +662,19 @@ wheels = [
660
662
  { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
661
663
  ]
662
664
 
665
+ [[package]]
666
+ name = "imageio"
667
+ version = "2.37.0"
668
+ source = { registry = "https://pypi.org/simple" }
669
+ dependencies = [
670
+ { name = "numpy" },
671
+ { name = "pillow" },
672
+ ]
673
+ sdist = { url = "https://files.pythonhosted.org/packages/0c/47/57e897fb7094afb2d26e8b2e4af9a45c7cf1a405acdeeca001fdf2c98501/imageio-2.37.0.tar.gz", hash = "sha256:71b57b3669666272c818497aebba2b4c5f20d5b37c81720e5e1a56d59c492996", size = 389963 }
674
+ wheels = [
675
+ { url = "https://files.pythonhosted.org/packages/cb/bd/b394387b598ed84d8d0fa90611a90bee0adc2021820ad5729f7ced74a8e2/imageio-2.37.0-py3-none-any.whl", hash = "sha256:11efa15b87bc7871b61590326b2d635439acc321cf7f8ce996f812543ce10eed", size = 315796 },
676
+ ]
677
+
663
678
  [[package]]
664
679
  name = "iniconfig"
665
680
  version = "2.0.0"
@@ -731,6 +746,18 @@ version = "3.0.1"
731
746
  source = { registry = "https://pypi.org/simple" }
732
747
  sdist = { url = "https://files.pythonhosted.org/packages/5e/73/e01e4c5e11ad0494f4407a3f623ad4d87714909f50b17a06ed121034ff6e/jsmin-3.0.1.tar.gz", hash = "sha256:c0959a121ef94542e807a674142606f7e90214a2b3d1eb17300244bbb5cc2bfc", size = 13925 }
733
748
 
749
+ [[package]]
750
+ name = "lazy-loader"
751
+ version = "0.4"
752
+ source = { registry = "https://pypi.org/simple" }
753
+ dependencies = [
754
+ { name = "packaging" },
755
+ ]
756
+ sdist = { url = "https://files.pythonhosted.org/packages/6f/6b/c875b30a1ba490860c93da4cabf479e03f584eba06fe5963f6f6644653d8/lazy_loader-0.4.tar.gz", hash = "sha256:47c75182589b91a4e1a85a136c074285a5ad4d9f39c63e0d7fb76391c4574cd1", size = 15431 }
757
+ wheels = [
758
+ { url = "https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl", hash = "sha256:342aa8e14d543a154047afb4ba8ef17f5563baad3fc610d7b15b213b0f119efc", size = 12097 },
759
+ ]
760
+
734
761
  [[package]]
735
762
  name = "lexid"
736
763
  version = "2021.1006"
@@ -1116,6 +1143,15 @@ wheels = [
1116
1143
  { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 },
1117
1144
  ]
1118
1145
 
1146
+ [[package]]
1147
+ name = "networkx"
1148
+ version = "3.4.2"
1149
+ source = { registry = "https://pypi.org/simple" }
1150
+ sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368 }
1151
+ wheels = [
1152
+ { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 },
1153
+ ]
1154
+
1119
1155
  [[package]]
1120
1156
  name = "nodeenv"
1121
1157
  version = "1.9.1"
@@ -1353,6 +1389,54 @@ version = "3.17.9"
1353
1389
  source = { registry = "https://pypi.org/simple" }
1354
1390
  sdist = { url = "https://files.pythonhosted.org/packages/57/09/4393bd378e70b7fc3163ee83353cc27bb520010a5c2b3c924121e7e7e068/peewee-3.17.9.tar.gz", hash = "sha256:fe15cd001758e324c8e3ca8c8ed900e7397c2907291789e1efc383e66b9bc7a8", size = 3026085 }
1355
1391
 
1392
+ [[package]]
1393
+ name = "pillow"
1394
+ version = "11.1.0"
1395
+ source = { registry = "https://pypi.org/simple" }
1396
+ sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 }
1397
+ wheels = [
1398
+ { url = "https://files.pythonhosted.org/packages/50/1c/2dcea34ac3d7bc96a1fd1bd0a6e06a57c67167fec2cff8d95d88229a8817/pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8", size = 3229983 },
1399
+ { url = "https://files.pythonhosted.org/packages/14/ca/6bec3df25e4c88432681de94a3531cc738bd85dea6c7aa6ab6f81ad8bd11/pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192", size = 3101831 },
1400
+ { url = "https://files.pythonhosted.org/packages/d4/2c/668e18e5521e46eb9667b09e501d8e07049eb5bfe39d56be0724a43117e6/pillow-11.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2", size = 4314074 },
1401
+ { url = "https://files.pythonhosted.org/packages/02/80/79f99b714f0fc25f6a8499ecfd1f810df12aec170ea1e32a4f75746051ce/pillow-11.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26", size = 4394933 },
1402
+ { url = "https://files.pythonhosted.org/packages/81/aa/8d4ad25dc11fd10a2001d5b8a80fdc0e564ac33b293bdfe04ed387e0fd95/pillow-11.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07", size = 4353349 },
1403
+ { url = "https://files.pythonhosted.org/packages/84/7a/cd0c3eaf4a28cb2a74bdd19129f7726277a7f30c4f8424cd27a62987d864/pillow-11.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482", size = 4476532 },
1404
+ { url = "https://files.pythonhosted.org/packages/8f/8b/a907fdd3ae8f01c7670dfb1499c53c28e217c338b47a813af8d815e7ce97/pillow-11.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e", size = 4279789 },
1405
+ { url = "https://files.pythonhosted.org/packages/6f/9a/9f139d9e8cccd661c3efbf6898967a9a337eb2e9be2b454ba0a09533100d/pillow-11.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269", size = 4413131 },
1406
+ { url = "https://files.pythonhosted.org/packages/a8/68/0d8d461f42a3f37432203c8e6df94da10ac8081b6d35af1c203bf3111088/pillow-11.1.0-cp310-cp310-win32.whl", hash = "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49", size = 2291213 },
1407
+ { url = "https://files.pythonhosted.org/packages/14/81/d0dff759a74ba87715509af9f6cb21fa21d93b02b3316ed43bda83664db9/pillow-11.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a", size = 2625725 },
1408
+ { url = "https://files.pythonhosted.org/packages/ce/1f/8d50c096a1d58ef0584ddc37e6f602828515219e9d2428e14ce50f5ecad1/pillow-11.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65", size = 2375213 },
1409
+ { url = "https://files.pythonhosted.org/packages/dd/d6/2000bfd8d5414fb70cbbe52c8332f2283ff30ed66a9cde42716c8ecbe22c/pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457", size = 3229968 },
1410
+ { url = "https://files.pythonhosted.org/packages/d9/45/3fe487010dd9ce0a06adf9b8ff4f273cc0a44536e234b0fad3532a42c15b/pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35", size = 3101806 },
1411
+ { url = "https://files.pythonhosted.org/packages/e3/72/776b3629c47d9d5f1c160113158a7a7ad177688d3a1159cd3b62ded5a33a/pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2", size = 4322283 },
1412
+ { url = "https://files.pythonhosted.org/packages/e4/c2/e25199e7e4e71d64eeb869f5b72c7ddec70e0a87926398785ab944d92375/pillow-11.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070", size = 4402945 },
1413
+ { url = "https://files.pythonhosted.org/packages/c1/ed/51d6136c9d5911f78632b1b86c45241c712c5a80ed7fa7f9120a5dff1eba/pillow-11.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6", size = 4361228 },
1414
+ { url = "https://files.pythonhosted.org/packages/48/a4/fbfe9d5581d7b111b28f1d8c2762dee92e9821bb209af9fa83c940e507a0/pillow-11.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1", size = 4484021 },
1415
+ { url = "https://files.pythonhosted.org/packages/39/db/0b3c1a5018117f3c1d4df671fb8e47d08937f27519e8614bbe86153b65a5/pillow-11.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2", size = 4287449 },
1416
+ { url = "https://files.pythonhosted.org/packages/d9/58/bc128da7fea8c89fc85e09f773c4901e95b5936000e6f303222490c052f3/pillow-11.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96", size = 4419972 },
1417
+ { url = "https://files.pythonhosted.org/packages/5f/bb/58f34379bde9fe197f51841c5bbe8830c28bbb6d3801f16a83b8f2ad37df/pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f", size = 2291201 },
1418
+ { url = "https://files.pythonhosted.org/packages/3a/c6/fce9255272bcf0c39e15abd2f8fd8429a954cf344469eaceb9d0d1366913/pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761", size = 2625686 },
1419
+ { url = "https://files.pythonhosted.org/packages/c8/52/8ba066d569d932365509054859f74f2a9abee273edcef5cd75e4bc3e831e/pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71", size = 2375194 },
1420
+ { url = "https://files.pythonhosted.org/packages/95/20/9ce6ed62c91c073fcaa23d216e68289e19d95fb8188b9fb7a63d36771db8/pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", size = 3226818 },
1421
+ { url = "https://files.pythonhosted.org/packages/b9/d8/f6004d98579a2596c098d1e30d10b248798cceff82d2b77aa914875bfea1/pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", size = 3101662 },
1422
+ { url = "https://files.pythonhosted.org/packages/08/d9/892e705f90051c7a2574d9f24579c9e100c828700d78a63239676f960b74/pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", size = 4329317 },
1423
+ { url = "https://files.pythonhosted.org/packages/8c/aa/7f29711f26680eab0bcd3ecdd6d23ed6bce180d82e3f6380fb7ae35fcf3b/pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", size = 4412999 },
1424
+ { url = "https://files.pythonhosted.org/packages/c8/c4/8f0fe3b9e0f7196f6d0bbb151f9fba323d72a41da068610c4c960b16632a/pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", size = 4368819 },
1425
+ { url = "https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", size = 4496081 },
1426
+ { url = "https://files.pythonhosted.org/packages/84/9c/9bcd66f714d7e25b64118e3952d52841a4babc6d97b6d28e2261c52045d4/pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", size = 4296513 },
1427
+ { url = "https://files.pythonhosted.org/packages/db/61/ada2a226e22da011b45f7104c95ebda1b63dcbb0c378ad0f7c2a710f8fd2/pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", size = 4431298 },
1428
+ { url = "https://files.pythonhosted.org/packages/e7/c4/fc6e86750523f367923522014b821c11ebc5ad402e659d8c9d09b3c9d70c/pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", size = 2291630 },
1429
+ { url = "https://files.pythonhosted.org/packages/08/5c/2104299949b9d504baf3f4d35f73dbd14ef31bbd1ddc2c1b66a5b7dfda44/pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", size = 2626369 },
1430
+ { url = "https://files.pythonhosted.org/packages/37/f3/9b18362206b244167c958984b57c7f70a0289bfb59a530dd8af5f699b910/pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", size = 2375240 },
1431
+ { url = "https://files.pythonhosted.org/packages/fa/c5/389961578fb677b8b3244fcd934f720ed25a148b9a5cc81c91bdf59d8588/pillow-11.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90", size = 3198345 },
1432
+ { url = "https://files.pythonhosted.org/packages/c4/fa/803c0e50ffee74d4b965229e816af55276eac1d5806712de86f9371858fd/pillow-11.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb", size = 3072938 },
1433
+ { url = "https://files.pythonhosted.org/packages/dc/67/2a3a5f8012b5d8c63fe53958ba906c1b1d0482ebed5618057ef4d22f8076/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442", size = 3400049 },
1434
+ { url = "https://files.pythonhosted.org/packages/e5/a0/514f0d317446c98c478d1872497eb92e7cde67003fed74f696441e647446/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83", size = 3422431 },
1435
+ { url = "https://files.pythonhosted.org/packages/cd/00/20f40a935514037b7d3f87adfc87d2c538430ea625b63b3af8c3f5578e72/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f", size = 3446208 },
1436
+ { url = "https://files.pythonhosted.org/packages/28/3c/7de681727963043e093c72e6c3348411b0185eab3263100d4490234ba2f6/pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73", size = 3509746 },
1437
+ { url = "https://files.pythonhosted.org/packages/41/67/936f9814bdd74b2dfd4822f1f7725ab5d8ff4103919a1664eb4874c58b2f/pillow-11.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0", size = 2626353 },
1438
+ ]
1439
+
1356
1440
  [[package]]
1357
1441
  name = "platformdirs"
1358
1442
  version = "4.3.6"
@@ -1669,6 +1753,77 @@ wheels = [
1669
1753
  { url = "https://files.pythonhosted.org/packages/31/d8/de873d1c1b020d668d8ec9855d390764cb90cf8f6486c0983da52be8b7b7/ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf", size = 10435860 },
1670
1754
  ]
1671
1755
 
1756
+ [[package]]
1757
+ name = "scikit-image"
1758
+ version = "0.25.2"
1759
+ source = { registry = "https://pypi.org/simple" }
1760
+ dependencies = [
1761
+ { name = "imageio" },
1762
+ { name = "lazy-loader" },
1763
+ { name = "networkx" },
1764
+ { name = "numpy" },
1765
+ { name = "packaging" },
1766
+ { name = "pillow" },
1767
+ { name = "scipy" },
1768
+ { name = "tifffile" },
1769
+ ]
1770
+ sdist = { url = "https://files.pythonhosted.org/packages/c7/a8/3c0f256012b93dd2cb6fda9245e9f4bff7dc0486880b248005f15ea2255e/scikit_image-0.25.2.tar.gz", hash = "sha256:e5a37e6cd4d0c018a7a55b9d601357e3382826d3888c10d0213fc63bff977dde", size = 22693594 }
1771
+ wheels = [
1772
+ { url = "https://files.pythonhosted.org/packages/11/cb/016c63f16065c2d333c8ed0337e18a5cdf9bc32d402e4f26b0db362eb0e2/scikit_image-0.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d3278f586793176599df6a4cf48cb6beadae35c31e58dc01a98023af3dc31c78", size = 13988922 },
1773
+ { url = "https://files.pythonhosted.org/packages/30/ca/ff4731289cbed63c94a0c9a5b672976603118de78ed21910d9060c82e859/scikit_image-0.25.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5c311069899ce757d7dbf1d03e32acb38bb06153236ae77fcd820fd62044c063", size = 13192698 },
1774
+ { url = "https://files.pythonhosted.org/packages/39/6d/a2aadb1be6d8e149199bb9b540ccde9e9622826e1ab42fe01de4c35ab918/scikit_image-0.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be455aa7039a6afa54e84f9e38293733a2622b8c2fb3362b822d459cc5605e99", size = 14153634 },
1775
+ { url = "https://files.pythonhosted.org/packages/96/08/916e7d9ee4721031b2f625db54b11d8379bd51707afaa3e5a29aecf10bc4/scikit_image-0.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c464b90e978d137330be433df4e76d92ad3c5f46a22f159520ce0fdbea8a09", size = 14767545 },
1776
+ { url = "https://files.pythonhosted.org/packages/5f/ee/c53a009e3997dda9d285402f19226fbd17b5b3cb215da391c4ed084a1424/scikit_image-0.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:60516257c5a2d2f74387c502aa2f15a0ef3498fbeaa749f730ab18f0a40fd054", size = 12812908 },
1777
+ { url = "https://files.pythonhosted.org/packages/c4/97/3051c68b782ee3f1fb7f8f5bb7d535cf8cb92e8aae18fa9c1cdf7e15150d/scikit_image-0.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f4bac9196fb80d37567316581c6060763b0f4893d3aca34a9ede3825bc035b17", size = 14003057 },
1778
+ { url = "https://files.pythonhosted.org/packages/19/23/257fc696c562639826065514d551b7b9b969520bd902c3a8e2fcff5b9e17/scikit_image-0.25.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d989d64ff92e0c6c0f2018c7495a5b20e2451839299a018e0e5108b2680f71e0", size = 13180335 },
1779
+ { url = "https://files.pythonhosted.org/packages/ef/14/0c4a02cb27ca8b1e836886b9ec7c9149de03053650e9e2ed0625f248dd92/scikit_image-0.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2cfc96b27afe9a05bc92f8c6235321d3a66499995675b27415e0d0c76625173", size = 14144783 },
1780
+ { url = "https://files.pythonhosted.org/packages/dd/9b/9fb556463a34d9842491d72a421942c8baff4281025859c84fcdb5e7e602/scikit_image-0.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24cc986e1f4187a12aa319f777b36008764e856e5013666a4a83f8df083c2641", size = 14785376 },
1781
+ { url = "https://files.pythonhosted.org/packages/de/ec/b57c500ee85885df5f2188f8bb70398481393a69de44a00d6f1d055f103c/scikit_image-0.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:b4f6b61fc2db6340696afe3db6b26e0356911529f5f6aee8c322aa5157490c9b", size = 12791698 },
1782
+ { url = "https://files.pythonhosted.org/packages/35/8c/5df82881284459f6eec796a5ac2a0a304bb3384eec2e73f35cfdfcfbf20c/scikit_image-0.25.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8db8dd03663112783221bf01ccfc9512d1cc50ac9b5b0fe8f4023967564719fb", size = 13986000 },
1783
+ { url = "https://files.pythonhosted.org/packages/ce/e6/93bebe1abcdce9513ffec01d8af02528b4c41fb3c1e46336d70b9ed4ef0d/scikit_image-0.25.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:483bd8cc10c3d8a7a37fae36dfa5b21e239bd4ee121d91cad1f81bba10cfb0ed", size = 13235893 },
1784
+ { url = "https://files.pythonhosted.org/packages/53/4b/eda616e33f67129e5979a9eb33c710013caa3aa8a921991e6cc0b22cea33/scikit_image-0.25.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d1e80107bcf2bf1291acfc0bf0425dceb8890abe9f38d8e94e23497cbf7ee0d", size = 14178389 },
1785
+ { url = "https://files.pythonhosted.org/packages/6b/b5/b75527c0f9532dd8a93e8e7cd8e62e547b9f207d4c11e24f0006e8646b36/scikit_image-0.25.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a17e17eb8562660cc0d31bb55643a4da996a81944b82c54805c91b3fe66f4824", size = 15003435 },
1786
+ { url = "https://files.pythonhosted.org/packages/34/e3/49beb08ebccda3c21e871b607c1cb2f258c3fa0d2f609fed0a5ba741b92d/scikit_image-0.25.2-cp312-cp312-win_amd64.whl", hash = "sha256:bdd2b8c1de0849964dbc54037f36b4e9420157e67e45a8709a80d727f52c7da2", size = 12899474 },
1787
+ ]
1788
+
1789
+ [[package]]
1790
+ name = "scipy"
1791
+ version = "1.15.2"
1792
+ source = { registry = "https://pypi.org/simple" }
1793
+ dependencies = [
1794
+ { name = "numpy" },
1795
+ ]
1796
+ sdist = { url = "https://files.pythonhosted.org/packages/b7/b9/31ba9cd990e626574baf93fbc1ac61cf9ed54faafd04c479117517661637/scipy-1.15.2.tar.gz", hash = "sha256:cd58a314d92838f7e6f755c8a2167ead4f27e1fd5c1251fd54289569ef3495ec", size = 59417316 }
1797
+ wheels = [
1798
+ { url = "https://files.pythonhosted.org/packages/95/df/ef233fff6838fe6f7840d69b5ef9f20d2b5c912a8727b21ebf876cb15d54/scipy-1.15.2-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a2ec871edaa863e8213ea5df811cd600734f6400b4af272e1c011e69401218e9", size = 38692502 },
1799
+ { url = "https://files.pythonhosted.org/packages/5c/20/acdd4efb8a68b842968f7bc5611b1aeb819794508771ad104de418701422/scipy-1.15.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:6f223753c6ea76983af380787611ae1291e3ceb23917393079dcc746ba60cfb5", size = 30085508 },
1800
+ { url = "https://files.pythonhosted.org/packages/42/55/39cf96ca7126f1e78ee72a6344ebdc6702fc47d037319ad93221063e6cf4/scipy-1.15.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:ecf797d2d798cf7c838c6d98321061eb3e72a74710e6c40540f0e8087e3b499e", size = 22359166 },
1801
+ { url = "https://files.pythonhosted.org/packages/51/48/708d26a4ab8a1441536bf2dfcad1df0ca14a69f010fba3ccbdfc02df7185/scipy-1.15.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:9b18aa747da280664642997e65aab1dd19d0c3d17068a04b3fe34e2559196cb9", size = 25112047 },
1802
+ { url = "https://files.pythonhosted.org/packages/dd/65/f9c5755b995ad892020381b8ae11f16d18616208e388621dfacc11df6de6/scipy-1.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87994da02e73549dfecaed9e09a4f9d58a045a053865679aeb8d6d43747d4df3", size = 35536214 },
1803
+ { url = "https://files.pythonhosted.org/packages/de/3c/c96d904b9892beec978562f64d8cc43f9cca0842e65bd3cd1b7f7389b0ba/scipy-1.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69ea6e56d00977f355c0f84eba69877b6df084516c602d93a33812aa04d90a3d", size = 37646981 },
1804
+ { url = "https://files.pythonhosted.org/packages/3d/74/c2d8a24d18acdeae69ed02e132b9bc1bb67b7bee90feee1afe05a68f9d67/scipy-1.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:888307125ea0c4466287191e5606a2c910963405ce9671448ff9c81c53f85f58", size = 37230048 },
1805
+ { url = "https://files.pythonhosted.org/packages/42/19/0aa4ce80eca82d487987eff0bc754f014dec10d20de2f66754fa4ea70204/scipy-1.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9412f5e408b397ff5641080ed1e798623dbe1ec0d78e72c9eca8992976fa65aa", size = 40010322 },
1806
+ { url = "https://files.pythonhosted.org/packages/d0/d2/f0683b7e992be44d1475cc144d1f1eeae63c73a14f862974b4db64af635e/scipy-1.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:b5e025e903b4f166ea03b109bb241355b9c42c279ea694d8864d033727205e65", size = 41233385 },
1807
+ { url = "https://files.pythonhosted.org/packages/40/1f/bf0a5f338bda7c35c08b4ed0df797e7bafe8a78a97275e9f439aceb46193/scipy-1.15.2-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:92233b2df6938147be6fa8824b8136f29a18f016ecde986666be5f4d686a91a4", size = 38703651 },
1808
+ { url = "https://files.pythonhosted.org/packages/de/54/db126aad3874601048c2c20ae3d8a433dbfd7ba8381551e6f62606d9bd8e/scipy-1.15.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:62ca1ff3eb513e09ed17a5736929429189adf16d2d740f44e53270cc800ecff1", size = 30102038 },
1809
+ { url = "https://files.pythonhosted.org/packages/61/d8/84da3fffefb6c7d5a16968fe5b9f24c98606b165bb801bb0b8bc3985200f/scipy-1.15.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4c6676490ad76d1c2894d77f976144b41bd1a4052107902238047fb6a473e971", size = 22375518 },
1810
+ { url = "https://files.pythonhosted.org/packages/44/78/25535a6e63d3b9c4c90147371aedb5d04c72f3aee3a34451f2dc27c0c07f/scipy-1.15.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8bf5cb4a25046ac61d38f8d3c3426ec11ebc350246a4642f2f315fe95bda655", size = 25142523 },
1811
+ { url = "https://files.pythonhosted.org/packages/e0/22/4b4a26fe1cd9ed0bc2b2cb87b17d57e32ab72c346949eaf9288001f8aa8e/scipy-1.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a8e34cf4c188b6dd004654f88586d78f95639e48a25dfae9c5e34a6dc34547e", size = 35491547 },
1812
+ { url = "https://files.pythonhosted.org/packages/32/ea/564bacc26b676c06a00266a3f25fdfe91a9d9a2532ccea7ce6dd394541bc/scipy-1.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28a0d2c2075946346e4408b211240764759e0fabaeb08d871639b5f3b1aca8a0", size = 37634077 },
1813
+ { url = "https://files.pythonhosted.org/packages/43/c2/bfd4e60668897a303b0ffb7191e965a5da4056f0d98acfb6ba529678f0fb/scipy-1.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:42dabaaa798e987c425ed76062794e93a243be8f0f20fff6e7a89f4d61cb3d40", size = 37231657 },
1814
+ { url = "https://files.pythonhosted.org/packages/4a/75/5f13050bf4f84c931bcab4f4e83c212a36876c3c2244475db34e4b5fe1a6/scipy-1.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f5e296ec63c5da6ba6fa0343ea73fd51b8b3e1a300b0a8cae3ed4b1122c7462", size = 40035857 },
1815
+ { url = "https://files.pythonhosted.org/packages/b9/8b/7ec1832b09dbc88f3db411f8cdd47db04505c4b72c99b11c920a8f0479c3/scipy-1.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:597a0c7008b21c035831c39927406c6181bcf8f60a73f36219b69d010aa04737", size = 41217654 },
1816
+ { url = "https://files.pythonhosted.org/packages/4b/5d/3c78815cbab499610f26b5bae6aed33e227225a9fa5290008a733a64f6fc/scipy-1.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c4697a10da8f8765bb7c83e24a470da5797e37041edfd77fd95ba3811a47c4fd", size = 38756184 },
1817
+ { url = "https://files.pythonhosted.org/packages/37/20/3d04eb066b471b6e171827548b9ddb3c21c6bbea72a4d84fc5989933910b/scipy-1.15.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:869269b767d5ee7ea6991ed7e22b3ca1f22de73ab9a49c44bad338b725603301", size = 30163558 },
1818
+ { url = "https://files.pythonhosted.org/packages/a4/98/e5c964526c929ef1f795d4c343b2ff98634ad2051bd2bbadfef9e772e413/scipy-1.15.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:bad78d580270a4d32470563ea86c6590b465cb98f83d760ff5b0990cb5518a93", size = 22437211 },
1819
+ { url = "https://files.pythonhosted.org/packages/1d/cd/1dc7371e29195ecbf5222f9afeedb210e0a75057d8afbd942aa6cf8c8eca/scipy-1.15.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b09ae80010f52efddb15551025f9016c910296cf70adbf03ce2a8704f3a5ad20", size = 25232260 },
1820
+ { url = "https://files.pythonhosted.org/packages/f0/24/1a181a9e5050090e0b5138c5f496fee33293c342b788d02586bc410c6477/scipy-1.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a6fd6eac1ce74a9f77a7fc724080d507c5812d61e72bd5e4c489b042455865e", size = 35198095 },
1821
+ { url = "https://files.pythonhosted.org/packages/c0/53/eaada1a414c026673eb983f8b4a55fe5eb172725d33d62c1b21f63ff6ca4/scipy-1.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b871df1fe1a3ba85d90e22742b93584f8d2b8e6124f8372ab15c71b73e428b8", size = 37297371 },
1822
+ { url = "https://files.pythonhosted.org/packages/e9/06/0449b744892ed22b7e7b9a1994a866e64895363572677a316a9042af1fe5/scipy-1.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:03205d57a28e18dfd39f0377d5002725bf1f19a46f444108c29bdb246b6c8a11", size = 36872390 },
1823
+ { url = "https://files.pythonhosted.org/packages/6a/6f/a8ac3cfd9505ec695c1bc35edc034d13afbd2fc1882a7c6b473e280397bb/scipy-1.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:601881dfb761311045b03114c5fe718a12634e5608c3b403737ae463c9885d53", size = 39700276 },
1824
+ { url = "https://files.pythonhosted.org/packages/f5/6f/e6e5aff77ea2a48dd96808bb51d7450875af154ee7cbe72188afb0b37929/scipy-1.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:e7c68b6a43259ba0aab737237876e5c2c549a031ddb7abc28c7b47f22e202ded", size = 40942317 },
1825
+ ]
1826
+
1672
1827
  [[package]]
1673
1828
  name = "six"
1674
1829
  version = "1.17.0"
@@ -1726,6 +1881,18 @@ wheels = [
1726
1881
  { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 },
1727
1882
  ]
1728
1883
 
1884
+ [[package]]
1885
+ name = "tifffile"
1886
+ version = "2025.2.18"
1887
+ source = { registry = "https://pypi.org/simple" }
1888
+ dependencies = [
1889
+ { name = "numpy" },
1890
+ ]
1891
+ sdist = { url = "https://files.pythonhosted.org/packages/de/1f/96d743b3417425f958dfed2518ad271b346a072d27b6859bb158e601bc21/tifffile-2025.2.18.tar.gz", hash = "sha256:8d731789e691b468746c1615d989bc550ac93cf753e9210865222e90a5a95d11", size = 365412 }
1892
+ wheels = [
1893
+ { url = "https://files.pythonhosted.org/packages/63/70/6f363ab13f9903557a567a4471a28ee231b962e34af8e1dd8d1b0f17e64e/tifffile-2025.2.18-py3-none-any.whl", hash = "sha256:54b36c4d5e5b8d8920134413edfe5a7cfb1c7617bb50cddf7e2772edb7149043", size = 226358 },
1894
+ ]
1895
+
1729
1896
  [[package]]
1730
1897
  name = "toml"
1731
1898
  version = "0.10.2"
@@ -1 +0,0 @@
1
- __version__ = "0.1.23"
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