yomitoku 0.8.1__tar.gz → 0.9.1__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.
- {yomitoku-0.8.1 → yomitoku-0.9.1}/PKG-INFO +4 -1
- {yomitoku-0.8.1 → yomitoku-0.9.1}/README.md +1 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/demo/simple_ocr.py +4 -1
- {yomitoku-0.8.1 → yomitoku-0.9.1}/docs/cli.en.md +15 -1
- {yomitoku-0.8.1 → yomitoku-0.9.1}/docs/cli.ja.md +15 -1
- yomitoku-0.9.1/docs/mcp.en.md +48 -0
- yomitoku-0.9.1/docs/mcp.ja.md +50 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/mkdocs.yml +1 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/pyproject.toml +6 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/cli/main.py +18 -8
- yomitoku-0.9.1/src/yomitoku/cli/mcp.py +165 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/data/dataset.py +20 -10
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/data/functions.py +19 -20
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/document_analyzer.py +21 -6
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/export/export_csv.py +2 -2
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/export/export_html.py +10 -5
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/export/export_json.py +2 -2
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/export/export_markdown.py +2 -2
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/reading_order.py +38 -8
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/utils/misc.py +61 -2
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery4_p1.html +6 -6
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery4_p1.md +9 -9
- yomitoku-0.9.1/static/out/in_gallery4_p1_layout.jpg +0 -0
- yomitoku-0.9.1/static/out/in_gallery4_p1_ocr.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/test_data.py +14 -31
- yomitoku-0.9.1/uv.lock +1815 -0
- yomitoku-0.8.1/static/out/in_gallery4_p1_layout.jpg +0 -0
- yomitoku-0.8.1/static/out/in_gallery4_p1_ocr.jpg +0 -0
- yomitoku-0.8.1/uv.lock +0 -1610
- {yomitoku-0.8.1 → yomitoku-0.9.1}/.github/FUNDING.yml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/.github/release-drafter.yml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/.github/workflows/build-and-publish-docs.yaml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/.github/workflows/build-and-publish.yml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/.github/workflows/create-release.yml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/.github/workflows/lint-and-test.yml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/.gitignore +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/.pre-commit-config.yaml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/.python-version +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/README_EN.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/configs/yomitoku-layout-parser-rtdtrv2-open-beta.yaml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/configs/yomitoku-table-structure-recognizer-rtdtrv2-open-beta.yaml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/configs/yomitoku-text-detector-dbnet-open-beta.yaml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/configs/yomitoku-text-recognizer-parseq-open-beta.yaml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/configs/yomitoku-text-recognizer-parseq-small-open-beta.yaml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/demo/sample.pdf +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/demo/setting_document_anaysis.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/demo/simple_document_analysis.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/demo/simple_layout.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/demo/text_detector.yaml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/dockerfile +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/docs/assets/logo.svg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/docs/configuration.en.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/docs/configuration.ja.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/docs/index.en.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/docs/index.ja.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/docs/installation.en.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/docs/installation.ja.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/docs/module.en.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/docs/module.ja.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/gallery.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/pytest.ini +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/scripts/register_hugging_face_hub.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/__init__.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/base.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/cli/__init__.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/configs/__init__.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/configs/cfg_layout_parser_rtdtrv2.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/configs/cfg_layout_parser_rtdtrv2_v2.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/configs/cfg_table_structure_recognizer_rtdtrv2.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/configs/cfg_text_detector_dbnet.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/configs/cfg_text_detector_dbnet_v2.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/configs/cfg_text_recognizer_parseq.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/configs/cfg_text_recognizer_parseq_small.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/configs/cfg_text_recognizer_parseq_v2.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/constants.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/data/__init__.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/export/__init__.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/layout_analyzer.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/layout_parser.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/models/__init__.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/models/dbnet_plus.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/models/layers/__init__.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/models/layers/activate.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/models/layers/dbnet_feature_attention.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/models/layers/parseq_transformer.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/models/layers/rtdetr_backbone.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/models/layers/rtdetr_hybrid_encoder.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/models/layers/rtdetrv2_decoder.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/models/parseq.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/models/rtdetr.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/ocr.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/onnx/.gitkeep +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/postprocessor/__init__.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/postprocessor/dbnet_postporcessor.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/postprocessor/parseq_tokenizer.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/postprocessor/rtdetr_postprocessor.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/resource/MPLUS1p-Medium.ttf +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/resource/charset.txt +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/table_structure_recognizer.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/text_detector.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/text_recognizer.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/utils/__init__.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/utils/graph.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/utils/logger.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/src/yomitoku/utils/visualizer.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/in/demo.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/in/gallery1.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/in/gallery2.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/in/gallery3.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/in/gallery4.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/in/gallery5.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/in/gallery6.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/in/gallery7.jpeg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/logo/horizontal.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/demo_html.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_demo_p1_figure_0.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery1_p1_figure_0.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery1_p1_figure_1.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery1_p1_figure_10.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery1_p1_figure_2.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery1_p1_figure_3.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery1_p1_figure_4.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery1_p1_figure_5.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery1_p1_figure_6.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery1_p1_figure_7.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery1_p1_figure_8.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery1_p1_figure_9.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery3_p1_figure_0.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery3_p1_figure_1.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery5_p1_figure_0.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery5_p1_figure_1.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery6_p1_figure_0.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery6_p1_figure_1.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/figures/in_gallery7_p1_figure_0.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_demo_p1.html +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_demo_p1.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_demo_p1_layout.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_demo_p1_ocr.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery1_p1.html +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery1_p1.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery1_p1_layout.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery1_p1_ocr.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery2_p1.html +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery2_p1.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery2_p1_layout.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery2_p1_ocr.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery3_p1.html +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery3_p1.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery3_p1_layout.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery3_p1_ocr.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery5_p1.html +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery5_p1.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery5_p1_layout.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery5_p1_ocr.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery6_p1.html +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery6_p1.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery6_p1_layout.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery6_p1_ocr.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery7_p1.html +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery7_p1.md +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery7_p1_layout.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/static/out/in_gallery7_p1_ocr.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/invalid.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/invalid.pdf +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/rgba.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/sampldoc.tif +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/small.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/subdir/test.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/test.bmp +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/test.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/test.pdf +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/test.png +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/test.tiff +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/test.txt +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/data/test_gray.jpg +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/test_base.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/test_cli.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/test_document_analyzer.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/test_export.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/test_layout_analyzer.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/test_ocr.py +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/yaml/layout_parser.yaml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/yaml/table_structure_recognizer.yaml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/yaml/text_detector.yaml +0 -0
- {yomitoku-0.8.1 → yomitoku-0.9.1}/tests/yaml/text_recognizer.yaml +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: yomitoku
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.9.1
|
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
|
@@ -19,6 +19,8 @@ Requires-Dist: shapely>=2.0.6
|
|
19
19
|
Requires-Dist: timm>=1.0.11
|
20
20
|
Requires-Dist: torch>=2.5.0
|
21
21
|
Requires-Dist: torchvision>=0.20.0
|
22
|
+
Provides-Extra: mcp
|
23
|
+
Requires-Dist: mcp[cli]>=1.6.0; extra == 'mcp'
|
22
24
|
Description-Content-Type: text/markdown
|
23
25
|
|
24
26
|
日本語版 | [English](README_EN.md)
|
@@ -64,6 +66,7 @@ Markdown でエクスポートした結果は関してはリポジトリ内の[s
|
|
64
66
|
|
65
67
|
## 📣 リリース情報
|
66
68
|
|
69
|
+
- 2025 年 4 月 4 日 YomiToku v0.8.0 手書き文字認識のサポート
|
67
70
|
- 2024 年 11 月 26 日 YomiToku v0.5.1 (beta) を公開
|
68
71
|
|
69
72
|
## 💡 インストールの方法
|
@@ -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="
|
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,18 @@ 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
|
-
```
|
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.
|
@@ -104,4 +104,18 @@ PDFに複数ページが含まれる場合に複数ページを一つのファ
|
|
104
104
|
|
105
105
|
```
|
106
106
|
yomitoku ${path_data} -f md --combine
|
107
|
-
```
|
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
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# MCP
|
2
|
+
|
3
|
+
This section explains how to use the Yomitoku MCP server in conjunction with Claude Desktop.
|
4
|
+
|
5
|
+
## Installing Yomitoku
|
6
|
+
|
7
|
+
First, install Yomitoku by following the "Installation with uv" section in [Installation](installation.en.md).
|
8
|
+
|
9
|
+
However, to add `mcp` as a dependency during installation, include `mcp` in `--extra` as shown below.
|
10
|
+
|
11
|
+
```bash
|
12
|
+
uv sync --extra mcp
|
13
|
+
```
|
14
|
+
|
15
|
+
## Setting up Claude Desktop
|
16
|
+
|
17
|
+
Next, add the following configuration to the `mcpServers` section of the Claude Desktop configuration file. (Refer to [here](https://modelcontextprotocol.io/quickstart/user) for how to open the configuration file)
|
18
|
+
|
19
|
+
```json
|
20
|
+
{
|
21
|
+
"mcpServers": {
|
22
|
+
"yomitoku": {
|
23
|
+
"command": "uv",
|
24
|
+
"args": [
|
25
|
+
"--directory",
|
26
|
+
"(Absolute path of the directory where Yomitoku was cloned)",
|
27
|
+
"run",
|
28
|
+
"yomitoku_mcp"
|
29
|
+
],
|
30
|
+
"env": {
|
31
|
+
"RESOURCE_DIR": "(Absolute path of the directory containing files for OCR)"
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
```
|
37
|
+
|
38
|
+
For example, if you executed `git clone https://github.com/kotaro-kinoshita/yomitoku.git` in `/Users/your-username/workspace`, then `(Directory where Yomitoku was cloned)` would be `/Users/your-username/workspace/yomitoku`, and if you use `sample.pdf` in the `yomitoku/demo` directory, specify `(Directory containing files for OCR)` as `/Users/your-username/workspace/yomitoku/demo`.
|
39
|
+
|
40
|
+
## Using Claude Desktop
|
41
|
+
|
42
|
+
* Please restart Claude Desktop to apply changes to the configuration file.
|
43
|
+
|
44
|
+
For example, if you use `yomitoku/demo/sample.pdf` as a sample, instruct as follows:
|
45
|
+
|
46
|
+
```txt
|
47
|
+
Analyze sample.pdf using OCR and translate it into English.
|
48
|
+
```
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# MCP
|
2
|
+
|
3
|
+
ここではYomitokuのMCPサーバーをClaude Desktopに連携して利用する方法を説明します。
|
4
|
+
|
5
|
+
## Yomitokuのインストール
|
6
|
+
|
7
|
+
まずは
|
8
|
+
[Installation](installation.ja.md)の「uvでのインストール」に従ってYomitokuをインストールしてください。
|
9
|
+
|
10
|
+
ただし、`mcp`を依存関係に追加するためにインストール時には下記のように`--extra`に`mcp`を加えます。
|
11
|
+
|
12
|
+
```bash
|
13
|
+
uv sync --extra mcp
|
14
|
+
```
|
15
|
+
|
16
|
+
## Claude Desktopの設定
|
17
|
+
|
18
|
+
次にClaude Desktopの設定ファイルの`mcpServers`に以下ように設定を追加します。(設定ファイルの開き方は[こちら](https://modelcontextprotocol.io/quickstart/user)を参照してください)
|
19
|
+
|
20
|
+
```json
|
21
|
+
{
|
22
|
+
"mcpServers": {
|
23
|
+
"yomitoku": {
|
24
|
+
"command": "uv",
|
25
|
+
"args": [
|
26
|
+
"--directory",
|
27
|
+
"(YomitokuをCloneしたディレクトリの絶対パス)",
|
28
|
+
"run",
|
29
|
+
"yomitoku_mcp"
|
30
|
+
],
|
31
|
+
"env": {
|
32
|
+
"RESOURCE_DIR": "(OCR対象のファイルがあるディレクトリの絶対パス)"
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
```
|
38
|
+
|
39
|
+
|
40
|
+
例えば、`/Users/your-username/workspace`で`git clone https://github.com/kotaro-kinoshita/yomitoku.git`を実行した場合は、`(YomitokuをCloneしたディレクトリ)`は`/Users/your-username/workspace/yomitoku`となり、`yomitoku/demo`ディレクトリの`sample.pdf`を用いる場合は`(OCR対象のファイルがあるディレクトリ)`を`/Users/your-username/workspace/yomitoku/demo`と指定します。
|
41
|
+
|
42
|
+
## Claude Desktopでの利用
|
43
|
+
|
44
|
+
※ 設定ファイルの変更を反映するにはClaude Desktopを再起動してください。
|
45
|
+
|
46
|
+
例えば`yomitoku/demo/sample.pdf`をサンプルとして用いる場合、下記のように指示してください。
|
47
|
+
|
48
|
+
```txt
|
49
|
+
sample.pdfをOCRで解析して要約してください。
|
50
|
+
```
|
@@ -3,7 +3,6 @@ import os
|
|
3
3
|
import time
|
4
4
|
from pathlib import Path
|
5
5
|
|
6
|
-
import cv2
|
7
6
|
import torch
|
8
7
|
|
9
8
|
from ..constants import SUPPORT_OUTPUT_FORMAT
|
@@ -14,6 +13,8 @@ from ..utils.logger import set_logger
|
|
14
13
|
from ..export import save_csv, save_html, save_json, save_markdown
|
15
14
|
from ..export import convert_json, convert_csv, convert_html, convert_markdown
|
16
15
|
|
16
|
+
from ..utils.misc import save_image
|
17
|
+
|
17
18
|
logger = set_logger(__name__, "INFO")
|
18
19
|
|
19
20
|
|
@@ -91,21 +92,23 @@ def process_single_file(args, analyzer, path, format):
|
|
91
92
|
|
92
93
|
if ocr is not None:
|
93
94
|
out_path = os.path.join(
|
94
|
-
args.outdir, f"{dirname}_{filename}_p{page+1}_ocr.jpg"
|
95
|
+
args.outdir, f"{dirname}_{filename}_p{page + 1}_ocr.jpg"
|
95
96
|
)
|
96
97
|
|
97
|
-
|
98
|
+
save_image(ocr, out_path)
|
98
99
|
logger.info(f"Output file: {out_path}")
|
99
100
|
|
100
101
|
if layout is not None:
|
101
102
|
out_path = os.path.join(
|
102
|
-
args.outdir, f"{dirname}_{filename}_p{page+1}_layout.jpg"
|
103
|
+
args.outdir, f"{dirname}_{filename}_p{page + 1}_layout.jpg"
|
103
104
|
)
|
104
105
|
|
105
|
-
|
106
|
+
save_image(layout, out_path)
|
106
107
|
logger.info(f"Output file: {out_path}")
|
107
108
|
|
108
|
-
out_path = os.path.join(
|
109
|
+
out_path = os.path.join(
|
110
|
+
args.outdir, f"{dirname}_{filename}_p{page + 1}.{format}"
|
111
|
+
)
|
109
112
|
|
110
113
|
if format == "json":
|
111
114
|
if args.combine:
|
@@ -340,6 +343,12 @@ def main():
|
|
340
343
|
action="store_true",
|
341
344
|
help="if set, ignore meta information(header, footer) in the output",
|
342
345
|
)
|
346
|
+
parser.add_argument(
|
347
|
+
"--reading_order",
|
348
|
+
default="auto",
|
349
|
+
type=str,
|
350
|
+
choices=["auto", "left2right", "top2bottom", "right2left"],
|
351
|
+
)
|
343
352
|
|
344
353
|
args = parser.parse_args()
|
345
354
|
|
@@ -393,6 +402,7 @@ def main():
|
|
393
402
|
visualize=args.vis,
|
394
403
|
device=args.device,
|
395
404
|
ignore_meta=args.ignore_meta,
|
405
|
+
reading_order=args.reading_order,
|
396
406
|
)
|
397
407
|
|
398
408
|
os.makedirs(args.outdir, exist_ok=True)
|
@@ -407,7 +417,7 @@ def main():
|
|
407
417
|
logger.info(f"Processing file: {file_path}")
|
408
418
|
process_single_file(args, analyzer, file_path, format)
|
409
419
|
end = time.time()
|
410
|
-
logger.info(f"Total Processing time: {end-start:.2f} sec")
|
420
|
+
logger.info(f"Total Processing time: {end - start:.2f} sec")
|
411
421
|
except Exception:
|
412
422
|
continue
|
413
423
|
else:
|
@@ -415,7 +425,7 @@ def main():
|
|
415
425
|
logger.info(f"Processing file: {path}")
|
416
426
|
process_single_file(args, analyzer, path, format)
|
417
427
|
end = time.time()
|
418
|
-
logger.info(f"Total Processing time: {end-start:.2f} sec")
|
428
|
+
logger.info(f"Total Processing time: {end - start:.2f} sec")
|
419
429
|
|
420
430
|
|
421
431
|
if __name__ == "__main__":
|
@@ -0,0 +1,165 @@
|
|
1
|
+
import json
|
2
|
+
import io
|
3
|
+
import csv
|
4
|
+
import os
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
from mcp.server.fastmcp import Context, FastMCP
|
8
|
+
|
9
|
+
from yomitoku import DocumentAnalyzer
|
10
|
+
from yomitoku.data.functions import load_image, load_pdf
|
11
|
+
from yomitoku.export import convert_json, convert_markdown, convert_csv, convert_html
|
12
|
+
|
13
|
+
try:
|
14
|
+
RESOURCE_DIR = os.environ["RESOURCE_DIR"]
|
15
|
+
except KeyError:
|
16
|
+
raise ValueError("Environment variable 'RESOURCE_DIR' is not set.")
|
17
|
+
|
18
|
+
|
19
|
+
analyzer = None
|
20
|
+
|
21
|
+
|
22
|
+
async def load_analyzer(ctx: Context) -> DocumentAnalyzer:
|
23
|
+
"""
|
24
|
+
Load the DocumentAnalyzer instance if not already loaded.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
ctx (Context): The context in which the analyzer is being loaded.
|
28
|
+
|
29
|
+
Returns:
|
30
|
+
DocumentAnalyzer: The loaded document analyzer instance.
|
31
|
+
"""
|
32
|
+
global analyzer
|
33
|
+
if analyzer is None:
|
34
|
+
await ctx.info("Load document analyzer")
|
35
|
+
analyzer = DocumentAnalyzer(visualize=False, device="cuda")
|
36
|
+
return analyzer
|
37
|
+
|
38
|
+
|
39
|
+
mcp = FastMCP("yomitoku")
|
40
|
+
|
41
|
+
|
42
|
+
@mcp.tool()
|
43
|
+
async def process_ocr(ctx: Context, filename: str, output_format: str) -> str:
|
44
|
+
"""
|
45
|
+
Perform OCR on the specified file in the resource direcory and convert
|
46
|
+
the results to the desired format.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
ctx (Context): The context in which the OCR processing is executed.
|
50
|
+
filename (str): The name of the file to process in the resource directory.
|
51
|
+
output_format (str): The desired format for the output. The available options are:
|
52
|
+
- json: Outputs the text as structured data along with positional information.
|
53
|
+
- markdown: Outputs texts and tables in Markdown format.
|
54
|
+
- html: Outputs texts and tables in HTML format.
|
55
|
+
- csv: Outputs texts and tables in CSV format.
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
str: The OCR results converted to the specified format.
|
59
|
+
"""
|
60
|
+
analyzer = await load_analyzer(ctx)
|
61
|
+
|
62
|
+
await ctx.info("Start ocr processing")
|
63
|
+
|
64
|
+
file_path = os.path.join(RESOURCE_DIR, filename)
|
65
|
+
if Path(file_path).suffix[1:].lower() in ["pdf"]:
|
66
|
+
imgs = load_pdf(file_path)
|
67
|
+
else:
|
68
|
+
imgs = load_image(file_path)
|
69
|
+
|
70
|
+
results = []
|
71
|
+
for page, img in enumerate(imgs):
|
72
|
+
analyzer.img = img
|
73
|
+
result, _, _ = await analyzer.run(img)
|
74
|
+
results.append(result)
|
75
|
+
await ctx.report_progress(page + 1, len(imgs))
|
76
|
+
|
77
|
+
if output_format == "json":
|
78
|
+
return json.dumps(
|
79
|
+
[
|
80
|
+
convert_json(
|
81
|
+
result,
|
82
|
+
out_path=None,
|
83
|
+
ignore_line_break=True,
|
84
|
+
img=img,
|
85
|
+
export_figure=False,
|
86
|
+
figure_dir=None,
|
87
|
+
).model_dump()
|
88
|
+
for img, result in zip(imgs, results)
|
89
|
+
],
|
90
|
+
ensure_ascii=False,
|
91
|
+
sort_keys=True,
|
92
|
+
separators=(",", ": "),
|
93
|
+
)
|
94
|
+
elif output_format == "markdown":
|
95
|
+
return "\n".join(
|
96
|
+
[
|
97
|
+
convert_markdown(
|
98
|
+
result,
|
99
|
+
out_path=None,
|
100
|
+
ignore_line_break=True,
|
101
|
+
img=img,
|
102
|
+
export_figure=False,
|
103
|
+
)[0]
|
104
|
+
for img, result in zip(imgs, results)
|
105
|
+
]
|
106
|
+
)
|
107
|
+
elif output_format == "html":
|
108
|
+
return "\n".join(
|
109
|
+
[
|
110
|
+
convert_html(
|
111
|
+
result,
|
112
|
+
out_path=None,
|
113
|
+
ignore_line_break=True,
|
114
|
+
img=img,
|
115
|
+
export_figure=False,
|
116
|
+
export_figure_letter="",
|
117
|
+
)[0]
|
118
|
+
for img, result in zip(imgs, results)
|
119
|
+
]
|
120
|
+
)
|
121
|
+
elif output_format == "csv":
|
122
|
+
output = io.StringIO()
|
123
|
+
writer = csv.writer(output, quoting=csv.QUOTE_MINIMAL)
|
124
|
+
for img, result in zip(imgs, results):
|
125
|
+
elements = convert_csv(
|
126
|
+
result,
|
127
|
+
out_path=None,
|
128
|
+
ignore_line_break=True,
|
129
|
+
img=img,
|
130
|
+
export_figure=False,
|
131
|
+
)
|
132
|
+
for element in elements:
|
133
|
+
if element["type"] == "table":
|
134
|
+
writer.writerows(element["element"])
|
135
|
+
else:
|
136
|
+
writer.writerow([element["element"]])
|
137
|
+
writer.writerow([""])
|
138
|
+
return output.getvalue()
|
139
|
+
else:
|
140
|
+
raise ValueError(
|
141
|
+
f"Unsupported output format: {output_format}."
|
142
|
+
" Supported formats are json, markdown, html or csv."
|
143
|
+
)
|
144
|
+
|
145
|
+
|
146
|
+
@mcp.resource("file://list")
|
147
|
+
async def get_file_list() -> list[str]:
|
148
|
+
"""
|
149
|
+
Retrieve a list of files in the resource directory.
|
150
|
+
|
151
|
+
Returns:
|
152
|
+
list[str]: A list of filenames in the resource directory.
|
153
|
+
"""
|
154
|
+
return os.listdir(RESOURCE_DIR)
|
155
|
+
|
156
|
+
|
157
|
+
def run_mcp_server():
|
158
|
+
"""
|
159
|
+
Run the MCP server.
|
160
|
+
"""
|
161
|
+
mcp.run(transport="stdio")
|
162
|
+
|
163
|
+
|
164
|
+
if __name__ == "__main__":
|
165
|
+
run_mcp_server()
|
@@ -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
|
-
|
27
|
+
with ThreadPoolExecutor(max_workers=num_workers) as executor:
|
28
|
+
data = list(executor.map(self.preprocess, self.quads))
|
26
29
|
|
27
|
-
|
28
|
-
|
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
|
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,
|
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
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
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
|
-
|
223
|
-
|
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.
|
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,
|
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,
|
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,
|
456
|
-
prediction_reading_order(footers,
|
457
|
-
|
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)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import csv
|
2
2
|
import os
|
3
3
|
|
4
|
-
import
|
4
|
+
from ..utils.misc import save_image
|
5
5
|
|
6
6
|
|
7
7
|
def table_to_csv(table, ignore_line_break):
|
@@ -54,7 +54,7 @@ def save_figure(
|
|
54
54
|
filename = os.path.splitext(os.path.basename(out_path))[0]
|
55
55
|
figure_name = f"{filename}_figure_{i}.png"
|
56
56
|
figure_path = os.path.join(save_dir, figure_name)
|
57
|
-
|
57
|
+
save_image(figure_img, figure_path)
|
58
58
|
|
59
59
|
|
60
60
|
def convert_csv(
|