yomitoku 0.9.0__tar.gz → 0.9.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. {yomitoku-0.9.0 → yomitoku-0.9.2}/PKG-INFO +6 -2
  2. {yomitoku-0.9.0 → yomitoku-0.9.2}/README.md +3 -1
  3. {yomitoku-0.9.0 → yomitoku-0.9.2}/README_EN.md +1 -0
  4. {yomitoku-0.9.0 → yomitoku-0.9.2}/demo/simple_ocr.py +4 -1
  5. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/cli.en.md +22 -0
  6. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/cli.ja.md +20 -0
  7. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/index.en.md +1 -1
  8. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/index.ja.md +1 -1
  9. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/mcp.en.md +16 -0
  10. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/mcp.ja.md +14 -0
  11. {yomitoku-0.9.0 → yomitoku-0.9.2}/pyproject.toml +3 -1
  12. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/cli/main.py +36 -10
  13. yomitoku-0.9.0/src/yomitoku/cli/mcp.py → yomitoku-0.9.2/src/yomitoku/cli/mcp_server.py +37 -6
  14. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/data/dataset.py +20 -10
  15. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/data/functions.py +19 -20
  16. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/document_analyzer.py +21 -6
  17. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/reading_order.py +38 -8
  18. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/utils/misc.py +49 -2
  19. yomitoku-0.9.2/src/yomitoku/utils/searchable_pdf.py +116 -0
  20. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery4_p1.html +6 -6
  21. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery4_p1.md +9 -9
  22. yomitoku-0.9.2/static/out/in_gallery4_p1_layout.jpg +0 -0
  23. yomitoku-0.9.2/static/out/in_gallery4_p1_ocr.jpg +0 -0
  24. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/test_data.py +14 -31
  25. yomitoku-0.9.2/uv.lock +1898 -0
  26. yomitoku-0.9.0/static/out/in_gallery4_p1_layout.jpg +0 -0
  27. yomitoku-0.9.0/static/out/in_gallery4_p1_ocr.jpg +0 -0
  28. yomitoku-0.9.0/uv.lock +0 -1815
  29. {yomitoku-0.9.0 → yomitoku-0.9.2}/.github/FUNDING.yml +0 -0
  30. {yomitoku-0.9.0 → yomitoku-0.9.2}/.github/release-drafter.yml +0 -0
  31. {yomitoku-0.9.0 → yomitoku-0.9.2}/.github/workflows/build-and-publish-docs.yaml +0 -0
  32. {yomitoku-0.9.0 → yomitoku-0.9.2}/.github/workflows/build-and-publish.yml +0 -0
  33. {yomitoku-0.9.0 → yomitoku-0.9.2}/.github/workflows/create-release.yml +0 -0
  34. {yomitoku-0.9.0 → yomitoku-0.9.2}/.github/workflows/lint-and-test.yml +0 -0
  35. {yomitoku-0.9.0 → yomitoku-0.9.2}/.gitignore +0 -0
  36. {yomitoku-0.9.0 → yomitoku-0.9.2}/.pre-commit-config.yaml +0 -0
  37. {yomitoku-0.9.0 → yomitoku-0.9.2}/.python-version +0 -0
  38. {yomitoku-0.9.0 → yomitoku-0.9.2}/configs/yomitoku-layout-parser-rtdtrv2-open-beta.yaml +0 -0
  39. {yomitoku-0.9.0 → yomitoku-0.9.2}/configs/yomitoku-table-structure-recognizer-rtdtrv2-open-beta.yaml +0 -0
  40. {yomitoku-0.9.0 → yomitoku-0.9.2}/configs/yomitoku-text-detector-dbnet-open-beta.yaml +0 -0
  41. {yomitoku-0.9.0 → yomitoku-0.9.2}/configs/yomitoku-text-recognizer-parseq-open-beta.yaml +0 -0
  42. {yomitoku-0.9.0 → yomitoku-0.9.2}/configs/yomitoku-text-recognizer-parseq-small-open-beta.yaml +0 -0
  43. {yomitoku-0.9.0 → yomitoku-0.9.2}/demo/sample.pdf +0 -0
  44. {yomitoku-0.9.0 → yomitoku-0.9.2}/demo/setting_document_anaysis.py +0 -0
  45. {yomitoku-0.9.0 → yomitoku-0.9.2}/demo/simple_document_analysis.py +0 -0
  46. {yomitoku-0.9.0 → yomitoku-0.9.2}/demo/simple_layout.py +0 -0
  47. {yomitoku-0.9.0 → yomitoku-0.9.2}/demo/text_detector.yaml +0 -0
  48. {yomitoku-0.9.0 → yomitoku-0.9.2}/dockerfile +0 -0
  49. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/assets/logo.svg +0 -0
  50. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/configuration.en.md +0 -0
  51. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/configuration.ja.md +0 -0
  52. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/installation.en.md +0 -0
  53. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/installation.ja.md +0 -0
  54. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/module.en.md +0 -0
  55. {yomitoku-0.9.0 → yomitoku-0.9.2}/docs/module.ja.md +0 -0
  56. {yomitoku-0.9.0 → yomitoku-0.9.2}/gallery.md +0 -0
  57. {yomitoku-0.9.0 → yomitoku-0.9.2}/mkdocs.yml +0 -0
  58. {yomitoku-0.9.0 → yomitoku-0.9.2}/pytest.ini +0 -0
  59. {yomitoku-0.9.0 → yomitoku-0.9.2}/scripts/register_hugging_face_hub.py +0 -0
  60. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/__init__.py +0 -0
  61. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/base.py +0 -0
  62. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/cli/__init__.py +0 -0
  63. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/configs/__init__.py +0 -0
  64. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/configs/cfg_layout_parser_rtdtrv2.py +0 -0
  65. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/configs/cfg_layout_parser_rtdtrv2_v2.py +0 -0
  66. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/configs/cfg_table_structure_recognizer_rtdtrv2.py +0 -0
  67. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/configs/cfg_text_detector_dbnet.py +0 -0
  68. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/configs/cfg_text_detector_dbnet_v2.py +0 -0
  69. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/configs/cfg_text_recognizer_parseq.py +0 -0
  70. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/configs/cfg_text_recognizer_parseq_small.py +0 -0
  71. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/configs/cfg_text_recognizer_parseq_v2.py +0 -0
  72. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/constants.py +0 -0
  73. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/data/__init__.py +0 -0
  74. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/export/__init__.py +0 -0
  75. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/export/export_csv.py +0 -0
  76. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/export/export_html.py +0 -0
  77. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/export/export_json.py +0 -0
  78. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/export/export_markdown.py +0 -0
  79. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/layout_analyzer.py +0 -0
  80. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/layout_parser.py +0 -0
  81. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/models/__init__.py +0 -0
  82. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/models/dbnet_plus.py +0 -0
  83. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/models/layers/__init__.py +0 -0
  84. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/models/layers/activate.py +0 -0
  85. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/models/layers/dbnet_feature_attention.py +0 -0
  86. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/models/layers/parseq_transformer.py +0 -0
  87. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/models/layers/rtdetr_backbone.py +0 -0
  88. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/models/layers/rtdetr_hybrid_encoder.py +0 -0
  89. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/models/layers/rtdetrv2_decoder.py +0 -0
  90. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/models/parseq.py +0 -0
  91. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/models/rtdetr.py +0 -0
  92. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/ocr.py +0 -0
  93. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/onnx/.gitkeep +0 -0
  94. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/postprocessor/__init__.py +0 -0
  95. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/postprocessor/dbnet_postporcessor.py +0 -0
  96. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/postprocessor/parseq_tokenizer.py +0 -0
  97. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/postprocessor/rtdetr_postprocessor.py +0 -0
  98. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/resource/MPLUS1p-Medium.ttf +0 -0
  99. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/resource/charset.txt +0 -0
  100. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/table_structure_recognizer.py +0 -0
  101. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/text_detector.py +0 -0
  102. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/text_recognizer.py +0 -0
  103. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/utils/__init__.py +0 -0
  104. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/utils/graph.py +0 -0
  105. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/utils/logger.py +0 -0
  106. {yomitoku-0.9.0 → yomitoku-0.9.2}/src/yomitoku/utils/visualizer.py +0 -0
  107. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/in/demo.jpg +0 -0
  108. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/in/gallery1.jpg +0 -0
  109. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/in/gallery2.jpg +0 -0
  110. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/in/gallery3.jpg +0 -0
  111. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/in/gallery4.jpg +0 -0
  112. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/in/gallery5.jpg +0 -0
  113. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/in/gallery6.jpg +0 -0
  114. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/in/gallery7.jpeg +0 -0
  115. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/logo/horizontal.png +0 -0
  116. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/demo_html.png +0 -0
  117. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_demo_p1_figure_0.png +0 -0
  118. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery1_p1_figure_0.png +0 -0
  119. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery1_p1_figure_1.png +0 -0
  120. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery1_p1_figure_10.png +0 -0
  121. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery1_p1_figure_2.png +0 -0
  122. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery1_p1_figure_3.png +0 -0
  123. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery1_p1_figure_4.png +0 -0
  124. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery1_p1_figure_5.png +0 -0
  125. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery1_p1_figure_6.png +0 -0
  126. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery1_p1_figure_7.png +0 -0
  127. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery1_p1_figure_8.png +0 -0
  128. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery1_p1_figure_9.png +0 -0
  129. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery3_p1_figure_0.png +0 -0
  130. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery3_p1_figure_1.png +0 -0
  131. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery5_p1_figure_0.png +0 -0
  132. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery5_p1_figure_1.png +0 -0
  133. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery6_p1_figure_0.png +0 -0
  134. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery6_p1_figure_1.png +0 -0
  135. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/figures/in_gallery7_p1_figure_0.png +0 -0
  136. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_demo_p1.html +0 -0
  137. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_demo_p1.md +0 -0
  138. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_demo_p1_layout.jpg +0 -0
  139. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_demo_p1_ocr.jpg +0 -0
  140. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery1_p1.html +0 -0
  141. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery1_p1.md +0 -0
  142. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery1_p1_layout.jpg +0 -0
  143. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery1_p1_ocr.jpg +0 -0
  144. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery2_p1.html +0 -0
  145. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery2_p1.md +0 -0
  146. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery2_p1_layout.jpg +0 -0
  147. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery2_p1_ocr.jpg +0 -0
  148. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery3_p1.html +0 -0
  149. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery3_p1.md +0 -0
  150. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery3_p1_layout.jpg +0 -0
  151. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery3_p1_ocr.jpg +0 -0
  152. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery5_p1.html +0 -0
  153. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery5_p1.md +0 -0
  154. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery5_p1_layout.jpg +0 -0
  155. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery5_p1_ocr.jpg +0 -0
  156. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery6_p1.html +0 -0
  157. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery6_p1.md +0 -0
  158. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery6_p1_layout.jpg +0 -0
  159. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery6_p1_ocr.jpg +0 -0
  160. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery7_p1.html +0 -0
  161. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery7_p1.md +0 -0
  162. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery7_p1_layout.jpg +0 -0
  163. {yomitoku-0.9.0 → yomitoku-0.9.2}/static/out/in_gallery7_p1_ocr.jpg +0 -0
  164. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/invalid.jpg +0 -0
  165. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/invalid.pdf +0 -0
  166. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/rgba.png +0 -0
  167. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/sampldoc.tif +0 -0
  168. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/small.jpg +0 -0
  169. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/subdir/test.jpg +0 -0
  170. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/test.bmp +0 -0
  171. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/test.jpg +0 -0
  172. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/test.pdf +0 -0
  173. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/test.png +0 -0
  174. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/test.tiff +0 -0
  175. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/test.txt +0 -0
  176. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/data/test_gray.jpg +0 -0
  177. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/test_base.py +0 -0
  178. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/test_cli.py +0 -0
  179. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/test_document_analyzer.py +0 -0
  180. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/test_export.py +0 -0
  181. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/test_layout_analyzer.py +0 -0
  182. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/test_ocr.py +0 -0
  183. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/yaml/layout_parser.yaml +0 -0
  184. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/yaml/table_structure_recognizer.yaml +0 -0
  185. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/yaml/text_detector.yaml +0 -0
  186. {yomitoku-0.9.0 → yomitoku-0.9.2}/tests/yaml/text_recognizer.yaml +0 -0
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: yomitoku
3
- Version: 0.9.0
3
+ Version: 0.9.2
4
4
  Summary: Yomitoku is an AI-powered document image analysis package designed specifically for the Japanese language.
5
5
  Author-email: Kotaro Kinoshita <kotaro.kinoshita@mlism.com>
6
6
  License: CC BY-NC-SA 4.0
7
7
  Keywords: Deep Learning,Japanese,OCR
8
8
  Requires-Python: <3.13,>=3.10
9
9
  Requires-Dist: huggingface-hub>=0.26.1
10
+ Requires-Dist: jaconv>=0.4.0
10
11
  Requires-Dist: lxml>=5.3.0
11
12
  Requires-Dist: omegaconf>=2.3.0
12
13
  Requires-Dist: onnx>=1.17.0
@@ -15,6 +16,7 @@ Requires-Dist: opencv-python>=4.10.0.84
15
16
  Requires-Dist: pyclipper>=1.3.0.post6
16
17
  Requires-Dist: pydantic>=2.9.2
17
18
  Requires-Dist: pypdfium2>=4.30.0
19
+ Requires-Dist: reportlab>=4.4.1
18
20
  Requires-Dist: shapely>=2.0.6
19
21
  Requires-Dist: timm>=1.0.11
20
22
  Requires-Dist: torch>=2.5.0
@@ -41,7 +43,7 @@ YomiToku は日本語に特化した AI 文章画像解析エンジン(Document
41
43
  - 🤖 日本語データセットで学習した 4 種類(文字位置の検知、文字列認識、レイアウト解析、表の構造認識)の AI モデルを搭載しています。4 種類のモデルはすべて独自に学習されたモデルで日本語文書に対して、高精度に推論可能です。
42
44
  - 🇯🇵 各モデルは日本語の文書画像に特化して学習されており、7000 文字を超える日本語文字の認識をサーポート、手書き文字、縦書きなど日本語特有のレイアウト構造の文書画像の解析も可能です。(日本語以外にも英語の文書に対しても対応しています)。
43
45
  - 📈 レイアウト解析、表の構造解析, 読み順推定機能により、文書画像のレイアウトの意味的構造を壊さずに情報を抽出することが可能です。
44
- - 📄 多様な出力形式をサポートしています。html やマークダウン、json、csv のいずれかのフォーマットに変換可能です。また、文書内に含まれる図表、画像の抽出の出力も可能です。
46
+ - 📄 多様な出力形式をサポートしています。html やマークダウン、json、csv のいずれかのフォーマットに変換可能です。また、文書内に含まれる図表、画像の抽出の出力も可能です。文書画像をサーチャブルPDFに変換する処理もサポートしています。
45
47
  - ⚡ GPU 環境で高速に動作し、効率的に文書の文字起こし解析が可能です。また、VRAM も 8GB 以内で動作し、ハイエンドな GPU を用意する必要はありません。
46
48
 
47
49
  ## 🖼️ デモ
@@ -66,6 +68,7 @@ Markdown でエクスポートした結果は関してはリポジトリ内の[s
66
68
 
67
69
  ## 📣 リリース情報
68
70
 
71
+ - 2025 年 4 月 4 日 YomiToku v0.8.0 手書き文字認識のサポート
69
72
  - 2024 年 11 月 26 日 YomiToku v0.5.1 (beta) を公開
70
73
 
71
74
  ## 💡 インストールの方法
@@ -95,6 +98,7 @@ yomitoku ${path_data} -f md -o results -v --figure --lite
95
98
  - `--encoding` エクスポートする出力ファイルの文字エンコーディングを指定します。サポートされていない文字コードが含まれる場合は、その文字を無視します。(utf-8, utf-8-sig, shift-jis, enc-jp, cp932)
96
99
  - `--combine` PDFを入力に与えたときに、複数ページが含まれる場合に、それらの予測結果を一つのファイルに統合してエクスポートします。
97
100
  - `--ignore_meta` 文章のheater, fotterなどの文字情報を出力ファイルに含めません。
101
+ - `--searchable_pdf` 読み取った文字情報をPDFに埋め込み全文検索可能なPDFを出力します。
98
102
 
99
103
  その他のオプションに関しては、ヘルプを参照
100
104
 
@@ -16,7 +16,7 @@ YomiToku は日本語に特化した AI 文章画像解析エンジン(Document
16
16
  - 🤖 日本語データセットで学習した 4 種類(文字位置の検知、文字列認識、レイアウト解析、表の構造認識)の AI モデルを搭載しています。4 種類のモデルはすべて独自に学習されたモデルで日本語文書に対して、高精度に推論可能です。
17
17
  - 🇯🇵 各モデルは日本語の文書画像に特化して学習されており、7000 文字を超える日本語文字の認識をサーポート、手書き文字、縦書きなど日本語特有のレイアウト構造の文書画像の解析も可能です。(日本語以外にも英語の文書に対しても対応しています)。
18
18
  - 📈 レイアウト解析、表の構造解析, 読み順推定機能により、文書画像のレイアウトの意味的構造を壊さずに情報を抽出することが可能です。
19
- - 📄 多様な出力形式をサポートしています。html やマークダウン、json、csv のいずれかのフォーマットに変換可能です。また、文書内に含まれる図表、画像の抽出の出力も可能です。
19
+ - 📄 多様な出力形式をサポートしています。html やマークダウン、json、csv のいずれかのフォーマットに変換可能です。また、文書内に含まれる図表、画像の抽出の出力も可能です。文書画像をサーチャブルPDFに変換する処理もサポートしています。
20
20
  - ⚡ GPU 環境で高速に動作し、効率的に文書の文字起こし解析が可能です。また、VRAM も 8GB 以内で動作し、ハイエンドな GPU を用意する必要はありません。
21
21
 
22
22
  ## 🖼️ デモ
@@ -41,6 +41,7 @@ Markdown でエクスポートした結果は関してはリポジトリ内の[s
41
41
 
42
42
  ## 📣 リリース情報
43
43
 
44
+ - 2025 年 4 月 4 日 YomiToku v0.8.0 手書き文字認識のサポート
44
45
  - 2024 年 11 月 26 日 YomiToku v0.5.1 (beta) を公開
45
46
 
46
47
  ## 💡 インストールの方法
@@ -70,6 +71,7 @@ yomitoku ${path_data} -f md -o results -v --figure --lite
70
71
  - `--encoding` エクスポートする出力ファイルの文字エンコーディングを指定します。サポートされていない文字コードが含まれる場合は、その文字を無視します。(utf-8, utf-8-sig, shift-jis, enc-jp, cp932)
71
72
  - `--combine` PDFを入力に与えたときに、複数ページが含まれる場合に、それらの予測結果を一つのファイルに統合してエクスポートします。
72
73
  - `--ignore_meta` 文章のheater, fotterなどの文字情報を出力ファイルに含めません。
74
+ - `--searchable_pdf` 読み取った文字情報をPDFに埋め込み全文検索可能なPDFを出力します。
73
75
 
74
76
  その他のオプションに関しては、ヘルプを参照
75
77
 
@@ -70,6 +70,7 @@ yomitoku ${path_data} -f md -o results -v --figure --lite
70
70
  - `--encoding` Specifies the character encoding for the output file to be exported. If unsupported characters are included, they will be ignored. (utf-8, utf-8-sig, shift-jis, enc-jp, cp932)
71
71
  - `--combine` When a PDF is provided as input and contains multiple pages, this option combines their prediction results into a single file for export.
72
72
  - `--ignore_meta` Excludes text information such as headers and footers from the output file.
73
+ - `--searchable_pdf` Embeds the recognized text into the PDF and outputs a fully searchable PDF.
73
74
 
74
75
  For other options, please refer to the help documentation.
75
76
 
@@ -4,9 +4,12 @@ from yomitoku import OCR
4
4
  from yomitoku.data.functions import load_pdf
5
5
 
6
6
  if __name__ == "__main__":
7
- ocr = OCR(visualize=True, device="cpu")
7
+ ocr = OCR(visualize=True, device="cuda")
8
8
  # PDFファイルを読み込み
9
9
  imgs = load_pdf("demo/sample.pdf")
10
+ import time
11
+
12
+ start = time.time()
10
13
  for i, img in enumerate(imgs):
11
14
  results, ocr_vis = ocr(img)
12
15
 
@@ -107,4 +107,26 @@ If the PDF contains multiple pages, you can export them as a single file.
107
107
 
108
108
  ```
109
109
  yomitoku ${path_data} -f md --combine
110
+ ```
111
+
112
+ ## Specifying Reading Order
113
+
114
+ By default, *Auto* mode automatically detects whether a document is written horizontally or vertically and estimates the appropriate reading order. However, you can explicitly specify a custom reading order. For horizontal documents, the default is `top2left`, and for vertical documents, it is `top2bottom`.
115
+
116
+ ```
117
+ yomitoku ${path_data} --reading_order left2right
118
+ ```
119
+
120
+ * `top2bottom`: Prioritizes reading from top to bottom. Useful for multi-column documents such as word processor files with vertical flow.
121
+
122
+ * `left2right`: Prioritizes reading from left to right. Suitable for layouts like receipts or health insurance cards, where key-value text pairs are arranged in columns.
123
+
124
+ * `right2left`: Prioritizes reading from right to left. Effective for vertically written documents.
125
+
126
+ ## Create a searchable PDF
127
+
128
+ Detect the text in the image and embed it into the PDF as invisible text, converting the file into a searchable PDF.
129
+
130
+ ```
131
+ yomitoku ${path_data} --searchable_pdf
110
132
  ```
@@ -104,4 +104,24 @@ PDFに複数ページが含まれる場合に複数ページを一つのファ
104
104
 
105
105
  ```
106
106
  yomitoku ${path_data} -f md --combine
107
+ ```
108
+
109
+ ## 読み取り順を指定する
110
+ Autoでは、横書きのドキュメント、縦書きのドキュメントを識別し、自動で読み取り順を推定しますが、任意の読み取り順の指定することが可能です。デフォルトでは横書きの文書は`top2left`, 縦書きは`top2bottom`になります。
111
+
112
+ ```
113
+ yomitoku ${path_data} --reading_order left2right
114
+ ```
115
+
116
+ - `top2bottom`: 上から下方向に優先的に読み取り順を推定します。段組みのワードドキュメントなどに対して、有効です。
117
+
118
+ - `left2right`: 左から右方向に優先的に読み取り順を推定します。レシートや保険証などキーに対して、値を示すテキストが段組みになっているようなレイアウトに有効です。
119
+
120
+ - `right2left:` 右から左方向に優先的に読み取り順を推定します。縦書きのドキュメントに対して有効です。
121
+
122
+ ## 検索可能なPDFを作成する
123
+ 画像内の文字情報を認識し、文字情報を透明テキストとして、PDFに埋め込むことで、サーチャブルPDFに変換します。
124
+
125
+ ```
126
+ yomitoku ${path_data} --searchable_pdf
107
127
  ```
@@ -5,7 +5,7 @@ YomiToku is a Document AI engine specialized in Japanese document image analysis
5
5
  - 🤖 Equipped with four AI models trained on Japanese datasets: text detection, text recognition, layout analysis, and table structure recognition. All models are independently trained and optimized for Japanese documents, delivering high-precision inference.
6
6
  - 🇯🇵 Each model is specifically trained for Japanese document images, supporting the recognition of over 7,000 Japanese characters, including vertical text and other layout structures unique to Japanese documents. (It also supports English documents.)
7
7
  - 📈 By leveraging layout analysis, table structure parsing, and reading order estimation, it extracts information while preserving the semantic structure of the document layout.
8
- - 📄 Supports a variety of output formats, including HTML, Markdown, JSON, and CSV. It also allows for the extraction of diagrams and images contained within the documents.
8
+ - 📄 Supports a variety of output formats, including HTML, Markdown, JSON, and CSV. It also allows for the extraction of diagrams and images contained within the documents.It also supports converting document images into fully text-searchable PDFs.
9
9
  - ⚡ Operates efficiently in GPU environments, enabling fast document transcription and analysis. It requires less than 8GB of VRAM, eliminating the need for high-end GPUs.。
10
10
 
11
11
  ## 🙋 FAQ
@@ -5,7 +5,7 @@ YomiToku は日本語に特化した AI 文章画像解析エンジン(Document
5
5
  - 🤖 日本語データセットで学習した 4 種類(文字位置の検知、文字列認識、レイアウト解析、表の構造認識)の AI モデルを搭載しています。4 種類のモデルはすべて独自に学習されたモデルで日本語文書に対して、高精度に推論可能です。
6
6
  - 🇯🇵 各モデルは日本語の文書画像に特化して学習されており、7000 文字を超える日本語文字の認識をサーポート、手書き文字、縦書きなど日本語特有のレイアウト構造の文書画像の解析も可能です。(日本語以外にも英語の文書に対しても対応しています)。
7
7
  - 📈 レイアウト解析、表の構造解析, 読み順推定機能により、文書画像のレイアウトの意味的構造を壊さずに情報を抽出することが可能です。
8
- - 📄 多様な出力形式をサポートしています。html やマークダウン、json、csv のいずれかのフォーマットに変換可能です。また、文書内に含まれる図表、画像の抽出の出力も可能です。
8
+ - 📄 多様な出力形式をサポートしています。html やマークダウン、json、csv のいずれかのフォーマットに変換可能です。また、文書内に含まれる図表、画像の抽出の出力も可能です。文書画像を全文検索可能なサーチャブルPDFに変換する処理もサポートしています。
9
9
  - ⚡ GPU 環境で高速に動作し、効率的に文書の文字起こし解析が可能です。また、VRAM も 8GB 以内で動作し、ハイエンドな GPU を用意する必要はありません。
10
10
 
11
11
  ## 🙋 FAQ
@@ -46,3 +46,19 @@ For example, if you use `yomitoku/demo/sample.pdf` as a sample, instruct as foll
46
46
  ```txt
47
47
  Analyze sample.pdf using OCR and translate it into English.
48
48
  ```
49
+
50
+ ## Starting the SSE Server
51
+
52
+ Set the path to the folder containing the images to be processed by OCR in the resource directory.
53
+
54
+ ```
55
+ export RESOURCE_DIR="path of dataset"
56
+ ```
57
+
58
+ Start the SSE server using the following command:
59
+
60
+ ```
61
+ uv run yomitoku_mcp -t sse
62
+ ```
63
+
64
+ The SSE server endpoint will be available at `http://127.0.0.1:8000/sse`.
@@ -13,6 +13,7 @@
13
13
  uv sync --extra mcp
14
14
  ```
15
15
 
16
+
16
17
  ## Claude Desktopの設定
17
18
 
18
19
  次にClaude Desktopの設定ファイルの`mcpServers`に以下ように設定を追加します。(設定ファイルの開き方は[こちら](https://modelcontextprotocol.io/quickstart/user)を参照してください)
@@ -48,3 +49,16 @@ uv sync --extra mcp
48
49
  ```txt
49
50
  sample.pdfをOCRで解析して要約してください。
50
51
  ```
52
+
53
+ ## SSEサーバーの起動
54
+ 環境変数の`RESOURCE_DIR`にOCRの対象画像が含まれたフォルダのパスを設定してください。
55
+ ```
56
+ export RESOURCE_DIR="path of dataset"
57
+ ```
58
+
59
+ 以下のコマンドでSSEサーバーを起動します。
60
+ ```
61
+ uv run yomitoku_mcp -t sse
62
+ ```
63
+
64
+ ` http://127.0.0.1:8000/sse`がSSEサーバーのエンドポイントになります。
@@ -30,6 +30,8 @@ dependencies = [
30
30
  "torch>=2.5.0",
31
31
  "torchvision>=0.20.0",
32
32
  "onnxruntime>=1.20.1",
33
+ "reportlab>=4.4.1",
34
+ "jaconv>=0.4.0",
33
35
  ]
34
36
 
35
37
  [tool.uv-dynamic-versioning]
@@ -70,7 +72,7 @@ explicit = true
70
72
 
71
73
  [project.scripts]
72
74
  yomitoku = "yomitoku.cli.main:main"
73
- yomitoku_mcp = "yomitoku.cli.mcp:run_mcp_server"
75
+ yomitoku_mcp = "yomitoku.cli.mcp_server:main"
74
76
 
75
77
  [project.optional-dependencies]
76
78
  mcp = [
@@ -9,6 +9,7 @@ from ..constants import SUPPORT_OUTPUT_FORMAT
9
9
  from ..data.functions import load_image, load_pdf
10
10
  from ..document_analyzer import DocumentAnalyzer
11
11
  from ..utils.logger import set_logger
12
+ from ..utils.searchable_pdf import create_searchable_pdf
12
13
 
13
14
  from ..export import save_csv, save_html, save_json, save_markdown
14
15
  from ..export import convert_json, convert_csv, convert_html, convert_markdown
@@ -80,11 +81,13 @@ def process_single_file(args, analyzer, path, format):
80
81
  else:
81
82
  imgs = load_image(path)
82
83
 
84
+ format_results = []
83
85
  results = []
84
86
  for page, img in enumerate(imgs):
85
87
  result, ocr, layout = analyzer(img)
86
88
  dirname = path.parent.name
87
89
  filename = path.stem
90
+ results.append(result)
88
91
 
89
92
  # cv2.imwrite(
90
93
  # os.path.join(args.outdir, f"{dirname}_{filename}_p{page+1}.jpg"), img
@@ -92,7 +95,7 @@ def process_single_file(args, analyzer, path, format):
92
95
 
93
96
  if ocr is not None:
94
97
  out_path = os.path.join(
95
- args.outdir, f"{dirname}_{filename}_p{page+1}_ocr.jpg"
98
+ args.outdir, f"{dirname}_{filename}_p{page + 1}_ocr.jpg"
96
99
  )
97
100
 
98
101
  save_image(ocr, out_path)
@@ -100,13 +103,15 @@ def process_single_file(args, analyzer, path, format):
100
103
 
101
104
  if layout is not None:
102
105
  out_path = os.path.join(
103
- args.outdir, f"{dirname}_{filename}_p{page+1}_layout.jpg"
106
+ args.outdir, f"{dirname}_{filename}_p{page + 1}_layout.jpg"
104
107
  )
105
108
 
106
109
  save_image(layout, out_path)
107
110
  logger.info(f"Output file: {out_path}")
108
111
 
109
- out_path = os.path.join(args.outdir, f"{dirname}_{filename}_p{page+1}.{format}")
112
+ out_path = os.path.join(
113
+ args.outdir, f"{dirname}_{filename}_p{page + 1}.{format}"
114
+ )
110
115
 
111
116
  if format == "json":
112
117
  if args.combine:
@@ -128,7 +133,7 @@ def process_single_file(args, analyzer, path, format):
128
133
  figure_dir=args.figure_dir,
129
134
  )
130
135
 
131
- results.append(
136
+ format_results.append(
132
137
  {
133
138
  "format": format,
134
139
  "data": json.model_dump(),
@@ -155,7 +160,7 @@ def process_single_file(args, analyzer, path, format):
155
160
  figure_dir=args.figure_dir,
156
161
  )
157
162
 
158
- results.append(
163
+ format_results.append(
159
164
  {
160
165
  "format": format,
161
166
  "data": csv,
@@ -186,7 +191,7 @@ def process_single_file(args, analyzer, path, format):
186
191
  encoding=args.encoding,
187
192
  )
188
193
 
189
- results.append(
194
+ format_results.append(
190
195
  {
191
196
  "format": format,
192
197
  "data": html,
@@ -217,14 +222,14 @@ def process_single_file(args, analyzer, path, format):
217
222
  encoding=args.encoding,
218
223
  )
219
224
 
220
- results.append(
225
+ format_results.append(
221
226
  {
222
227
  "format": format,
223
228
  "data": md,
224
229
  }
225
230
  )
226
231
 
227
- out = merge_all_pages(results)
232
+ out = merge_all_pages(format_results)
228
233
  if args.combine:
229
234
  out_path = os.path.join(args.outdir, f"{dirname}_{filename}.{format}")
230
235
  save_merged_file(
@@ -233,6 +238,15 @@ def process_single_file(args, analyzer, path, format):
233
238
  out,
234
239
  )
235
240
 
241
+ if args.searchable_pdf:
242
+ pdf_path = os.path.join(args.outdir, f"{filename}.pdf")
243
+ create_searchable_pdf(
244
+ imgs,
245
+ results,
246
+ output_path=pdf_path,
247
+ )
248
+ logger.info(f"Output SearchablePDF: {pdf_path}")
249
+
236
250
 
237
251
  def main():
238
252
  parser = argparse.ArgumentParser()
@@ -341,6 +355,17 @@ def main():
341
355
  action="store_true",
342
356
  help="if set, ignore meta information(header, footer) in the output",
343
357
  )
358
+ parser.add_argument(
359
+ "--reading_order",
360
+ default="auto",
361
+ type=str,
362
+ choices=["auto", "left2right", "top2bottom", "right2left"],
363
+ )
364
+ parser.add_argument(
365
+ "--searchable_pdf",
366
+ action="store_true",
367
+ help="if set, create searchable PDF",
368
+ )
344
369
 
345
370
  args = parser.parse_args()
346
371
 
@@ -394,6 +419,7 @@ def main():
394
419
  visualize=args.vis,
395
420
  device=args.device,
396
421
  ignore_meta=args.ignore_meta,
422
+ reading_order=args.reading_order,
397
423
  )
398
424
 
399
425
  os.makedirs(args.outdir, exist_ok=True)
@@ -408,7 +434,7 @@ def main():
408
434
  logger.info(f"Processing file: {file_path}")
409
435
  process_single_file(args, analyzer, file_path, format)
410
436
  end = time.time()
411
- logger.info(f"Total Processing time: {end-start:.2f} sec")
437
+ logger.info(f"Total Processing time: {end - start:.2f} sec")
412
438
  except Exception:
413
439
  continue
414
440
  else:
@@ -416,7 +442,7 @@ def main():
416
442
  logger.info(f"Processing file: {path}")
417
443
  process_single_file(args, analyzer, path, format)
418
444
  end = time.time()
419
- logger.info(f"Total Processing time: {end-start:.2f} sec")
445
+ logger.info(f"Total Processing time: {end - start:.2f} sec")
420
446
 
421
447
 
422
448
  if __name__ == "__main__":
@@ -1,14 +1,20 @@
1
- import json
2
- import io
3
1
  import csv
2
+ import io
3
+ import json
4
4
  import os
5
+ from argparse import ArgumentParser
5
6
  from pathlib import Path
6
7
 
7
8
  from mcp.server.fastmcp import Context, FastMCP
8
9
 
9
10
  from yomitoku import DocumentAnalyzer
10
11
  from yomitoku.data.functions import load_image, load_pdf
11
- from yomitoku.export import convert_json, convert_markdown, convert_csv, convert_html
12
+ from yomitoku.export import (
13
+ convert_csv,
14
+ convert_html,
15
+ convert_json,
16
+ convert_markdown,
17
+ )
12
18
 
13
19
  try:
14
20
  RESOURCE_DIR = os.environ["RESOURCE_DIR"]
@@ -154,12 +160,37 @@ async def get_file_list() -> list[str]:
154
160
  return os.listdir(RESOURCE_DIR)
155
161
 
156
162
 
157
- def run_mcp_server():
163
+ def run_mcp_server(transport="stdio", mount_path=None):
158
164
  """
159
165
  Run the MCP server.
160
166
  """
161
- mcp.run(transport="stdio")
167
+
168
+ if transport == "stdio":
169
+ mcp.run()
170
+ elif transport == "sse":
171
+ mcp.run(transport=transport, mount_path=mount_path)
172
+
173
+
174
+ def main():
175
+ parser = ArgumentParser(description="Run the MCP server.")
176
+ parser.add_argument(
177
+ "--transport",
178
+ "-t",
179
+ type=str,
180
+ default="stdio",
181
+ choices=["stdio", "sse"],
182
+ help="Transport method for the MCP server.",
183
+ )
184
+ parser.add_argument(
185
+ "--mount_path",
186
+ "-m",
187
+ type=str,
188
+ default=None,
189
+ help="Mount path for the MCP server (only used with SSE transport).",
190
+ )
191
+ args = parser.parse_args()
192
+ run_mcp_server(transport=args.transport, mount_path=args.mount_path)
162
193
 
163
194
 
164
195
  if __name__ == "__main__":
165
- run_mcp_server()
196
+ main()
@@ -8,9 +8,11 @@ from .functions import (
8
8
  validate_quads,
9
9
  )
10
10
 
11
+ from concurrent.futures import ThreadPoolExecutor
12
+
11
13
 
12
14
  class ParseqDataset(Dataset):
13
- def __init__(self, cfg, img, quads):
15
+ def __init__(self, cfg, img, quads, num_workers=8):
14
16
  self.img = img[:, :, ::-1]
15
17
  self.quads = quads
16
18
  self.cfg = cfg
@@ -22,19 +24,27 @@ class ParseqDataset(Dataset):
22
24
  ]
23
25
  )
24
26
 
25
- validate_quads(self.img, self.quads)
27
+ with ThreadPoolExecutor(max_workers=num_workers) as executor:
28
+ data = list(executor.map(self.preprocess, self.quads))
26
29
 
27
- def __len__(self):
28
- return len(self.quads)
30
+ self.data = [tensor for tensor in data if tensor is not None]
31
+
32
+ def preprocess(self, quad):
33
+ if validate_quads(self.img, quad) is None:
34
+ return None
35
+
36
+ roi_img = extract_roi_with_perspective(self.img, quad)
29
37
 
30
- def __getitem__(self, index):
31
- polygon = self.quads[index]
32
- roi_img = extract_roi_with_perspective(self.img, polygon)
33
38
  if roi_img is None:
34
- return
39
+ return None
35
40
 
36
41
  roi_img = rotate_text_image(roi_img, thresh_aspect=2)
37
42
  resized = resize_with_padding(roi_img, self.cfg.data.img_size)
38
- tensor = self.transform(resized)
39
43
 
40
- return tensor
44
+ return resized
45
+
46
+ def __len__(self):
47
+ return len(self.data)
48
+
49
+ def __getitem__(self, index):
50
+ return self.transform(self.data[index])
@@ -191,7 +191,7 @@ def array_to_tensor(img: np.ndarray) -> torch.Tensor:
191
191
  return tensor
192
192
 
193
193
 
194
- def validate_quads(img: np.ndarray, quads: list[list[list[int]]]):
194
+ def validate_quads(img: np.ndarray, quad: list[list[list[int]]]):
195
195
  """
196
196
  Validate the vertices of the quadrilateral.
197
197
 
@@ -204,23 +204,23 @@ def validate_quads(img: np.ndarray, quads: list[list[list[int]]]):
204
204
  """
205
205
 
206
206
  h, w = img.shape[:2]
207
- for quad in quads:
208
- if len(quad) != 4:
209
- raise ValueError("The number of vertices must be 4.")
210
-
211
- for point in quad:
212
- if len(point) != 2:
213
- raise ValueError("The number of coordinates must be 2.")
214
-
215
- quad = np.array(quad, dtype=int)
216
- x1 = np.min(quad[:, 0])
217
- x2 = np.max(quad[:, 0])
218
- y1 = np.min(quad[:, 1])
219
- y2 = np.max(quad[:, 1])
220
- h, w = img.shape[:2]
207
+ if len(quad) != 4:
208
+ # raise ValueError("The number of vertices must be 4.")
209
+ return None
210
+
211
+ for point in quad:
212
+ if len(point) != 2:
213
+ return None
214
+
215
+ quad = np.array(quad, dtype=int)
216
+ x1 = np.min(quad[:, 0])
217
+ x2 = np.max(quad[:, 0])
218
+ y1 = np.min(quad[:, 1])
219
+ y2 = np.max(quad[:, 1])
220
+ h, w = img.shape[:2]
221
221
 
222
- if x1 < 0 or x2 > w or y1 < 0 or y2 > h:
223
- raise ValueError(f"The vertices are out of the image. {quad.tolist()}")
222
+ if x1 < 0 or x2 > w or y1 < 0 or y2 > h:
223
+ return None
224
224
 
225
225
  return True
226
226
 
@@ -237,19 +237,18 @@ def extract_roi_with_perspective(img, quad):
237
237
  np.ndarray: extracted image
238
238
  """
239
239
  dst = img.copy()
240
- quad = np.array(quad, dtype=np.float32)
240
+ quad = np.array(quad, dtype=np.int64)
241
+
241
242
  width = np.linalg.norm(quad[0] - quad[1])
242
243
  height = np.linalg.norm(quad[1] - quad[2])
243
244
 
244
245
  width = int(width)
245
246
  height = int(height)
246
-
247
247
  pts1 = np.float32(quad)
248
248
  pts2 = np.float32([[0, 0], [width, 0], [width, height], [0, height]])
249
249
 
250
250
  M = cv2.getPerspectiveTransform(pts1, pts2)
251
251
  dst = cv2.warpPerspective(dst, M, (width, height))
252
-
253
252
  return dst
254
253
 
255
254
 
@@ -86,8 +86,12 @@ def extract_paragraph_within_figure(paragraphs, figures):
86
86
  check_list[i] = True
87
87
 
88
88
  figure["direction"] = judge_page_direction(contained_paragraphs)
89
+ reading_order = (
90
+ "left2right" if figure["direction"] == "horizontal" else "right2left"
91
+ )
92
+
89
93
  figure_paragraphs = prediction_reading_order(
90
- contained_paragraphs, figure["direction"]
94
+ contained_paragraphs, reading_order
91
95
  )
92
96
  figure["paragraphs"] = sorted(figure_paragraphs, key=lambda x: x.order)
93
97
  figure = FigureSchema(**figure)
@@ -126,8 +130,8 @@ def extract_words_within_element(pred_words, element):
126
130
  cnt_vertical = word_direction.count("vertical")
127
131
 
128
132
  element_direction = "horizontal" if cnt_horizontal > cnt_vertical else "vertical"
129
-
130
- prediction_reading_order(contained_words, element_direction)
133
+ order = "left2right" if element_direction == "horizontal" else "right2left"
134
+ prediction_reading_order(contained_words, order)
131
135
  contained_words = sorted(contained_words, key=lambda x: x.order)
132
136
 
133
137
  contained_words = "\n".join([content.contents for content in contained_words])
@@ -328,6 +332,7 @@ class DocumentAnalyzer:
328
332
  device="cuda",
329
333
  visualize=False,
330
334
  ignore_meta=False,
335
+ reading_order="auto",
331
336
  ):
332
337
  default_configs = {
333
338
  "ocr": {
@@ -352,6 +357,8 @@ class DocumentAnalyzer:
352
357
  },
353
358
  }
354
359
 
360
+ self.reading_order = reading_order
361
+
355
362
  if isinstance(configs, dict):
356
363
  recursive_update(default_configs, configs)
357
364
  else:
@@ -452,9 +459,17 @@ class DocumentAnalyzer:
452
459
 
453
460
  elements = page_contents + layout_res.tables + figures
454
461
 
455
- prediction_reading_order(headers, page_direction)
456
- prediction_reading_order(footers, page_direction)
457
- prediction_reading_order(elements, page_direction, self.img)
462
+ prediction_reading_order(headers, "left2right")
463
+ prediction_reading_order(footers, "left2right")
464
+
465
+ if self.reading_order == "auto":
466
+ reading_order = (
467
+ "right2left" if page_direction == "vertical" else "top2bottom"
468
+ )
469
+ else:
470
+ reading_order = self.reading_order
471
+
472
+ prediction_reading_order(elements, reading_order, self.img)
458
473
 
459
474
  for i, element in enumerate(elements):
460
475
  element.order += len(headers)