Compare commits

..

220 Commits
v0.0.8 ... main

Author SHA1 Message Date
Clemens Neudecker 4af0bc079c
Merge pull request #132 from qurator-spk/extracting_images_only
Extracting images only
1 day ago
michalbubula d168edfd77
Update cli.py to block other processing in the case of extract_image_only 2 days ago
michalbubula 723f27bec4
Add -eoi option to README.md 2 days ago
vahidrezanezhad 74a0699f6b extracting images only now works for a single image input 2 days ago
Clemens Neudecker 327b446a16
update Makefile with v0.3.1 models 4 days ago
Clemens Neudecker 351e9a897a
update `ocrd-tool.json` with v0.3.1 models 4 days ago
Clemens Neudecker c156a1612e
Exclude `run_image_extraction_over_ppn_lists.py` from merge 3 weeks ago
vahidrezanezhad 6b2e5d110e all tests are passed 3 weeks ago
cneud b6d3d2bdbf fix indentation 3 weeks ago
cneud de32d86fb6 Merge branch 'refs/heads/main' into extracting_images_only
# Conflicts:
#	src/eynollah/eynollah.py
3 weeks ago
Clemens Neudecker 256a7c347f
Merge pull request #133 from qurator-spk/src-layout
Merging package src layout as agreed per meeting today.
3 weeks ago
kba 84b844203d switch from qurator namespace to src-layout 3 weeks ago
kba 9367f86483 remove setup.py stub completely 3 weeks ago
kba 62314c453c fully transition to pyproject 4 weeks ago
kba a5c7f223d1 📦 v0.3.1 4 weeks ago
kba 9ae0575436 📝 changelog 4 weeks ago
Clemens Neudecker 78bfa97c06
Merge pull request #129 from qurator-spk/resolving_issue_106
fix OCR-D regression
4 weeks ago
kba 84d05bd0ae s,url,local_filename, 4 weeks ago
cneud 7f99526b9d update Makefile model location 1 month ago
cneud 8f76966394 update pyproject.toml for v0.3.1 1 month ago
cneud 28ee1e527e update pyproject.toml for v0.3.1 1 month ago
Clemens Neudecker 23ac58405c
update pyproject.toml 1 month ago
vahidrezanezhad e3edb0ec30 update 1 month ago
vahidrezanezhad 8e2cdad1be extracting images only - avoid artifacts with heuristics 1 month ago
vahidrezanezhad 9170a9f21c only images extraction - update inference parameters 2 months ago
cneud f0e7f75499 Update README.md 2 months ago
cneud 7ded54a8d2 rename GH action 2 months ago
cneud c9f63826c0 create draft pyproject.toml 2 months ago
cneud 8862df9156 format options table 2 months ago
cneud 38698c6609 Update README.md 2 months ago
cneud 40f5408b1e improve huggingface url 2 months ago
cneud 3cfa447e84 remove CircleCI 2 months ago
cneud ad133e3425 Update model download url 2 months ago
vahidrezanezhad 721d3f70a0
Merge pull request #127 from bertsky/new-namespace-pkg
non-legacy namespace package
4 months ago
Robert Sachunsky 45bd76f5e8 fix namespace pkg setup 4 months ago
Robert Sachunsky f88ee99f3c
non-legacy namespace package 4 months ago
Clemens Neudecker 899bb9f00c
update GitHub actions 5 months ago
Clemens Neudecker ba64282118
Update README.md 6 months ago
Clemens Neudecker 533736a3e3
update supported Python+Tensorflow version combinations 6 months ago
cneud b3fa684395 pin tf2 version to 2.12.1
until we fix keras compatibility
6 months ago
cneud f09b7c1bef use tf1 compatibility for keras backend 6 months ago
vahidrezanezhad 7cbca79f16 replacing images cotour with bounding box 10 months ago
vahidrezanezhad aa41e4df20 The contours of images can now be written in an XML file 10 months ago
vahidrezanezhad 364ccacab2 adding extracting images only in cli 10 months ago
vahidrezanezhad 6aac0b8faf avoiding artifact images on the boundary of documents 10 months ago
vahidrezanezhad e7d12d3549 first update for only images extraction 10 months ago
vahidrezanezhad 6018b354aa comment unnecessary print commands 10 months ago
Clemens Neudecker f2811ee469
add supported OS to readme 11 months ago
vahidrezanezhad 7983a65006 filtering separators in a correct way without missing them 11 months ago
vahidrezanezhad fc9e9cc29f
Merge pull request #117 from qurator-spk/tf-2.12-or-greater
Update tensorflow
12 months ago
cneud 4254ce3bdb Update README.md 1 year ago
cneud 56934c876a remove duplicate test for Python 3.8 1 year ago
Clemens Neudecker 6c65fc4dfe
Update config.yml 1 year ago
Clemens Neudecker 9d3a1a5b76
Update test-eynollah.yml 1 year ago
Clemens Neudecker 03bfd7a390
Update requirements.txt
Update to `tensorflow>=2.12` (drops Python 3.7 support)
* fix #114 
* fix #115

Tested by @vahidrezanezhad @cneud
1 year ago
vahidrezanezhad 7a70b20c77 apply missed commit #a56988a back 1 year ago
vahidrezanezhad 0ea90b7509
Merge pull request #116 from qurator-spk/fix-typos
fix some typos
1 year ago
vahidrezanezhad 830b0fc8fa
Merge pull request #113 from qurator-spk/tf_<2.12.0
introduce (temporary) upper bounds for tensorflow and numpy version
1 year ago
cneud d3b06baa84 fix some typos 1 year ago
Clemens Neudecker 3e62657570
added the TF upper bound in the README 1 year ago
Clemens Neudecker b58a327c5d
cap numpy to <1.24.0
OK so now numpy is the culprit (shipped unbound via ocrd) which had several deprecations expire with release of v1.24.0 that require changes to our codebase, e.g. 
* The deprecation for the aliases np.object, np.bool, np.float, np.complex, np.str, and np.int is expired
* Ragged array creation will now always raise a ValueError unless dtype=object is passed.

See also here: https://numpy.org/devdocs/release/1.24.0-notes.html#expired-deprecations
1 year ago
Clemens Neudecker 935332e863
test fix for keras.backend import error with Python 3.8 1 year ago
Clemens Neudecker e5acee09ab
cap tensorflow version to <2.12.0
Cap tensorflow version to <2.12.0 until we have time to adapt to the API changes such as e.g.
* Support for Python 3.11 has been added.
* Support for Python 3.7 has been removed. 
See also https://github.com/tensorflow/tensorflow/releases/tag/v2.12.0.
1 year ago
Clemens Neudecker 6ba5cdcc11
Update citation 1 year ago
Clemens Neudecker 2db588a146
Update bibtex entry 1 year ago
Clemens Neudecker aad80696e6
format citation info as bibtex 1 year ago
Clemens Neudecker a0949fd74a
add HIP'23 paper reference 1 year ago
vahidrezanezhad 5f7d9a475c
Merge pull request #109 from bertsky/patch-3
pil_cv2.check_dpi: fix class membership test
1 year ago
Clemens Neudecker bafad0c511
Merge pull request #105 from bertsky/fix-model-archive-path
update tool json resource path_in_archive
1 year ago
Robert Sachunsky 867a7261de
pil_cv2.check_dpi: fix class membership test
(depending on how the `PIL.Image` was instantiated – file plugin or array interface – the previous `isinstance` could fail, provoking a fall-through to `cv2pil` which does not work)
1 year ago
Robert Sachunsky b049e475e3 update tool json resource path_in_archive 1 year ago
Clemens Neudecker fbe2f2302f
Merge pull request #104 from bertsky/reinstate-namespace-pkg
Revert "Merge pull request #97 from qurator-spk/420-namespace-package"
1 year ago
Robert Sachunsky 69c1d6b3d6 Revert "Merge pull request #97 from qurator-spk/420-namespace-package"
This reverts commit fd56b86acf, reversing
changes made to ea792d1e4a.
1 year ago
vahidrezanezhad 68923e0a5d
Merge pull request #102 from qurator-spk/right2left_reading_order
Right2left reading order
1 year ago
vahid 0b35011847 right2left reading order detection accomplished 1 year ago
vahid 0cda1f3c7a reading order type 1: right to left 1 year ago
vahid b01888da31 delete printing resized image shape 1 year ago
vahid 45c40a58fc issue #67 solved 1 year ago
cneud a96147b621 improve links to GT guidelines 1 year ago
cneud c7057bad5e Update README.md 1 year ago
cneud 419e589df7 Update CHANGELOG.md 1 year ago
cneud 6eab7a60a9 Update ocrd-tool.json 1 year ago
Clemens Neudecker fd9431a678
Merge pull request #86 from qurator-spk/eynollah_light
Eynollah light integration
1 year ago
cneud 48f2ce6203 re-enable Action for Python 3.8 1 year ago
vahidrezanezhad 1621532092
Merge branch 'main' into eynollah_light 1 year ago
vahid 4c217018cc textline light version -tll can not work without enabling -light option 1 year ago
vahid d68f240b59 loading TensorFlow SavedModel format is now present 1 year ago
vahid 380f59ad67 let hybrid textline light model be loaded 1 year ago
vahidrezanezhad 29e6ad076f
renaming textline light model 1 year ago
Robert Sachunsky 529f2c0e19 set_memory_growth to all GPU devices alike 1 year ago
vahidrezanezhad 52d2e0b098
Merge pull request #100 from bertsky/patch-2
set_memory_growth to all GPU devices alike
1 year ago
Clemens Neudecker cb5ffaee14
Update README.md 1 year ago
cneud 70786377dc Update README.md 1 year ago
cneud 1e172cca5d Update README.md 1 year ago
cneud fef7cf309b Update README.md 1 year ago
cneud 000e39c676 Update README.md 1 year ago
cneud d98689edad Update README.md 1 year ago
cneud 50b9ce3350 Update README.md 1 year ago
cneud c251c4f4c8 update badges 1 year ago
Robert Sachunsky 8fe3567123
set_memory_growth to all GPU devices alike 1 year ago
Clemens Neudecker cb8cfad761
Update config.yml 1 year ago
Clemens Neudecker 0462ae0b97
Update config.yml 1 year ago
Clemens Neudecker f264eaf424
test CircleCI machine executor (more RAM?) 1 year ago
cneud 63d996880d include 3.8 in GitHub Actions 1 year ago
cneud 456fccb35e use the SavedModel format 1 year ago
Konstantin Baierer abb0b293f5 use find_namespace_packages in setup.py 1 year ago
vahidrezanezhad fd56b86acf
Merge pull request #97 from qurator-spk/420-namespace-package
use PEP420 style qurator namespace
1 year ago
vahid d3735b12f4 pushing commits 2d9ccac and 7345f6b into eynollah_light 1 year ago
Clemens Neudecker 22a8e93031
Update README.md 1 year ago
Konstantin Baierer 14fc040428 use find_namespace_packages in setup.py 1 year ago
Clemens Neudecker 0279ebfe13
Update README.md 2 years ago
Clemens Neudecker aecc2ea543
Update README.md
added some badges
2 years ago
cneud fd4c0ed4e8 Update Makefile 2 years ago
cneud 31be7892a0 Makefile hack to rename model dir 2 years ago
cneud 817e5a6af9 update docstring 2 years ago
cneud a9728bb899 Update eynollah.py
predict quietly please
2 years ago
Robert Sachunsky fb6d97091b OCR-D wrapper: expose tables param 2 years ago
cneud d4dd532212 Update Makefile
caj
2 years ago
cneud 9849541061 Update Makefile
test hdf5 models
2 years ago
Robert Sachunsky 1ac0a7e06f try loading as TF SavedModel instead of HDF5 2 years ago
Robert Sachunsky 73057d57d1 silentium! 2 years ago
vahid a078a18530 issue #77 is resolved on main branch 2 years ago
cneud 3d54719c87 fix import 2 years ago
cneud 58ca226f2d apply some fixes from main 2 years ago
cneud d21cc42d87 Update README.md
remove Python 3.6 from supported versions
2 years ago
cneud 2c13f1bddc Update README.md 2 years ago
cneud 4642ccb36d Update config.yml 2 years ago
cneud 27834ce33d update CI 2 years ago
cneud f37d324812 Use renamed models in SavedModel format 2 years ago
cneud 4276417938 Update README.md 2 years ago
cneud 4807be1b62 Update requirements.txt 2 years ago
Konstantin Baierer 6c8f60993f Merge branch 'main' into 420-namespace-package 2 years ago
Konstantin Baierer ea792d1e4a 📦 v0.2.0 2 years ago
Konstantin Baierer e167e0863d 📝 changelog 2 years ago
Konstantin Baierer 8e894e5b7b Merge remote-tracking branch 'bertsky/savedmodel-format' 2 years ago
Konstantin Baierer 7cd07dd550 use PEP420 style qurator namespace 2 years ago
Konstantin Baierer 71d0ec8dfe 📦 v0.1.0 2 years ago
Konstantin Baierer 31a2ec8fe6 📝 changelog 2 years ago
Robert Sachunsky 875e4fe32b log number of detected regions 2 years ago
Robert Sachunsky 318ea6acca OCR-D wrapper: expose tables param 2 years ago
Robert Sachunsky 23f0c0b40a ocrd-tool: replace by persistent model URL 2 years ago
Robert Sachunsky 5c26bdf402 ocrd-tool: add model archive to resmgr resources 2 years ago
Robert Sachunsky 7345f6bf67 remove TF1 session and GC controls, avoid repeating load_model 2 years ago
Robert Sachunsky a56988a35a contours: numpy now needs dtype=object 2 years ago
Clemens Neudecker 13bc2378d9
Update config.yml (#90)
* Update config.yml

enable CI for Python 3.8

* Update test-eynollah.yml

Use 3.7 for actions
2 years ago
Robert Sachunsky 2d9ccac354 contours: simplify 2 years ago
Robert Sachunsky ab4bb7cd7b silentium! 2 years ago
Robert Sachunsky 79e897d3b2 try loading as TF SavedModel instead of HDF5 2 years ago
vahidrezanezhad a6fe781033
Merge pull request #88 from emresvd/patch-1
Update README.md
2 years ago
Clemens Neudecker ac69136e8f
Update config.yml (#89)
* Update config.yml

* Update config.yml
2 years ago
emresvd 98529d6325
Update README.md 2 years ago
Clemens Neudecker 30ef006dfd
Update README.md
Clarify CLI options
2 years ago
Clemens Neudecker 5ca857018b
Update README.md 2 years ago
Clemens Neudecker ffc7f82906
Update README.md 2 years ago
Clemens Neudecker b75d8afb1d
Update README.md 2 years ago
Clemens Neudecker 000402f0dc
Update README.md 2 years ago
vahid 38bf0d8740 solving issue by loading model by directory as input 2 years ago
vahid 89e58910aa new (hybrid cnn+transformer) textline model which can accelerate to extract contour textlines faster 2 years ago
vahid 583cdcee2c new (hybrid cnn+transformer) textline model which can accelerate to extract contour textlines faster 2 years ago
Clemens Neudecker 07fe0d827d
Update Makefile 2 years ago
Clemens Neudecker dbf91876e1
Adapt to new location of models 2 years ago
vahid 8d5079c909 issue #77 is resolved on main branch 2 years ago
vahid 402c5339ac issue #77 is resolved 2 years ago
vahid 01bfc3914d extracting page as an option 2 years ago
Clemens Neudecker 00be99d29b
add short section on supported Python, TF and CUDA versions 2 years ago
vahidrezanezhad ae7c424889
Update eynollah.py 2 years ago
vahid cd9920eea7 extracting page 2 years ago
Robert Sachunsky 34a061782c
depend on tensorflow instead of tensorflow-gpu (#76) 2 years ago
vahid 735abc43f3 option to ignore page extraction 2 years ago
vahidrezanezhad ae1990a48e
Merge pull request #74 from cneud/main
Adapt to use tf1.compat session in tf2
2 years ago
cneud 934bbd5892 cleanup 2 years ago
cneud 8c11b2253d update requirements (use tf2) 2 years ago
cneud ecf117ca95 adapt to tf1.compat session mode in tf2 2 years ago
Clemens Neudecker 568391ec4a
require model command line option (fix #59) (#73) 2 years ago
vahid 3bbbeecfec all options are enabled for light version 2 years ago
vahid 3871e22c35 how the models are trained 2 years ago
Clemens Neudecker a33a1995cb
Update README.md 2 years ago
vahidrezanezhad ceb9b8f2b9
Merge pull request #71 from mikegerber/fix/downgrade-patch-dim-log
🧹 Downgrade "Patch size" log message to debug
2 years ago
Gerber, Mike f27ac155ae 🧹 Downgrade "Patch size" log message to debug
Fixes gh-55.
2 years ago
vahid adf10942fa issue #55 resolved 2 years ago
vahidrezanezhad d19170035d
updating model directory 3 years ago
vahid e564451861 updating readme 3 years ago
vahidrezanezhad bd6e38334c
Merge pull request #70 from qurator-spk/cneud-readme
README.md cleanup / restructuring
3 years ago
vahid 94c3b0fc28 updating readme 3 years ago
vahid 8d19c4c632 updating readme 3 years ago
vahid 2eacb9a8ec renaming the models 3 years ago
Clemens Neudecker 441c8566dd
additional details on OCR-D usage 3 years ago
Clemens Neudecker aa64a54feb
markdown 3 years ago
Clemens Neudecker 5dafa2095b
use <details> instead of wiki 3 years ago
vahid c606391c31 flow from directory 3 years ago
Clemens Neudecker 571dc84c3f
README.md cleanup / restructuring 3 years ago
vahid cf5ef8f5ae light version as option 3 years ago
vahidrezanezhad b8a532180a light version integration 3 years ago
vahidrezanezhad 2736ddb42d light version 3 years ago
vahidrezanezhad 10f1acef29
Merge pull request #65 from mikegerber/fix/enhanced-message
Fix/enhanced message
3 years ago
vahidrezanezhad c30d4d5c30
Merge pull request #64 from mikegerber/feat/better-time-msgs
💄 Improve timing messages (Fixes #62)
3 years ago
vahidrezanezhad 6a9d5d2076
Merge pull request #68 from mikegerber/fix/remove-spurious-textequivs
🧹 Don't produce spurious TextEquiv elements.
3 years ago
Gerber, Mike 11d9b00510 🧹 Don't produce spurious TextEquiv elements.
eynollah produces spurious - and empy - pcGts TextEquiv elements. This
is a. unnecessary, b. wrong and c. produces a lot of warning messages
in subsequent OCR processing steps because the OCR processor warns
about already existing text.

Fix this by not generating any TextEquiv elements.

Fixes gh-37.
3 years ago
Gerber, Mike 1fe8f92afc 🐛 Clarify message if an image was enhanced 3 years ago
Gerber, Mike 7ccd7663e1 💄 Improve more timing messages 3 years ago
Gerber, Mike cdea0acffe 💄 Improve timing messages (Fixes #62) 3 years ago
Konstantin Baierer f0ac0bb090 📦 v0.0.11 3 years ago
Konstantin Baierer 8943c90b02
Merge pull request #61 from qurator-spk/resource-directory
ocrd-tool: "models" parameter is a directory
3 years ago
Konstantin Baierer d75803b11d ocrd-tool: "models" parameter is a directory 3 years ago
Konstantin Baierer e769f625fe 📦 v0.0.10 3 years ago
Konstantin Baierer 09d85bee87 Merge remote-tracking branch 'vahidrezanezhad/main' into main 3 years ago
vahidrezanezhad b018138cf8 fixed: empty page error due None table contours 3 years ago
vahidrezanezhad 9b28a71db7 Merge branch 'main' of https://github.com/vahidrezanezhad/eynollah into main 3 years ago
vahidrezanezhad 169b50aaaf fixed: empty page error due None table contours 3 years ago
vahidrezanezhad c8f0feb5bd test 3 years ago
Konstantin Baierer 0e63ebcbe5 📦 v0.0.9 3 years ago
Konstantin Baierer 4223fed628 Merge remote-tracking branch 'vahidrezanezhad/main' into main 3 years ago
vahid 0859d22f4c modifications 3 years ago
vahid 14c588e162 resolving an issue 3 years ago
vahid 254abf4d3d more modifications for tables 3 years ago
vahid 9f64110513 README is updated 3 years ago
vahid b3b49272a5 README is updated 3 years ago
vahid c67e155431 table detection completed, enhanced images can be now written to output 3 years ago
vahid a5c940705a tables are integrated 3 years ago
vahid 80b17af40c #47 fixed 3 years ago

@ -1,28 +0,0 @@
version: 2
jobs:
build-python36:
docker:
- image: python:3.6
steps:
- checkout
- restore_cache:
keys:
- model-cache
- run: make models
- save_cache:
key: model-cache
paths:
models_eynollah.tar.gz
models_eynollah
- run: make install
- run: make smoke-test
workflows:
version: 2
build:
jobs:
- build-python36
#- build-python37
#- build-python38 # no tensorflow for python 3.8

@ -1,9 +1,9 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: Python package name: Test
on: [push, pull_request] on: [push]
jobs: jobs:
build: build:
@ -11,11 +11,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: ['3.6'] # '3.7' python-version: ['3.8', '3.9', '3.10', '3.11']
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- uses: actions/cache@v2 - uses: actions/cache@v4
id: model_cache id: model_cache
with: with:
path: models_eynollah path: models_eynollah
@ -24,7 +24,7 @@ jobs:
if: steps.model_cache.outputs.cache-hit != 'true' if: steps.model_cache.outputs.cache-hit != 'true'
run: make models run: make models
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2 uses: actions/setup-python@v5
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Install dependencies - name: Install dependencies

@ -5,7 +5,82 @@ Versioned according to [Semantic Versioning](http://semver.org/).
## Unreleased ## Unreleased
## [0.0.7] - 2021-07-27 ## [0.3.1] - 2024-08-27
Fixed:
* regression in OCR-D processor, #106
* Expected Ptrcv::UMat for argument 'contour', #110
* Memory usage explosion with very narrow images (e.g. book spine), #67
## [0.3.0] - 2023-05-13
Changed:
* Eynollah light integration, #86
* use PEP420 style qurator namespace, #97
* set_memory_growth to all GPU devices alike, #100
Fixed:
* PAGE-XML coordinates can have self-intersections, #20
* reading order representation (XML order vs index), #22
* allow cropping separately, #26
* Order of regions, #51
* error while running inference, #75
* Eynollah crashes while processing image, #77
* ValueError: bad marshal data, #87
* contour extraction: inhomogeneous shape, #92
* Confusing model dir variables, #93
* New release?, #96
## [0.2.0] - 2023-03-24
Changed:
* Convert default model from HDFS to TF SavedModel, #91
Added:
* parmeter `tables` to toggle table detectino, #91
* default model described in ocrd-tool.json, #91
## [0.1.0] - 2023-03-22
Fixed:
* Do not produce spurious `TextEquiv`, #68
* Less spammy logging, #64, #65, #71
Changed:
* Upgrade to tensorflow 2.4.0, #74
* Improved README
* CI: test for python 3.7+, #90
## [0.0.11] - 2022-02-02
Fixed:
* `models` parameter should have `content-type`, #61, OCR-D/core#777
## [0.0.10] - 2021-09-27
Fixed:
* call to `uild_pagexml_no_full_layout` for empty pages, #52
## [0.0.9] - 2021-08-16
Added:
* Table detection, #48
Fixed:
* Catch exception, #47
## [0.0.8] - 2021-07-27
Fixed: Fixed:
@ -50,6 +125,13 @@ Fixed:
Initial release Initial release
<!-- link-labels --> <!-- link-labels -->
[0.3.1]: ../../compare/v0.3.1...v0.3.0
[0.3.0]: ../../compare/v0.3.0...v0.2.0
[0.2.0]: ../../compare/v0.2.0...v0.1.0
[0.1.0]: ../../compare/v0.1.0...v0.0.11
[0.0.11]: ../../compare/v0.0.11...v0.0.10
[0.0.10]: ../../compare/v0.0.10...v0.0.9
[0.0.9]: ../../compare/v0.0.9...v0.0.8
[0.0.8]: ../../compare/v0.0.8...v0.0.7 [0.0.8]: ../../compare/v0.0.8...v0.0.7
[0.0.7]: ../../compare/v0.0.7...v0.0.6 [0.0.7]: ../../compare/v0.0.7...v0.0.6
[0.0.6]: ../../compare/v0.0.6...v0.0.5 [0.0.6]: ../../compare/v0.0.6...v0.0.5

@ -25,7 +25,11 @@ models_eynollah: models_eynollah.tar.gz
tar xf models_eynollah.tar.gz tar xf models_eynollah.tar.gz
models_eynollah.tar.gz: models_eynollah.tar.gz:
wget 'https://qurator-data.de/eynollah/models_eynollah.tar.gz' # wget 'https://qurator-data.de/eynollah/2021-04-25/models_eynollah.tar.gz'
# wget 'https://qurator-data.de/eynollah/2022-04-05/models_eynollah_renamed.tar.gz'
# wget 'https://qurator-data.de/eynollah/2022-04-05/models_eynollah_renamed_savedmodel.tar.gz'
# wget 'https://github.com/qurator-spk/eynollah/releases/download/v0.3.0/models_eynollah.tar.gz'
wget 'https://github.com/qurator-spk/eynollah/releases/download/v0.3.1/models_eynollah.tar.gz'
# Install with pip # Install with pip
install: install:

@ -1,122 +1,122 @@
# Eynollah # Eynollah
> Document Layout Analysis > Document Layout Analysis with Deep Learning and Heuristics
![](https://user-images.githubusercontent.com/952378/102350683-8a74db80-3fa5-11eb-8c7e-f743f7d6eae2.jpg) [![PyPI Version](https://img.shields.io/pypi/v/eynollah)](https://pypi.org/project/eynollah/)
[![GH Actions Test](https://github.com/qurator-spk/eynollah/actions/workflows/test-eynollah.yml/badge.svg)](https://github.com/qurator-spk/eynollah/actions/workflows/test-eynollah.yml)
## Introduction [![License: ASL](https://img.shields.io/github/license/qurator-spk/eynollah)](https://opensource.org/license/apache-2-0/)
This tool performs document layout analysis (segmentation) from image data and returns the results as [PAGE-XML](https://github.com/PRImA-Research-Lab/PAGE-XML). [![DOI](https://img.shields.io/badge/DOI-10.1145%2F3604951.3605513-red)](https://doi.org/10.1145/3604951.3605513)
It can currently detect the following layout classes/elements:
* [Border](https://ocr-d.de/en/gt-guidelines/pagexml/pagecontent_xsd_Complex_Type_pc_BorderType.html)
* [Textregion](https://ocr-d.de/en/gt-guidelines/pagexml/pagecontent_xsd_Complex_Type_pc_TextRegionType.html)
* [Textline](https://ocr-d.de/en/gt-guidelines/pagexml/pagecontent_xsd_Complex_Type_pc_TextLineType.html)
* [Image](https://ocr-d.de/en/gt-guidelines/pagexml/pagecontent_xsd_Complex_Type_pc_ImageRegionType.html)
* [Separator](https://ocr-d.de/en/gt-guidelines/pagexml/pagecontent_xsd_Complex_Type_pc_SeparatorRegionType.html)
* [Marginalia](https://ocr-d.de/en/gt-guidelines/trans/lyMarginalie.html)
* [Initial (Drop Capital)](https://ocr-d.de/en/gt-guidelines/trans/lyInitiale.html)
In addition, the tool can be used to detect the _[ReadingOrder](https://ocr-d.de/en/gt-guidelines/trans/lyLeserichtung.html)_ of regions. The final goal is to feed the output to an OCR model.
The tool uses a combination of various models and heuristics (see flowchart below for the different stages and how they interact):
* [Border detection](https://github.com/qurator-spk/eynollah#border-detection)
* [Layout detection](https://github.com/qurator-spk/eynollah#layout-detection)
* [Textline detection](https://github.com/qurator-spk/eynollah#textline-detection)
* [Image enhancement](https://github.com/qurator-spk/eynollah#Image_enhancement)
* [Scale classification](https://github.com/qurator-spk/eynollah#Scale_classification)
* [Heuristic methods](https://https://github.com/qurator-spk/eynollah#heuristic-methods)
The first three stages are based on [pixel-wise segmentation](https://github.com/qurator-spk/sbb_pixelwise_segmentation).
![](https://user-images.githubusercontent.com/952378/100619946-1936f680-331e-11eb-9297-6e8b4cab3c16.png) ![](https://user-images.githubusercontent.com/952378/102350683-8a74db80-3fa5-11eb-8c7e-f743f7d6eae2.jpg)
## Border detection
For the purpose of text recognition (OCR) and in order to avoid noise being introduced from texts outside the printspace, one first needs to detect the border of the printed frame. This is done by a binary pixel-wise-segmentation model trained on a dataset of 2,000 documents where about 1,200 of them come from the [dhSegment](https://github.com/dhlab-epfl/dhSegment/) project (you can download the dataset from [here](https://github.com/dhlab-epfl/dhSegment/releases/download/v0.2/pages.zip)) and the remainder having been annotated in SBB. For border detection, the model needs to be fed with the whole image at once rather than separated in patches.
## Layout detection
As a next step, text regions need to be identified by means of layout detection. Again a pixel-wise segmentation model was trained on 131 labeled images from the SBB digital collections, including some data augmentation. Since the target of this tool are historical documents, we consider as main region types text regions, separators, images, tables and background - each with their own subclasses, e.g. in the case of text regions, subclasses like header/heading, drop capital, main body text etc. While it would be desirable to detect and classify each of these classes in a granular way, there are also limitations due to having a suitably large and balanced training set. Accordingly, the current version of this tool is focussed on the main region types background, text region, image and separator.
## Textline detection ## Features
In a subsequent step, binary pixel-wise segmentation is used again to classify pixels in a document that constitute textlines. For textline segmentation, a model was initially trained on documents with only one column/block of text and some augmentation with regard to scaling. By fine-tuning the parameters also for multi-column documents, additional training data was produced that resulted in a much more robust textline detection model. * Support for up to 10 segmentation classes:
* background, [page border](https://ocr-d.de/en/gt-guidelines/trans/lyRand.html), [text region](https://ocr-d.de/en/gt-guidelines/trans/lytextregion.html#textregionen__textregion_), [text line](https://ocr-d.de/en/gt-guidelines/pagexml/pagecontent_xsd_Complex_Type_pc_TextLineType.html), [header](https://ocr-d.de/en/gt-guidelines/trans/lyUeberschrift.html), [image](https://ocr-d.de/en/gt-guidelines/trans/lyBildbereiche.html), [separator](https://ocr-d.de/en/gt-guidelines/trans/lySeparatoren.html), [marginalia](https://ocr-d.de/en/gt-guidelines/trans/lyMarginalie.html), [initial](https://ocr-d.de/en/gt-guidelines/trans/lyInitiale.html), [table](https://ocr-d.de/en/gt-guidelines/trans/lyTabellen.html)
* Support for various image optimization operations:
* cropping (border detection), binarization, deskewing, dewarping, scaling, enhancing, resizing
* Text line segmentation to bounding boxes or polygons (contours) including for curved lines and vertical text
* Detection of reading order (left-to-right or right-to-left)
* Output in [PAGE-XML](https://github.com/PRImA-Research-Lab/PAGE-XML)
* [OCR-D](https://github.com/qurator-spk/eynollah#use-as-ocr-d-processor) interface
## Image enhancement :warning: Development is currently focused on achieving the best possible quality of results for a wide variety of historical documents and therefore processing can be very slow. We aim to improve this, but contributions are welcome.
This is an image to image model which input was low quality of an image and label was actually the original image. For this one we did not have any GT, so we decreased the quality of documents in SBB and then feed them into model.
## Scale classification ## Installation
This is simply an image classifier which classifies images based on their scales or better to say based on their number of columns. Python `3.8-3.11` with Tensorflow `2.12-2.15` on Linux are currently supported.
## Heuristic methods For (limited) GPU support the CUDA toolkit needs to be installed.
Some heuristic methods are also employed to further improve the model predictions:
* After border detection, the largest contour is determined by a bounding box, and the image cropped to these coordinates.
* For text region detection, the image is scaled up to make it easier for the model to detect background space between text regions.
* A minimum area is defined for text regions in relation to the overall image dimensions, so that very small regions that are noise can be filtered out.
* Deskewing is applied on the text region level (due to regions having different degrees of skew) in order to improve the textline segmentation result.
* After deskewing, a calculation of the pixel distribution on the X-axis allows the separation of textlines (foreground) and background pixels.
* Finally, using the derived coordinates, bounding boxes are determined for each textline.
## Installation You can either install from PyPI
`pip install .` or
`pip install . -e` for editable installation ```
pip install eynollah
```
Alternatively, you can also use `make` with these targets: or clone the repository, enter it and install (editable) with
`make install` or ```
git clone git@github.com:qurator-spk/eynollah.git
cd eynollah; pip install -e .
```
`make install-dev` for editable installation Alternatively, you can run `make install` or `make install-dev` for editable installation.
### Models ## Models
Pre-trained models can be downloaded from [qurator-data.de](https://qurator-data.de/eynollah/) or [huggingface](https://huggingface.co/SBB?search_models=eynollah).
In order to run this tool you also need trained models. You can download our pretrained models from [qurator-data.de](https://qurator-data.de/eynollah/). ## Train
🚧 **Work in progress**
Alternatively, running `make models` will download and extract models to `$(PWD)/models_eynollah`. In case you want to train your own model, have a look at [`sbb_pixelwise_segmentation`](https://github.com/qurator-spk/sbb_pixelwise_segmentation).
## Usage ## Usage
The command-line interface can be called like this:
The basic command-line interface can be called like this:
```sh ```sh
eynollah \ eynollah \
-i <image file name> \ -i <single image file> | -di <directory containing image files> \
-o <directory to write output xml or enhanced image> \ -o <output directory> \
-m <directory of models> \ -m <directory containing model files> \
-fl <if true, the tool will perform full layout analysis> \ [OPTIONS]
-ae <if true, the tool will resize and enhance the image and produce the resulting image as output> \
-as <if true, the tool will check whether the document needs rescaling or not> \
-cl <if true, the tool will extract the contours of curved textlines instead of rectangle bounding boxes> \
-si <if a directory is given here, the tool will output image regions inside documents there>
``` ```
The tool does accept and works better on original images (RGB format) than binarized images. The following options can be used to further configure the processing:
### `--full-layout` vs `--no-full-layout`
Here are the difference in elements detected depending on the `--full-layout`/`--no-full-layout` command line flags:
| | `--full-layout` | `--no-full-layout` | | option | description |
| --- | --- | --- | |-------------------|:-------------------------------------------------------------------------------|
| reading order | x | x | | `-fl` | full layout analysis including all steps and segmentation classes |
| header regions | x | - | | `-light` | lighter and faster but simpler method for main region detection and deskewing |
| text regions | x | x | | `-tab` | apply table detection |
| text regions / text line | x | x | | `-ae` | apply enhancement (the resulting image is saved to the output directory) |
| drop-capitals | x | - | | `-as` | apply scaling |
| marginals | x | x | | `-cl` | apply contour detection for curved text lines instead of bounding boxes |
| marginals / text line | x | x | | `-ib` | apply binarization (the resulting image is saved to the output directory) |
| image region | x | x | | `-ep` | enable plotting (MUST always be used with `-sl`, `-sd`, `-sa`, `-si` or `-ae`) |
| `-eoi` | extract only images to output directory (other processing will not be done) |
| `-ho` | ignore headers for reading order dectection |
| `-si <directory>` | save image regions detected to this directory |
| `-sd <directory>` | save deskewed image to this directory |
| `-sl <directory>` | save layout prediction as plot to this directory |
| `-sp <directory>` | save cropped page image to this directory |
| `-sa <directory>` | save all (plot, enhanced/binary image, layout) to this directory |
### How to use If no option is set, the tool performs layout detection of main regions (background, text, images, separators and marginals).
The best output quality is produced when RGB images are used as input rather than greyscale or binarized images.
First, this model makes use of up to 9 trained models which are responsible for different operations like size detection, column classification, image enhancement, page extraction, main layout detection, full layout detection and textline detection.That does not mean that all 9 models are always required for every document. Based on the document characteristics and parameters specified, different scenarios can be applied. #### Use as OCR-D processor
🚧 **Work in progress**
* If none of the parameters is set to `true`, the tool will perform a layout detection of main regions (background, text, images, separators and marginals). An advantage of this tool is that it tries to extract main text regions separately as much as possible. Eynollah ships with a CLI interface to be used as [OCR-D](https://ocr-d.de) processor.
* If you set `-ae` (**a**llow image **e**nhancement) parameter to `true`, the tool will first check the ppi (pixel-per-inch) of the image and when it is less than 300, the tool will resize it and only then image enhancement will occur. Image enhancement can also take place without this option, but by setting this option to `true`, the layout xml data (e.g. coordinates) will be based on the resized and enhanced image instead of the original image. In this case, the source image file group with (preferably) RGB images should be used as input like this:
* For some documents, while the quality is good, their scale is very large, and the performance of tool decreases. In such cases you can set `-as` (**a**llow **s**caling) to `true`. With this option enabled, the tool will try to rescale the image and only then the layout detection process will begin. ```
ocrd-eynollah-segment -I OCR-D-IMG -O SEG-LINE -P models
* If you care about drop capitals (initials) and headings, you can set `-fl` (**f**ull **l**ayout) to `true`. With this setting, the tool can currently distinguish 7 document layout classes/elements. ```
* In cases where the document includes curved headers or curved lines, rectangular bounding boxes for textlines will not be a great option. In such cases it is strongly recommended setting the flag `-cl` (**c**urved **l**ines) to `true` to find contours of curved lines instead of rectangular bounding boxes. Be advised that enabling this option increases the processing time of the tool. Any image referenced by `@imageFilename` in PAGE-XML is passed on directly to Eynollah as a processor, so that e.g.
* To crop and save image regions inside the document, set the parameter `-si` (**s**ave **i**mages) to true and provide a directory path to store the extracted images. ```
ocrd-eynollah-segment -I OCR-D-IMG-BIN -O SEG-LINE -P models
```
* This tool is actively being developed. If problems occur, or the performance does not meet your expectations, we welcome your feedback via [issues](https://github.com/qurator-spk/eynollah/issues). uses the original (RGB) image despite any binarization that may have occured in previous OCR-D processing steps
#### Additional documentation
Please check the [wiki](https://github.com/qurator-spk/eynollah/wiki).
## How to cite
If you find this tool useful in your work, please consider citing our paper:
```bibtex
@inproceedings{hip23rezanezhad,
title = {Document Layout Analysis with Deep Learning and Heuristics},
author = {Rezanezhad, Vahid and Baierer, Konstantin and Gerber, Mike and Labusch, Kai and Neudecker, Clemens},
booktitle = {Proceedings of the 7th International Workshop on Historical Document Imaging and Processing {HIP} 2023,
San José, CA, USA, August 25-26, 2023},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
year = {2023},
pages = {73--78},
url = {https://doi.org/10.1145/3604951.3605513}
}
```

@ -1 +1 @@
qurator/eynollah/ocrd-tool.json src/eynollah/ocrd-tool.json

@ -0,0 +1,43 @@
[build-system]
requires = ["setuptools>=61.0", "wheel", "setuptools-ocrd"]
[project]
name = "eynollah"
authors = [
{name = "Vahid Rezanezhad"},
{name = "Staatsbibliothek zu Berlin - Preußischer Kulturbesitz"},
]
description = "Document Layout Analysis"
readme = "README.md"
license.file = "LICENSE"
requires-python = ">=3.8"
keywords = ["document layout analysis", "image segmentation"]
dynamic = ["dependencies", "version"]
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Scientific/Engineering :: Image Processing",
]
[project.scripts]
eynollah = "eynollah.cli:main"
ocrd-eynollah-segment = "eynollah.ocrd_cli:main"
[project.urls]
Homepage = "https://github.com/qurator-spk/eynollah"
Repository = "https://github.com/qurator-spk/eynollah.git"
[tool.setuptools.dynamic]
dependencies = {file = ["requirements.txt"]}
[tool.setuptools.packages.find]
where = ["src"]
[tool.setuptools.package-data]
"*" = ["*.json", '*.yml', '*.xml', '*.xsd']

@ -1 +0,0 @@
__import__("pkg_resources").declare_namespace(__name__)

File diff suppressed because it is too large Load Diff

@ -1,8 +1,8 @@
# ocrd includes opencv, numpy, shapely, click # ocrd includes opencv, numpy, shapely, click
ocrd >= 2.23.3 ocrd >= 2.23.3
keras >= 2.3.1, < 2.4 numpy <1.24.0
scikit-learn >= 0.23.2 scikit-learn >= 0.23.2
tensorflow-gpu >= 1.15, < 2 tensorflow == 2.12.1
imutils >= 0.5.3 imutils >= 0.5.3
matplotlib matplotlib
setuptools >= 50 setuptools >= 50

@ -1,28 +0,0 @@
from setuptools import setup, find_packages
from json import load
install_requires = open('requirements.txt').read().split('\n')
with open('ocrd-tool.json', 'r', encoding='utf-8') as f:
version = load(f)['version']
setup(
name='eynollah',
version=version,
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
author='Vahid Rezanezhad',
url='https://github.com/qurator-spk/eynollah',
license='Apache License 2.0',
namespace_packages=['qurator'],
packages=find_packages(exclude=['tests']),
install_requires=install_requires,
package_data={
'': ['*.json']
},
entry_points={
'console_scripts': [
'eynollah=qurator.eynollah.cli:main',
'ocrd-eynollah-segment=qurator.eynollah.ocrd_cli:main',
]
},
)

@ -1,7 +1,7 @@
import sys import sys
import click import click
from ocrd_utils import initLogging, setOverrideLogLevel from ocrd_utils import initLogging, setOverrideLogLevel
from qurator.eynollah.eynollah import Eynollah from eynollah.eynollah import Eynollah
@click.command() @click.command()
@ -10,7 +10,6 @@ from qurator.eynollah.eynollah import Eynollah
"-i", "-i",
help="image filename", help="image filename",
type=click.Path(exists=True, dir_okay=False), type=click.Path(exists=True, dir_okay=False),
required=True,
) )
@click.option( @click.option(
"--out", "--out",
@ -19,11 +18,18 @@ from qurator.eynollah.eynollah import Eynollah
type=click.Path(exists=True, file_okay=False), type=click.Path(exists=True, file_okay=False),
required=True, required=True,
) )
@click.option(
"--dir_in",
"-di",
help="directory of images",
type=click.Path(exists=True, file_okay=False),
)
@click.option( @click.option(
"--model", "--model",
"-m", "-m",
help="directory of models", help="directory of models",
type=click.Path(exists=True, file_okay=False), type=click.Path(exists=True, file_okay=False),
required=True,
) )
@click.option( @click.option(
"--save_images", "--save_images",
@ -49,12 +55,24 @@ from qurator.eynollah.eynollah import Eynollah
help="if a directory is given, all plots needed for documentation will be saved there", help="if a directory is given, all plots needed for documentation will be saved there",
type=click.Path(exists=True, file_okay=False), type=click.Path(exists=True, file_okay=False),
) )
@click.option(
"--save_page",
"-sp",
help="if a directory is given, page crop of image will be saved there",
type=click.Path(exists=True, file_okay=False),
)
@click.option( @click.option(
"--enable-plotting/--disable-plotting", "--enable-plotting/--disable-plotting",
"-ep/-noep", "-ep/-noep",
is_flag=True, is_flag=True,
help="If set, will plot intermediary files and images", help="If set, will plot intermediary files and images",
) )
@click.option(
"--extract_only_images/--disable-extracting_only_images",
"-eoi/-noeoi",
is_flag=True,
help="If a directory is given, only images in documents will be cropped and saved there and the other processing will not be done",
)
@click.option( @click.option(
"--allow-enhancement/--no-allow-enhancement", "--allow-enhancement/--no-allow-enhancement",
"-ae/-noae", "-ae/-noae",
@ -65,7 +83,13 @@ from qurator.eynollah.eynollah import Eynollah
"--curved-line/--no-curvedline", "--curved-line/--no-curvedline",
"-cl/-nocl", "-cl/-nocl",
is_flag=True, is_flag=True,
help="if this parameter set to true, this tool will try to return contoure of textlines instead of rectabgle bounding box of textline. This should be taken into account that with this option the tool need more time to do process.", help="if this parameter set to true, this tool will try to return contoure of textlines instead of rectangle bounding box of textline. This should be taken into account that with this option the tool need more time to do process.",
)
@click.option(
"--textline_light/--no-textline_light",
"-tll/-notll",
is_flag=True,
help="if this parameter set to true, this tool will try to return contoure of textlines instead of rectangle bounding box of textline with a faster method.",
) )
@click.option( @click.option(
"--full-layout/--no-full-layout", "--full-layout/--no-full-layout",
@ -73,6 +97,18 @@ from qurator.eynollah.eynollah import Eynollah
is_flag=True, is_flag=True,
help="if this parameter set to true, this tool will try to return all elements of layout.", help="if this parameter set to true, this tool will try to return all elements of layout.",
) )
@click.option(
"--tables/--no-tables",
"-tab/-notab",
is_flag=True,
help="if this parameter set to true, this tool will try to detect tables.",
)
@click.option(
"--right2left/--left2right",
"-r2l/-l2r",
is_flag=True,
help="if this parameter set to true, this tool will extract right-to-left reading order.",
)
@click.option( @click.option(
"--input_binary/--input-RGB", "--input_binary/--input-RGB",
"-ib/-irgb", "-ib/-irgb",
@ -86,11 +122,23 @@ from qurator.eynollah.eynollah import Eynollah
help="if this parameter set to true, this tool would check the scale and if needed it will scale it to perform better layout detection", help="if this parameter set to true, this tool would check the scale and if needed it will scale it to perform better layout detection",
) )
@click.option( @click.option(
"--headers-off/--headers-on", "--headers_off/--headers-on",
"-ho/-noho", "-ho/-noho",
is_flag=True, is_flag=True,
help="if this parameter set to true, this tool would ignore headers role in reading order", help="if this parameter set to true, this tool would ignore headers role in reading order",
) )
@click.option(
"--light_version/--original",
"-light/-org",
is_flag=True,
help="if this parameter set to true, this tool would use lighter version",
)
@click.option(
"--ignore_page_extraction/--extract_page_included",
"-ipe/-epi",
is_flag=True,
help="if this parameter set to true, this tool would ignore page extraction",
)
@click.option( @click.option(
"--log-level", "--log-level",
"-l", "-l",
@ -100,45 +148,70 @@ from qurator.eynollah.eynollah import Eynollah
def main( def main(
image, image,
out, out,
dir_in,
model, model,
save_images, save_images,
save_layout, save_layout,
save_deskewed, save_deskewed,
save_all, save_all,
extract_only_images,
save_page,
enable_plotting, enable_plotting,
allow_enhancement, allow_enhancement,
curved_line, curved_line,
textline_light,
full_layout, full_layout,
tables,
right2left,
input_binary, input_binary,
allow_scaling, allow_scaling,
headers_off, headers_off,
light_version,
ignore_page_extraction,
log_level log_level
): ):
if log_level: if log_level:
setOverrideLogLevel(log_level) setOverrideLogLevel(log_level)
initLogging() initLogging()
if not enable_plotting and (save_layout or save_deskewed or save_all or save_images): if not enable_plotting and (save_layout or save_deskewed or save_all or save_page or save_images or allow_enhancement):
print("Error: You used one of -sl, -sd, -sa or -si but did not enable plotting with -ep") print("Error: You used one of -sl, -sd, -sa, -sp, -si or -ae but did not enable plotting with -ep")
sys.exit(1)
elif enable_plotting and not (save_layout or save_deskewed or save_all or save_page or save_images or allow_enhancement):
print("Error: You used -ep to enable plotting but set none of -sl, -sd, -sa, -sp, -si or -ae")
sys.exit(1)
if textline_light and not light_version:
print('Error: You used -tll to enable light textline detection but -light is not enabled')
sys.exit(1) sys.exit(1)
elif enable_plotting and not (save_layout or save_deskewed or save_all or save_images): if extract_only_images and (allow_enhancement or allow_scaling or light_version or curved_line or textline_light or full_layout or tables or right2left or headers_off) :
print("Error: You used -ep to enable plotting but set none of -sl, -sd, -sa or -si") print('Error: You used -eoi which can not be enabled alongside light_version -light or allow_scaling -as or allow_enhancement -ae or curved_line -cl or textline_light -tll or full_layout -fl or tables -tab or right2left -r2l or headers_off -ho')
sys.exit(1) sys.exit(1)
eynollah = Eynollah( eynollah = Eynollah(
image_filename=image, image_filename=image,
dir_out=out, dir_out=out,
dir_in=dir_in,
dir_models=model, dir_models=model,
dir_of_cropped_images=save_images, dir_of_cropped_images=save_images,
extract_only_images=extract_only_images,
dir_of_layout=save_layout, dir_of_layout=save_layout,
dir_of_deskewed=save_deskewed, dir_of_deskewed=save_deskewed,
dir_of_all=save_all, dir_of_all=save_all,
dir_save_page=save_page,
enable_plotting=enable_plotting, enable_plotting=enable_plotting,
allow_enhancement=allow_enhancement, allow_enhancement=allow_enhancement,
curved_line=curved_line, curved_line=curved_line,
textline_light=textline_light,
full_layout=full_layout, full_layout=full_layout,
tables=tables,
right2left=right2left,
input_binary=input_binary, input_binary=input_binary,
allow_scaling=allow_scaling, allow_scaling=allow_scaling,
headers_off=headers_off, headers_off=headers_off,
light_version=light_version,
ignore_page_extraction=ignore_page_extraction,
) )
if dir_in:
eynollah.run()
else:
pcgts = eynollah.run() pcgts = eynollah.run()
eynollah.writer.write_pagexml(pcgts) eynollah.writer.write_pagexml(pcgts)

File diff suppressed because it is too large Load Diff

@ -1,5 +1,5 @@
{ {
"version": "0.0.8", "version": "0.3.1",
"git_url": "https://github.com/qurator-spk/eynollah", "git_url": "https://github.com/qurator-spk/eynollah",
"tools": { "tools": {
"ocrd-eynollah-segment": { "ocrd-eynollah-segment": {
@ -13,6 +13,7 @@
"models": { "models": {
"type": "string", "type": "string",
"format": "file", "format": "file",
"content-type": "text/directory",
"cacheable": true, "cacheable": true,
"description": "Path to directory containing models to be used (See https://qurator-data.de/eynollah)", "description": "Path to directory containing models to be used (See https://qurator-data.de/eynollah)",
"required": true "required": true
@ -28,6 +29,11 @@
"default": true, "default": true,
"description": "Try to detect all element subtypes, including drop-caps and headings" "description": "Try to detect all element subtypes, including drop-caps and headings"
}, },
"tables": {
"type": "boolean",
"default": false,
"description": "Try to detect table regions"
},
"curved_line": { "curved_line": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
@ -43,7 +49,17 @@
"default": false, "default": false,
"description": "ignore the special role of headings during reading order detection" "description": "ignore the special role of headings during reading order detection"
} }
},
"resources": [
{
"description": "models for eynollah (TensorFlow SavedModel format)",
"url": "https://github.com/qurator-spk/eynollah/releases/download/v0.3.1/models_eynollah.tar.gz",
"name": "default",
"size": 1894627041,
"type": "archive",
"path_in_archive": "models_eynollah"
} }
]
} }
} }
} }

@ -17,7 +17,9 @@ class EynollahPlotter():
def __init__( def __init__(
self, self,
*, *,
dir_out,
dir_of_all, dir_of_all,
dir_save_page,
dir_of_deskewed, dir_of_deskewed,
dir_of_layout, dir_of_layout,
dir_of_cropped_images, dir_of_cropped_images,
@ -26,7 +28,9 @@ class EynollahPlotter():
scale_x=1, scale_x=1,
scale_y=1, scale_y=1,
): ):
self.dir_out = dir_out
self.dir_of_all = dir_of_all self.dir_of_all = dir_of_all
self.dir_save_page = dir_save_page
self.dir_of_layout = dir_of_layout self.dir_of_layout = dir_of_layout
self.dir_of_cropped_images = dir_of_cropped_images self.dir_of_cropped_images = dir_of_cropped_images
self.dir_of_deskewed = dir_of_deskewed self.dir_of_deskewed = dir_of_deskewed
@ -72,8 +76,8 @@ class EynollahPlotter():
if self.dir_of_layout is not None: if self.dir_of_layout is not None:
values = np.unique(text_regions_p[:, :]) values = np.unique(text_regions_p[:, :])
# pixels=['Background' , 'Main text' , 'Heading' , 'Marginalia' ,'Drop capitals' , 'Images' , 'Seperators' , 'Tables', 'Graphics'] # pixels=['Background' , 'Main text' , 'Heading' , 'Marginalia' ,'Drop capitals' , 'Images' , 'Seperators' , 'Tables', 'Graphics']
pixels = ["Background", "Main text", "Header", "Marginalia", "Drop capital", "Image", "Separator"] pixels = ["Background", "Main text", "Header", "Marginalia", "Drop capital", "Image", "Separator", "Tables"]
values_indexes = [0, 1, 2, 8, 4, 5, 6] values_indexes = [0, 1, 2, 8, 4, 5, 6, 10]
plt.figure(figsize=(40, 40)) plt.figure(figsize=(40, 40))
plt.rcParams["font.size"] = "40" plt.rcParams["font.size"] = "40"
im = plt.imshow(text_regions_p[:, :]) im = plt.imshow(text_regions_p[:, :])
@ -86,8 +90,8 @@ class EynollahPlotter():
if self.dir_of_all is not None: if self.dir_of_all is not None:
values = np.unique(text_regions_p[:, :]) values = np.unique(text_regions_p[:, :])
# pixels=['Background' , 'Main text' , 'Heading' , 'Marginalia' ,'Drop capitals' , 'Images' , 'Seperators' , 'Tables', 'Graphics'] # pixels=['Background' , 'Main text' , 'Heading' , 'Marginalia' ,'Drop capitals' , 'Images' , 'Seperators' , 'Tables', 'Graphics']
pixels = ["Background", "Main text", "Header", "Marginalia", "Drop capital", "Image", "Separator"] pixels = ["Background", "Main text", "Header", "Marginalia", "Drop capital", "Image", "Separator", "Tables"]
values_indexes = [0, 1, 2, 8, 4, 5, 6] values_indexes = [0, 1, 2, 8, 4, 5, 6, 10]
plt.figure(figsize=(80, 40)) plt.figure(figsize=(80, 40))
plt.rcParams["font.size"] = "40" plt.rcParams["font.size"] = "40"
plt.subplot(1, 2, 1) plt.subplot(1, 2, 1)
@ -125,6 +129,10 @@ class EynollahPlotter():
def save_page_image(self, image_page): def save_page_image(self, image_page):
if self.dir_of_all is not None: if self.dir_of_all is not None:
cv2.imwrite(os.path.join(self.dir_of_all, self.image_filename_stem + "_page.png"), image_page) cv2.imwrite(os.path.join(self.dir_of_all, self.image_filename_stem + "_page.png"), image_page)
if self.dir_save_page is not None:
cv2.imwrite(os.path.join(self.dir_save_page, self.image_filename_stem + "_page.png"), image_page)
def save_enhanced_image(self, img_res):
cv2.imwrite(os.path.join(self.dir_out, self.image_filename_stem + "_enhanced.png"), img_res)
def save_plot_of_textline_density(self, img_patch_org): def save_plot_of_textline_density(self, img_patch_org):
if self.dir_of_all is not None: if self.dir_of_all is not None:

@ -42,7 +42,7 @@ class EynollahProcessor(Processor):
page = pcgts.get_Page() page = pcgts.get_Page()
# XXX loses DPI information # XXX loses DPI information
# page_image, _, _ = self.workspace.image_from_page(page, page_id, feature_filter='binarized') # page_image, _, _ = self.workspace.image_from_page(page, page_id, feature_filter='binarized')
image_filename = self.workspace.download_file(next(self.workspace.mets.find_files(url=page.imageFilename))).local_filename image_filename = self.workspace.download_file(next(self.workspace.mets.find_files(local_filename=page.imageFilename))).local_filename
eynollah_kwargs = { eynollah_kwargs = {
'dir_models': self.resolve_resource(self.parameter['models']), 'dir_models': self.resolve_resource(self.parameter['models']),
'allow_enhancement': False, 'allow_enhancement': False,
@ -50,6 +50,7 @@ class EynollahProcessor(Processor):
'full_layout': self.parameter['full_layout'], 'full_layout': self.parameter['full_layout'],
'allow_scaling': self.parameter['allow_scaling'], 'allow_scaling': self.parameter['allow_scaling'],
'headers_off': self.parameter['headers_off'], 'headers_off': self.parameter['headers_off'],
'tables': self.parameter['tables'],
'override_dpi': self.parameter['dpi'], 'override_dpi': self.parameter['dpi'],
'logger': LOG, 'logger': LOG,
'pcgts': pcgts, 'pcgts': pcgts,

@ -294,7 +294,7 @@ def return_x_start_end_mothers_childs_and_type_of_reading_order(x_min_hor_some,x
#print(args_to_be_unified,'args_to_be_unified') #print(args_to_be_unified,'args_to_be_unified')
return reading_orther_type,x_start_returned, x_end_returned ,y_sep_returned,y_diff_returned,y_lines_without_mother,x_start_without_mother,x_end_without_mother,there_is_sep_with_child,y_lines_with_child_without_mother,x_start_with_child_without_mother,x_end_with_child_without_mother return reading_orther_type,x_start_returned, x_end_returned ,y_sep_returned,y_diff_returned,y_lines_without_mother,x_start_without_mother,x_end_without_mother,there_is_sep_with_child,y_lines_with_child_without_mother,x_start_with_child_without_mother,x_end_with_child_without_mother,new_main_sep_y
def crop_image_inside_box(box, img_org_copy): def crop_image_inside_box(box, img_org_copy):
image_box = img_org_copy[box[1] : box[1] + box[3], box[0] : box[0] + box[2]] image_box = img_org_copy[box[1] : box[1] + box[3], box[0] : box[0] + box[2]]
return image_box, [box[1], box[1] + box[3], box[0], box[0] + box[2]] return image_box, [box[1], box[1] + box[3], box[0], box[0] + box[2]]
@ -360,7 +360,7 @@ def find_num_col_deskew(regions_without_separators, sigma_, multiplier=3.8):
return np.std(z) return np.std(z)
def find_num_col(regions_without_separators, multiplier=3.8): def find_num_col(regions_without_separators, num_col_classifier, tables, multiplier=3.8):
regions_without_separators_0 = regions_without_separators[:, :].sum(axis=0) regions_without_separators_0 = regions_without_separators[:, :].sum(axis=0)
##plt.plot(regions_without_separators_0) ##plt.plot(regions_without_separators_0)
##plt.show() ##plt.show()
@ -417,6 +417,19 @@ def find_num_col(regions_without_separators, multiplier=3.8):
peaks_neg_fin = peaks_neg[(interest_neg < grenze)] peaks_neg_fin = peaks_neg[(interest_neg < grenze)]
# interest_neg_fin=interest_neg[(interest_neg<grenze)] # interest_neg_fin=interest_neg[(interest_neg<grenze)]
if not tables:
if ( num_col_classifier - ( (len(interest_neg_fin))+1 ) ) >= 3:
index_sort_interest_neg_fin= np.argsort(interest_neg_fin)
peaks_neg_sorted = np.array(peaks_neg)[index_sort_interest_neg_fin]
interest_neg_fin_sorted = np.array(interest_neg_fin)[index_sort_interest_neg_fin]
if len(index_sort_interest_neg_fin)>=num_col_classifier:
peaks_neg_fin = list( peaks_neg_sorted[:num_col_classifier] )
interest_neg_fin = list( interest_neg_fin_sorted[:num_col_classifier] )
else:
peaks_neg_fin = peaks_neg[:]
interest_neg_fin = interest_neg[:]
num_col = (len(interest_neg_fin)) + 1 num_col = (len(interest_neg_fin)) + 1
# print(peaks_neg_fin,'peaks_neg_fin') # print(peaks_neg_fin,'peaks_neg_fin')
@ -489,9 +502,9 @@ def find_num_col(regions_without_separators, multiplier=3.8):
num_col = 1 num_col = 1
peaks_neg_true = [] peaks_neg_true = []
diff_peaks_annormal = diff_peaks[diff_peaks < 360] diff_peaks_abnormal = diff_peaks[diff_peaks < 360]
if len(diff_peaks_annormal) > 0: if len(diff_peaks_abnormal) > 0:
arg_help = np.array(range(len(diff_peaks))) arg_help = np.array(range(len(diff_peaks)))
arg_help_ann = arg_help[diff_peaks < 360] arg_help_ann = arg_help[diff_peaks < 360]
@ -783,7 +796,8 @@ def putt_bb_of_drop_capitals_of_model_in_patches_in_layout(layout_in_patch):
return layout_in_patch return layout_in_patch
def check_any_text_region_in_model_one_is_main_or_header(regions_model_1,regions_model_full,contours_only_text_parent,all_box_coord,all_found_texline_polygons,slopes,contours_only_text_parent_d_ordered): def check_any_text_region_in_model_one_is_main_or_header(regions_model_1,regions_model_full,contours_only_text_parent,all_box_coord,all_found_textline_polygons,slopes,contours_only_text_parent_d_ordered):
cx_main,cy_main ,x_min_main , x_max_main, y_min_main ,y_max_main,y_corr_x_min_from_argmin=find_new_features_of_contours(contours_only_text_parent) cx_main,cy_main ,x_min_main , x_max_main, y_min_main ,y_max_main,y_corr_x_min_from_argmin=find_new_features_of_contours(contours_only_text_parent)
length_con=x_max_main-x_min_main length_con=x_max_main-x_min_main
@ -791,8 +805,8 @@ def check_any_text_region_in_model_one_is_main_or_header(regions_model_1,regions
all_found_texline_polygons_main=[] all_found_textline_polygons_main=[]
all_found_texline_polygons_head=[] all_found_textline_polygons_head=[]
all_box_coord_main=[] all_box_coord_main=[]
all_box_coord_head=[] all_box_coord_head=[]
@ -826,7 +840,7 @@ def check_any_text_region_in_model_one_is_main_or_header(regions_model_1,regions
contours_only_text_parent_head_d.append(contours_only_text_parent_d_ordered[ii]) contours_only_text_parent_head_d.append(contours_only_text_parent_d_ordered[ii])
all_box_coord_head.append(all_box_coord[ii]) all_box_coord_head.append(all_box_coord[ii])
slopes_head.append(slopes[ii]) slopes_head.append(slopes[ii])
all_found_texline_polygons_head.append(all_found_texline_polygons[ii]) all_found_textline_polygons_head.append(all_found_textline_polygons[ii])
else: else:
regions_model_1[:,:][(regions_model_1[:,:]==1) & (img[:,:,0]==255) ]=1 regions_model_1[:,:][(regions_model_1[:,:]==1) & (img[:,:,0]==255) ]=1
contours_only_text_parent_main.append(con) contours_only_text_parent_main.append(con)
@ -834,15 +848,90 @@ def check_any_text_region_in_model_one_is_main_or_header(regions_model_1,regions
contours_only_text_parent_main_d.append(contours_only_text_parent_d_ordered[ii]) contours_only_text_parent_main_d.append(contours_only_text_parent_d_ordered[ii])
all_box_coord_main.append(all_box_coord[ii]) all_box_coord_main.append(all_box_coord[ii])
slopes_main.append(slopes[ii]) slopes_main.append(slopes[ii])
all_found_texline_polygons_main.append(all_found_texline_polygons[ii]) all_found_textline_polygons_main.append(all_found_textline_polygons[ii])
#print(all_pixels,pixels_main,pixels_header) #print(all_pixels,pixels_main,pixels_header)
return regions_model_1,contours_only_text_parent_main,contours_only_text_parent_head,all_box_coord_main,all_box_coord_head,all_found_textline_polygons_main,all_found_textline_polygons_head,slopes_main,slopes_head,contours_only_text_parent_main_d,contours_only_text_parent_head_d
#plt.imshow(img[:,:,0]) def check_any_text_region_in_model_one_is_main_or_header_light(regions_model_1,regions_model_full,contours_only_text_parent,all_box_coord,all_found_textline_polygons,slopes,contours_only_text_parent_d_ordered):
#plt.show()
return regions_model_1,contours_only_text_parent_main,contours_only_text_parent_head,all_box_coord_main,all_box_coord_head,all_found_texline_polygons_main,all_found_texline_polygons_head,slopes_main,slopes_head,contours_only_text_parent_main_d,contours_only_text_parent_head_d ### to make it faster
h_o = regions_model_1.shape[0]
w_o = regions_model_1.shape[1]
regions_model_1 = cv2.resize(regions_model_1, (int(regions_model_1.shape[1]/3.), int(regions_model_1.shape[0]/3.)), interpolation=cv2.INTER_NEAREST)
regions_model_full = cv2.resize(regions_model_full, (int(regions_model_full.shape[1]/3.), int(regions_model_full.shape[0]/3.)), interpolation=cv2.INTER_NEAREST)
contours_only_text_parent = [ (i/3.).astype(np.int32) for i in contours_only_text_parent]
###
cx_main,cy_main ,x_min_main , x_max_main, y_min_main ,y_max_main,y_corr_x_min_from_argmin=find_new_features_of_contours(contours_only_text_parent)
length_con=x_max_main-x_min_main
height_con=y_max_main-y_min_main
all_found_textline_polygons_main=[]
all_found_textline_polygons_head=[]
all_box_coord_main=[]
all_box_coord_head=[]
slopes_main=[]
slopes_head=[]
contours_only_text_parent_main=[]
contours_only_text_parent_head=[]
contours_only_text_parent_main_d=[]
contours_only_text_parent_head_d=[]
for ii in range(len(contours_only_text_parent)):
con=contours_only_text_parent[ii]
img=np.zeros((regions_model_1.shape[0],regions_model_1.shape[1],3))
img = cv2.fillPoly(img, pts=[con], color=(255, 255, 255))
all_pixels=((img[:,:,0]==255)*1).sum()
pixels_header=( ( (img[:,:,0]==255) & (regions_model_full[:,:,0]==2) )*1 ).sum()
pixels_main=all_pixels-pixels_header
if (pixels_header>=pixels_main) and ( (length_con[ii]/float(height_con[ii]) )>=1.3 ):
regions_model_1[:,:][(regions_model_1[:,:]==1) & (img[:,:,0]==255) ]=2
contours_only_text_parent_head.append(con)
if contours_only_text_parent_d_ordered is not None:
contours_only_text_parent_head_d.append(contours_only_text_parent_d_ordered[ii])
all_box_coord_head.append(all_box_coord[ii])
slopes_head.append(slopes[ii])
all_found_textline_polygons_head.append(all_found_textline_polygons[ii])
else:
regions_model_1[:,:][(regions_model_1[:,:]==1) & (img[:,:,0]==255) ]=1
contours_only_text_parent_main.append(con)
if contours_only_text_parent_d_ordered is not None:
contours_only_text_parent_main_d.append(contours_only_text_parent_d_ordered[ii])
all_box_coord_main.append(all_box_coord[ii])
slopes_main.append(slopes[ii])
all_found_textline_polygons_main.append(all_found_textline_polygons[ii])
#print(all_pixels,pixels_main,pixels_header)
### to make it faster
regions_model_1 = cv2.resize(regions_model_1, (w_o, h_o), interpolation=cv2.INTER_NEAREST)
#regions_model_full = cv2.resize(img, (int(regions_model_full.shape[1]/3.), int(regions_model_full.shape[0]/3.)), interpolation=cv2.INTER_NEAREST)
contours_only_text_parent_head = [ (i*3.).astype(np.int32) for i in contours_only_text_parent_head]
contours_only_text_parent_main = [ (i*3.).astype(np.int32) for i in contours_only_text_parent_main]
###
return regions_model_1,contours_only_text_parent_main,contours_only_text_parent_head,all_box_coord_main,all_box_coord_head,all_found_textline_polygons_main,all_found_textline_polygons_head,slopes_main,slopes_head,contours_only_text_parent_main_d,contours_only_text_parent_head_d
def small_textlines_to_parent_adherence2(textlines_con, textline_iamge, num_col): def small_textlines_to_parent_adherence2(textlines_con, textline_iamge, num_col):
# print(textlines_con) # print(textlines_con)
@ -1248,7 +1337,7 @@ def return_points_with_boundies(peaks_neg_fin, first_point, last_point):
peaks_neg_tot.append(last_point) peaks_neg_tot.append(last_point)
return peaks_neg_tot return peaks_neg_tot
def find_number_of_columns_in_document(region_pre_p, num_col_classifier, pixel_lines, contours_h=None): def find_number_of_columns_in_document(region_pre_p, num_col_classifier, tables, pixel_lines, contours_h=None):
separators_closeup=( (region_pre_p[:,:,:]==pixel_lines))*1 separators_closeup=( (region_pre_p[:,:,:]==pixel_lines))*1
@ -1561,7 +1650,7 @@ def find_number_of_columns_in_document(region_pre_p, num_col_classifier, pixel_l
#regions_without_separators_tile=cv2.erode(regions_without_separators_tile,kernel,iterations = 3) #regions_without_separators_tile=cv2.erode(regions_without_separators_tile,kernel,iterations = 3)
# #
try: try:
num_col, peaks_neg_fin = find_num_col(regions_without_separators_tile,multiplier=7.0) num_col, peaks_neg_fin = find_num_col(regions_without_separators_tile, num_col_classifier, tables, multiplier=7.0)
except: except:
num_col = 0 num_col = 0
peaks_neg_fin = [] peaks_neg_fin = []
@ -1583,9 +1672,11 @@ def find_number_of_columns_in_document(region_pre_p, num_col_classifier, pixel_l
return num_col_fin, peaks_neg_fin_fin,matrix_of_lines_ch,splitter_y_new,separators_closeup_n return num_col_fin, peaks_neg_fin_fin,matrix_of_lines_ch,splitter_y_new,separators_closeup_n
def return_boxes_of_images_by_order_of_reading_new(splitter_y_new, regions_without_separators, matrix_of_lines_ch, num_col_classifier, erosion_hurts): def return_boxes_of_images_by_order_of_reading_new(splitter_y_new, regions_without_separators, matrix_of_lines_ch, num_col_classifier, erosion_hurts, tables, right2left_readingorder):
if right2left_readingorder:
regions_without_separators = cv2.flip(regions_without_separators,1)
boxes=[] boxes=[]
peaks_neg_tot_tables = []
for i in range(len(splitter_y_new)-1): for i in range(len(splitter_y_new)-1):
#print(splitter_y_new[i],splitter_y_new[i+1]) #print(splitter_y_new[i],splitter_y_new[i+1])
@ -1599,20 +1690,21 @@ def return_boxes_of_images_by_order_of_reading_new(splitter_y_new, regions_witho
try: try:
if erosion_hurts: if erosion_hurts:
num_col, peaks_neg_fin=find_num_col(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),:],multiplier=6.) num_col, peaks_neg_fin=find_num_col(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),:], num_col_classifier, tables, multiplier=6.)
else: else:
num_col, peaks_neg_fin=find_num_col(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),:],multiplier=7.) num_col, peaks_neg_fin=find_num_col(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),:],num_col_classifier, tables, multiplier=7.)
except: except:
peaks_neg_fin=[] peaks_neg_fin=[]
num_col = 0
try: try:
peaks_neg_fin_org=np.copy(peaks_neg_fin) peaks_neg_fin_org=np.copy(peaks_neg_fin)
if (len(peaks_neg_fin)+1)<num_col_classifier: if (len(peaks_neg_fin)+1)<num_col_classifier or num_col_classifier==6:
#print('burda') #print('burda')
if len(peaks_neg_fin)==0: if len(peaks_neg_fin)==0:
num_col, peaks_neg_fin=find_num_col(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),:],multiplier=3.) num_col, peaks_neg_fin=find_num_col(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),:],num_col_classifier, tables, multiplier=3.)
peaks_neg_fin_early=[] peaks_neg_fin_early=[]
peaks_neg_fin_early.append(0) peaks_neg_fin_early.append(0)
#print(peaks_neg_fin,'peaks_neg_fin') #print(peaks_neg_fin,'peaks_neg_fin')
@ -1628,12 +1720,12 @@ def return_boxes_of_images_by_order_of_reading_new(splitter_y_new, regions_witho
#plt.plot(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),peaks_neg_fin_early[i_n]:peaks_neg_fin_early[i_n+1]].sum(axis=0) ) #plt.plot(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),peaks_neg_fin_early[i_n]:peaks_neg_fin_early[i_n+1]].sum(axis=0) )
#plt.show() #plt.show()
try: try:
num_col, peaks_neg_fin1=find_num_col(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),peaks_neg_fin_early[i_n]:peaks_neg_fin_early[i_n+1]],multiplier=7.) num_col, peaks_neg_fin1=find_num_col(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),peaks_neg_fin_early[i_n]:peaks_neg_fin_early[i_n+1]],num_col_classifier,tables, multiplier=7.)
except: except:
peaks_neg_fin1=[] peaks_neg_fin1=[]
try: try:
num_col, peaks_neg_fin2=find_num_col(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),peaks_neg_fin_early[i_n]:peaks_neg_fin_early[i_n+1]],multiplier=5.) num_col, peaks_neg_fin2=find_num_col(regions_without_separators[int(splitter_y_new[i]):int(splitter_y_new[i+1]),peaks_neg_fin_early[i_n]:peaks_neg_fin_early[i_n+1]],num_col_classifier,tables, multiplier=5.)
except: except:
peaks_neg_fin2=[] peaks_neg_fin2=[]
@ -1673,14 +1765,22 @@ def return_boxes_of_images_by_order_of_reading_new(splitter_y_new, regions_witho
cy_hor_diff=matrix_new[:,7][ (matrix_new[:,9]==0) ] cy_hor_diff=matrix_new[:,7][ (matrix_new[:,9]==0) ]
arg_org_hor_some=matrix_new[:,0][ (matrix_new[:,9]==0) ] arg_org_hor_some=matrix_new[:,0][ (matrix_new[:,9]==0) ]
if right2left_readingorder:
x_max_hor_some_new = regions_without_separators.shape[1] - x_min_hor_some
x_min_hor_some_new = regions_without_separators.shape[1] - x_max_hor_some
x_min_hor_some =list(np.copy(x_min_hor_some_new))
x_max_hor_some =list(np.copy(x_max_hor_some_new))
peaks_neg_tot=return_points_with_boundies(peaks_neg_fin,0, regions_without_separators[:,:].shape[1]) peaks_neg_tot=return_points_with_boundies(peaks_neg_fin,0, regions_without_separators[:,:].shape[1])
reading_order_type,x_starting,x_ending,y_type_2,y_diff_type_2,y_lines_without_mother,x_start_without_mother,x_end_without_mother,there_is_sep_with_child,y_lines_with_child_without_mother,x_start_with_child_without_mother,x_end_with_child_without_mother=return_x_start_end_mothers_childs_and_type_of_reading_order(x_min_hor_some,x_max_hor_some,cy_hor_some,peaks_neg_tot,cy_hor_diff) peaks_neg_tot_tables.append(peaks_neg_tot)
reading_order_type,x_starting,x_ending,y_type_2,y_diff_type_2,y_lines_without_mother,x_start_without_mother,x_end_without_mother,there_is_sep_with_child,y_lines_with_child_without_mother,x_start_with_child_without_mother,x_end_with_child_without_mother,new_main_sep_y=return_x_start_end_mothers_childs_and_type_of_reading_order(x_min_hor_some,x_max_hor_some,cy_hor_some,peaks_neg_tot,cy_hor_diff)
if (reading_order_type==1) or (reading_order_type==0 and (len(y_lines_without_mother)>=2 or there_is_sep_with_child==1)): if (reading_order_type==1) or (reading_order_type==0 and (len(y_lines_without_mother)>=2 or there_is_sep_with_child==1)):
@ -1936,6 +2036,7 @@ def return_boxes_of_images_by_order_of_reading_new(splitter_y_new, regions_witho
columns_not_covered_child_no_mother=np.sort(columns_not_covered_child_no_mother) columns_not_covered_child_no_mother=np.sort(columns_not_covered_child_no_mother)
ind_args=np.array(range(len(y_type_2))) ind_args=np.array(range(len(y_type_2)))
@ -2148,6 +2249,15 @@ def return_boxes_of_images_by_order_of_reading_new(splitter_y_new, regions_witho
##y_lines_by_order.append(int(splitter_y_new[i])) ##y_lines_by_order.append(int(splitter_y_new[i]))
##x_start_by_order.append(0) ##x_start_by_order.append(0)
#y_type_2.append(int(splitter_y_new[i]))
#x_starting.append(x_starting[0])
#x_ending.append(x_ending[0])
if len(new_main_sep_y)>0:
y_type_2.append(int(splitter_y_new[i]))
x_starting.append(0)
x_ending.append(len(peaks_neg_tot)-1)
else:
y_type_2.append(int(splitter_y_new[i])) y_type_2.append(int(splitter_y_new[i]))
x_starting.append(x_starting[0]) x_starting.append(x_starting[0])
x_ending.append(x_ending[0]) x_ending.append(x_ending[0])
@ -2180,7 +2290,6 @@ def return_boxes_of_images_by_order_of_reading_new(splitter_y_new, regions_witho
ind_args=np.array(range(len(y_type_2))) ind_args=np.array(range(len(y_type_2)))
#ind_args=np.array(ind_args) #ind_args=np.array(ind_args)
#print(ind_args,'ind_args')
for column in range(len(peaks_neg_tot)-1): for column in range(len(peaks_neg_tot)-1):
#print(column,'column') #print(column,'column')
ind_args_in_col=ind_args[x_starting==column] ind_args_in_col=ind_args[x_starting==column]
@ -2237,4 +2346,20 @@ def return_boxes_of_images_by_order_of_reading_new(splitter_y_new, regions_witho
#else: #else:
#boxes.append([ 0, regions_without_separators[:,:].shape[1] ,splitter_y_new[i],splitter_y_new[i+1]]) #boxes.append([ 0, regions_without_separators[:,:].shape[1] ,splitter_y_new[i],splitter_y_new[i+1]])
return boxes if right2left_readingorder:
peaks_neg_tot_tables_new = []
if len(peaks_neg_tot_tables)>=1:
for peaks_tab_ind in peaks_neg_tot_tables:
peaks_neg_tot_tables_ind = regions_without_separators.shape[1] - np.array(peaks_tab_ind)
peaks_neg_tot_tables_ind = list(peaks_neg_tot_tables_ind[::-1])
peaks_neg_tot_tables_new.append(peaks_neg_tot_tables_ind)
for i in range(len(boxes)):
x_start_new = regions_without_separators.shape[1] - boxes[i][1]
x_end_new = regions_without_separators.shape[1] - boxes[i][0]
boxes[i][0] = x_start_new
boxes[i][1] = x_end_new
return boxes, peaks_neg_tot_tables_new
else:
return boxes, peaks_neg_tot_tables

@ -3,7 +3,8 @@ import numpy as np
from shapely import geometry from shapely import geometry
from .rotate import rotate_image, rotation_image_new from .rotate import rotate_image, rotation_image_new
from multiprocessing import Process, Queue, cpu_count
from multiprocessing import Pool
def contours_in_same_horizon(cy_main_hor): def contours_in_same_horizon(cy_main_hor):
X1 = np.zeros((len(cy_main_hor), len(cy_main_hor))) X1 = np.zeros((len(cy_main_hor), len(cy_main_hor)))
X2 = np.zeros((len(cy_main_hor), len(cy_main_hor))) X2 = np.zeros((len(cy_main_hor), len(cy_main_hor)))
@ -19,7 +20,7 @@ def contours_in_same_horizon(cy_main_hor):
list_h.append(i) list_h.append(i)
if len(list_h) > 1: if len(list_h) > 1:
all_args.append(list(set(list_h))) all_args.append(list(set(list_h)))
return np.unique(all_args) return np.unique(np.array(all_args, dtype=object))
def find_contours_mean_y_diff(contours_main): def find_contours_mean_y_diff(contours_main):
M_main = [cv2.moments(contours_main[j]) for j in range(len(contours_main))] M_main = [cv2.moments(contours_main[j]) for j in range(len(contours_main))]
@ -43,8 +44,8 @@ def get_text_region_boxes_by_given_contours(contours):
def filter_contours_area_of_image(image, contours, hierarchy, max_area, min_area): def filter_contours_area_of_image(image, contours, hierarchy, max_area, min_area):
found_polygons_early = list() found_polygons_early = list()
jv = 0
for c in contours: for jv,c in enumerate(contours):
if len(c) < 3: # A polygon cannot have less than 3 points if len(c) < 3: # A polygon cannot have less than 3 points
continue continue
@ -52,14 +53,12 @@ def filter_contours_area_of_image(image, contours, hierarchy, max_area, min_area
area = polygon.area area = polygon.area
if area >= min_area * np.prod(image.shape[:2]) and area <= max_area * np.prod(image.shape[:2]) and hierarchy[0][jv][3] == -1: # and hierarchy[0][jv][3]==-1 : if area >= min_area * np.prod(image.shape[:2]) and area <= max_area * np.prod(image.shape[:2]) and hierarchy[0][jv][3] == -1: # and hierarchy[0][jv][3]==-1 :
found_polygons_early.append(np.array([[point] for point in polygon.exterior.coords], dtype=np.uint)) found_polygons_early.append(np.array([[point] for point in polygon.exterior.coords], dtype=np.uint))
jv += 1
return found_polygons_early return found_polygons_early
def filter_contours_area_of_image_tables(image, contours, hierarchy, max_area, min_area): def filter_contours_area_of_image_tables(image, contours, hierarchy, max_area, min_area):
found_polygons_early = list() found_polygons_early = list()
jv = 0 for jv,c in enumerate(contours):
for c in contours:
if len(c) < 3: # A polygon cannot have less than 3 points if len(c) < 3: # A polygon cannot have less than 3 points
continue continue
@ -72,7 +71,6 @@ def filter_contours_area_of_image_tables(image, contours, hierarchy, max_area, m
if area >= min_area * np.prod(image.shape[:2]) and area <= max_area * np.prod(image.shape[:2]): # and hierarchy[0][jv][3]==-1 : if area >= min_area * np.prod(image.shape[:2]) and area <= max_area * np.prod(image.shape[:2]): # and hierarchy[0][jv][3]==-1 :
# print(c[0][0][1]) # print(c[0][0][1])
found_polygons_early.append(np.array([[point] for point in polygon.exterior.coords], dtype=np.int32)) found_polygons_early.append(np.array([[point] for point in polygon.exterior.coords], dtype=np.int32))
jv += 1
return found_polygons_early return found_polygons_early
def find_new_features_of_contours(contours_main): def find_new_features_of_contours(contours_main):
@ -109,7 +107,21 @@ def find_new_features_of_contours(contours_main):
# dis_x=np.abs(x_max_main-x_min_main) # dis_x=np.abs(x_max_main-x_min_main)
return cx_main, cy_main, x_min_main, x_max_main, y_min_main, y_max_main, y_corr_x_min_from_argmin return cx_main, cy_main, x_min_main, x_max_main, y_min_main, y_max_main, y_corr_x_min_from_argmin
def find_features_of_contours(contours_main):
areas_main=np.array([cv2.contourArea(contours_main[j]) for j in range(len(contours_main))])
M_main=[cv2.moments(contours_main[j]) for j in range(len(contours_main))]
cx_main=[(M_main[j]['m10']/(M_main[j]['m00']+1e-32)) for j in range(len(M_main))]
cy_main=[(M_main[j]['m01']/(M_main[j]['m00']+1e-32)) for j in range(len(M_main))]
x_min_main=np.array([np.min(contours_main[j][:,0,0]) for j in range(len(contours_main))])
x_max_main=np.array([np.max(contours_main[j][:,0,0]) for j in range(len(contours_main))])
y_min_main=np.array([np.min(contours_main[j][:,0,1]) for j in range(len(contours_main))])
y_max_main=np.array([np.max(contours_main[j][:,0,1]) for j in range(len(contours_main))])
return y_min_main, y_max_main
def return_parent_contours(contours, hierarchy): def return_parent_contours(contours, hierarchy):
contours_parent = [contours[i] for i in range(len(contours)) if hierarchy[0][i][3] == -1] contours_parent = [contours[i] for i in range(len(contours)) if hierarchy[0][i][3] == -1]
return contours_parent return contours_parent
@ -133,6 +145,94 @@ def return_contours_of_interested_region(region_pre_p, pixel, min_area=0.0002):
return contours_imgs return contours_imgs
def do_work_of_contours_in_image(queue_of_all_params, contours_per_process, indexes_r_con_per_pro, img, slope_first):
cnts_org_per_each_subprocess = []
index_by_text_region_contours = []
for mv in range(len(contours_per_process)):
index_by_text_region_contours.append(indexes_r_con_per_pro[mv])
img_copy = np.zeros(img.shape)
img_copy = cv2.fillPoly(img_copy, pts=[contours_per_process[mv]], color=(1, 1, 1))
img_copy = rotation_image_new(img_copy, -slope_first)
img_copy = img_copy.astype(np.uint8)
imgray = cv2.cvtColor(img_copy, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 0, 255, 0)
cont_int, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cont_int[0][:, 0, 0] = cont_int[0][:, 0, 0] + np.abs(img_copy.shape[1] - img.shape[1])
cont_int[0][:, 0, 1] = cont_int[0][:, 0, 1] + np.abs(img_copy.shape[0] - img.shape[0])
cnts_org_per_each_subprocess.append(cont_int[0])
queue_of_all_params.put([ cnts_org_per_each_subprocess, index_by_text_region_contours])
def get_textregion_contours_in_org_image_multi(cnts, img, slope_first):
num_cores = cpu_count()
queue_of_all_params = Queue()
processes = []
nh = np.linspace(0, len(cnts), num_cores + 1)
indexes_by_text_con = np.array(range(len(cnts)))
for i in range(num_cores):
contours_per_process = cnts[int(nh[i]) : int(nh[i + 1])]
indexes_text_con_per_process = indexes_by_text_con[int(nh[i]) : int(nh[i + 1])]
processes.append(Process(target=do_work_of_contours_in_image, args=(queue_of_all_params, contours_per_process, indexes_text_con_per_process, img,slope_first )))
for i in range(num_cores):
processes[i].start()
cnts_org = []
all_index_text_con = []
for i in range(num_cores):
list_all_par = queue_of_all_params.get(True)
contours_for_sub_process = list_all_par[0]
indexes_for_sub_process = list_all_par[1]
for j in range(len(contours_for_sub_process)):
cnts_org.append(contours_for_sub_process[j])
all_index_text_con.append(indexes_for_sub_process[j])
for i in range(num_cores):
processes[i].join()
print(all_index_text_con)
return cnts_org
def loop_contour_image(index_l, cnts,img, slope_first):
img_copy = np.zeros(img.shape)
img_copy = cv2.fillPoly(img_copy, pts=[cnts[index_l]], color=(1, 1, 1))
# plt.imshow(img_copy)
# plt.show()
# print(img.shape,'img')
img_copy = rotation_image_new(img_copy, -slope_first)
##print(img_copy.shape,'img_copy')
# plt.imshow(img_copy)
# plt.show()
img_copy = img_copy.astype(np.uint8)
imgray = cv2.cvtColor(img_copy, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 0, 255, 0)
cont_int, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cont_int[0][:, 0, 0] = cont_int[0][:, 0, 0] + np.abs(img_copy.shape[1] - img.shape[1])
cont_int[0][:, 0, 1] = cont_int[0][:, 0, 1] + np.abs(img_copy.shape[0] - img.shape[0])
# print(np.shape(cont_int[0]))
return cont_int[0]
def get_textregion_contours_in_org_image_multi2(cnts, img, slope_first):
cnts_org = []
# print(cnts,'cnts')
with Pool(cpu_count()) as p:
cnts_org = p.starmap(loop_contour_image, [(index_l,cnts, img,slope_first) for index_l in range(len(cnts))])
return cnts_org
def get_textregion_contours_in_org_image(cnts, img, slope_first): def get_textregion_contours_in_org_image(cnts, img, slope_first):
cnts_org = [] cnts_org = []
@ -161,11 +261,43 @@ def get_textregion_contours_in_org_image(cnts, img, slope_first):
# print(np.shape(cont_int[0])) # print(np.shape(cont_int[0]))
cnts_org.append(cont_int[0]) cnts_org.append(cont_int[0])
# print(cnts_org,'cnts_org') return cnts_org
def get_textregion_contours_in_org_image_light(cnts, img, slope_first):
h_o = img.shape[0]
w_o = img.shape[1]
img = cv2.resize(img, (int(img.shape[1]/3.), int(img.shape[0]/3.)), interpolation=cv2.INTER_NEAREST)
##cnts = list( (np.array(cnts)/2).astype(np.int16) )
#cnts = cnts/2
cnts = [(i/ 3).astype(np.int32) for i in cnts]
cnts_org = []
#print(cnts,'cnts')
for i in range(len(cnts)):
img_copy = np.zeros(img.shape)
img_copy = cv2.fillPoly(img_copy, pts=[cnts[i]], color=(1, 1, 1))
# plt.imshow(img_copy)
# plt.show()
# print(img.shape,'img')
img_copy = rotation_image_new(img_copy, -slope_first)
##print(img_copy.shape,'img_copy')
# plt.imshow(img_copy)
# plt.show()
img_copy = img_copy.astype(np.uint8)
imgray = cv2.cvtColor(img_copy, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 0, 255, 0)
cont_int, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cont_int[0][:, 0, 0] = cont_int[0][:, 0, 0] + np.abs(img_copy.shape[1] - img.shape[1])
cont_int[0][:, 0, 1] = cont_int[0][:, 0, 1] + np.abs(img_copy.shape[0] - img.shape[0])
# print(np.shape(cont_int[0]))
cnts_org.append(cont_int[0]*3)
# sys.exit()
# self.y_shift = np.abs(img_copy.shape[0] - img.shape[0])
# self.x_shift = np.abs(img_copy.shape[1] - img.shape[1])
return cnts_org return cnts_org
def return_contours_of_interested_textline(region_pre_p, pixel): def return_contours_of_interested_textline(region_pre_p, pixel):

@ -7,13 +7,13 @@ class EynollahIdCounter():
def __init__(self, region_idx=0, line_idx=0): def __init__(self, region_idx=0, line_idx=0):
self._counter = Counter() self._counter = Counter()
self._inital_region_idx = region_idx self._initial_region_idx = region_idx
self._inital_line_idx = line_idx self._initial_line_idx = line_idx
self.reset() self.reset()
def reset(self): def reset(self):
self.set('region', self._inital_region_idx) self.set('region', self._initial_region_idx)
self.set('line', self._inital_line_idx) self.set('line', self._initial_line_idx)
def inc(self, name, val=1): def inc(self, name, val=1):
self._counter.update({name: val}) self._counter.update({name: val})

@ -13,13 +13,13 @@ def adhere_drop_capital_region_into_corresponding_textline(
contours_only_text_parent_h, contours_only_text_parent_h,
all_box_coord, all_box_coord,
all_box_coord_h, all_box_coord_h,
all_found_texline_polygons, all_found_textline_polygons,
all_found_texline_polygons_h, all_found_textline_polygons_h,
kernel=None, kernel=None,
curved_line=False, curved_line=False,
): ):
# print(np.shape(all_found_texline_polygons),np.shape(all_found_texline_polygons[3]),'all_found_texline_polygonsshape') # print(np.shape(all_found_textline_polygons),np.shape(all_found_textline_polygons[3]),'all_found_textline_polygonsshape')
# print(all_found_texline_polygons[3]) # print(all_found_textline_polygons[3])
cx_m, cy_m, _, _, _, _, _ = find_new_features_of_contours(contours_only_text_parent) cx_m, cy_m, _, _, _, _, _ = find_new_features_of_contours(contours_only_text_parent)
cx_h, cy_h, _, _, _, _, _ = find_new_features_of_contours(contours_only_text_parent_h) cx_h, cy_h, _, _, _, _, _ = find_new_features_of_contours(contours_only_text_parent_h)
cx_d, cy_d, _, _, y_min_d, y_max_d, _ = find_new_features_of_contours(polygons_of_drop_capitals) cx_d, cy_d, _, _, y_min_d, y_max_d, _ = find_new_features_of_contours(polygons_of_drop_capitals)
@ -87,9 +87,9 @@ def adhere_drop_capital_region_into_corresponding_textline(
region_final = region_with_intersected_drop[np.argmax(sum_pixels_of_intersection)] - 1 region_final = region_with_intersected_drop[np.argmax(sum_pixels_of_intersection)] - 1
# print(region_final,'region_final') # print(region_final,'region_final')
# cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_texline_polygons[int(region_final)]) # cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_textline_polygons[int(region_final)])
try: try:
cx_t, cy_t, _, _, _, _, _ = find_new_features_of_contours(all_found_texline_polygons[int(region_final)]) cx_t, cy_t, _, _, _, _, _ = find_new_features_of_contours(all_found_textline_polygons[int(region_final)])
# print(all_box_coord[j_cont]) # print(all_box_coord[j_cont])
# print(cx_t) # print(cx_t)
# print(cy_t) # print(cy_t)
@ -105,9 +105,9 @@ def adhere_drop_capital_region_into_corresponding_textline(
arg_min = np.argmin(np.abs(y_lines - y_min_d[i_drop])) arg_min = np.argmin(np.abs(y_lines - y_min_d[i_drop]))
# print(arg_min) # print(arg_min)
cnt_nearest = np.copy(all_found_texline_polygons[int(region_final)][arg_min]) cnt_nearest = np.copy(all_found_textline_polygons[int(region_final)][arg_min])
cnt_nearest[:, 0, 0] = all_found_texline_polygons[int(region_final)][arg_min][:, 0, 0] # +all_box_coord[int(region_final)][2] cnt_nearest[:, 0, 0] = all_found_textline_polygons[int(region_final)][arg_min][:, 0, 0] # +all_box_coord[int(region_final)][2]
cnt_nearest[:, 0, 1] = all_found_texline_polygons[int(region_final)][arg_min][:, 0, 1] # +all_box_coord[int(region_final)][0] cnt_nearest[:, 0, 1] = all_found_textline_polygons[int(region_final)][arg_min][:, 0, 1] # +all_box_coord[int(region_final)][0]
img_textlines = np.zeros((text_regions_p.shape[0], text_regions_p.shape[1], 3)) img_textlines = np.zeros((text_regions_p.shape[0], text_regions_p.shape[1], 3))
img_textlines = cv2.fillPoly(img_textlines, pts=[cnt_nearest], color=(255, 255, 255)) img_textlines = cv2.fillPoly(img_textlines, pts=[cnt_nearest], color=(255, 255, 255))
@ -131,7 +131,7 @@ def adhere_drop_capital_region_into_corresponding_textline(
# contours_biggest=contours_biggest.reshape(np.shape(contours_biggest)[0],np.shape(contours_biggest)[2]) # contours_biggest=contours_biggest.reshape(np.shape(contours_biggest)[0],np.shape(contours_biggest)[2])
all_found_texline_polygons[int(region_final)][arg_min] = contours_biggest all_found_textline_polygons[int(region_final)][arg_min] = contours_biggest
except: except:
# print('gordun1') # print('gordun1')
@ -139,11 +139,11 @@ def adhere_drop_capital_region_into_corresponding_textline(
elif len(region_with_intersected_drop) == 1: elif len(region_with_intersected_drop) == 1:
region_final = region_with_intersected_drop[0] - 1 region_final = region_with_intersected_drop[0] - 1
# areas_main=np.array([cv2.contourArea(all_found_texline_polygons[int(region_final)][0][j] ) for j in range(len(all_found_texline_polygons[int(region_final)]))]) # areas_main=np.array([cv2.contourArea(all_found_textline_polygons[int(region_final)][0][j] ) for j in range(len(all_found_textline_polygons[int(region_final)]))])
# cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_texline_polygons[int(region_final)]) # cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_textline_polygons[int(region_final)])
try:
cx_t, cy_t, _, _, _, _, _ = find_new_features_of_contours(all_found_texline_polygons[int(region_final)]) cx_t, cy_t, _, _, _, _, _ = find_new_features_of_contours(all_found_textline_polygons[int(region_final)])
# print(all_box_coord[j_cont]) # print(all_box_coord[j_cont])
# print(cx_t) # print(cx_t)
# print(cy_t) # print(cy_t)
@ -157,9 +157,9 @@ def adhere_drop_capital_region_into_corresponding_textline(
arg_min = np.argmin(np.abs(y_lines - y_min_d[i_drop])) arg_min = np.argmin(np.abs(y_lines - y_min_d[i_drop]))
# print(arg_min) # print(arg_min)
cnt_nearest = np.copy(all_found_texline_polygons[int(region_final)][arg_min]) cnt_nearest = np.copy(all_found_textline_polygons[int(region_final)][arg_min])
cnt_nearest[:, 0, 0] = all_found_texline_polygons[int(region_final)][arg_min][:, 0, 0] # +all_box_coord[int(region_final)][2] cnt_nearest[:, 0, 0] = all_found_textline_polygons[int(region_final)][arg_min][:, 0, 0] # +all_box_coord[int(region_final)][2]
cnt_nearest[:, 0, 1] = all_found_texline_polygons[int(region_final)][arg_min][:, 0, 1] # +all_box_coord[int(region_final)][0] cnt_nearest[:, 0, 1] = all_found_textline_polygons[int(region_final)][arg_min][:, 0, 1] # +all_box_coord[int(region_final)][0]
img_textlines = np.zeros((text_regions_p.shape[0], text_regions_p.shape[1], 3)) img_textlines = np.zeros((text_regions_p.shape[0], text_regions_p.shape[1], 3))
img_textlines = cv2.fillPoly(img_textlines, pts=[cnt_nearest], color=(255, 255, 255)) img_textlines = cv2.fillPoly(img_textlines, pts=[cnt_nearest], color=(255, 255, 255))
@ -184,14 +184,15 @@ def adhere_drop_capital_region_into_corresponding_textline(
# contours_biggest[:,0,0]=contours_biggest[:,0,0]#-all_box_coord[int(region_final)][2] # contours_biggest[:,0,0]=contours_biggest[:,0,0]#-all_box_coord[int(region_final)][2]
# contours_biggest[:,0,1]=contours_biggest[:,0,1]#-all_box_coord[int(region_final)][0] # contours_biggest[:,0,1]=contours_biggest[:,0,1]#-all_box_coord[int(region_final)][0]
# print(np.shape(contours_biggest),'contours_biggest') # print(np.shape(contours_biggest),'contours_biggest')
# print(np.shape(all_found_texline_polygons[int(region_final)][arg_min])) # print(np.shape(all_found_textline_polygons[int(region_final)][arg_min]))
##contours_biggest=contours_biggest.reshape(np.shape(contours_biggest)[0],np.shape(contours_biggest)[2]) ##contours_biggest=contours_biggest.reshape(np.shape(contours_biggest)[0],np.shape(contours_biggest)[2])
all_found_texline_polygons[int(region_final)][arg_min] = contours_biggest all_found_textline_polygons[int(region_final)][arg_min] = contours_biggest
except:
pass
# print(cx_t,'print')
try: try:
# print(all_found_texline_polygons[j_cont][0]) # print(all_found_textline_polygons[j_cont][0])
cx_t, cy_t, _, _, _, _, _ = find_new_features_of_contours(all_found_texline_polygons[int(region_final)]) cx_t, cy_t, _, _, _, _, _ = find_new_features_of_contours(all_found_textline_polygons[int(region_final)])
# print(all_box_coord[j_cont]) # print(all_box_coord[j_cont])
# print(cx_t) # print(cx_t)
# print(cy_t) # print(cy_t)
@ -205,9 +206,9 @@ def adhere_drop_capital_region_into_corresponding_textline(
arg_min = np.argmin(np.abs(y_lines - y_min_d[i_drop])) arg_min = np.argmin(np.abs(y_lines - y_min_d[i_drop]))
# print(arg_min) # print(arg_min)
cnt_nearest = np.copy(all_found_texline_polygons[int(region_final)][arg_min]) cnt_nearest = np.copy(all_found_textline_polygons[int(region_final)][arg_min])
cnt_nearest[:, 0, 0] = all_found_texline_polygons[int(region_final)][arg_min][:, 0, 0] # +all_box_coord[int(region_final)][2] cnt_nearest[:, 0, 0] = all_found_textline_polygons[int(region_final)][arg_min][:, 0, 0] # +all_box_coord[int(region_final)][2]
cnt_nearest[:, 0, 1] = all_found_texline_polygons[int(region_final)][arg_min][:, 0, 1] # +all_box_coord[int(region_final)][0] cnt_nearest[:, 0, 1] = all_found_textline_polygons[int(region_final)][arg_min][:, 0, 1] # +all_box_coord[int(region_final)][0]
img_textlines = np.zeros((text_regions_p.shape[0], text_regions_p.shape[1], 3)) img_textlines = np.zeros((text_regions_p.shape[0], text_regions_p.shape[1], 3))
img_textlines = cv2.fillPoly(img_textlines, pts=[cnt_nearest], color=(255, 255, 255)) img_textlines = cv2.fillPoly(img_textlines, pts=[cnt_nearest], color=(255, 255, 255))
@ -230,15 +231,15 @@ def adhere_drop_capital_region_into_corresponding_textline(
contours_biggest[:, 0, 1] = contours_biggest[:, 0, 1] # -all_box_coord[int(region_final)][0] contours_biggest[:, 0, 1] = contours_biggest[:, 0, 1] # -all_box_coord[int(region_final)][0]
##contours_biggest=contours_biggest.reshape(np.shape(contours_biggest)[0],np.shape(contours_biggest)[2]) ##contours_biggest=contours_biggest.reshape(np.shape(contours_biggest)[0],np.shape(contours_biggest)[2])
all_found_texline_polygons[int(region_final)][arg_min] = contours_biggest all_found_textline_polygons[int(region_final)][arg_min] = contours_biggest
# all_found_texline_polygons[int(region_final)][arg_min]=contours_biggest # all_found_textline_polygons[int(region_final)][arg_min]=contours_biggest
except: except:
pass pass
else: else:
pass pass
##cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_texline_polygons[int(region_final)]) ##cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_textline_polygons[int(region_final)])
###print(all_box_coord[j_cont]) ###print(all_box_coord[j_cont])
###print(cx_t) ###print(cx_t)
###print(cy_t) ###print(cy_t)
@ -252,9 +253,9 @@ def adhere_drop_capital_region_into_corresponding_textline(
##arg_min=np.argmin(np.abs(y_lines-y_min_d[i_drop]) ) ##arg_min=np.argmin(np.abs(y_lines-y_min_d[i_drop]) )
###print(arg_min) ###print(arg_min)
##cnt_nearest=np.copy(all_found_texline_polygons[int(region_final)][arg_min]) ##cnt_nearest=np.copy(all_found_textline_polygons[int(region_final)][arg_min])
##cnt_nearest[:,0,0]=all_found_texline_polygons[int(region_final)][arg_min][:,0,0]#+all_box_coord[int(region_final)][2] ##cnt_nearest[:,0,0]=all_found_textline_polygons[int(region_final)][arg_min][:,0,0]#+all_box_coord[int(region_final)][2]
##cnt_nearest[:,0,1]=all_found_texline_polygons[int(region_final)][arg_min][:,0,1]#+all_box_coord[int(region_final)][0] ##cnt_nearest[:,0,1]=all_found_textline_polygons[int(region_final)][arg_min][:,0,1]#+all_box_coord[int(region_final)][0]
##img_textlines=np.zeros((text_regions_p.shape[0],text_regions_p.shape[1],3)) ##img_textlines=np.zeros((text_regions_p.shape[0],text_regions_p.shape[1],3))
##img_textlines=cv2.fillPoly(img_textlines,pts=[cnt_nearest],color=(255,255,255)) ##img_textlines=cv2.fillPoly(img_textlines,pts=[cnt_nearest],color=(255,255,255))
@ -280,7 +281,7 @@ def adhere_drop_capital_region_into_corresponding_textline(
##contours_biggest[:,0,1]=contours_biggest[:,0,1]#-all_box_coord[int(region_final)][0] ##contours_biggest[:,0,1]=contours_biggest[:,0,1]#-all_box_coord[int(region_final)][0]
##contours_biggest=contours_biggest.reshape(np.shape(contours_biggest)[0],np.shape(contours_biggest)[2]) ##contours_biggest=contours_biggest.reshape(np.shape(contours_biggest)[0],np.shape(contours_biggest)[2])
##all_found_texline_polygons[int(region_final)][arg_min]=contours_biggest ##all_found_textline_polygons[int(region_final)][arg_min]=contours_biggest
else: else:
if len(region_with_intersected_drop) > 1: if len(region_with_intersected_drop) > 1:
@ -292,9 +293,9 @@ def adhere_drop_capital_region_into_corresponding_textline(
region_final = region_with_intersected_drop[np.argmax(sum_pixels_of_intersection)] - 1 region_final = region_with_intersected_drop[np.argmax(sum_pixels_of_intersection)] - 1
# print(region_final,'region_final') # print(region_final,'region_final')
# cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_texline_polygons[int(region_final)]) # cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_textline_polygons[int(region_final)])
try: try:
cx_t, cy_t, _, _, _, _, _ = find_new_features_of_contours(all_found_texline_polygons[int(region_final)]) cx_t, cy_t, _, _, _, _, _ = find_new_features_of_contours(all_found_textline_polygons[int(region_final)])
# print(all_box_coord[j_cont]) # print(all_box_coord[j_cont])
# print(cx_t) # print(cx_t)
# print(cy_t) # print(cy_t)
@ -310,9 +311,9 @@ def adhere_drop_capital_region_into_corresponding_textline(
arg_min = np.argmin(np.abs(y_lines - y_min_d[i_drop])) arg_min = np.argmin(np.abs(y_lines - y_min_d[i_drop]))
# print(arg_min) # print(arg_min)
cnt_nearest = np.copy(all_found_texline_polygons[int(region_final)][arg_min]) cnt_nearest = np.copy(all_found_textline_polygons[int(region_final)][arg_min])
cnt_nearest[:, 0] = all_found_texline_polygons[int(region_final)][arg_min][:, 0] + all_box_coord[int(region_final)][2] cnt_nearest[:, 0] = all_found_textline_polygons[int(region_final)][arg_min][:, 0] + all_box_coord[int(region_final)][2]
cnt_nearest[:, 1] = all_found_texline_polygons[int(region_final)][arg_min][:, 1] + all_box_coord[int(region_final)][0] cnt_nearest[:, 1] = all_found_textline_polygons[int(region_final)][arg_min][:, 1] + all_box_coord[int(region_final)][0]
img_textlines = np.zeros((text_regions_p.shape[0], text_regions_p.shape[1], 3)) img_textlines = np.zeros((text_regions_p.shape[0], text_regions_p.shape[1], 3))
img_textlines = cv2.fillPoly(img_textlines, pts=[cnt_nearest], color=(255, 255, 255)) img_textlines = cv2.fillPoly(img_textlines, pts=[cnt_nearest], color=(255, 255, 255))
@ -336,7 +337,7 @@ def adhere_drop_capital_region_into_corresponding_textline(
contours_biggest = contours_biggest.reshape(np.shape(contours_biggest)[0], np.shape(contours_biggest)[2]) contours_biggest = contours_biggest.reshape(np.shape(contours_biggest)[0], np.shape(contours_biggest)[2])
all_found_texline_polygons[int(region_final)][arg_min] = contours_biggest all_found_textline_polygons[int(region_final)][arg_min] = contours_biggest
except: except:
# print('gordun1') # print('gordun1')
@ -344,14 +345,14 @@ def adhere_drop_capital_region_into_corresponding_textline(
elif len(region_with_intersected_drop) == 1: elif len(region_with_intersected_drop) == 1:
region_final = region_with_intersected_drop[0] - 1 region_final = region_with_intersected_drop[0] - 1
# areas_main=np.array([cv2.contourArea(all_found_texline_polygons[int(region_final)][0][j] ) for j in range(len(all_found_texline_polygons[int(region_final)]))]) # areas_main=np.array([cv2.contourArea(all_found_textline_polygons[int(region_final)][0][j] ) for j in range(len(all_found_textline_polygons[int(region_final)]))])
# cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_texline_polygons[int(region_final)]) # cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_textline_polygons[int(region_final)])
# print(cx_t,'print') # print(cx_t,'print')
try: try:
# print(all_found_texline_polygons[j_cont][0]) # print(all_found_textline_polygons[j_cont][0])
cx_t, cy_t, _, _, _, _, _ = find_new_features_of_contours(all_found_texline_polygons[int(region_final)]) cx_t, cy_t, _, _, _, _, _ = find_new_features_of_contours(all_found_textline_polygons[int(region_final)])
# print(all_box_coord[j_cont]) # print(all_box_coord[j_cont])
# print(cx_t) # print(cx_t)
# print(cy_t) # print(cy_t)
@ -365,9 +366,9 @@ def adhere_drop_capital_region_into_corresponding_textline(
arg_min = np.argmin(np.abs(y_lines - y_min_d[i_drop])) arg_min = np.argmin(np.abs(y_lines - y_min_d[i_drop]))
# print(arg_min) # print(arg_min)
cnt_nearest = np.copy(all_found_texline_polygons[int(region_final)][arg_min]) cnt_nearest = np.copy(all_found_textline_polygons[int(region_final)][arg_min])
cnt_nearest[:, 0] = all_found_texline_polygons[int(region_final)][arg_min][:, 0] + all_box_coord[int(region_final)][2] cnt_nearest[:, 0] = all_found_textline_polygons[int(region_final)][arg_min][:, 0] + all_box_coord[int(region_final)][2]
cnt_nearest[:, 1] = all_found_texline_polygons[int(region_final)][arg_min][:, 1] + all_box_coord[int(region_final)][0] cnt_nearest[:, 1] = all_found_textline_polygons[int(region_final)][arg_min][:, 1] + all_box_coord[int(region_final)][0]
img_textlines = np.zeros((text_regions_p.shape[0], text_regions_p.shape[1], 3)) img_textlines = np.zeros((text_regions_p.shape[0], text_regions_p.shape[1], 3))
img_textlines = cv2.fillPoly(img_textlines, pts=[cnt_nearest], color=(255, 255, 255)) img_textlines = cv2.fillPoly(img_textlines, pts=[cnt_nearest], color=(255, 255, 255))
@ -390,8 +391,8 @@ def adhere_drop_capital_region_into_corresponding_textline(
contours_biggest[:, 0, 1] = contours_biggest[:, 0, 1] - all_box_coord[int(region_final)][0] contours_biggest[:, 0, 1] = contours_biggest[:, 0, 1] - all_box_coord[int(region_final)][0]
contours_biggest = contours_biggest.reshape(np.shape(contours_biggest)[0], np.shape(contours_biggest)[2]) contours_biggest = contours_biggest.reshape(np.shape(contours_biggest)[0], np.shape(contours_biggest)[2])
all_found_texline_polygons[int(region_final)][arg_min] = contours_biggest all_found_textline_polygons[int(region_final)][arg_min] = contours_biggest
# all_found_texline_polygons[int(region_final)][arg_min]=contours_biggest # all_found_textline_polygons[int(region_final)][arg_min]=contours_biggest
except: except:
pass pass
@ -416,8 +417,8 @@ def adhere_drop_capital_region_into_corresponding_textline(
######plt.show() ######plt.show()
#####try: #####try:
#####if len(contours_new_parent)==1: #####if len(contours_new_parent)==1:
######print(all_found_texline_polygons[j_cont][0]) ######print(all_found_textline_polygons[j_cont][0])
#####cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_texline_polygons[j_cont]) #####cx_t,cy_t ,_, _, _ ,_,_= find_new_features_of_contours(all_found_textline_polygons[j_cont])
######print(all_box_coord[j_cont]) ######print(all_box_coord[j_cont])
######print(cx_t) ######print(cx_t)
######print(cy_t) ######print(cy_t)
@ -430,9 +431,9 @@ def adhere_drop_capital_region_into_corresponding_textline(
#####arg_min=np.argmin(np.abs(y_lines-y_min_d[i_drop]) ) #####arg_min=np.argmin(np.abs(y_lines-y_min_d[i_drop]) )
######print(arg_min) ######print(arg_min)
#####cnt_nearest=np.copy(all_found_texline_polygons[j_cont][arg_min]) #####cnt_nearest=np.copy(all_found_textline_polygons[j_cont][arg_min])
#####cnt_nearest[:,0]=all_found_texline_polygons[j_cont][arg_min][:,0]+all_box_coord[j_cont][2] #####cnt_nearest[:,0]=all_found_textline_polygons[j_cont][arg_min][:,0]+all_box_coord[j_cont][2]
#####cnt_nearest[:,1]=all_found_texline_polygons[j_cont][arg_min][:,1]+all_box_coord[j_cont][0] #####cnt_nearest[:,1]=all_found_textline_polygons[j_cont][arg_min][:,1]+all_box_coord[j_cont][0]
#####img_textlines=np.zeros((text_regions_p.shape[0],text_regions_p.shape[1],3)) #####img_textlines=np.zeros((text_regions_p.shape[0],text_regions_p.shape[1],3))
#####img_textlines=cv2.fillPoly(img_textlines,pts=[cnt_nearest],color=(255,255,255)) #####img_textlines=cv2.fillPoly(img_textlines,pts=[cnt_nearest],color=(255,255,255))
@ -453,7 +454,7 @@ def adhere_drop_capital_region_into_corresponding_textline(
#####contours_biggest[:,0,0]=contours_biggest[:,0,0]-all_box_coord[j_cont][2] #####contours_biggest[:,0,0]=contours_biggest[:,0,0]-all_box_coord[j_cont][2]
#####contours_biggest[:,0,1]=contours_biggest[:,0,1]-all_box_coord[j_cont][0] #####contours_biggest[:,0,1]=contours_biggest[:,0,1]-all_box_coord[j_cont][0]
#####all_found_texline_polygons[j_cont][arg_min]=contours_biggest #####all_found_textline_polygons[j_cont][arg_min]=contours_biggest
######print(contours_biggest) ######print(contours_biggest)
######plt.imshow(img_textlines[:,:,0]) ######plt.imshow(img_textlines[:,:,0])
######plt.show() ######plt.show()
@ -461,7 +462,7 @@ def adhere_drop_capital_region_into_corresponding_textline(
#####pass #####pass
#####except: #####except:
#####pass #####pass
return all_found_texline_polygons return all_found_textline_polygons
def filter_small_drop_capitals_from_no_patch_layout(layout_no_patch, layout1): def filter_small_drop_capitals_from_no_patch_layout(layout_no_patch, layout1):

@ -16,7 +16,7 @@ def pil2cv(img):
def check_dpi(img): def check_dpi(img):
try: try:
if isinstance(img, Image.__class__): if isinstance(img, Image.Image):
pil_image = img pil_image = img
elif isinstance(img, str): elif isinstance(img, str):
pil_image = Image.open(img) pil_image = Image.open(img)

@ -52,20 +52,21 @@ def rotate_image_different( img, slope):
img_rotation = cv2.warpAffine(img, rotation_matrix, (num_cols, num_rows)) img_rotation = cv2.warpAffine(img, rotation_matrix, (num_cols, num_rows))
return img_rotation return img_rotation
def rotate_max_area(image, rotated, rotated_textline, rotated_layout, angle): def rotate_max_area(image, rotated, rotated_textline, rotated_layout, rotated_table_prediction, angle):
wr, hr = rotatedRectWithMaxArea(image.shape[1], image.shape[0], math.radians(angle)) wr, hr = rotatedRectWithMaxArea(image.shape[1], image.shape[0], math.radians(angle))
h, w, _ = rotated.shape h, w, _ = rotated.shape
y1 = h // 2 - int(hr / 2) y1 = h // 2 - int(hr / 2)
y2 = y1 + int(hr) y2 = y1 + int(hr)
x1 = w // 2 - int(wr / 2) x1 = w // 2 - int(wr / 2)
x2 = x1 + int(wr) x2 = x1 + int(wr)
return rotated[y1:y2, x1:x2], rotated_textline[y1:y2, x1:x2], rotated_layout[y1:y2, x1:x2] return rotated[y1:y2, x1:x2], rotated_textline[y1:y2, x1:x2], rotated_layout[y1:y2, x1:x2], rotated_table_prediction[y1:y2, x1:x2]
def rotation_not_90_func(img, textline, text_regions_p_1, thetha): def rotation_not_90_func(img, textline, text_regions_p_1, table_prediction, thetha):
rotated = imutils.rotate(img, thetha) rotated = imutils.rotate(img, thetha)
rotated_textline = imutils.rotate(textline, thetha) rotated_textline = imutils.rotate(textline, thetha)
rotated_layout = imutils.rotate(text_regions_p_1, thetha) rotated_layout = imutils.rotate(text_regions_p_1, thetha)
return rotate_max_area(img, rotated, rotated_textline, rotated_layout, thetha) rotated_table_prediction = imutils.rotate(table_prediction, thetha)
return rotate_max_area(img, rotated, rotated_textline, rotated_layout, rotated_table_prediction, thetha)
def rotation_not_90_func_full_layout(img, textline, text_regions_p_1, text_regions_p_fully, thetha): def rotation_not_90_func_full_layout(img, textline, text_regions_p_1, text_regions_p_fully, thetha):
rotated = imutils.rotate(img, thetha) rotated = imutils.rotate(img, thetha)

@ -21,7 +21,6 @@ from ocrd_models.ocrd_page import (
RegionRefType, RegionRefType,
SeparatorRegionType, SeparatorRegionType,
TableRegionType, TableRegionType,
TextEquivType,
TextLineType, TextLineType,
TextRegionType, TextRegionType,
UnorderedGroupIndexedType, UnorderedGroupIndexedType,

@ -10,7 +10,6 @@ from ocrd_utils import getLogger
from ocrd_models.ocrd_page import ( from ocrd_models.ocrd_page import (
BorderType, BorderType,
CoordsType, CoordsType,
TextEquivType,
PcGtsType, PcGtsType,
TextLineType, TextLineType,
TextRegionType, TextRegionType,
@ -23,12 +22,13 @@ import numpy as np
class EynollahXmlWriter(): class EynollahXmlWriter():
def __init__(self, *, dir_out, image_filename, curved_line, pcgts=None): def __init__(self, *, dir_out, image_filename, curved_line,textline_light, pcgts=None):
self.logger = getLogger('eynollah.writer') self.logger = getLogger('eynollah.writer')
self.counter = EynollahIdCounter() self.counter = EynollahIdCounter()
self.dir_out = dir_out self.dir_out = dir_out
self.image_filename = image_filename self.image_filename = image_filename
self.curved_line = curved_line self.curved_line = curved_line
self.textline_light = textline_light
self.pcgts = pcgts self.pcgts = pcgts
self.scale_x = None # XXX set outside __init__ self.scale_x = None # XXX set outside __init__
self.scale_y = None # XXX set outside __init__ self.scale_y = None # XXX set outside __init__
@ -54,56 +54,55 @@ class EynollahXmlWriter():
points_page_print = points_page_print + ' ' points_page_print = points_page_print + ' '
return points_page_print[:-1] return points_page_print[:-1]
def serialize_lines_in_marginal(self, marginal_region, all_found_texline_polygons_marginals, marginal_idx, page_coord, all_box_coord_marginals, slopes_marginals, counter): def serialize_lines_in_marginal(self, marginal_region, all_found_textline_polygons_marginals, marginal_idx, page_coord, all_box_coord_marginals, slopes_marginals, counter):
for j in range(len(all_found_texline_polygons_marginals[marginal_idx])): for j in range(len(all_found_textline_polygons_marginals[marginal_idx])):
coords = CoordsType() coords = CoordsType()
textline = TextLineType(id=counter.next_line_id, Coords=coords) textline = TextLineType(id=counter.next_line_id, Coords=coords)
marginal_region.add_TextLine(textline) marginal_region.add_TextLine(textline)
textline.add_TextEquiv(TextEquivType(Unicode=''))
points_co = '' points_co = ''
for l in range(len(all_found_texline_polygons_marginals[marginal_idx][j])): for l in range(len(all_found_textline_polygons_marginals[marginal_idx][j])):
if not self.curved_line: if not (self.curved_line or self.textline_light):
if len(all_found_texline_polygons_marginals[marginal_idx][j][l]) == 2: if len(all_found_textline_polygons_marginals[marginal_idx][j][l]) == 2:
textline_x_coord = max(0, int((all_found_texline_polygons_marginals[marginal_idx][j][l][0] + all_box_coord_marginals[marginal_idx][2] + page_coord[2]) / self.scale_x) ) textline_x_coord = max(0, int((all_found_textline_polygons_marginals[marginal_idx][j][l][0] + all_box_coord_marginals[marginal_idx][2] + page_coord[2]) / self.scale_x) )
textline_y_coord = max(0, int((all_found_texline_polygons_marginals[marginal_idx][j][l][1] + all_box_coord_marginals[marginal_idx][0] + page_coord[0]) / self.scale_y) ) textline_y_coord = max(0, int((all_found_textline_polygons_marginals[marginal_idx][j][l][1] + all_box_coord_marginals[marginal_idx][0] + page_coord[0]) / self.scale_y) )
else: else:
textline_x_coord = max(0, int((all_found_texline_polygons_marginals[marginal_idx][j][l][0][0] + all_box_coord_marginals[marginal_idx][2] + page_coord[2]) / self.scale_x) ) textline_x_coord = max(0, int((all_found_textline_polygons_marginals[marginal_idx][j][l][0][0] + all_box_coord_marginals[marginal_idx][2] + page_coord[2]) / self.scale_x) )
textline_y_coord = max(0, int((all_found_texline_polygons_marginals[marginal_idx][j][l][0][1] + all_box_coord_marginals[marginal_idx][0] + page_coord[0]) / self.scale_y) ) textline_y_coord = max(0, int((all_found_textline_polygons_marginals[marginal_idx][j][l][0][1] + all_box_coord_marginals[marginal_idx][0] + page_coord[0]) / self.scale_y) )
points_co += str(textline_x_coord) points_co += str(textline_x_coord)
points_co += ',' points_co += ','
points_co += str(textline_y_coord) points_co += str(textline_y_coord)
if self.curved_line and np.abs(slopes_marginals[marginal_idx]) <= 45: if (self.curved_line or self.textline_light) and np.abs(slopes_marginals[marginal_idx]) <= 45:
if len(all_found_texline_polygons_marginals[marginal_idx][j][l]) == 2: if len(all_found_textline_polygons_marginals[marginal_idx][j][l]) == 2:
points_co += str(int((all_found_texline_polygons_marginals[marginal_idx][j][l][0] + page_coord[2]) / self.scale_x)) points_co += str(int((all_found_textline_polygons_marginals[marginal_idx][j][l][0] + page_coord[2]) / self.scale_x))
points_co += ',' points_co += ','
points_co += str(int((all_found_texline_polygons_marginals[marginal_idx][j][l][1] + page_coord[0]) / self.scale_y)) points_co += str(int((all_found_textline_polygons_marginals[marginal_idx][j][l][1] + page_coord[0]) / self.scale_y))
else: else:
points_co += str(int((all_found_texline_polygons_marginals[marginal_idx][j][l][0][0] + page_coord[2]) / self.scale_x)) points_co += str(int((all_found_textline_polygons_marginals[marginal_idx][j][l][0][0] + page_coord[2]) / self.scale_x))
points_co += ',' points_co += ','
points_co += str(int((all_found_texline_polygons_marginals[marginal_idx][j][l][0][1] + page_coord[0]) / self.scale_y)) points_co += str(int((all_found_textline_polygons_marginals[marginal_idx][j][l][0][1] + page_coord[0]) / self.scale_y))
elif self.curved_line and np.abs(slopes_marginals[marginal_idx]) > 45: elif (self.curved_line or self.textline_light) and np.abs(slopes_marginals[marginal_idx]) > 45:
if len(all_found_texline_polygons_marginals[marginal_idx][j][l]) == 2: if len(all_found_textline_polygons_marginals[marginal_idx][j][l]) == 2:
points_co += str(int((all_found_texline_polygons_marginals[marginal_idx][j][l][0] + all_box_coord_marginals[marginal_idx][2] + page_coord[2]) / self.scale_x)) points_co += str(int((all_found_textline_polygons_marginals[marginal_idx][j][l][0] + all_box_coord_marginals[marginal_idx][2] + page_coord[2]) / self.scale_x))
points_co += ',' points_co += ','
points_co += str(int((all_found_texline_polygons_marginals[marginal_idx][j][l][1] + all_box_coord_marginals[marginal_idx][0] + page_coord[0]) / self.scale_y)) points_co += str(int((all_found_textline_polygons_marginals[marginal_idx][j][l][1] + all_box_coord_marginals[marginal_idx][0] + page_coord[0]) / self.scale_y))
else: else:
points_co += str(int((all_found_texline_polygons_marginals[marginal_idx][j][l][0][0] + all_box_coord_marginals[marginal_idx][2] + page_coord[2]) / self.scale_x)) points_co += str(int((all_found_textline_polygons_marginals[marginal_idx][j][l][0][0] + all_box_coord_marginals[marginal_idx][2] + page_coord[2]) / self.scale_x))
points_co += ',' points_co += ','
points_co += str(int((all_found_texline_polygons_marginals[marginal_idx][j][l][0][1] + all_box_coord_marginals[marginal_idx][0] + page_coord[0]) / self.scale_y)) points_co += str(int((all_found_textline_polygons_marginals[marginal_idx][j][l][0][1] + all_box_coord_marginals[marginal_idx][0] + page_coord[0]) / self.scale_y))
points_co += ' ' points_co += ' '
coords.set_points(points_co[:-1]) coords.set_points(points_co[:-1])
def serialize_lines_in_region(self, text_region, all_found_texline_polygons, region_idx, page_coord, all_box_coord, slopes, counter): def serialize_lines_in_region(self, text_region, all_found_textline_polygons, region_idx, page_coord, all_box_coord, slopes, counter):
self.logger.debug('enter serialize_lines_in_region') self.logger.debug('enter serialize_lines_in_region')
for j in range(len(all_found_texline_polygons[region_idx])): for j in range(len(all_found_textline_polygons[region_idx])):
coords = CoordsType() coords = CoordsType()
textline = TextLineType(id=counter.next_line_id, Coords=coords, TextEquiv=[TextEquivType(index=0, Unicode='')]) textline = TextLineType(id=counter.next_line_id, Coords=coords)
text_region.add_TextLine(textline) text_region.add_TextLine(textline)
region_bboxes = all_box_coord[region_idx] region_bboxes = all_box_coord[region_idx]
points_co = '' points_co = ''
for idx_contour_textline, contour_textline in enumerate(all_found_texline_polygons[region_idx][j]): for idx_contour_textline, contour_textline in enumerate(all_found_textline_polygons[region_idx][j]):
if not self.curved_line: if not (self.curved_line or self.textline_light):
if len(contour_textline) == 2: if len(contour_textline) == 2:
textline_x_coord = max(0, int((contour_textline[0] + region_bboxes[2] + page_coord[2]) / self.scale_x)) textline_x_coord = max(0, int((contour_textline[0] + region_bboxes[2] + page_coord[2]) / self.scale_x))
textline_y_coord = max(0, int((contour_textline[1] + region_bboxes[0] + page_coord[0]) / self.scale_y)) textline_y_coord = max(0, int((contour_textline[1] + region_bboxes[0] + page_coord[0]) / self.scale_y))
@ -114,7 +113,7 @@ class EynollahXmlWriter():
points_co += ',' points_co += ','
points_co += str(textline_y_coord) points_co += str(textline_y_coord)
if self.curved_line and np.abs(slopes[region_idx]) <= 45: if (self.curved_line or self.textline_light) and np.abs(slopes[region_idx]) <= 45:
if len(contour_textline) == 2: if len(contour_textline) == 2:
points_co += str(int((contour_textline[0] + page_coord[2]) / self.scale_x)) points_co += str(int((contour_textline[0] + page_coord[2]) / self.scale_x))
points_co += ',' points_co += ','
@ -123,7 +122,7 @@ class EynollahXmlWriter():
points_co += str(int((contour_textline[0][0] + page_coord[2]) / self.scale_x)) points_co += str(int((contour_textline[0][0] + page_coord[2]) / self.scale_x))
points_co += ',' points_co += ','
points_co += str(int((contour_textline[0][1] + page_coord[0])/self.scale_y)) points_co += str(int((contour_textline[0][1] + page_coord[0])/self.scale_y))
elif self.curved_line and np.abs(slopes[region_idx]) > 45: elif (self.curved_line or self.textline_light) and np.abs(slopes[region_idx]) > 45:
if len(contour_textline)==2: if len(contour_textline)==2:
points_co += str(int((contour_textline[0] + region_bboxes[2] + page_coord[2])/self.scale_x)) points_co += str(int((contour_textline[0] + region_bboxes[2] + page_coord[2])/self.scale_x))
points_co += ',' points_co += ','
@ -141,7 +140,7 @@ class EynollahXmlWriter():
with open(out_fname, 'w') as f: with open(out_fname, 'w') as f:
f.write(to_xml(pcgts)) f.write(to_xml(pcgts))
def build_pagexml_no_full_layout(self, found_polygons_text_region, page_coord, order_of_texts, id_of_texts, all_found_texline_polygons, all_box_coord, found_polygons_text_region_img, found_polygons_marginals, all_found_texline_polygons_marginals, all_box_coord_marginals, slopes, slopes_marginals, cont_page, polygons_lines_to_be_written_in_xml): def build_pagexml_no_full_layout(self, found_polygons_text_region, page_coord, order_of_texts, id_of_texts, all_found_textline_polygons, all_box_coord, found_polygons_text_region_img, found_polygons_marginals, all_found_textline_polygons_marginals, all_box_coord_marginals, slopes, slopes_marginals, cont_page, polygons_lines_to_be_written_in_xml, found_polygons_tables):
self.logger.debug('enter build_pagexml_no_full_layout') self.logger.debug('enter build_pagexml_no_full_layout')
# create the file structure # create the file structure
@ -158,25 +157,33 @@ class EynollahXmlWriter():
for mm in range(len(found_polygons_text_region)): for mm in range(len(found_polygons_text_region)):
textregion = TextRegionType(id=counter.next_region_id, type_='paragraph', textregion = TextRegionType(id=counter.next_region_id, type_='paragraph',
Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_text_region[mm], page_coord)), Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_text_region[mm], page_coord)),
TextEquiv=[TextEquivType(index=0, Unicode='')]) )
page.add_TextRegion(textregion) page.add_TextRegion(textregion)
self.serialize_lines_in_region(textregion, all_found_texline_polygons, mm, page_coord, all_box_coord, slopes, counter) self.serialize_lines_in_region(textregion, all_found_textline_polygons, mm, page_coord, all_box_coord, slopes, counter)
for mm in range(len(found_polygons_marginals)): for mm in range(len(found_polygons_marginals)):
marginal = TextRegionType(id=counter.next_region_id, type_='marginalia', marginal = TextRegionType(id=counter.next_region_id, type_='marginalia',
Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_marginals[mm], page_coord))) Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_marginals[mm], page_coord)))
page.add_TextRegion(marginal) page.add_TextRegion(marginal)
self.serialize_lines_in_marginal(marginal, all_found_texline_polygons_marginals, mm, page_coord, all_box_coord_marginals, slopes_marginals, counter) self.serialize_lines_in_marginal(marginal, all_found_textline_polygons_marginals, mm, page_coord, all_box_coord_marginals, slopes_marginals, counter)
for mm in range(len(found_polygons_text_region_img)): for mm in range(len(found_polygons_text_region_img)):
img_region = ImageRegionType(id=counter.next_region_id, Coords=CoordsType()) img_region = ImageRegionType(id=counter.next_region_id, Coords=CoordsType())
page.add_ImageRegion(img_region) page.add_ImageRegion(img_region)
points_co = '' points_co = ''
for lmm in range(len(found_polygons_text_region_img[mm])): for lmm in range(len(found_polygons_text_region_img[mm])):
try:
points_co += str(int((found_polygons_text_region_img[mm][lmm,0,0] + page_coord[2]) / self.scale_x)) points_co += str(int((found_polygons_text_region_img[mm][lmm,0,0] + page_coord[2]) / self.scale_x))
points_co += ',' points_co += ','
points_co += str(int((found_polygons_text_region_img[mm][lmm,0,1] + page_coord[0]) / self.scale_y)) points_co += str(int((found_polygons_text_region_img[mm][lmm,0,1] + page_coord[0]) / self.scale_y))
points_co += ' ' points_co += ' '
except:
points_co += str(int((found_polygons_text_region_img[mm][lmm][0] + page_coord[2])/ self.scale_x ))
points_co += ','
points_co += str(int((found_polygons_text_region_img[mm][lmm][1] + page_coord[0])/ self.scale_y ))
points_co += ' '
img_region.get_Coords().set_points(points_co[:-1]) img_region.get_Coords().set_points(points_co[:-1])
for mm in range(len(polygons_lines_to_be_written_in_xml)): for mm in range(len(polygons_lines_to_be_written_in_xml)):
@ -189,10 +196,20 @@ class EynollahXmlWriter():
points_co += str(int((polygons_lines_to_be_written_in_xml[mm][lmm,0,1] ) / self.scale_y)) points_co += str(int((polygons_lines_to_be_written_in_xml[mm][lmm,0,1] ) / self.scale_y))
points_co += ' ' points_co += ' '
sep_hor.get_Coords().set_points(points_co[:-1]) sep_hor.get_Coords().set_points(points_co[:-1])
for mm in range(len(found_polygons_tables)):
tab_region = TableRegionType(id=counter.next_region_id, Coords=CoordsType())
page.add_TableRegion(tab_region)
points_co = ''
for lmm in range(len(found_polygons_tables[mm])):
points_co += str(int((found_polygons_tables[mm][lmm,0,0] + page_coord[2]) / self.scale_x))
points_co += ','
points_co += str(int((found_polygons_tables[mm][lmm,0,1] + page_coord[0]) / self.scale_y))
points_co += ' '
tab_region.get_Coords().set_points(points_co[:-1])
return pcgts return pcgts
def build_pagexml_full_layout(self, found_polygons_text_region, found_polygons_text_region_h, page_coord, order_of_texts, id_of_texts, all_found_texline_polygons, all_found_texline_polygons_h, all_box_coord, all_box_coord_h, found_polygons_text_region_img, found_polygons_tables, found_polygons_drop_capitals, found_polygons_marginals, all_found_texline_polygons_marginals, all_box_coord_marginals, slopes, slopes_h, slopes_marginals, cont_page, polygons_lines_to_be_written_in_xml): def build_pagexml_full_layout(self, found_polygons_text_region, found_polygons_text_region_h, page_coord, order_of_texts, id_of_texts, all_found_textline_polygons, all_found_textline_polygons_h, all_box_coord, all_box_coord_h, found_polygons_text_region_img, found_polygons_tables, found_polygons_drop_capitals, found_polygons_marginals, all_found_textline_polygons_marginals, all_box_coord_marginals, slopes, slopes_h, slopes_marginals, cont_page, polygons_lines_to_be_written_in_xml):
self.logger.debug('enter build_pagexml_full_layout') self.logger.debug('enter build_pagexml_full_layout')
# create the file structure # create the file structure
@ -207,29 +224,25 @@ class EynollahXmlWriter():
for mm in range(len(found_polygons_text_region)): for mm in range(len(found_polygons_text_region)):
textregion = TextRegionType(id=counter.next_region_id, type_='paragraph', textregion = TextRegionType(id=counter.next_region_id, type_='paragraph',
TextEquiv=[TextEquivType(index=0, Unicode='')],
Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_text_region[mm], page_coord))) Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_text_region[mm], page_coord)))
page.add_TextRegion(textregion) page.add_TextRegion(textregion)
self.serialize_lines_in_region(textregion, all_found_texline_polygons, mm, page_coord, all_box_coord, slopes, counter) self.serialize_lines_in_region(textregion, all_found_textline_polygons, mm, page_coord, all_box_coord, slopes, counter)
self.logger.debug('len(found_polygons_text_region_h) %s', len(found_polygons_text_region_h)) self.logger.debug('len(found_polygons_text_region_h) %s', len(found_polygons_text_region_h))
for mm in range(len(found_polygons_text_region_h)): for mm in range(len(found_polygons_text_region_h)):
textregion = TextRegionType(id=counter.next_region_id, type_='header', textregion = TextRegionType(id=counter.next_region_id, type_='header',
TextEquiv=[TextEquivType(index=0, Unicode='')],
Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_text_region_h[mm], page_coord))) Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_text_region_h[mm], page_coord)))
page.add_TextRegion(textregion) page.add_TextRegion(textregion)
self.serialize_lines_in_region(textregion, all_found_texline_polygons_h, mm, page_coord, all_box_coord_h, slopes_h, counter) self.serialize_lines_in_region(textregion, all_found_textline_polygons_h, mm, page_coord, all_box_coord_h, slopes_h, counter)
for mm in range(len(found_polygons_marginals)): for mm in range(len(found_polygons_marginals)):
marginal = TextRegionType(id=counter.next_region_id, type_='marginalia', marginal = TextRegionType(id=counter.next_region_id, type_='marginalia',
TextEquiv=[TextEquivType(index=0, Unicode='')],
Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_marginals[mm], page_coord))) Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_marginals[mm], page_coord)))
page.add_TextRegion(marginal) page.add_TextRegion(marginal)
self.serialize_lines_in_marginal(marginal, all_found_texline_polygons_marginals, mm, page_coord, all_box_coord_marginals, slopes_marginals, counter) self.serialize_lines_in_marginal(marginal, all_found_textline_polygons_marginals, mm, page_coord, all_box_coord_marginals, slopes_marginals, counter)
for mm in range(len(found_polygons_drop_capitals)): for mm in range(len(found_polygons_drop_capitals)):
page.add_TextRegion(TextRegionType(id=counter.next_region_id, type_='drop-capital', page.add_TextRegion(TextRegionType(id=counter.next_region_id, type_='drop-capital',
TextEquiv=[TextEquivType(index=0, Unicode='')],
Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_drop_capitals[mm], page_coord)))) Coords=CoordsType(points=self.calculate_polygon_coords(found_polygons_drop_capitals[mm], page_coord))))
for mm in range(len(found_polygons_text_region_img)): for mm in range(len(found_polygons_text_region_img)):

@ -1,5 +1,5 @@
from tests.base import main from tests.base import main
from qurator.eynollah.utils.counter import EynollahIdCounter from eynollah.utils.counter import EynollahIdCounter
def test_counter_string(): def test_counter_string():
c = EynollahIdCounter() c = EynollahIdCounter()

@ -1,6 +1,6 @@
import cv2 import cv2
from pathlib import Path from pathlib import Path
from qurator.eynollah.utils.pil_cv2 import check_dpi from eynollah.utils.pil_cv2 import check_dpi
from tests.base import main from tests.base import main
def test_dpi(): def test_dpi():

@ -2,7 +2,7 @@ from os import environ
from pathlib import Path from pathlib import Path
from ocrd_utils import pushd_popd from ocrd_utils import pushd_popd
from tests.base import CapturingTestCase as TestCase, main from tests.base import CapturingTestCase as TestCase, main
from qurator.eynollah.cli import main as eynollah_cli from eynollah.cli import main as eynollah_cli
testdir = Path(__file__).parent.resolve() testdir = Path(__file__).parent.resolve()

@ -1,7 +1,7 @@
def test_utils_import(): def test_utils_import():
import qurator.eynollah.utils import eynollah.utils
import qurator.eynollah.utils.contour import eynollah.utils.contour
import qurator.eynollah.utils.drop_capitals import eynollah.utils.drop_capitals
import qurator.eynollah.utils.drop_capitals import eynollah.utils.drop_capitals
import qurator.eynollah.utils.is_nan import eynollah.utils.is_nan
import qurator.eynollah.utils.rotate import eynollah.utils.rotate

@ -1,5 +1,5 @@
from pytest import main from pytest import main
from qurator.eynollah.utils.xml import create_page_xml from eynollah.utils.xml import create_page_xml
from ocrd_models.ocrd_page import to_xml from ocrd_models.ocrd_page import to_xml
PAGE_2019 = 'http://schema.primaresearch.org/PAGE/gts/pagecontent/2019-07-15' PAGE_2019 = 'http://schema.primaresearch.org/PAGE/gts/pagecontent/2019-07-15'

Loading…
Cancel
Save