From a1449da1d189887fff1683815b753216390452c8 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 19 May 2026 03:32:19 +0200 Subject: [PATCH 001/121] Revert "fix model loading in mb_ro and ocr" This reverts commit 218a95e6a0c8881ca1919b3f00d8b15475d03e1f. --- src/eynollah/eynollah_ocr.py | 12 ++++++------ src/eynollah/mb_ro_on_layout.py | 5 +++-- .../model_zoo/.nfs00000002feddea7d00000031 | Bin 20480 -> 0 bytes src/eynollah/model_zoo/model_zoo.py | 8 ++------ 4 files changed, 11 insertions(+), 14 deletions(-) delete mode 100644 src/eynollah/model_zoo/.nfs00000002feddea7d00000031 diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 1b49077..3c918e5 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -65,14 +65,14 @@ class Eynollah_ocr: self.b_s = 2 if batch_size is None and tr_ocr else 8 if batch_size is None else batch_size if tr_ocr: - self.model_zoo.load_models('trocr_processor') - self.model_zoo.load_models(['ocr', 'tr']) + self.model_zoo.load_model('trocr_processor') + self.model_zoo.load_model('ocr', 'tr') self.model_zoo.get('ocr').to(self.device) else: - self.model_zoo.load_models('ocr') - self.model_zoo.load_models('num_to_char') - self.model_zoo.load_models('characters') - self.end_character = len(self.model_zoo.get('characters')) + 2 + self.model_zoo.load_model('ocr', '') + self.model_zoo.load_model('num_to_char') + self.model_zoo.load_model('characters') + self.end_character = len(self.model_zoo.get('characters', list)) + 2 @property def device(self): diff --git a/src/eynollah/mb_ro_on_layout.py b/src/eynollah/mb_ro_on_layout.py index b0b5910..22fe97b 100644 --- a/src/eynollah/mb_ro_on_layout.py +++ b/src/eynollah/mb_ro_on_layout.py @@ -19,6 +19,7 @@ import statistics os.environ['TF_USE_LEGACY_KERAS'] = '1' # avoid Keras 3 after TF 2.15 import tensorflow as tf +from tensorflow.keras.models import Model from .model_zoo import EynollahModelZoo from .utils.resize import resize_image @@ -49,7 +50,7 @@ class machine_based_reading_order_on_layout: except: self.logger.warning("no GPU device available") - self.model_zoo.load_models('reading_order') + self.model_zoo.load_model('reading_order') def read_xml(self, xml_file): tree1 = ET.parse(xml_file, parser = ET.XMLParser(encoding='utf-8')) @@ -675,7 +676,7 @@ class machine_based_reading_order_on_layout: tot_counter += 1 batch.append(j) if tot_counter % inference_bs == 0 or tot_counter == len(ij_list): - y_pr = self.model_zoo.get('reading_order').predict(input_1 , verbose='0') + y_pr = self.model_zoo.get('reading_order', Model).predict(input_1 , verbose='0') for jb, j in enumerate(batch): if y_pr[jb][0]>=0.5: post_list.append(j) diff --git a/src/eynollah/model_zoo/.nfs00000002feddea7d00000031 b/src/eynollah/model_zoo/.nfs00000002feddea7d00000031 deleted file mode 100644 index c7dd87d9c2ff0b51a6c1e942f4d82b5b4ac0da25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 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 diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 9611388..fffd389 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -94,12 +94,8 @@ class EynollahModelZoo: elif model_category.endswith('_patched'): load_args[0] = model_category[:-8] load_kwargs["patched"] = True - spec = self.specs.get(model_category, load_args[1] if len(load_args) > 1 else '') - if spec.type in ['Keras'] and spec.category != 'ocr': - ret[model_category] = Predictor(self.logger, self) - ret[model_category].load_model(*load_args, **load_kwargs, device=device) - else: - ret[model_category] = self.load_model(*load_args, **load_kwargs, device=device) + ret[model_category] = Predictor(self.logger, self) + ret[model_category].load_model(*load_args, **load_kwargs, device=device) self._loaded.update(ret) return self._loaded From 7e8b9311d3eb96b320b928088c4b9a3a88e882cf Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 19 May 2026 03:32:37 +0200 Subject: [PATCH 002/121] Revert "test_model_zoo: fix calls" This reverts commit 5a98f55be365e740c31aabf05f339749b6e2c6fd. --- tests/test_model_zoo.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_model_zoo.py b/tests/test_model_zoo.py index 9d37431..2042b28 100644 --- a/tests/test_model_zoo.py +++ b/tests/test_model_zoo.py @@ -6,11 +6,11 @@ def test_trocr1( model_zoo = EynollahModelZoo(model_dir) try: from transformers import TrOCRProcessor, VisionEncoderDecoderModel - model_zoo.load_models('trocr_processor') - proc = model_zoo.get('trocr_processor') + model_zoo.load_model('trocr_processor') + proc = model_zoo.get('trocr_processor', TrOCRProcessor) assert isinstance(proc, TrOCRProcessor) - model_zoo.load_models(['ocr', 'tr']) - model = model_zoo.get('ocr') + model_zoo.load_model('ocr', 'tr') + model = model_zoo.get('ocr', VisionEncoderDecoderModel) assert isinstance(model, VisionEncoderDecoderModel) except ImportError: pass From 98e6fbbcbbe5c1ebc8f9729cbcd6c8864ad389ac Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Mon, 11 May 2026 11:30:39 +0200 Subject: [PATCH 003/121] mbreorder: make work again, re-use Eynollah base class --- src/eynollah/cli/cli_readingorder.py | 11 ++++++++--- src/eynollah/mb_ro_on_layout.py | 29 ++++++++++++++++------------ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/eynollah/cli/cli_readingorder.py b/src/eynollah/cli/cli_readingorder.py index 0f44b7f..eed9fb9 100644 --- a/src/eynollah/cli/cli_readingorder.py +++ b/src/eynollah/cli/cli_readingorder.py @@ -20,14 +20,19 @@ import click type=click.Path(exists=True, file_okay=False), required=True, ) +@click.option( + "--device", + "-D", + help="placement of computations in predictors for each model type; if none (by default), will try to use first available GPU or fall back to CPU; set string to force using a device (e.g. 'GPU0', 'GPU1' or 'CPU'). Can also be a comma-separated list of model category to device mappings (e.g. 'col_classifier:CPU,page:GPU0,*:GPU1')", +) @click.pass_context -def readingorder_cli(ctx, input, dir_in, out): +def readingorder_cli(ctx, input, dir_in, out, device): """ Generate ReadingOrder with a ML model """ - from ..mb_ro_on_layout import machine_based_reading_order_on_layout + from ..mb_ro_on_layout import Reorder assert bool(input) != bool(dir_in), "Either -i (single input) or -di (directory) must be provided, but not both." - orderer = machine_based_reading_order_on_layout(model_zoo=ctx.obj.model_zoo) + orderer = Reorder(model_zoo=ctx.obj.model_zoo, device=device) orderer.run(xml_filename=input, dir_in=dir_in, dir_out=out, diff --git a/src/eynollah/mb_ro_on_layout.py b/src/eynollah/mb_ro_on_layout.py index 22fe97b..5725ba1 100644 --- a/src/eynollah/mb_ro_on_layout.py +++ b/src/eynollah/mb_ro_on_layout.py @@ -21,6 +21,7 @@ os.environ['TF_USE_LEGACY_KERAS'] = '1' # avoid Keras 3 after TF 2.15 import tensorflow as tf from tensorflow.keras.models import Model +from .eynollah import Eynollah from .model_zoo import EynollahModelZoo from .utils.resize import resize_image from .utils.contour import ( @@ -34,23 +35,27 @@ DPI_THRESHOLD = 298 KERNEL = np.ones((5, 5), np.uint8) -class machine_based_reading_order_on_layout: +class Reorder(Eynollah): def __init__( - self, - *, - model_zoo: EynollahModelZoo, - logger : Optional[logging.Logger] = None, + self, + *, + model_zoo: EynollahModelZoo, + logger : Optional[logging.Logger] = None, + device: str = '', ): self.logger = logger or logging.getLogger('eynollah.mbreorder') self.model_zoo = model_zoo - try: - for device in tf.config.list_physical_devices('GPU'): - tf.config.experimental.set_memory_growth(device, True) - except: - self.logger.warning("no GPU device available") - self.model_zoo.load_model('reading_order') + self.setup_models(device=device) + + def setup_models(self, device=''): + loadable = ['reading_order'] + self.model_zoo.load_models(*loadable, device=device) + for model in loadable: + self.logger.debug("model %s has input shape %s", model, + self.model_zoo.get(model).input_shape) + def read_xml(self, xml_file): tree1 = ET.parse(xml_file, parser = ET.XMLParser(encoding='utf-8')) @@ -676,7 +681,7 @@ class machine_based_reading_order_on_layout: tot_counter += 1 batch.append(j) if tot_counter % inference_bs == 0 or tot_counter == len(ij_list): - y_pr = self.model_zoo.get('reading_order', Model).predict(input_1 , verbose='0') + y_pr = self.model_zoo.get('reading_order').predict(input_1, verbose=0) for jb, j in enumerate(batch): if y_pr[jb][0]>=0.5: post_list.append(j) From ded668a2562d2dc59646554a06338303cf2a6034 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 12 May 2026 18:17:43 +0200 Subject: [PATCH 004/121] =?UTF-8?q?model=5Fzoo:=20fix=20clash=20between=20?= =?UTF-8?q?Predictor=20and=20direct=20(OCR)=20use-cases=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `load_models()`: uniformly handle arg types - `load_model()`: move handling of non-model categories to `load_models()` - `load_model()`: move SavedModel preference over HDF5 to `model_path()` - `_load_ocr_model()`: add user-selected device handling and reporting for Torch (as for TF) - `_load_ocr_model()`: move (TF-based) CNN-RNN case to `load_model()` (including Keras layer mapping) - `shutdown()`: only apply `shutdown()` to Predictor model types --- src/eynollah/model_zoo/model_zoo.py | 144 +++++++++++++++++----------- 1 file changed, 87 insertions(+), 57 deletions(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index fffd389..7f3cd6c 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -70,6 +70,9 @@ class EynollahModelZoo: model_path = Path(self.model_basedir).joinpath(spec.filename) else: model_path = Path(spec.filename) + if model_path.suffix == '.h5' and Path(model_path.stem).exists(): + # prefer SavedModel over HDF5 format if it exists + model_path = Path(model_path.stem) return model_path def load_models( @@ -82,28 +85,50 @@ class EynollahModelZoo: """ ret = {} # cannot use self._loaded here, yet – first spawn all predictors for load_args in all_load_args: + load_kwargs = dict(device=device) if isinstance(load_args, str): - model_category = load_args - load_args = [model_category] + model_category, model_variant = load_args, "" + elif len(load_args) > 2: + # for calls to self.model_path + self.override_models(load_args) + # for calls to Predictor.load_model + model_category, model_variant, model_path = load_args + load_kwargs["model_variant"] = model_variant + load_kwargs["model_path_override"] = model_path else: - model_category = load_args[0] - load_kwargs = {} + model_category, model_variant = load_args + load_kwargs["model_variant"] = model_variant + if model_category.endswith('_resized'): - load_args[0] = model_category[:-8] + model_category = model_category[:-8] load_kwargs["resized"] = True elif model_category.endswith('_patched'): - load_args[0] = model_category[:-8] + model_category = model_category[:-8] load_kwargs["patched"] = True - ret[model_category] = Predictor(self.logger, self) - ret[model_category].load_model(*load_args, **load_kwargs, device=device) + + if model_category == 'ocr': + model = self._load_ocr_model(variant=model_variant, device=device) + elif model_category == 'num_to_char': + model = self._load_num_to_char() + elif model_category == 'characters': + model = self._load_characters() + elif model_category == 'trocr_processor': + from transformers import TrOCRProcessor + model_path = self.model_path(model_category, model_variant) + model = TrOCRProcessor.from_pretrained(model_path) + else: + model = Predictor(self.logger, self) + model.load_model(model_category, **load_kwargs) + + ret[model_category] = model self._loaded.update(ret) return self._loaded def load_model( - self, - model_category: str, - model_variant: str = '', - model_path_override: Optional[str] = None, + self, + model_category: str, + model_variant: str = '', + model_path_override: Optional[str] = None, patched: bool = False, resized: bool = False, device: str = '', @@ -117,6 +142,7 @@ class EynollahModelZoo: import tensorflow as tf from tensorflow.keras.models import load_model + from tensorflow.keras.models import Model as KerasModel from ..patch_encoder import ( PatchEncoder, @@ -162,38 +188,33 @@ class EynollahModelZoo: if model_path_override: self.override_models((model_category, model_variant, model_path_override)) model_path = self.model_path(model_category, model_variant) - if model_path.suffix == '.h5' and Path(model_path.stem).exists(): - # prefer SavedModel over HDF5 format if it exists - model_path = Path(model_path.stem) - if model_category == 'ocr': - model = self._load_ocr_model(variant=model_variant) - elif model_category == 'num_to_char': - model = self._load_num_to_char() - elif model_category == 'characters': - model = self._load_characters() - elif model_category == 'trocr_processor': - from transformers import TrOCRProcessor - model = TrOCRProcessor.from_pretrained(model_path) + try: + # avoid wasting VRAM on non-transformer models + model = load_model(model_path, compile=False) + except Exception as e: + self.logger.error(e) + model = load_model( + model_path, compile=False, + custom_objects=dict(PatchEncoder=PatchEncoder, + Patches=Patches)) + assert isinstance(model, KerasModel) + model._name = model_category + if resized: + model = wrap_layout_model_resized(model) + model._name = model_category + '_resized' + elif patched: + model = wrap_layout_model_patched(model) + model._name = model_category + '_patched' else: - try: - # avoid wasting VRAM on non-transformer models - model = load_model(model_path, compile=False) - except Exception as e: - self.logger.error(e) - model = load_model( - model_path, compile=False, - custom_objects=dict(PatchEncoder=PatchEncoder, - Patches=Patches)) - model._name = model_category - if resized: - model = wrap_layout_model_resized(model) - model._name = model_category + '_resized' - elif patched: - model = wrap_layout_model_patched(model) - model._name = model_category + '_patched' - else: - model.jit_compile = True - model.make_predict_function() + model.jit_compile = True + + if model_category == 'ocr': + model = KerasModel( + model.get_layer(name="image").input, # type: ignore + model.get_layer(name="dense2").output, # type: ignore + ) + + model.make_predict_function() return model def get(self, model_category: str) -> Predictor: @@ -201,26 +222,34 @@ class EynollahModelZoo: raise ValueError(f'Model "{model_category}" not previously loaded with "load_model(..)"') return self._loaded[model_category] - def _load_ocr_model(self, variant: str) -> AnyModel: + def _load_ocr_model(self, variant: str, device: str = "") -> AnyModel: """ Load OCR model """ - from tensorflow.keras.models import Model as KerasModel - from tensorflow.keras.models import load_model - - ocr_model_dir = self.model_path('ocr', variant) + model_dir = self.model_path('ocr', variant) if variant == 'tr': from transformers import VisionEncoderDecoderModel - ret = VisionEncoderDecoderModel.from_pretrained(ocr_model_dir) + import torch + ret = VisionEncoderDecoderModel.from_pretrained(model_dir) assert isinstance(ret, VisionEncoderDecoderModel) + dev = torch.device('cpu') + if not device and torch.cuda.is_available(): + device = 'GPU' # try + if device and device.startswith('GPU'): + try: + dev = torch.device('cuda', int(device[3:] or 0)) + name = torch.cuda.get_device_name(dev) + self.logger.info("using GPU %s (%s) for model ocr:tr", dev, name) + except: + self.logger.exception("cannot configure GPU device") + dev = torch.device('cpu') + if dev.type == 'cuda': + ret.to(dev) + else: + self.logger.warning("no GPU device available") return ret - else: - ocr_model = load_model(ocr_model_dir, compile=False) - assert isinstance(ocr_model, KerasModel) - return KerasModel( - ocr_model.get_layer(name="image").input, # type: ignore - ocr_model.get_layer(name="dense2").output, # type: ignore - ) + + return self.load_model('ocr', model_variant=variant, device=device) def _load_characters(self) -> List[str]: """ @@ -273,5 +302,6 @@ class EynollahModelZoo: """ if hasattr(self, '_loaded') and getattr(self, '_loaded'): for needle in list(self._loaded.keys()): - self._loaded[needle].shutdown() + if isinstance(self._loaded[needle], Predictor): + self._loaded[needle].shutdown() del self._loaded[needle] From cd62f13872419deb3c8740e8d5ded6a21cdec3c9 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 12 May 2026 18:31:18 +0200 Subject: [PATCH 005/121] =?UTF-8?q?eynollah=5Focr:=20make=20work=20again,?= =?UTF-8?q?=20re-use=20Eynollah=20base=20class=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - re-use Eynollah base class - use `ModelZoo.load_models()` instead of `load_model()` - pass in `device` init kwarg, delegate to `ModelZoo.load_models()` - `device`: return Torch device at loaded model tensors instead of ad-hoc selection - make numeric init kwargs non-optional (only numeric) --- src/eynollah/cli/cli_ocr.py | 10 ++++++-- src/eynollah/eynollah_ocr.py | 48 ++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/eynollah/cli/cli_ocr.py b/src/eynollah/cli/cli_ocr.py index 406af61..f9b74c8 100644 --- a/src/eynollah/cli/cli_ocr.py +++ b/src/eynollah/cli/cli_ocr.py @@ -66,6 +66,10 @@ import click "--min_conf_value_of_textline_text", "-min_conf", help="minimum OCR confidence value. Text lines with a confidence value lower than this threshold will not be included in the output XML file.", +@click.option( + "--device", + "-D", + help="placement of computations in predictors for each model type; if none (by default), will try to use first available GPU or fall back to CPU; set string to force using a device (e.g. 'GPU0', 'GPU1' or 'CPU'). Can also be a comma-separated list of model category to device mappings (e.g. 'col_classifier:CPU,page:GPU0,*:GPU1')", ) @click.pass_context def ocr_cli( @@ -81,18 +85,20 @@ def ocr_cli( do_not_mask_with_textline_contour, batch_size, min_conf_value_of_textline_text, + device, ): """ Recognize text with a CNN/RNN or transformer ML model. """ - assert bool(image) ^ bool(dir_in), "Either -i (single image) or -di (directory) must be provided, but not both." + assert bool(image) != bool(dir_in), "Either -i (single image) or -di (directory) must be provided, but not both." from ..eynollah_ocr import Eynollah_ocr eynollah_ocr = Eynollah_ocr( model_zoo=ctx.obj.model_zoo, tr_ocr=tr_ocr, do_not_mask_with_textline_contour=do_not_mask_with_textline_contour, batch_size=batch_size, - min_conf_value_of_textline_text=min_conf_value_of_textline_text) + min_conf_value_of_textline_text=min_conf_value_of_textline_text, + device=device) eynollah_ocr.run(overwrite=overwrite, dir_in=dir_in, dir_in_bin=dir_in_bin, diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 3c918e5..4470671 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -14,16 +14,17 @@ from cv2.typing import MatLike from xml.etree import ElementTree as ET from PIL import Image, ImageDraw import numpy as np -from eynollah.model_zoo import EynollahModelZoo -from eynollah.utils.font import get_font -from eynollah.utils.xml import etree_namespace_for_element_tag try: import torch except ImportError: torch = None +from .eynollah import Eynollah +from .model_zoo import EynollahModelZoo from .utils import is_image_filename +from .utils.font import get_font +from .utils.xml import etree_namespace_for_element_tag from .utils.resize import resize_image from .utils.utils_ocr import ( break_curved_line_into_small_pieces_and_then_merge, @@ -44,45 +45,44 @@ class EynollahOcrResult: cropped_lines_region_indexer: List total_bb_coordinates:List -class Eynollah_ocr: +class Eynollah_ocr(Eynollah): def __init__( self, *, model_zoo: EynollahModelZoo, tr_ocr=False, - batch_size: Optional[int]=None, + batch_size: int=0, do_not_mask_with_textline_contour: bool=False, - min_conf_value_of_textline_text : Optional[float]=None, + min_conf_value_of_textline_text : float=0.3, logger: Optional[Logger]=None, + device: str = '', ): self.tr_ocr = tr_ocr # masking for OCR and GT generation, relevant for skewed lines and bounding boxes self.do_not_mask_with_textline_contour = do_not_mask_with_textline_contour self.logger = logger if logger else getLogger('eynollah.ocr') - self.model_zoo = model_zoo - self.min_conf_value_of_textline_text = min_conf_value_of_textline_text if min_conf_value_of_textline_text else 0.3 - self.b_s = 2 if batch_size is None and tr_ocr else 8 if batch_size is None else batch_size + self.min_conf_value_of_textline_text = min_conf_value_of_textline_text + self.b_s = batch_size or 2 if tr_ocr else 8 - if tr_ocr: - self.model_zoo.load_model('trocr_processor') - self.model_zoo.load_model('ocr', 'tr') - self.model_zoo.get('ocr').to(self.device) + self.model_zoo = model_zoo + self.setup_models(device=device) + + def setup_models(self, device=''): + if self.tr_ocr: + self.model_zoo.load_models('trocr_processor', + ('ocr', 'tr'), + device=device) else: - self.model_zoo.load_model('ocr', '') - self.model_zoo.load_model('num_to_char') - self.model_zoo.load_model('characters') - self.end_character = len(self.model_zoo.get('characters', list)) + 2 + self.model_zoo.load_models('ocr', + 'num_to_char', + 'characters', + device=device) + self.end_character = len(self.model_zoo.get('characters')) + 2 @property def device(self): - assert torch - if torch.cuda.is_available(): - self.logger.info("Using GPU acceleration") - return torch.device("cuda:0") - else: - self.logger.info("Using CPU processing") - return torch.device("cpu") + return self.model_zoo.get('ocr').device def run_trocr( self, From 7ed1a1ebac0c4b34db02b254c3dbb5c3d639ed9c Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 12 May 2026 18:34:56 +0200 Subject: [PATCH 006/121] CLIs: allow `-h` and show defaults uniformly, harmonise help, drop remaining redundant negative options --- src/eynollah/cli/cli_binarize.py | 4 +++- src/eynollah/cli/cli_enhance.py | 4 +++- src/eynollah/cli/cli_extract_images.py | 32 +++++++++++++++----------- src/eynollah/cli/cli_ocr.py | 21 +++++++++++------ src/eynollah/cli/cli_readingorder.py | 4 +++- 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/eynollah/cli/cli_binarize.py b/src/eynollah/cli/cli_binarize.py index f0e56f5..d544a67 100644 --- a/src/eynollah/cli/cli_binarize.py +++ b/src/eynollah/cli/cli_binarize.py @@ -1,6 +1,8 @@ import click -@click.command() +@click.command(context_settings=dict( + help_option_names=['-h', '--help'], + show_default=True)) @click.option( '--patches/--no-patches', default=True, diff --git a/src/eynollah/cli/cli_enhance.py b/src/eynollah/cli/cli_enhance.py index 517e1e8..42b1d41 100644 --- a/src/eynollah/cli/cli_enhance.py +++ b/src/eynollah/cli/cli_enhance.py @@ -1,6 +1,8 @@ import click -@click.command() +@click.command(context_settings=dict( + help_option_names=['-h', '--help'], + show_default=True)) @click.option( "--image", "-i", diff --git a/src/eynollah/cli/cli_extract_images.py b/src/eynollah/cli/cli_extract_images.py index 0add5b5..acd31f1 100644 --- a/src/eynollah/cli/cli_extract_images.py +++ b/src/eynollah/cli/cli_extract_images.py @@ -1,6 +1,8 @@ import click -@click.command() +@click.command(context_settings=dict( + help_option_names=['-h', '--help'], + show_default=True)) @click.option( "--image", "-i", @@ -30,36 +32,40 @@ import click @click.option( "--save_images", "-si", - help="if a directory is given, images in documents will be cropped and saved there", + help="if a directory is given, cropped images of pages will be saved there", type=click.Path(exists=True, file_okay=False), ) @click.option( - "--enable-plotting/--disable-plotting", - "-ep/-noep", + "--enable-plotting", + "-ep", is_flag=True, - help="If set, will plot intermediary files and images", + help="plot intermediary diagnostic images to files", ) @click.option( - "--input_binary/--input-RGB", - "-ib/-irgb", + "--input_binary", + "-ib", is_flag=True, - help="In general, eynollah uses RGB as input but if the input document is very dark, very bright or for any other reason you can turn on input binarization. When this flag is set, eynollah will binarize the RGB input document, you should always provide RGB images to eynollah.", + help="In general, eynollah uses RGB as input, but if the input document is very dark, very bright or for any other reason you can turn on internal binarization here. When set, eynollah will binarize the RGB input document first.", ) @click.option( - "--ignore_page_extraction/--extract_page_included", - "-ipe/-epi", + "--ignore_page_extraction", + "-ipe", is_flag=True, - help="if this parameter set to true, this tool would ignore page extraction", + help="ignore page extraction (cropping via page frame detection model)", ) @click.option( "--num_col_upper", "-ncu", - help="lower limit of columns in document image", + default=0, + type=click.IntRange(min=0), + help="lower limit of columns in document image; 0 means autodetected from model", ) @click.option( "--num_col_lower", "-ncl", - help="upper limit of columns in document image", + default=0, + type=click.IntRange(min=0), + help="upper limit of columns in document image; 0 means autodetected from model", ) @click.pass_context def extract_images_cli( diff --git a/src/eynollah/cli/cli_ocr.py b/src/eynollah/cli/cli_ocr.py index f9b74c8..99e03c5 100644 --- a/src/eynollah/cli/cli_ocr.py +++ b/src/eynollah/cli/cli_ocr.py @@ -1,6 +1,8 @@ import click -@click.command() +@click.command(context_settings=dict( + help_option_names=['-h', '--help'], + show_default=True)) @click.option( "--image", "-i", @@ -16,7 +18,7 @@ import click @click.option( "--dir_in_bin", "-dib", - help=("directory of binarized images (in addition to --dir_in for RGB images; filename stems must match the RGB image files, with '.png' \n Perform prediction using both RGB and binary images. (This does not necessarily improve results, however it may be beneficial for certain document images."), + help=("directory of binarized images (in addition to --dir_in for RGB images; filename stems must match the RGB image files, with '.png'. \n Perform prediction using both RGB and binary images. (This may improve results for certain document images.)"), type=click.Path(exists=True, file_okay=False), ) @click.option( @@ -47,25 +49,30 @@ import click ) @click.option( "--tr_ocr", - "-trocr/-notrocr", + "-trocr", is_flag=True, - help="if this parameter set to true, transformer ocr will be applied, otherwise cnn_rnn model.", + help="use transformer OCR (instead of classic CNN-RNN) model", ) @click.option( "--do_not_mask_with_textline_contour", - "-nmtc/-mtc", + "-nmtc", is_flag=True, - help="if this parameter set to true, cropped textline images will not be masked with textline contour.", + help="skip masking each cropped textline image with its corresponding textline contour", ) @click.option( "--batch_size", "-bs", + default=0, + type=click.IntRange(min=0), help="number of inference batch size. Default b_s for trocr and cnn_rnn models are 2 and 8 respectively", ) @click.option( "--min_conf_value_of_textline_text", "-min_conf", - help="minimum OCR confidence value. Text lines with a confidence value lower than this threshold will not be included in the output XML file.", + default=0.3, + type=click.FloatRange(min=0.0, max=1.0), + help="minimum OCR confidence threshold. Text lines with a lower confidence value will not be included in the output XML file.", +) @click.option( "--device", "-D", diff --git a/src/eynollah/cli/cli_readingorder.py b/src/eynollah/cli/cli_readingorder.py index eed9fb9..9bb7092 100644 --- a/src/eynollah/cli/cli_readingorder.py +++ b/src/eynollah/cli/cli_readingorder.py @@ -1,6 +1,8 @@ import click -@click.command() +@click.command(context_settings=dict( + help_option_names=['-h', '--help'], + show_default=True)) @click.option( "--input", "-i", From 21ecb043f763045aabc043805acb4d39da0316c9 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 12 May 2026 18:41:21 +0200 Subject: [PATCH 007/121] CLIs: move `--device` option to group level --- src/eynollah/cli/cli.py | 9 ++++++++- src/eynollah/cli/cli_binarize.py | 9 ++------- src/eynollah/cli/cli_enhance.py | 9 ++------- src/eynollah/cli/cli_layout.py | 8 +------- src/eynollah/cli/cli_ocr.py | 9 ++------- src/eynollah/cli/cli_readingorder.py | 10 +++------- 6 files changed, 18 insertions(+), 36 deletions(-) diff --git a/src/eynollah/cli/cli.py b/src/eynollah/cli/cli.py index ace3f1c..2a4c8d1 100644 --- a/src/eynollah/cli/cli.py +++ b/src/eynollah/cli/cli.py @@ -15,6 +15,7 @@ class EynollahCliCtx: Holds options relevant for all eynollah subcommands """ model_zoo: EynollahModelZoo + device: str = '' log_level : Union[str, None] = 'INFO' @@ -35,6 +36,11 @@ class EynollahCliCtx: type=(str, str, str), multiple=True, ) +@click.option( + "--device", + "-D", + help="placement of computations in predictors for each model type; if none (by default), will try to use first available GPU or fall back to CPU; set string to force using a device (e.g. 'GPU0', 'GPU1' or 'CPU'). Can also be a comma-separated list of model category to device mappings (e.g. 'col_classifier:CPU,page:GPU0,*:GPU1')", +) @click.option( "--log_level", "-l", @@ -42,7 +48,7 @@ class EynollahCliCtx: help="Override log level globally to this", ) @click.pass_context -def main(ctx, model_basedir, model_overrides, log_level): +def main(ctx, model_basedir, model_overrides, device, log_level): """ eynollah - Document Layout Analysis, Image Enhancement, OCR """ @@ -58,6 +64,7 @@ def main(ctx, model_basedir, model_overrides, log_level): # Initialize CLI context ctx.obj = EynollahCliCtx( model_zoo=model_zoo, + device=device, log_level=log_level, ) diff --git a/src/eynollah/cli/cli_binarize.py b/src/eynollah/cli/cli_binarize.py index d544a67..82209be 100644 --- a/src/eynollah/cli/cli_binarize.py +++ b/src/eynollah/cli/cli_binarize.py @@ -33,11 +33,6 @@ import click help="overwrite (instead of skipping) if output xml exists", is_flag=True, ) -@click.option( - "--device", - "-D", - help="placement of computations in predictors for each model type; if none (by default), will try to use first available GPU or fall back to CPU; set string to force using a device (e.g. 'GPU0', 'GPU1' or 'CPU'). Can also be a comma-separated list of model category to device mappings (e.g. 'col_classifier:CPU,page:GPU0,*:GPU1')", -) @click.pass_context def binarize_cli( ctx, @@ -46,14 +41,14 @@ def binarize_cli( dir_in, output, overwrite, - device, ): """ Binarize images with a ML model """ from ..sbb_binarize import SbbBinarizer assert bool(input_image) != bool(dir_in), "Either -i (single input) or -di (directory) must be provided, but not both." - binarizer = SbbBinarizer(model_zoo=ctx.obj.model_zoo, device=device) + binarizer = SbbBinarizer(model_zoo=ctx.obj.model_zoo, + device=ctx.obj.device) binarizer.run( image_filename=input_image, use_patches=patches, diff --git a/src/eynollah/cli/cli_enhance.py b/src/eynollah/cli/cli_enhance.py index 42b1d41..bcb8263 100644 --- a/src/eynollah/cli/cli_enhance.py +++ b/src/eynollah/cli/cli_enhance.py @@ -48,13 +48,8 @@ import click is_flag=True, help="save the enhanced image in original image size", ) -@click.option( - "--device", - "-D", - help="placement of computations in predictors for each model type; if none (by default), will try to use first available GPU or fall back to CPU; set string to force using a device (e.g. 'GPU0', 'GPU1' or 'CPU'). Can also be a comma-separated list of model category to device mappings (e.g. 'col_classifier:CPU,page:GPU0,*:GPU1')", -) @click.pass_context -def enhance_cli(ctx, image, out, overwrite, dir_in, num_col_upper, num_col_lower, save_org_scale, device): +def enhance_cli(ctx, image, out, overwrite, dir_in, num_col_upper, num_col_lower, save_org_scale): """ Enhance image """ @@ -62,10 +57,10 @@ def enhance_cli(ctx, image, out, overwrite, dir_in, num_col_upper, num_col_lower from ..image_enhancer import Enhancer enhancer = Enhancer( model_zoo=ctx.obj.model_zoo, + device=ctx.obj.device, num_col_upper=num_col_upper, num_col_lower=num_col_lower, save_org_scale=save_org_scale, - device=device, ) enhancer.run(overwrite=overwrite, dir_in=dir_in, diff --git a/src/eynollah/cli/cli_layout.py b/src/eynollah/cli/cli_layout.py index 417b202..0a083d5 100644 --- a/src/eynollah/cli/cli_layout.py +++ b/src/eynollah/cli/cli_layout.py @@ -172,11 +172,6 @@ import click type=click.FloatRange(min=0), help="abort when number of failed images exceeds this value (if >=1) or ratio of failed over total images exceeds this value (if <1); 0 means ignore failures", ) -@click.option( - "--device", - "-D", - help="placement of computations in predictors for each model type; if none (by default), will try to use first available GPU or fall back to CPU; set string to force using a device (e.g. 'GPU0', 'GPU1' or 'CPU'). Can also be a comma-separated list of model category to device mappings (e.g. 'col_classifier:CPU,page:GPU0,*:GPU1')", -) @click.pass_context def layout_cli( ctx, @@ -207,7 +202,6 @@ def layout_cli( ignore_page_extraction, num_jobs, halt_fail, - device, ): """ Detect Layout (with optional image enhancement and reading order detection) @@ -223,7 +217,7 @@ def layout_cli( assert bool(image) != bool(dir_in), "Either -i (single input) or -di (directory) must be provided, but not both." eynollah = Eynollah( model_zoo=ctx.obj.model_zoo, - device=device, + device=ctx.obj.device, enable_plotting=enable_plotting, allow_enhancement=allow_enhancement, curved_line=curved_line, diff --git a/src/eynollah/cli/cli_ocr.py b/src/eynollah/cli/cli_ocr.py index 99e03c5..daeccbe 100644 --- a/src/eynollah/cli/cli_ocr.py +++ b/src/eynollah/cli/cli_ocr.py @@ -73,11 +73,6 @@ import click type=click.FloatRange(min=0.0, max=1.0), help="minimum OCR confidence threshold. Text lines with a lower confidence value will not be included in the output XML file.", ) -@click.option( - "--device", - "-D", - help="placement of computations in predictors for each model type; if none (by default), will try to use first available GPU or fall back to CPU; set string to force using a device (e.g. 'GPU0', 'GPU1' or 'CPU'). Can also be a comma-separated list of model category to device mappings (e.g. 'col_classifier:CPU,page:GPU0,*:GPU1')", -) @click.pass_context def ocr_cli( ctx, @@ -92,7 +87,6 @@ def ocr_cli( do_not_mask_with_textline_contour, batch_size, min_conf_value_of_textline_text, - device, ): """ Recognize text with a CNN/RNN or transformer ML model. @@ -101,11 +95,12 @@ def ocr_cli( from ..eynollah_ocr import Eynollah_ocr eynollah_ocr = Eynollah_ocr( model_zoo=ctx.obj.model_zoo, + device=ctx.obj.device, tr_ocr=tr_ocr, do_not_mask_with_textline_contour=do_not_mask_with_textline_contour, batch_size=batch_size, min_conf_value_of_textline_text=min_conf_value_of_textline_text, - device=device) + ) eynollah_ocr.run(overwrite=overwrite, dir_in=dir_in, dir_in_bin=dir_in_bin, diff --git a/src/eynollah/cli/cli_readingorder.py b/src/eynollah/cli/cli_readingorder.py index 9bb7092..ac52e38 100644 --- a/src/eynollah/cli/cli_readingorder.py +++ b/src/eynollah/cli/cli_readingorder.py @@ -22,19 +22,15 @@ import click type=click.Path(exists=True, file_okay=False), required=True, ) -@click.option( - "--device", - "-D", - help="placement of computations in predictors for each model type; if none (by default), will try to use first available GPU or fall back to CPU; set string to force using a device (e.g. 'GPU0', 'GPU1' or 'CPU'). Can also be a comma-separated list of model category to device mappings (e.g. 'col_classifier:CPU,page:GPU0,*:GPU1')", -) @click.pass_context -def readingorder_cli(ctx, input, dir_in, out, device): +def readingorder_cli(ctx, input, dir_in, out): """ Generate ReadingOrder with a ML model """ from ..mb_ro_on_layout import Reorder assert bool(input) != bool(dir_in), "Either -i (single input) or -di (directory) must be provided, but not both." - orderer = Reorder(model_zoo=ctx.obj.model_zoo, device=device) + orderer = Reorder(model_zoo=ctx.obj.model_zoo, + device=ctx.obj.device) orderer.run(xml_filename=input, dir_in=dir_in, dir_out=out, From 1ed633bc254ce76e6422d7496661ab5a173e5551 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 12 May 2026 19:02:43 +0200 Subject: [PATCH 008/121] test_model_zoo: adapt (`load_models` instead of `load_model`) --- tests/test_model_zoo.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_model_zoo.py b/tests/test_model_zoo.py index 2042b28..341bc21 100644 --- a/tests/test_model_zoo.py +++ b/tests/test_model_zoo.py @@ -6,11 +6,11 @@ def test_trocr1( model_zoo = EynollahModelZoo(model_dir) try: from transformers import TrOCRProcessor, VisionEncoderDecoderModel - model_zoo.load_model('trocr_processor') - proc = model_zoo.get('trocr_processor', TrOCRProcessor) + model_zoo.load_models('trocr_processor', + ('ocr', 'tr')) + proc = model_zoo.get('trocr_processor') assert isinstance(proc, TrOCRProcessor) - model_zoo.load_model('ocr', 'tr') - model = model_zoo.get('ocr', VisionEncoderDecoderModel) + model = model_zoo.get('ocr') assert isinstance(model, VisionEncoderDecoderModel) except ImportError: pass From 87cce6c9636ff4a0c726fb2be0bbdc37b3838a32 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 12 May 2026 19:03:32 +0200 Subject: [PATCH 009/121] CLI tests: add opt-in envvar `EYNOLLAH_OPTIONS` for device selection, model directory etc. --- tests/cli_tests/conftest.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/cli_tests/conftest.py b/tests/cli_tests/conftest.py index 601d76b..2e1501c 100644 --- a/tests/cli_tests/conftest.py +++ b/tests/cli_tests/conftest.py @@ -1,4 +1,5 @@ from typing import List +import os import pytest import logging @@ -31,6 +32,8 @@ def run_eynollah_ok_and_check_logs( subcommand, *args ] + if 'EYNOLLAH_OPTIONS' in os.environ: + args = os.environ['EYNOLLAH_OPTIONS'].split() + args if pytestconfig.getoption('verbose') > 0: args = ['-l', 'DEBUG'] + args caplog.set_level(logging.INFO) From be4fe8c263ed219e8d3df08e53e271d68556e7ff Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 12 May 2026 19:04:37 +0200 Subject: [PATCH 010/121] contour: drop unused functions depending on `rotation_image_new()` --- src/eynollah/utils/contour.py | 90 +---------------------------------- src/eynollah/utils/rotate.py | 4 -- 2 files changed, 1 insertion(+), 93 deletions(-) diff --git a/src/eynollah/utils/contour.py b/src/eynollah/utils/contour.py index f1a7a8e..1dbead1 100644 --- a/src/eynollah/utils/contour.py +++ b/src/eynollah/utils/contour.py @@ -11,7 +11,7 @@ from shapely.geometry.polygon import orient from shapely import set_precision, affinity from shapely.ops import unary_union, nearest_points -from .rotate import rotate_image, rotation_image_new +from .rotate import rotate_image def contours_in_same_horizon(cy_main_hor): """ @@ -120,94 +120,6 @@ def return_contours_of_interested_region(region_pre_p, label, min_area=0.0002, d dilate=dilate) return contours_imgs -def do_work_of_contours_in_image(contour, index_r_con, img, slope_first): - img_copy = np.zeros(img.shape[:2], dtype=np.uint8) - img_copy = cv2.fillPoly(img_copy, pts=[contour], color=1) - - img_copy = rotation_image_new(img_copy, -slope_first) - _, thresh = cv2.threshold(img_copy, 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]) - - return cont_int[0], index_r_con - -def get_textregion_contours_in_org_image_multi(cnts, img, slope_first, map=map): - if not len(cnts): - return [], [] - results = map(partial(do_work_of_contours_in_image, - img=img, - slope_first=slope_first, - ), - cnts, range(len(cnts))) - return tuple(zip(*results)) - -def get_textregion_contours_in_org_image(cnts, img, slope_first): - cnts_org = [] - # print(cnts,'cnts') - for i in range(len(cnts)): - img_copy = np.zeros(img.shape[:2], dtype=np.uint8) - img_copy = cv2.fillPoly(img_copy, pts=[cnts[i]], color=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() - - _, thresh = cv2.threshold(img_copy, 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]) - - return cnts_org - -def get_textregion_confidences_old(cnts, img, slope_first): - zoom = 3 - img = cv2.resize(img, (img.shape[1] // zoom, - img.shape[0] // zoom), - interpolation=cv2.INTER_NEAREST) - cnts_org = [] - for cnt in cnts: - img_copy = np.zeros(img.shape[:2], dtype=np.uint8) - img_copy = cv2.fillPoly(img_copy, pts=[cnt // zoom], color=1) - - img_copy = rotation_image_new(img_copy, -slope_first).astype(np.uint8) - _, thresh = cv2.threshold(img_copy, 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.append(cont_int[0] * zoom) - - return cnts_org - -def do_back_rotation_and_get_cnt_back(contour_par, index_r_con, img, slope_first, confidence_matrix): - img_copy = np.zeros(img.shape[:2], dtype=np.uint8) - img_copy = cv2.fillPoly(img_copy, pts=[contour_par], color=1) - confidence_matrix_mapped_with_contour = confidence_matrix * img_copy - confidence_contour = np.sum(confidence_matrix_mapped_with_contour) / float(np.sum(img_copy)) - - img_copy = rotation_image_new(img_copy, -slope_first).astype(np.uint8) - _, thresh = cv2.threshold(img_copy, 0, 255, 0) - - cont_int, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) - if len(cont_int)==0: - cont_int = [contour_par] - confidence_contour = 0 - else: - 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]) - return cont_int[0], index_r_con, confidence_contour - def get_region_confidences(cnts, confidence_matrix): if not len(cnts): return [] diff --git a/src/eynollah/utils/rotate.py b/src/eynollah/utils/rotate.py index 6651c4e..e45a438 100644 --- a/src/eynollah/utils/rotate.py +++ b/src/eynollah/utils/rotate.py @@ -2,10 +2,6 @@ import math import cv2 -def rotation_image_new(img, thetha): - rotated = rotate_image(img, thetha) - return rotate_max_area_new(img, rotated, thetha) - def rotate_image(img_patch, slope): (h, w) = img_patch.shape[:2] center = (w // 2, h // 2) From 17b311441a30cd3599b9414be8a734922aa6077d Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 12 May 2026 20:02:40 +0200 Subject: [PATCH 011/121] model_zoo: also parse comma/colon syntax for `device` in Torch case --- src/eynollah/model_zoo/model_zoo.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 7f3cd6c..f1d8824 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -154,7 +154,7 @@ class EynollahModelZoo: try: gpus = tf.config.list_physical_devices('GPU') if device: - if ',' in device: + if ':' in device: for spec in device.split(','): cat, dev = spec.split(':') if fnmatchcase(model_category, cat): @@ -235,6 +235,12 @@ class EynollahModelZoo: dev = torch.device('cpu') if not device and torch.cuda.is_available(): device = 'GPU' # try + if device and ':' in device: + for spec in device.split(','): + cat, dev = spec.split(':') + if fnmatchcase('ocr', cat): + device = dev + break if device and device.startswith('GPU'): try: dev = torch.device('cuda', int(device[3:] or 0)) From f329e10a805b57f18c454981757948e52dcabf9d Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 12 May 2026 20:04:41 +0200 Subject: [PATCH 012/121] test_layout: rm ignored `--allow_scaling` option --- tests/cli_tests/test_layout.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/cli_tests/test_layout.py b/tests/cli_tests/test_layout.py index 7cbe013..503aeac 100644 --- a/tests/cli_tests/test_layout.py +++ b/tests/cli_tests/test_layout.py @@ -6,11 +6,12 @@ from ocrd_models.constants import NAMESPACES as NS "options", [ [], # defaults - #["--allow_scaling", "--curved-line"], - ["--allow_scaling", "--curved-line", "--full-layout"], - ["--allow_scaling", "--curved-line", "--full-layout", "--reading_order_machine_based"], + #["--curved-line"], + ["--curved-line", "--full-layout"], + ["--curved-line", "--full-layout", "--reading_order_machine_based"], # -ep ... - # -eoi ... + # --input_binary + # --ignore_page_extraction # --skip_layout_and_reading_order ], ids=str) def test_run_eynollah_layout_filename( From 481c286da9522d1117cc57f1775423e833076325 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 19 May 2026 02:08:14 +0200 Subject: [PATCH 013/121] ModelZoo.load_model: no XLA compilation --- src/eynollah/model_zoo/model_zoo.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index f1d8824..054552a 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -35,7 +35,7 @@ class EynollahModelZoo: self._overrides = [] if model_overrides: self.override_models(*model_overrides) - self._loaded: Dict[str, Predictor] = {} + self._loaded: Dict[str, Union[Predictor, AnyModel]] = {} @property def model_overrides(self): @@ -197,6 +197,7 @@ class EynollahModelZoo: model_path, compile=False, custom_objects=dict(PatchEncoder=PatchEncoder, Patches=Patches)) + model.make_predict_function() assert isinstance(model, KerasModel) model._name = model_category if resized: @@ -206,7 +207,10 @@ class EynollahModelZoo: model = wrap_layout_model_patched(model) model._name = model_category + '_patched' else: - model.jit_compile = True + # increases required VRAM, does not always work + # (depending on CUDA/libcudnn/TF version): + #model.jit_compile = True + pass if model_category == 'ocr': model = KerasModel( @@ -214,10 +218,9 @@ class EynollahModelZoo: model.get_layer(name="dense2").output, # type: ignore ) - model.make_predict_function() return model - def get(self, model_category: str) -> Predictor: + def get(self, model_category: str) -> Union[Predictor, AnyModel]: if model_category not in self._loaded: raise ValueError(f'Model "{model_category}" not previously loaded with "load_model(..)"') return self._loaded[model_category] From ffe5cdc5197b7e9c11e77b10969647ae8b1e2a75 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 19 May 2026 02:09:49 +0200 Subject: [PATCH 014/121] ModelZoo.shutdown: drop extra `del` (already done by `shutdown()`) --- src/eynollah/model_zoo/model_zoo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 054552a..3de8b6b 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -313,4 +313,3 @@ class EynollahModelZoo: for needle in list(self._loaded.keys()): if isinstance(self._loaded[needle], Predictor): self._loaded[needle].shutdown() - del self._loaded[needle] From 9efce5e9f2b5afb3c7cf1c44f4d262e383c737fd Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 19 May 2026 03:16:15 +0200 Subject: [PATCH 015/121] Predictor.shutdown: use `join()` instead of `terminate()` --- src/eynollah/predictor.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/eynollah/predictor.py b/src/eynollah/predictor.py index e1159e7..3c6890e 100644 --- a/src/eynollah/predictor.py +++ b/src/eynollah/predictor.py @@ -194,17 +194,18 @@ class Predictor(mp.context.SpawnProcess): def shutdown(self): # do not terminate from forked processor instances - if mp.parent_process() is None: + if not hasattr(self, 'model'): self.stopped.set() + self.join() self.taskq.close() self.taskq.cancel_join_thread() self.resultq.close() self.resultq.cancel_join_thread() self.logq.close() - self.terminate() + #self.terminate() else: del self.model def __del__(self): - #self.logger.debug(f"deinit of {self} in {mp.current_process().name}") + #self.logger.debug(f"deinit of {self.name} in {mp.current_process().name}") self.shutdown() From 86adaf299ade201b178fe851c7b4f884a680fc0c Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 19 May 2026 03:17:31 +0200 Subject: [PATCH 016/121] =?UTF-8?q?training.models.transformer=5Fblock:=20?= =?UTF-8?q?tf.reshape=20=E2=86=92=20Keras=20Reshape=20layer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/eynollah/training/models.py | 9 ++++----- src/eynollah/training/reload-models-v0.8.mk | 7 ++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/eynollah/training/models.py b/src/eynollah/training/models.py index 3494249..f700d14 100644 --- a/src/eynollah/training/models.py +++ b/src/eynollah/training/models.py @@ -309,11 +309,10 @@ def transformer_block(img, # Skip connection 2. encoded_patches = Add()([x3, x2]) - encoded_patches = tf.reshape(encoded_patches, - [-1, - img.shape[1], - img.shape[2], - projection_dim // (patchsize_x * patchsize_y)]) + encoded_patches = Reshape(target_shape=(img.shape[1], + img.shape[2], + projection_dim // (patchsize_x * patchsize_y)), + name="reshape_patches")(encoded_patches) return encoded_patches def vit_resnet50_unet(num_patches, diff --git a/src/eynollah/training/reload-models-v0.8.mk b/src/eynollah/training/reload-models-v0.8.mk index b7a38dd..07be7cf 100644 --- a/src/eynollah/training/reload-models-v0.8.mk +++ b/src/eynollah/training/reload-models-v0.8.mk @@ -26,16 +26,17 @@ RELOADABLE_MODELS = \ all: $(RELOADABLE_MODELS) $(MODELS_DST)/%: $(MODELS_SRC)/% - mkdir -p $@ test -e $&1 | tee $(notdir $<).log - cp $ Date: Tue, 19 May 2026 03:20:24 +0200 Subject: [PATCH 017/121] =?UTF-8?q?reload=5Fweights:=20`save()`=20?= =?UTF-8?q?=E2=86=92=20`export()`=20w/=20`serve()`=20inference?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/eynollah/model_zoo/model_zoo.py | 12 +++++------- src/eynollah/training/train.py | 9 ++++++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 3de8b6b..815663e 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -191,14 +191,12 @@ class EynollahModelZoo: try: # avoid wasting VRAM on non-transformer models model = load_model(model_path, compile=False) - except Exception as e: - self.logger.error(e) - model = load_model( - model_path, compile=False, - custom_objects=dict(PatchEncoder=PatchEncoder, - Patches=Patches)) + assert isinstance(model, KerasModel) model.make_predict_function() - assert isinstance(model, KerasModel) + except ValueError: + model = tf.saved_model.load(model_path) + model.predict_on_batch = model.serve + model.input_shape = model.signatures.get('serving_default').inputs[0].shape model._name = model_category if resized: model = wrap_layout_model_resized(model) diff --git a/src/eynollah/training/train.py b/src/eynollah/training/train.py index de998fd..00ed6ee 100644 --- a/src/eynollah/training/train.py +++ b/src/eynollah/training/train.py @@ -562,7 +562,8 @@ def run(_config, if reload_weights: model.load_weights(dir_of_start_model).assert_existing_objects_matched().expect_partial() dir_save = os.path.join(dir_output, os.path.basename(os.path.normpath(dir_of_start_model))) - model.save(dir_save, include_optimizer=False) + #model.save(dir_save, include_optimizer=False) + model.export(dir_save) with open(os.path.join(dir_save, "config.json"), "w") as fp: json.dump(_config, fp) # encode dict into JSON _log.info("reloaded model from %s to %s", dir_of_start_model, dir_save) @@ -725,7 +726,8 @@ def run(_config, if reload_weights: model.load_weights(dir_of_start_model).assert_existing_objects_matched().expect_partial() dir_save = os.path.join(dir_output, os.path.basename(os.path.normpath(dir_of_start_model))) - model.save(dir_save, include_optimizer=False) + #model.save(dir_save, include_optimizer=False) + model.export(dir_save) with open(os.path.join(dir_save, "config.json"), "w") as fp: json.dump(_config, fp) # encode dict into JSON _log.info("reloaded model from %s to %s", dir_of_start_model, dir_save) @@ -843,7 +845,8 @@ def run(_config, if reload_weights: model.load_weights(dir_of_start_model).assert_existing_objects_matched().expect_partial() dir_save = os.path.join(dir_output, os.path.basename(os.path.normpath(dir_of_start_model))) - model.save(dir_save, include_optimizer=False) + #model.save(dir_save, include_optimizer=False) + model.export(dir_save) with open(os.path.join(dir_save, "config.json"), "w") as fp: json.dump(_config, fp) # encode dict into JSON _log.info("reloaded model from %s to %s", dir_of_start_model, dir_save) From 3de1407d1811d1c3135a3f353ef3260947ab3a93 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 02:38:20 +0200 Subject: [PATCH 018/121] drop unnecessary TF / Torch imports --- src/eynollah/cli/__init__.py | 4 ---- src/eynollah/extract_images.py | 7 ------- src/eynollah/eynollah_imports.py | 13 ------------- src/eynollah/eynollah_ocr.py | 4 ---- src/eynollah/mb_ro_on_layout.py | 4 ---- src/eynollah/model_zoo/model_zoo.py | 4 ++++ src/eynollah/ocrd_cli.py | 6 ++---- 7 files changed, 6 insertions(+), 36 deletions(-) delete mode 100644 src/eynollah/eynollah_imports.py diff --git a/src/eynollah/cli/__init__.py b/src/eynollah/cli/__init__.py index 43ed046..1584fa5 100644 --- a/src/eynollah/cli/__init__.py +++ b/src/eynollah/cli/__init__.py @@ -1,7 +1,3 @@ -# NOTE: For predictable order of imports of torch/shapely/tensorflow -# this must be the first import of the CLI! -from ..eynollah_imports import imported_libs - from .cli import main from .cli_binarize import binarize_cli from .cli_enhance import enhance_cli diff --git a/src/eynollah/extract_images.py b/src/eynollah/extract_images.py index 7a7e3f6..40476a3 100644 --- a/src/eynollah/extract_images.py +++ b/src/eynollah/extract_images.py @@ -9,7 +9,6 @@ import os import time from typing import Optional from pathlib import Path -import tensorflow as tf import numpy as np import cv2 @@ -64,12 +63,6 @@ class EynollahImageExtractor(Eynollah): t_start = time.time() - try: - for device in tf.config.list_physical_devices('GPU'): - tf.config.experimental.set_memory_growth(device, True) - except: - self.logger.warning("no GPU device available") - self.logger.info("Loading models...") self.setup_models() self.logger.info(f"Model initialization complete ({time.time() - t_start:.1f}s)") diff --git a/src/eynollah/eynollah_imports.py b/src/eynollah/eynollah_imports.py deleted file mode 100644 index 496406c..0000000 --- a/src/eynollah/eynollah_imports.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -Load libraries with possible race conditions once. This must be imported as the first module of eynollah. -""" -import os -os.environ['TF_USE_LEGACY_KERAS'] = '1' # avoid Keras 3 after TF 2.15 - -from ocrd_utils import tf_disable_interactive_logs -from torch import * -tf_disable_interactive_logs() -import tensorflow.keras -from shapely import * -imported_libs = True -__all__ = ['imported_libs'] diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 4470671..77ad98f 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -14,10 +14,6 @@ from cv2.typing import MatLike from xml.etree import ElementTree as ET from PIL import Image, ImageDraw import numpy as np -try: - import torch -except ImportError: - torch = None from .eynollah import Eynollah diff --git a/src/eynollah/mb_ro_on_layout.py b/src/eynollah/mb_ro_on_layout.py index 5725ba1..6c0477b 100644 --- a/src/eynollah/mb_ro_on_layout.py +++ b/src/eynollah/mb_ro_on_layout.py @@ -17,10 +17,6 @@ import cv2 import numpy as np import statistics -os.environ['TF_USE_LEGACY_KERAS'] = '1' # avoid Keras 3 after TF 2.15 -import tensorflow as tf -from tensorflow.keras.models import Model - from .eynollah import Eynollah from .model_zoo import EynollahModelZoo from .utils.resize import resize_image diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 815663e..ec35a80 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -269,6 +269,10 @@ class EynollahModelZoo: """ Load decoder for OCR """ + os.environ['TF_USE_LEGACY_KERAS'] = '1' # avoid Keras 3 after TF 2.15 + from ocrd_utils import tf_disable_interactive_logs + tf_disable_interactive_logs() + from tensorflow.keras.layers import StringLookup characters = self._load_characters() diff --git a/src/eynollah/ocrd_cli.py b/src/eynollah/ocrd_cli.py index acd8d4e..effecb2 100644 --- a/src/eynollah/ocrd_cli.py +++ b/src/eynollah/ocrd_cli.py @@ -1,10 +1,8 @@ -# NOTE: For predictable order of imports of torch/shapely/tensorflow -# this must be the first import of the CLI! -from .eynollah_imports import imported_libs -from .processor import EynollahProcessor from click import command from ocrd.decorators import ocrd_cli_options, ocrd_cli_wrap_processor +from .processor import EynollahProcessor + @command() @ocrd_cli_options def main(*args, **kwargs): From 7f2bf715df02911325dea68228dca33dd9137fa7 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 02:39:59 +0200 Subject: [PATCH 019/121] ModelZoo.load_model: fix loading exported vs saved models --- src/eynollah/model_zoo/model_zoo.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index ec35a80..a1f9a24 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -191,9 +191,8 @@ class EynollahModelZoo: try: # avoid wasting VRAM on non-transformer models model = load_model(model_path, compile=False) - assert isinstance(model, KerasModel) model.make_predict_function() - except ValueError: + except (AttributeError, ValueError): model = tf.saved_model.load(model_path) model.predict_on_batch = model.serve model.input_shape = model.signatures.get('serving_default').inputs[0].shape From 94a5e9da149967b4f3a54c87da7108035d1dd236 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 02:41:19 +0200 Subject: [PATCH 020/121] ModelZoo.load_model: avoid attempting to load exported models as Keras models (which causes a warning), but switch to TF-Serving import right away --- src/eynollah/model_zoo/model_zoo.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index a1f9a24..b97911a 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -189,7 +189,8 @@ class EynollahModelZoo: self.override_models((model_category, model_variant, model_path_override)) model_path = self.model_path(model_category, model_variant) try: - # avoid wasting VRAM on non-transformer models + if model_path.is_dir() and not (model_path / "keras_metadata.pb").exists(): + raise ValueError() model = load_model(model_path, compile=False) model.make_predict_function() except (AttributeError, ValueError): From bf7ec0233df245ff14b18472fdaa2cb2bda51a1e Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 02:43:34 +0200 Subject: [PATCH 021/121] =?UTF-8?q?ModelZoo.load=5Fmodel:=20use=20`memory?= =?UTF-8?q?=5Flimit`=20instead=20of=20`memory=5Fgrowth`=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - growth strategy is more flexible, but uses much more VRAM - limit strategy needs to be calibrated to models (currently fixed), and batch size, but needs much less VRAM and is faster --- src/eynollah/model_zoo/model_zoo.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index b97911a..c63a58d 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -169,7 +169,23 @@ class EynollahModelZoo: gpus = gpus[:1] # TF will always use first allowable tf.config.set_visible_devices(gpus, 'GPU') for device in gpus: - tf.config.experimental.set_memory_growth(device, True) + # tf.config.experimental.set_memory_growth(device, True) + # dynamic growth never frees memory (to avoid fragmentation), + # so the VRAM requirements end up much larger than feasible + # (for small GPUs); so try hard (calibrated) limits instead: + tf.config.set_logical_device_configuration( + device, + [tf.config.LogicalDeviceConfiguration(memory_limit={ + "binarization": 868, # due to bs 5 + "enhancement": 980, # due to bs 3 + "col_classifier": 210, + "page": 618, + "textline": 1680, # 954 for bs 1 + "region_1_2": 1580, + "region_fl_np": 1756, + "table": 1818, + "reading_order": 632, + }[model_category])]) vendor_name = ( tf.config.experimental.get_device_details(device) .get('device_name', 'unknown')) From f9f9130dbbb4c755d96e56f9855d9871db592806 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 03:21:36 +0200 Subject: [PATCH 022/121] do_order_of_regions: remove redundant+overcautious assertion --- src/eynollah/eynollah.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/eynollah/eynollah.py b/src/eynollah/eynollah.py index c632941..9db47ce 100644 --- a/src/eynollah/eynollah.py +++ b/src/eynollah/eynollah.py @@ -1148,7 +1148,6 @@ class Eynollah: boxes, textline_mask_tot ): - assert np.any(textline_mask_tot) self.logger.debug("enter do_order_of_regions") contours_only_text_parent = ensure_array(contours_only_text_parent) contours_only_text_parent_h = ensure_array(contours_only_text_parent_h) From d50bd7c650fe6413efe5d70bfdc235716d22e5d7 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 14:20:51 +0200 Subject: [PATCH 023/121] trocr: avoid warnings by passing `clean_up_tokenization_spaces=False` --- src/eynollah/eynollah_ocr.py | 50 ++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 77ad98f..4371453 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -139,11 +139,14 @@ class Eynollah_ocr(Eynollah): cropped_lines = [] indexer_b_s = 0 - pixel_values_merged = self.model_zoo.get('trocr_processor')(imgs, return_tensors="pt").pixel_values + pixel_values_merged = self.model_zoo.get('trocr_processor')( + imgs, return_tensors="pt").pixel_values generated_ids_merged = self.model_zoo.get('ocr').generate( pixel_values_merged.to(self.device)) generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode( - generated_ids_merged, skip_special_tokens=True) + generated_ids_merged, + skip_special_tokens=True, + clean_up_tokenization_spaces=False) extracted_texts = extracted_texts + generated_text_merged @@ -162,11 +165,14 @@ class Eynollah_ocr(Eynollah): cropped_lines = [] indexer_b_s = 0 - pixel_values_merged = self.model_zoo.get('trocr_processor')(imgs, return_tensors="pt").pixel_values + pixel_values_merged = self.model_zoo.get('trocr_processor')( + imgs, return_tensors="pt").pixel_values generated_ids_merged = self.model_zoo.get('ocr').generate( pixel_values_merged.to(self.device)) generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode( - generated_ids_merged, skip_special_tokens=True) + generated_ids_merged, + skip_special_tokens=True, + clean_up_tokenization_spaces=False) extracted_texts = extracted_texts + generated_text_merged @@ -182,11 +188,14 @@ class Eynollah_ocr(Eynollah): cropped_lines = [] indexer_b_s = 0 - pixel_values_merged = self.model_zoo.get('trocr_processor')(imgs, return_tensors="pt").pixel_values + pixel_values_merged = self.model_zoo.get('trocr_processor')( + imgs, return_tensors="pt").pixel_values generated_ids_merged = self.model_zoo.get('ocr').generate( pixel_values_merged.to(self.device)) generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode( - generated_ids_merged, skip_special_tokens=True) + generated_ids_merged, + skip_special_tokens=True, + clean_up_tokenization_spaces=False) extracted_texts = extracted_texts + generated_text_merged @@ -194,22 +203,23 @@ class Eynollah_ocr(Eynollah): cropped_lines.append(img_crop) cropped_lines_meging_indexing.append(0) indexer_b_s+=1 - + if indexer_b_s==self.b_s: imgs = cropped_lines[:] cropped_lines = [] indexer_b_s = 0 - - pixel_values_merged = self.model_zoo.get('trocr_processor')(imgs, return_tensors="pt").pixel_values + + pixel_values_merged = self.model_zoo.get('trocr_processor')( + imgs, return_tensors="pt").pixel_values generated_ids_merged = self.model_zoo.get('ocr').generate( pixel_values_merged.to(self.device)) generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode( - generated_ids_merged, skip_special_tokens=True) - + generated_ids_merged, + skip_special_tokens=True, + clean_up_tokenization_spaces=False) + extracted_texts = extracted_texts + generated_text_merged - - - + indexer_text_region = indexer_text_region +1 if indexer_b_s!=0: @@ -217,9 +227,14 @@ class Eynollah_ocr(Eynollah): cropped_lines = [] indexer_b_s = 0 - pixel_values_merged = self.model_zoo.get('trocr_processor')(imgs, return_tensors="pt").pixel_values - generated_ids_merged = self.model_zoo.get('ocr').generate(pixel_values_merged.to(self.device)) - generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode(generated_ids_merged, skip_special_tokens=True) + pixel_values_merged = self.model_zoo.get('trocr_processor')( + imgs, return_tensors="pt").pixel_values + generated_ids_merged = self.model_zoo.get('ocr').generate( + pixel_values_merged.to(self.device)) + generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode( + generated_ids_merged, + skip_special_tokens=True, + clean_up_tokenization_spaces=False) extracted_texts = extracted_texts + generated_text_merged @@ -750,6 +765,7 @@ class Eynollah_ocr(Eynollah): indexer_textregion = indexer_textregion + 1 ET.register_namespace("",page_ns) + self.logger.info("output filename: '%s'", out_file_ocr) page_tree.write(out_file_ocr, xml_declaration=True, method='xml', encoding="utf-8", default_namespace=None) def run( From 1d67e65f11ad5266ba27262d38b4c49a7a864714 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 15:48:21 +0200 Subject: [PATCH 024/121] =?UTF-8?q?trocr:=20simplify,=20batch=20over=20ent?= =?UTF-8?q?ire=20page=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - batching over entire page instead of region-wise (underfilling batches) - avoid copied redundant code --- src/eynollah/eynollah_ocr.py | 201 +++++++------------------------- src/eynollah/utils/utils_ocr.py | 6 + 2 files changed, 51 insertions(+), 156 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 4371453..747d2f5 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -14,6 +14,7 @@ from cv2.typing import MatLike from xml.etree import ElementTree as ET from PIL import Image, ImageDraw import numpy as np +from ocrd_utils import polygon_from_points, xywh_from_polygon from .eynollah import Eynollah @@ -31,6 +32,7 @@ from .utils.utils_ocr import ( preprocess_and_resize_image_for_ocrcnn_model, return_textlines_split_if_needed, rotate_image_with_padding, + batched, ) # TODO: refine typing @@ -90,143 +92,55 @@ class Eynollah_ocr(Eynollah): ) -> EynollahOcrResult: total_bb_coordinates = [] - - cropped_lines = [] cropped_lines_region_indexer = [] cropped_lines_meging_indexing = [] - extracted_texts = [] - indexer_text_region = 0 - indexer_b_s = 0 - - for nn in page_tree.getroot().iter(f'{{{page_ns}}}TextRegion'): - for child_textregion in nn: - if child_textregion.tag.endswith("TextLine"): - - for child_textlines in child_textregion: - if child_textlines.tag.endswith("Coords"): - cropped_lines_region_indexer.append(indexer_text_region) - p_h=child_textlines.attrib['points'].split(' ') - textline_coords = np.array( [ [int(x.split(',')[0]), - int(x.split(',')[1]) ] - for x in p_h] ) - x,y,w,h = cv2.boundingRect(textline_coords) - - total_bb_coordinates.append([x,y,w,h]) - - h2w_ratio = h/float(w) - - img_poly_on_img = np.copy(img) - mask_poly = np.zeros(img.shape) - mask_poly = cv2.fillPoly(mask_poly, pts=[textline_coords], color=(1, 1, 1)) - - mask_poly = mask_poly[y:y+h, x:x+w, :] - img_crop = img_poly_on_img[y:y+h, x:x+w, :] - img_crop[mask_poly==0] = 255 - - self.logger.debug("processing %d lines for '%s'", - len(cropped_lines), nn.attrib['id']) - if h2w_ratio > 0.1: - cropped_lines.append(resize_image(img_crop, - tr_ocr_input_height_and_width, - tr_ocr_input_height_and_width) ) - cropped_lines_meging_indexing.append(0) - indexer_b_s+=1 - if indexer_b_s==self.b_s: - imgs = cropped_lines[:] - cropped_lines = [] - indexer_b_s = 0 - - pixel_values_merged = self.model_zoo.get('trocr_processor')( - imgs, return_tensors="pt").pixel_values - generated_ids_merged = self.model_zoo.get('ocr').generate( - pixel_values_merged.to(self.device)) - generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode( - generated_ids_merged, - skip_special_tokens=True, - clean_up_tokenization_spaces=False) - - extracted_texts = extracted_texts + generated_text_merged - - else: - splited_images, _ = return_textlines_split_if_needed(img_crop, None) - #print(splited_images) - if splited_images: - cropped_lines.append(resize_image(splited_images[0], - tr_ocr_input_height_and_width, - tr_ocr_input_height_and_width)) - cropped_lines_meging_indexing.append(1) - indexer_b_s+=1 - - if indexer_b_s==self.b_s: - imgs = cropped_lines[:] - cropped_lines = [] - indexer_b_s = 0 - - pixel_values_merged = self.model_zoo.get('trocr_processor')( - imgs, return_tensors="pt").pixel_values - generated_ids_merged = self.model_zoo.get('ocr').generate( - pixel_values_merged.to(self.device)) - generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode( - generated_ids_merged, - skip_special_tokens=True, - clean_up_tokenization_spaces=False) - - extracted_texts = extracted_texts + generated_text_merged - - - cropped_lines.append(resize_image(splited_images[1], - tr_ocr_input_height_and_width, - tr_ocr_input_height_and_width)) - cropped_lines_meging_indexing.append(-1) - indexer_b_s+=1 - - if indexer_b_s==self.b_s: - imgs = cropped_lines[:] - cropped_lines = [] - indexer_b_s = 0 - - pixel_values_merged = self.model_zoo.get('trocr_processor')( - imgs, return_tensors="pt").pixel_values - generated_ids_merged = self.model_zoo.get('ocr').generate( - pixel_values_merged.to(self.device)) - generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode( - generated_ids_merged, - skip_special_tokens=True, - clean_up_tokenization_spaces=False) - - extracted_texts = extracted_texts + generated_text_merged - - else: - cropped_lines.append(img_crop) - cropped_lines_meging_indexing.append(0) - indexer_b_s+=1 + for n_region, region in enumerate(page_tree.getroot().iter('{%s}TextRegion' % page_ns)): + for n_line, line in enumerate(region.iter('{%s}TextLine' % page_ns)): + cropped_lines_region_indexer.append(n_region) - if indexer_b_s==self.b_s: - imgs = cropped_lines[:] - cropped_lines = [] - indexer_b_s = 0 + coords = line.find('{%s}Coords' % page_ns) + if coords is None: + self.logger.warning("region '%s' line '%s' has no Coords", region.attrib['id'], line.attrib['id']) + continue + poly = np.array(polygon_from_points(coords.attrib['points'])).astype(int) + cont = poly[:, np.newaxis] + xywh = xywh_from_polygon(poly) + x, y, w, h = xywh['x'], xywh['y'], xywh['w'], xywh['h'] - pixel_values_merged = self.model_zoo.get('trocr_processor')( - imgs, return_tensors="pt").pixel_values - generated_ids_merged = self.model_zoo.get('ocr').generate( - pixel_values_merged.to(self.device)) - generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode( - generated_ids_merged, - skip_special_tokens=True, - clean_up_tokenization_spaces=False) + total_bb_coordinates.append([x, y, w, h]) - extracted_texts = extracted_texts + generated_text_merged + img_crop = img[y: y + h, x: x + w] + mask_poly = np.zeros(img_crop.shape[:2], dtype=np.uint8) + mask_poly = cv2.fillPoly(mask_poly, pts=[cont - [x, y]], color=1) + img_crop[mask_poly == 0] = 255 # FIXME: or median color? - indexer_text_region = indexer_text_region +1 + if h > 0.1 * w: + cropped_lines.append(resize_image(img_crop, + tr_ocr_input_height_and_width, + tr_ocr_input_height_and_width) ) + cropped_lines_meging_indexing.append(0) + else: + splited_images, _ = return_textlines_split_if_needed(img_crop, None) + if splited_images: + cropped_lines.append(resize_image(splited_images[0], + tr_ocr_input_height_and_width, + tr_ocr_input_height_and_width)) + cropped_lines_meging_indexing.append(1) + cropped_lines.append(resize_image(splited_images[1], + tr_ocr_input_height_and_width, + tr_ocr_input_height_and_width)) + cropped_lines_meging_indexing.append(-1) + else: + cropped_lines.append(img_crop) + cropped_lines_meging_indexing.append(0) - if indexer_b_s!=0: - imgs = cropped_lines[:] - cropped_lines = [] - indexer_b_s = 0 - + + self.logger.debug("processing %d lines for %d regions", + len(cropped_lines), len(set(cropped_lines_region_indexer))) + for imgs in batched(cropped_lines, self.b_s): pixel_values_merged = self.model_zoo.get('trocr_processor')( imgs, return_tensors="pt").pixel_values generated_ids_merged = self.model_zoo.get('ocr').generate( @@ -235,40 +149,15 @@ class Eynollah_ocr(Eynollah): generated_ids_merged, skip_special_tokens=True, clean_up_tokenization_spaces=False) - extracted_texts = extracted_texts + generated_text_merged - - ####extracted_texts = [] - ####n_iterations = math.ceil(len(cropped_lines) / self.b_s) - - ####for i in range(n_iterations): - ####if i==(n_iterations-1): - ####n_start = i*self.b_s - ####imgs = cropped_lines[n_start:] - ####else: - ####n_start = i*self.b_s - ####n_end = (i+1)*self.b_s - ####imgs = cropped_lines[n_start:n_end] - ####pixel_values_merged = self.model_zoo.get('trocr_processor')(imgs, return_tensors="pt").pixel_values - ####generated_ids_merged = self.model_ocr.generate( - #### pixel_values_merged.to(self.device)) - ####generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode( - #### generated_ids_merged, skip_special_tokens=True) - - ####extracted_texts = extracted_texts + generated_text_merged - del cropped_lines gc.collect() extracted_texts_merged = [extracted_texts[ind] - if cropped_lines_meging_indexing[ind]==0 - else extracted_texts[ind]+" "+extracted_texts[ind+1] - if cropped_lines_meging_indexing[ind]==1 - else None - for ind in range(len(cropped_lines_meging_indexing))] - - extracted_texts_merged = [ind for ind in extracted_texts_merged if ind is not None] - #print(extracted_texts_merged, len(extracted_texts_merged)) + if cropped_lines_meging_indexing[ind] == 0 + else extracted_texts[ind] + " " + extracted_texts[ind + 1] + for ind in range(len(cropped_lines_meging_indexing)) + if cropped_lines_meging_indexing[ind] >= 0] return EynollahOcrResult( extracted_texts_merged=extracted_texts_merged, diff --git a/src/eynollah/utils/utils_ocr.py b/src/eynollah/utils/utils_ocr.py index 93d1137..6914fee 100644 --- a/src/eynollah/utils/utils_ocr.py +++ b/src/eynollah/utils/utils_ocr.py @@ -1,5 +1,6 @@ import math import copy +from itertools import islice import numpy as np import cv2 @@ -502,3 +503,8 @@ def return_rnn_cnn_ocr_of_given_textlines(image, ocr_textline_in_textregion.append(text_textline) ocr_all_textlines.append(ocr_textline_in_textregion) return ocr_all_textlines + +def batched(iterable, n): + iterator = iter(iterable) + while batch := tuple(islice(iterator, n)): + yield batch From f3649adbf24eb6b4d189846d67eeed88f153ea06 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 17:23:11 +0200 Subject: [PATCH 025/121] trocr: apply `do_not_mask_with_textline_contour` here, too --- src/eynollah/eynollah_ocr.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 747d2f5..f1b155b 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -113,9 +113,10 @@ class Eynollah_ocr(Eynollah): total_bb_coordinates.append([x, y, w, h]) img_crop = img[y: y + h, x: x + w] - mask_poly = np.zeros(img_crop.shape[:2], dtype=np.uint8) - mask_poly = cv2.fillPoly(mask_poly, pts=[cont - [x, y]], color=1) - img_crop[mask_poly == 0] = 255 # FIXME: or median color? + if not self.do_not_mask_with_textline_contour: + mask_poly = np.zeros(img_crop.shape[:2], dtype=np.uint8) + mask_poly = cv2.fillPoly(mask_poly, pts=[cont - [x, y]], color=1) + img_crop[mask_poly == 0] = 255 # FIXME: or median color? if h > 0.1 * w: cropped_lines.append(resize_image(img_crop, From 000e4ac8d8b66f874b0423c627c9bdccab880b57 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 17:25:39 +0200 Subject: [PATCH 026/121] trocr: extract confidence, too --- src/eynollah/eynollah_ocr.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index f1b155b..faeb042 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -90,12 +90,14 @@ class Eynollah_ocr(Eynollah): page_ns, tr_ocr_input_height_and_width, ) -> EynollahOcrResult: + import torch total_bb_coordinates = [] cropped_lines = [] cropped_lines_region_indexer = [] cropped_lines_meging_indexing = [] extracted_texts = [] + extracted_confs = [] for n_region, region in enumerate(page_tree.getroot().iter('{%s}TextRegion' % page_ns)): for n_line, line in enumerate(region.iter('{%s}TextLine' % page_ns)): @@ -142,15 +144,20 @@ class Eynollah_ocr(Eynollah): self.logger.debug("processing %d lines for %d regions", len(cropped_lines), len(set(cropped_lines_region_indexer))) for imgs in batched(cropped_lines, self.b_s): - pixel_values_merged = self.model_zoo.get('trocr_processor')( + pixel_values = self.model_zoo.get('trocr_processor')( imgs, return_tensors="pt").pixel_values - generated_ids_merged = self.model_zoo.get('ocr').generate( - pixel_values_merged.to(self.device)) - generated_text_merged = self.model_zoo.get('trocr_processor').batch_decode( - generated_ids_merged, + output = self.model_zoo.get('ocr').generate( + pixel_values.to(self.device), + output_scores=True, + return_dict_in_generate=True) + conf = torch.max(torch.softmax(torch.cat( + output.scores, dim=0), dim=1), dim=1).values.tolist() + text = self.model_zoo.get('trocr_processor').batch_decode( + output.sequences, skip_special_tokens=True, clean_up_tokenization_spaces=False) - extracted_texts = extracted_texts + generated_text_merged + extracted_confs.extend(conf) + extracted_texts.extend(text) del cropped_lines gc.collect() @@ -159,10 +166,15 @@ class Eynollah_ocr(Eynollah): else extracted_texts[ind] + " " + extracted_texts[ind + 1] for ind in range(len(cropped_lines_meging_indexing)) if cropped_lines_meging_indexing[ind] >= 0] + extracted_confs_merged = [extracted_confs[ind] + if cropped_lines_meging_indexing[ind] == 0 + else 0.5 * (extracted_confs[ind] + extracted_confs[ind + 1]) + for ind in range(len(cropped_lines_meging_indexing)) + if cropped_lines_meging_indexing[ind] >= 0] return EynollahOcrResult( extracted_texts_merged=extracted_texts_merged, - extracted_conf_value_merged=None, + extracted_conf_value_merged=extracted_confs_merged, cropped_lines_region_indexer=cropped_lines_region_indexer, total_bb_coordinates=total_bb_coordinates, ) @@ -618,6 +630,7 @@ class Eynollah_ocr(Eynollah): has_textline = False for child_textregion in nn: + # FIXME: should remove Word level, if it already exists if child_textregion.tag.endswith("TextLine"): is_textline_text = False From 074753a98e647b83c99358034a610e1f5364c79f Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 17:25:53 +0200 Subject: [PATCH 027/121] ModelZoo: fix Torch device selection --- src/eynollah/model_zoo/model_zoo.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index c63a58d..be41d2a 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -247,9 +247,9 @@ class EynollahModelZoo: if variant == 'tr': from transformers import VisionEncoderDecoderModel import torch - ret = VisionEncoderDecoderModel.from_pretrained(model_dir) - assert isinstance(ret, VisionEncoderDecoderModel) - dev = torch.device('cpu') + model = VisionEncoderDecoderModel.from_pretrained(model_dir) + assert isinstance(model, VisionEncoderDecoderModel) + device0 = torch.device('cpu') if not device and torch.cuda.is_available(): device = 'GPU' # try if device and ':' in device: @@ -260,17 +260,17 @@ class EynollahModelZoo: break if device and device.startswith('GPU'): try: - dev = torch.device('cuda', int(device[3:] or 0)) - name = torch.cuda.get_device_name(dev) - self.logger.info("using GPU %s (%s) for model ocr:tr", dev, name) + device0 = torch.device('cuda', int(device[3:] or 0)) + name = torch.cuda.get_device_name(device0) + self.logger.info("using GPU %s (%s) for model ocr:tr", device0, name) except: self.logger.exception("cannot configure GPU device") - dev = torch.device('cpu') - if dev.type == 'cuda': - ret.to(dev) + device0 = torch.device('cpu') + if device0.type == 'cuda': + model.to(device0) else: self.logger.warning("no GPU device available") - return ret + return model return self.load_model('ocr', model_variant=variant, device=device) From ea41dcae1d401ac2b4b74403d4cc515d8da6c4ba Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 17:52:27 +0200 Subject: [PATCH 028/121] trocr: use beam search instead of greedy decoding --- src/eynollah/eynollah_ocr.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index faeb042..b94853b 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -90,7 +90,6 @@ class Eynollah_ocr(Eynollah): page_ns, tr_ocr_input_height_and_width, ) -> EynollahOcrResult: - import torch total_bb_coordinates = [] cropped_lines = [] @@ -148,10 +147,16 @@ class Eynollah_ocr(Eynollah): imgs, return_tensors="pt").pixel_values output = self.model_zoo.get('ocr').generate( pixel_values.to(self.device), + # beam search instead of greedy decoding: + num_beams=4, + # also return probability output_scores=True, return_dict_in_generate=True) - conf = torch.max(torch.softmax(torch.cat( - output.scores, dim=0), dim=1), dim=1).values.tolist() + if output.sequences_scores is not None: + # log-prob averaged over length + conf = output.sequences_scores.exp().clamp(0.0, 1.0).tolist() + else: + conf = [1.0] * len(output.sequences) text = self.model_zoo.get('trocr_processor').batch_decode( output.sequences, skip_special_tokens=True, From f3a93983c0848bc02785a24656b9524f90dfd22a Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 22:50:13 +0200 Subject: [PATCH 029/121] ModelZoo: add `ocr` key for `memory_limit` --- src/eynollah/model_zoo/model_zoo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index be41d2a..2bac7f3 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -185,6 +185,7 @@ class EynollahModelZoo: "region_fl_np": 1756, "table": 1818, "reading_order": 632, + "ocr": 850, }[model_category])]) vendor_name = ( tf.config.experimental.get_device_details(device) From 0836230c6b29384e7ecb6700d92573518dac64ef Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 21 May 2026 22:50:53 +0200 Subject: [PATCH 030/121] utils_ocr: avoid module-level import of TF --- src/eynollah/utils/utils_ocr.py | 8 +++++++- tests/cli_tests/test_ocr.py | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/eynollah/utils/utils_ocr.py b/src/eynollah/utils/utils_ocr.py index 6914fee..817406c 100644 --- a/src/eynollah/utils/utils_ocr.py +++ b/src/eynollah/utils/utils_ocr.py @@ -4,7 +4,9 @@ from itertools import islice import numpy as np import cv2 -import tensorflow as tf +# avoid module-level import: +# import tensorflow as tf +# (wait for tf-keras and logging setup in ModelZoo.load_model) from scipy.signal import find_peaks from scipy.ndimage import gaussian_filter1d from PIL import Image, ImageDraw, ImageFont @@ -13,6 +15,8 @@ from .resize import resize_image def decode_batch_predictions(pred, num_to_char, max_len = 128): + import tensorflow as tf + # input_len is the product of the batch size and the # number of time steps. input_len = np.ones(pred.shape[0]) * pred.shape[1] @@ -40,6 +44,8 @@ def decode_batch_predictions(pred, num_to_char, max_len = 128): def distortion_free_resize(image, img_size): + import tensorflow as tf + w, h = img_size image = tf.image.resize(image, size=(h, w), preserve_aspect_ratio=True) diff --git a/tests/cli_tests/test_ocr.py b/tests/cli_tests/test_ocr.py index 6bf3080..cf34e06 100644 --- a/tests/cli_tests/test_ocr.py +++ b/tests/cli_tests/test_ocr.py @@ -30,7 +30,7 @@ def test_run_eynollah_ocr_filename( '-o', str(outfile.parent), ] + options, [ - # FIXME: ocr has no logging! + 'output filename:' ] ) assert outfile.exists() @@ -57,7 +57,7 @@ def test_run_eynollah_ocr_directory( '-o', str(outdir), ], [ - # FIXME: ocr has no logging! + 'output filename:' ] ) assert len(list(outdir.iterdir())) == 2 From 26afc5ddab34c2d0c966a706f8b283b891280209 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 22 May 2026 12:35:44 +0200 Subject: [PATCH 031/121] ModelZoo: ensure exported TensorShape is converted to plain tuple --- src/eynollah/model_zoo/model_zoo.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 2bac7f3..d5e69a2 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -207,13 +207,14 @@ class EynollahModelZoo: model_path = self.model_path(model_category, model_variant) try: if model_path.is_dir() and not (model_path / "keras_metadata.pb").exists(): + # short-cut to avoid warning for exported models raise ValueError() model = load_model(model_path, compile=False) model.make_predict_function() except (AttributeError, ValueError): model = tf.saved_model.load(model_path) model.predict_on_batch = model.serve - model.input_shape = model.signatures.get('serving_default').inputs[0].shape + model.input_shape = tuple(model.signatures.get('serving_default').inputs[0].shape) model._name = model_category if resized: model = wrap_layout_model_resized(model) From 9801129aa6da83af1562fd14b47a37b67011de5a Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 22 May 2026 12:37:07 +0200 Subject: [PATCH 032/121] estimate_skew_contours: ensure retval is always float --- src/eynollah/utils/contour.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eynollah/utils/contour.py b/src/eynollah/utils/contour.py index 1dbead1..eda60e9 100644 --- a/src/eynollah/utils/contour.py +++ b/src/eynollah/utils/contour.py @@ -330,7 +330,7 @@ def estimate_skew_contours(contours): if not np.any(usable): raise ValueError("not enough contours with consistent length") if np.count_nonzero(usable) == 1: - return angle_in[usable] + return angle_in[usable][0] # 4. there is no way to distinguish between +90 and -89.9 here, # so map to [0,180] when calculating averages, then map back to [-90,90] # (we don't want -90 and +89 to average zero, or +1 and +179 to average 90) From c4a7eec5b3195cd3114a0d2b54de723c806e5bab Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 27 May 2026 01:58:21 +0200 Subject: [PATCH 033/121] models: cosmetics - using `Reshape`, do not pass `target_shape` as kwarg - add a default `name` for `Patches` and `PatchEncoder` --- src/eynollah/patch_encoder.py | 8 ++++---- src/eynollah/training/models.py | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/eynollah/patch_encoder.py b/src/eynollah/patch_encoder.py index f163132..610f0b4 100644 --- a/src/eynollah/patch_encoder.py +++ b/src/eynollah/patch_encoder.py @@ -6,8 +6,8 @@ from tensorflow.keras import layers, models class PatchEncoder(layers.Layer): # 441=21*21 # 14*14 # 28*28 - def __init__(self, num_patches=441, projection_dim=64): - super().__init__() + def __init__(self, num_patches=441, projection_dim=64, name='encode_patches'): + super().__init__(name=name) self.num_patches = num_patches self.projection_dim = projection_dim self.projection = layers.Dense(self.projection_dim) @@ -23,8 +23,8 @@ class PatchEncoder(layers.Layer): **super().get_config()) class Patches(layers.Layer): - def __init__(self, patch_size_x=1, patch_size_y=1): - super().__init__() + def __init__(self, patch_size_x=1, patch_size_y=1, name='extract_patches'): + super().__init__(name=name) self.patch_size_x = patch_size_x self.patch_size_y = patch_size_y diff --git a/src/eynollah/training/models.py b/src/eynollah/training/models.py index f700d14..c5510f8 100644 --- a/src/eynollah/training/models.py +++ b/src/eynollah/training/models.py @@ -309,9 +309,9 @@ def transformer_block(img, # Skip connection 2. encoded_patches = Add()([x3, x2]) - encoded_patches = Reshape(target_shape=(img.shape[1], - img.shape[2], - projection_dim // (patchsize_x * patchsize_y)), + encoded_patches = Reshape((img.shape[1], + img.shape[2], + projection_dim // (patchsize_x * patchsize_y)), name="reshape_patches")(encoded_patches) return encoded_patches @@ -464,23 +464,23 @@ def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_s new_shape2 = (x2d.shape[1]*x2d.shape[2], x2d.shape[3]) new_shape4 = (x4d.shape[1]*x4d.shape[2], x4d.shape[3]) - x = Reshape(target_shape=new_shape, name="reshape")(x) - x2d = Reshape(target_shape=new_shape2, name="reshape2")(x2d) - x4d = Reshape(target_shape=new_shape4, name="reshape4")(x4d) + x = Reshape(new_shape, name="reshape")(x) + x2d = Reshape(new_shape2, name="reshape2")(x2d) + x4d = Reshape(new_shape4, name="reshape4")(x4d) xrnnorg = Bidirectional(LSTM(image_width, return_sequences=True, dropout=0.25))(x) xrnn2d = Bidirectional(LSTM(image_width, return_sequences=True, dropout=0.25))(x2d) xrnn4d = Bidirectional(LSTM(image_width, return_sequences=True, dropout=0.25))(x4d) - xrnn2d = Reshape(target_shape=(1, xrnn2d.shape[1], xrnn2d.shape[2]), name="reshape6")(xrnn2d) - xrnn4d = Reshape(target_shape=(1, xrnn4d.shape[1], xrnn4d.shape[2]), name="reshape8")(xrnn4d) + xrnn2d = Reshape((1, xrnn2d.shape[1], xrnn2d.shape[2]), name="reshape6")(xrnn2d) + xrnn4d = Reshape((1, xrnn4d.shape[1], xrnn4d.shape[2]), name="reshape8")(xrnn4d) xrnn2dup = UpSampling2D(size=(1, 2), interpolation="nearest")(xrnn2d) xrnn4dup = UpSampling2D(size=(1, 4), interpolation="nearest")(xrnn4d) - xrnn2dup = Reshape(target_shape=(xrnn2dup.shape[2], xrnn2dup.shape[3]), name="reshape10")(xrnn2dup) - xrnn4dup = Reshape(target_shape=(xrnn4dup.shape[2], xrnn4dup.shape[3]), name="reshape12")(xrnn4dup) + xrnn2dup = Reshape((xrnn2dup.shape[2], xrnn2dup.shape[3]), name="reshape10")(xrnn2dup) + xrnn4dup = Reshape((xrnn4dup.shape[2], xrnn4dup.shape[3]), name="reshape12")(xrnn4dup) addition = Add()([xrnnorg, xrnn2dup, xrnn4dup]) From faef1967f87fc497fa988ea4c4d0a2e454d3f633 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 28 May 2026 17:32:02 +0200 Subject: [PATCH 034/121] models.cnn_rnn_ocr_model: add `inference` option, drop model name --- src/eynollah/training/models.py | 34 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/eynollah/training/models.py b/src/eynollah/training/models.py index c5510f8..eb621c6 100644 --- a/src/eynollah/training/models.py +++ b/src/eynollah/training/models.py @@ -422,11 +422,11 @@ def machine_based_reading_order_model(n_classes,input_height=224,input_width=224 return model -def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_seq=None): - input_img = Input(shape=(image_height, image_width, 3), name="image") +def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_len=None, inference=False): + inputs = Input(shape=(image_height, image_width, 3), name="image") labels = Input(name="label", shape=(None,)) - x = Conv2D(64,kernel_size=(3,3),padding="same")(input_img) + x = Conv2D(64,kernel_size=(3,3),padding="same")(inputs) x = BatchNormalization(name="bn1")(x) x = Activation("relu", name="relu1")(x) x = Conv2D(64,kernel_size=(3,3),padding="same")(x) @@ -458,44 +458,44 @@ def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_s x = Activation("relu", name="relu8")(x) x2d = MaxPooling2D(pool_size=(1,2),strides=(1,2))(x) x4d = MaxPooling2D(pool_size=(1,2),strides=(1,2))(x2d) - new_shape = (x.shape[1]*x.shape[2], x.shape[3]) new_shape2 = (x2d.shape[1]*x2d.shape[2], x2d.shape[3]) new_shape4 = (x4d.shape[1]*x4d.shape[2], x4d.shape[3]) - + x = Reshape(new_shape, name="reshape")(x) x2d = Reshape(new_shape2, name="reshape2")(x2d) x4d = Reshape(new_shape4, name="reshape4")(x4d) - + xrnnorg = Bidirectional(LSTM(image_width, return_sequences=True, dropout=0.25))(x) xrnn2d = Bidirectional(LSTM(image_width, return_sequences=True, dropout=0.25))(x2d) xrnn4d = Bidirectional(LSTM(image_width, return_sequences=True, dropout=0.25))(x4d) - + xrnn2d = Reshape((1, xrnn2d.shape[1], xrnn2d.shape[2]), name="reshape6")(xrnn2d) xrnn4d = Reshape((1, xrnn4d.shape[1], xrnn4d.shape[2]), name="reshape8")(xrnn4d) - xrnn2dup = UpSampling2D(size=(1, 2), interpolation="nearest")(xrnn2d) xrnn4dup = UpSampling2D(size=(1, 4), interpolation="nearest")(xrnn4d) - + xrnn2dup = Reshape((xrnn2dup.shape[2], xrnn2dup.shape[3]), name="reshape10")(xrnn2dup) xrnn4dup = Reshape((xrnn4dup.shape[2], xrnn4dup.shape[3]), name="reshape12")(xrnn4dup) addition = Add()([xrnnorg, xrnn2dup, xrnn4dup]) - + addition_rnn = Bidirectional(LSTM(image_width, return_sequences=True, dropout=0.25))(addition) - - out = Conv1D(max_seq, 1, data_format="channels_first")(addition_rnn) + + out = Conv1D(max_len, 1, data_format="channels_first")(addition_rnn) out = BatchNormalization(name="bn9")(out) out = Activation("relu", name="relu9")(out) #out = Conv1D(n_classes, 1, activation='relu', data_format="channels_last")(out) out = Dense(n_classes, activation="softmax", name="dense2")(out) - # Add CTC layer for calculating CTC loss at each step. - output = CTCLayer(name="ctc_loss")(labels, out) - - model = Model(inputs=(input_img, labels), outputs=output, name="handwriting_recognizer") + if inference: + return Model(inputs, out) + + # Add CTC layer for calculating CTC loss at each step. + out = CTCLayer(name="ctc_loss")(labels, out) + + return Model((inputs, labels), out) - return model From 093030f503e0032c97260540ad42c671f3f0d6a1 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 28 May 2026 17:37:45 +0200 Subject: [PATCH 035/121] =?UTF-8?q?train/models:=20move=20all=20model=20bu?= =?UTF-8?q?ilders=20to=20`models.get=5Fmodel()`=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - models: add new `get_model()`, passing in Sacred config to capture builder function arguments - train: fewer imports - train: no need to pass `custom_objects` if loading with `compile=False` (and we custom-compile later, anyway) --- src/eynollah/training/models.py | 49 ++++++++++++++++++++ src/eynollah/training/train.py | 81 ++++----------------------------- 2 files changed, 57 insertions(+), 73 deletions(-) diff --git a/src/eynollah/training/models.py b/src/eynollah/training/models.py index eb621c6..83058ee 100644 --- a/src/eynollah/training/models.py +++ b/src/eynollah/training/models.py @@ -499,3 +499,52 @@ def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_l return Model((inputs, labels), out) +def get_model(config, logger): + from sacred.config import create_captured_function + + task = config['task'] + if task in ["segmentation", "enhancement", "binarization"]: + if config['backbone_type'] == 'nontransformer': + builder = resnet50_unet + else: + num_patches_x, num_patches_y = config['transformer_num_patches_xy'] + num_patches = num_patches_x * num_patches_y + + if config['transformer_cnn_first']: + builder = vit_resnet50_unet + multiple = 32 + else: + builder = vit_resnet50_unet_transformer_before_cnn + multiple = 1 + + assert config['input_height'] == ( + num_patches_y * config['transformer_patchsize_y'] * multiple), ( + "transformer_patchsize_y or transformer_num_patches_xy height value error: " + "input_height should be equal to " + "(transformer_num_patches_xy height value * transformer_patchsize_y * %d)" % multiple) + assert config['input_width'] == ( + num_patches_x * config['transformer_patchsize_x'] * multiple), ( + "transformer_patchsize_x or transformer_num_patches_xy width value error: " + "input_width should be equal to " + "(transformer_num_patches_xy width value * transformer_patchsize_x * %d)" % multiple) + assert 0 == (config['transformer_projection_dim'] % + (config['transformer_patchsize_y'] * + config['transformer_patchsize_x'])), ( + "transformer_projection_dim error: " + "The remainder when parameter transformer_projection_dim is divided by " + "(transformer_patchsize_y*transformer_patchsize_x) should be zero") + + config['num_patches'] = num_patches + elif task == "cnn-rnn-ocr": + builder = cnn_rnn_ocr_model + elif task=='classification': + builder = resnet50_classifier + elif task=='reading_order': + builder = machine_based_reading_order_model + else: + raise ValueError("unknown model task '%s'" % task) + + builder = create_captured_function(builder) + builder.config = config + builder.logger = logger + return builder() diff --git a/src/eynollah/training/train.py b/src/eynollah/training/train.py index 00ed6ee..2cb42b6 100644 --- a/src/eynollah/training/train.py +++ b/src/eynollah/training/train.py @@ -17,7 +17,6 @@ from tensorflow.keras.layers import StringLookup from tensorflow.keras.utils import image_dataset_from_directory from tensorflow.keras.backend import one_hot from sacred import Experiment -from sacred.config import create_captured_function import numpy as np import cv2 @@ -32,16 +31,9 @@ from .metrics import ( connected_components_loss, ) from .models import ( - PatchEncoder, - Patches, - machine_based_reading_order_model, - resnet50_classifier, - resnet50_unet, - vit_resnet50_unet, - vit_resnet50_unet_transformer_before_cnn, - cnn_rnn_ocr_model, RESNET50_WEIGHTS_PATH, - RESNET50_WEIGHTS_URL + RESNET50_WEIGHTS_URL, + get_model ) from .utils import ( generate_arrays_from_folder_reading_order, @@ -477,58 +469,12 @@ def run(_config, if task == "enhancement": assert not is_loss_soft_dice, "for enhancement, soft_dice loss does not apply" assert not weighted_loss, "for enhancement, weighted loss does not apply" + if continue_training: - custom_objects = dict() - if is_loss_soft_dice: - custom_objects.update(soft_dice_loss=soft_dice_loss) - elif weighted_loss: - custom_objects.update(loss=weighted_categorical_crossentropy(weights)) - if backbone_type == 'transformer': - custom_objects.update(PatchEncoder=PatchEncoder, - Patches=Patches) - model = load_model(dir_of_start_model, compile=False, - custom_objects=custom_objects) + model = load_model(dir_of_start_model, compile=False) else: index_start = 0 - if backbone_type == 'nontransformer': - model = resnet50_unet(n_classes, - input_height, - input_width, - task, - weight_decay, - pretraining) - else: - num_patches_x = transformer_num_patches_xy[0] - num_patches_y = transformer_num_patches_xy[1] - num_patches = num_patches_x * num_patches_y - - if transformer_cnn_first: - model_builder = vit_resnet50_unet - multiple = 32 - else: - model_builder = vit_resnet50_unet_transformer_before_cnn - multiple = 1 - - assert input_height == ( - num_patches_y * transformer_patchsize_y * multiple), ( - "transformer_patchsize_y or transformer_num_patches_xy height value error: " - "input_height should be equal to " - "(transformer_num_patches_xy height value * transformer_patchsize_y * %d)" % multiple) - assert input_width == ( - num_patches_x * transformer_patchsize_x * multiple), ( - "transformer_patchsize_x or transformer_num_patches_xy width value error: " - "input_width should be equal to " - "(transformer_num_patches_xy width value * transformer_patchsize_x * %d)" % multiple) - assert 0 == (transformer_projection_dim % - (transformer_patchsize_y * transformer_patchsize_x)), ( - "transformer_projection_dim error: " - "The remainder when parameter transformer_projection_dim is divided by " - "(transformer_patchsize_y*transformer_patchsize_x) should be zero") - - model_builder = create_captured_function(model_builder) - model_builder.config = _config - model_builder.logger = _log - model = model_builder(num_patches) + model = get_model(_config, _log) assert model is not None #if you want to see the model structure just uncomment model summary. @@ -709,10 +655,7 @@ def run(_config, model = load_model(dir_of_start_model) else: index_start = 0 - model = cnn_rnn_ocr_model(image_height=input_height, - image_width=input_width, - n_classes=n_classes, - max_seq=max_len) + model = get_model(_config, _log) #initial_learning_rate = 1e-4 #decay_steps = int (n_epochs * ( len_dataset / n_batch )) #alpha = 0.01 @@ -774,11 +717,7 @@ def run(_config, model = load_model(dir_of_start_model, compile=False) else: index_start = 0 - model = resnet50_classifier(n_classes, - input_height, - input_width, - weight_decay, - pretraining) + model = get_model(_config, _log) model.compile(loss='categorical_crossentropy', optimizer=Adam(learning_rate=0.001), # rs: why not learning_rate? @@ -830,11 +769,7 @@ def run(_config, model = load_model(dir_of_start_model, compile=False) else: index_start = 0 - model = machine_based_reading_order_model(n_classes, - input_height, - input_width, - weight_decay, - pretraining) + model = get_model(_config, _log) #f1score_tot = [0] model.compile(loss="binary_crossentropy", From 62b55a3809ff37711f2ef21b1f31f6c715408cd8 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 28 May 2026 17:42:55 +0200 Subject: [PATCH 036/121] =?UTF-8?q?train=20params:=20drop=20`reload=5Fweig?= =?UTF-8?q?hts`,=20re-use=20`dir=5Fof=5Fstart=5Fmodel`=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - drop ad-hoc configuration parameter `reload_weights` (used for conversion/export of models for inference, to be replaced by extra CLI) - re-interprete `dir_of_start_model` to also load weights if not `continue_training` --- src/eynollah/training/train.py | 56 +++++++++------------------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/src/eynollah/training/train.py b/src/eynollah/training/train.py index 2cb42b6..f4cf08b 100644 --- a/src/eynollah/training/train.py +++ b/src/eynollah/training/train.py @@ -347,10 +347,9 @@ def config_params(): dir_output = None # Directory where the augmented training data and the model checkpoints will be saved. pretraining = False # Set to true to (down)load pretrained weights of ResNet50 encoder. save_interval = None # frequency for writing model checkpoints (positive integer for number of batches saved under "model_step_{batch:04d}", otherwise epoch saved under "model_{epoch:02d}") - reload_weights = False # Set true to build new model from config, load weights from dir_of_start_model, save under dir_output and exit. continue_training = False # Whether to continue training an existing model. + dir_of_start_model = '' # Directory of model checkpoint to load to continue training or load weights from. (E.g. if you already trained for 3 epochs, set "dir_of_start_model=dir_output/model_03".) if continue_training: - dir_of_start_model = '' # Directory of model checkpoint to load to continue training. (E.g. if you already trained for 3 epochs, set "dir_of_start_model=dir_output/model_03".) index_start = 0 # Epoch counter initial value to continue training. (E.g. if you already trained for 3 epochs, set "index_start=3" to continue naming checkpoints model_04, model_05 etc.) data_is_provided = False # Whether the preprocessed input data (subdirectories "images" and "labels" in both subdirectories "train" and "eval" of "dir_output") has already been generated (in the first epoch of a previous run). @@ -371,7 +370,6 @@ def run(_config, weight_decay, learning_rate, continue_training, - reload_weights, save_interval, augmentation, # dependent config keys need a default, @@ -475,6 +473,9 @@ def run(_config, else: index_start = 0 model = get_model(_config, _log) + if dir_of_start_model: + model.load_weights(dir_of_start_model).assert_existing_objects_matched().expect_partial() + _log.info("reloaded weights from %s", dir_of_start_model) assert model is not None #if you want to see the model structure just uncomment model summary. @@ -505,16 +506,6 @@ def run(_config, optimizer=Adam(learning_rate=learning_rate), metrics=metrics) - if reload_weights: - model.load_weights(dir_of_start_model).assert_existing_objects_matched().expect_partial() - dir_save = os.path.join(dir_output, os.path.basename(os.path.normpath(dir_of_start_model))) - #model.save(dir_save, include_optimizer=False) - model.export(dir_save) - with open(os.path.join(dir_save, "config.json"), "w") as fp: - json.dump(_config, fp) # encode dict into JSON - _log.info("reloaded model from %s to %s", dir_of_start_model, dir_save) - return - if not data_is_provided: # first create a directory in output for both training and evaluations # in order to flow data from these directories. @@ -656,6 +647,10 @@ def run(_config, else: index_start = 0 model = get_model(_config, _log) + if dir_of_start_model: + model.load_weights(dir_of_start_model).assert_existing_objects_matched().expect_partial() + _log.info("reloaded weights from %s", dir_of_start_model) + #initial_learning_rate = 1e-4 #decay_steps = int (n_epochs * ( len_dataset / n_batch )) #alpha = 0.01 @@ -666,16 +661,6 @@ def run(_config, #print(model.summary()) - if reload_weights: - model.load_weights(dir_of_start_model).assert_existing_objects_matched().expect_partial() - dir_save = os.path.join(dir_output, os.path.basename(os.path.normpath(dir_of_start_model))) - #model.save(dir_save, include_optimizer=False) - model.export(dir_save) - with open(os.path.join(dir_save, "config.json"), "w") as fp: - json.dump(_config, fp) # encode dict into JSON - _log.info("reloaded model from %s to %s", dir_of_start_model, dir_save) - return - # todo: use Dataset.map() on Dataset.list_files() def get_dataset(dir_img, dir_lab): def gen(): @@ -718,20 +703,14 @@ def run(_config, else: index_start = 0 model = get_model(_config, _log) + if dir_of_start_model: + model.load_weights(dir_of_start_model).assert_existing_objects_matched().expect_partial() + _log.info("reloaded weights from %s", dir_of_start_model) model.compile(loss='categorical_crossentropy', optimizer=Adam(learning_rate=0.001), # rs: why not learning_rate? metrics=['accuracy', F1Score(average='macro', name='f1')]) - if reload_weights: - model.load_weights(dir_of_start_model).assert_existing_objects_matched().expect_partial() - dir_save = os.path.join(dir_output, os.path.basename(os.path.normpath(dir_of_start_model))) - model.save(dir_save, include_optimizer=False) - with open(os.path.join(dir_save, "config.json"), "w") as fp: - json.dump(_config, fp) # encode dict into JSON - _log.info("reloaded model from %s to %s", dir_of_start_model, dir_save) - return - list_classes = list(classification_classes_name.values()) data_args = dict(label_mode="categorical", class_names=list_classes, @@ -770,6 +749,9 @@ def run(_config, else: index_start = 0 model = get_model(_config, _log) + if dir_of_start_model: + model.load_weights(dir_of_start_model).assert_existing_objects_matched().expect_partial() + _log.info("reloaded weights from %s", dir_of_start_model) #f1score_tot = [0] model.compile(loss="binary_crossentropy", @@ -777,16 +759,6 @@ def run(_config, optimizer=Adam(learning_rate=0.0001), # rs: why not learning_rate? metrics=['accuracy']) - if reload_weights: - model.load_weights(dir_of_start_model).assert_existing_objects_matched().expect_partial() - dir_save = os.path.join(dir_output, os.path.basename(os.path.normpath(dir_of_start_model))) - #model.save(dir_save, include_optimizer=False) - model.export(dir_save) - with open(os.path.join(dir_save, "config.json"), "w") as fp: - json.dump(_config, fp) # encode dict into JSON - _log.info("reloaded model from %s to %s", dir_of_start_model, dir_save) - return - dir_flow_train_imgs = os.path.join(dir_train, 'images') dir_flow_train_labels = os.path.join(dir_train, 'labels') From f833a516e7fb20949f2919f4792f2aaf315ead06 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 28 May 2026 17:48:21 +0200 Subject: [PATCH 037/121] =?UTF-8?q?training:=20add=20CLI=20command=20`conv?= =?UTF-8?q?ert`=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - move `train_cli` from cli.py to train.py, add docstring - add `convert_cli`: - load any (supported) model format (i.e. not exported TF-Serving or ONNX) - if SavedModel format with `config.json` present, and `--rebuild` is requested, create new model from `models.get_model()` for this configuration, and load weights - if model type is `cnn-rnn-ocr` and configuration is still for training (`ctc_loss`), then extract inference model - apply requested `--format` conversion: HDF5, Keras native, Keras SavedModel, TF-Serving SavedModel or ONNX - if output format is directory (i.e. SavedModel), then copy over `config.json`, too - reload-models-v0.8.mk: - adapt recipe for converter CLI (i.e. `--format tf-serving` w/ `--rebuild` if possible) - add targets for other useful data formats - extend list of model names to all current models (as all benefit from TF-Serving export) - cancel ONNX conversion for vision transformer models (as these do not work, yet) --- src/eynollah/training/cli.py | 11 +- src/eynollah/training/convert.py | 107 ++++++++++++++++++++ src/eynollah/training/reload-models-v0.8.mk | 85 ++++++++++------ src/eynollah/training/train.py | 21 ++++ src/eynollah/training/weights_ensembling.py | 1 + 5 files changed, 187 insertions(+), 38 deletions(-) create mode 100644 src/eynollah/training/convert.py diff --git a/src/eynollah/training/cli.py b/src/eynollah/training/cli.py index ae14f04..ccabb82 100644 --- a/src/eynollah/training/cli.py +++ b/src/eynollah/training/cli.py @@ -7,17 +7,11 @@ import sys from .build_model_load_pretrained_weights_and_save import build_model_load_pretrained_weights_and_save from .generate_gt_for_training import main as generate_gt_cli from .inference import main as inference_cli -from .train import ex +from .train import train_cli +from .convert import convert_cli from .extract_line_gt import linegt_cli from .weights_ensembling import ensemble_cli -@click.command(context_settings=dict( - ignore_unknown_options=True, -)) -@click.argument('SACRED_ARGS', nargs=-1, type=click.UNPROCESSED) -def train_cli(sacred_args): - ex.run_commandline([sys.argv[0]] + list(sacred_args)) - @click.group('training') def main(): pass @@ -26,5 +20,6 @@ main.add_command(build_model_load_pretrained_weights_and_save) main.add_command(generate_gt_cli, 'generate-gt') main.add_command(inference_cli, 'inference') main.add_command(train_cli, 'train') +main.add_command(convert_cli, 'convert') main.add_command(linegt_cli, 'export_textline_images_and_text') main.add_command(ensemble_cli, 'ensembling') diff --git a/src/eynollah/training/convert.py b/src/eynollah/training/convert.py new file mode 100644 index 0000000..dd4271f --- /dev/null +++ b/src/eynollah/training/convert.py @@ -0,0 +1,107 @@ +import os +from pathlib import Path +from shutil import copy2 +import logging + +import click + +@click.command(context_settings=dict( + help_option_names=['-h', '--help'], + show_default=True)) +@click.option( + "--rebuild", + "-r", + help="build new model from code and then load existing weights (requires input in SavedModel directory format with config.json present)", + is_flag=True +) +@click.option( + "--format", + "-f", + "format_", + help="data format to convert to", + type=click.Choice(["hdf5", "keras", "tf", "tf-serving", "onnx"]), + default="tf" +) +@click.option( + "--in", + "-i", + "in_", + help="path to input model (file in hdf5 / keras format, or directory in tf format)", + required=True, + type=click.Path(exists=True, dir_okay=True) +) +@click.option( + "--out", + "-o", + help="path to output model (file in hdf5 / keras / onnx format, or directory in tf / tf-serving format)", + required=True, + type=click.Path(exists=False, dir_okay=True) +) +def convert_cli(rebuild, format_, in_, out): + """ + convert models for inference + + Load model from path, optionally by rebuilding, convert to output format and write model to path. + """ + os.environ['TF_USE_LEGACY_KERAS'] = '1' # avoid Keras 3 after TF 2.15 + from ocrd_utils import tf_disable_interactive_logs + tf_disable_interactive_logs() + + import tensorflow as tf + from tensorflow.keras.models import load_model + from tensorflow.keras.models import Model as KerasModel + + model_path = Path(in_) + config_path = model_path / "config.json" + if model_path.is_dir(): + assert (model_path / "keras_metadata.pb").exists(), ( + "input directory must be Keras model in SavedModel format") + if rebuild: + from .train import ex + from .models import get_model + + assert config_path.exists(), ( + "rebuilding requires input model in SavedModel format with config.json") + + # merge defaults with existing config file + ex.add_config(str(config_path)) + # some models deviate between training and inference + ex.add_config(inference=True) + # just retrieve final config (via pseudo-run) + ex.main(lambda: 0) + config = ex.run(options={'--loglevel': 'ERROR'}).config + # use the config to capture the model builder + model = get_model(config, logging.root) + model.load_weights(model_path).assert_existing_objects_matched().expect_partial() + else: + model = load_model(model_path, compile=False) + + if isinstance(model, KerasModel): + # cnn-rnn-ocr task deviates between training and inference + try: + model.get_layer(name='ctc_loss') + except ValueError: + pass + else: + model = KerasModel( + model.get_layer(name='image').input, + model.get_layer(name='dense2').output) + + if format_ in ["hdf5", "keras", "tf"]: + kwargs = {"save_format": {"hdf5": "h5"}.get(format_, format_)} + if format_ != "keras": + kwargs["include_optimizer"] = False + model.save(out, **kwargs) + elif format_ == "tf-serving": + model.export(out) + elif format_ == "onnx": + import tf2onnx + tf2onnx.convert.from_keras(model, opset=18, output_path=out) + else: + raise ValueError("unknown output format '%s'" % format_) + + # copy config.json if possible + if config_path.exists() and format_ in ['tf', 'tf-serving']: + copy2(config_path, Path(out) / config_path.name) + + diff --git a/src/eynollah/training/reload-models-v0.8.mk b/src/eynollah/training/reload-models-v0.8.mk index 07be7cf..9855f0f 100644 --- a/src/eynollah/training/reload-models-v0.8.mk +++ b/src/eynollah/training/reload-models-v0.8.mk @@ -4,39 +4,65 @@ MODELS_SRC = models_eynollah MODELS_DST = reloaded/models_eynollah -# $(MODELS_DST)/eynollah-binarization_20210425 \ -# $(MODELS_DST)/eynollah-column-classifier_20210425 \ -# $(MODELS_DST)/eynollah-enhancement_20210425 \ -# $(MODELS_DST)/eynollah-main-regions-aug-rotation_20210425 \ -# $(MODELS_DST)/eynollah-main-regions-aug-scaling_20210425 \ -# $(MODELS_DST)/eynollah-main-regions-ensembled_20210425 \ -# $(MODELS_DST)/eynollah-main-regions_20220314 \ -# $(MODELS_DST)/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 \ -# $(MODELS_DST)/eynollah-tables_20210319 \ -# $(MODELS_DST)/model_eynollah_ocr_cnnrnn_20250930 \ +# eynollah-main-regions-aug-rotation_20210425 +# eynollah-main-regions-aug-scaling_20210425 +# eynollah-main-regions-ensembled_20210425 +# eynollah-main-regions_20220314 +# eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 +# eynollah-tables_20210319 -RELOADABLE_MODELS = \ - $(MODELS_DST)/model_eynollah_page_extraction_20250915 \ - $(MODELS_DST)/model_eynollah_reading_order_20250824 \ - $(MODELS_DST)/modelens_e_l_all_sp_0_1_2_3_4_171024 \ - $(MODELS_DST)/modelens_full_lay_1__4_3_091124 \ - $(MODELS_DST)/modelens_table_0t4_201124 \ - $(MODELS_DST)/modelens_textline_0_1__2_4_16092024 +CURRENT_MODELS := +CURRENT_MODELS += model_eynollah_page_extraction_20250915 +CURRENT_MODELS += model_eynollah_reading_order_20250824 +CURRENT_MODELS += modelens_e_l_all_sp_0_1_2_3_4_171024 +CURRENT_MODELS += modelens_full_lay_1__4_3_091124 +CURRENT_MODELS += modelens_table_0t4_201124 +CURRENT_MODELS += modelens_textline_0_1__2_4_16092024 +CURRENT_MODELS += model_eynollah_ocr_cnnrnn_20250930 +CURRENT_MODELS += eynollah-binarization_20210425 +CURRENT_MODELS += eynollah-column-classifier_20210425 +CURRENT_MODELS += eynollah-enhancement_20210425 -all: $(RELOADABLE_MODELS) +all: tf-serving + +tf-serving: $(CURRENT_MODELS:%=$(MODELS_DST)/%) +keras: $(CURRENT_MODELS:%=$(MODELS_DST)/%.keras) +hdf5: $(CURRENT_MODELS:%=$(MODELS_DST)/%.h5) +onnx: $(CURRENT_MODELS:%=$(MODELS_DST)/%.onnx) $(MODELS_DST)/%: $(MODELS_SRC)/% - test -e $&1 | tee $(notdir $<).log + eynollah-training convert \ + $(and $(wildcard $&1 | tee $(notdir $<).tf-serving.log + +$(MODELS_DST)/%.keras: $(MODELS_SRC)/% + eynollah-training convert \ + $(and $(wildcard $&1 | tee $(notdir $<).keras.log + +$(MODELS_DST)/%.h5: $(MODELS_SRC)/% + eynollah-training convert \ + $(and $(wildcard $&1 | tee $(notdir $<).hdf5.log + +$(MODELS_DST)/%.onnx: $(MODELS_SRC)/% + if jq -e '.task == "segmentation" and .backbone_type == "transformer"' $/dev/null; then \ + echo skipping $@: vision transformer architecture currently does not work with ONNX; else \ + eynollah-training convert \ + $(and $(wildcard $&1 | tee $(notdir $<).onnx.log; fi compare: for i in `find $(MODELS_DST) -mindepth 2`;do \ @@ -44,6 +70,5 @@ compare: du -bs $$n $$i ; \ done - clear: rm -rf $(MODELS_DST) diff --git a/src/eynollah/training/train.py b/src/eynollah/training/train.py index f4cf08b..62d8e51 100644 --- a/src/eynollah/training/train.py +++ b/src/eynollah/training/train.py @@ -2,6 +2,7 @@ import os import sys import io import json +import click from tqdm import tqdm import requests @@ -791,3 +792,23 @@ def run(_config, model_dir = os.path.join(dir_out,'model_best') model.save(model_dir) ''' + +@click.command(context_settings=dict( + ignore_unknown_options=True, +)) +@click.argument('SACRED_ARGS', nargs=-1, type=click.UNPROCESSED) +def train_cli(sacred_args): + """ + train model on extracted GT + + SACRED_ARGS as per CLI interface of Sacred, cf. + https://sacred.readthedocs.io/en/stable/command_line.html: + + \b + To configure the learning task, pass the string `with`, + followed by any number of + - config JSON file paths + - parameter overrides in the form of key=value + (where the later settings will override the former). + """ + ex.run_commandline([sys.argv[0]] + list(sacred_args)) diff --git a/src/eynollah/training/weights_ensembling.py b/src/eynollah/training/weights_ensembling.py index e3ede24..f651c56 100644 --- a/src/eynollah/training/weights_ensembling.py +++ b/src/eynollah/training/weights_ensembling.py @@ -43,6 +43,7 @@ def run_ensembling(model_dirs, out_dir): @click.option( "--in", "-i", + "in_", help="input directory of checkpoint models to be read", multiple=True, required=True, From 13f2f81c45fc31bad000dfe99c61c0e602b5846e Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 28 May 2026 18:08:08 +0200 Subject: [PATCH 038/121] =?UTF-8?q?ModelZoo:=20support=20inference=20with?= =?UTF-8?q?=20ONNX/TensorRT=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - comment out ad-hoc conversion/loading of autosized models - refactor predictor backends for model types into separate functions - only attempt inference conversion of cnn-rnn-ocr model if applicable (`ctc_loss` layer still present) - apply VRAM limits across model types (Keras, TF-Serving, ONNX) - apply TF device selection across model types (Keras, TF-Serving) - implement predictor backend for ONNX models: - using onnxruntime - covering CUDA and TensorRT providers - trying to support manual device selection - hiding session management details - converting float32 to float16 --- src/eynollah/model_zoo/model_zoo.py | 209 ++++++++++++++++++++-------- 1 file changed, 151 insertions(+), 58 deletions(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index d5e69a2..0a68203 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -14,6 +14,19 @@ from .default_specs import DEFAULT_MODEL_SPECS from .types import AnyModel, T +MODEL_VRAM_LIMITS = { + "binarization": 868, # due to bs 5 + "enhancement": 980, # due to bs 3 + "col_classifier": 210, + "page": 618, + "textline": 1680, # 954 for bs 1 + "region_1_2": 1580, + "region_fl_np": 1756, + "table": 1818, + "reading_order": 632, + "ocr": 850, +} + class EynollahModelZoo: """ Wrapper class that handles storage and loading of models for all eynollah runners. @@ -73,6 +86,10 @@ class EynollahModelZoo: if model_path.suffix == '.h5' and Path(model_path.stem).exists(): # prefer SavedModel over HDF5 format if it exists model_path = Path(model_path.stem) + if model_path.with_suffix('.onnx').exists(): + # prefer ONNX over SavedModel format if it exists + model_path = model_path.with_suffix('.onnx') + return model_path def load_models( @@ -136,20 +153,34 @@ class EynollahModelZoo: """ Load any model """ - os.environ['TF_USE_LEGACY_KERAS'] = '1' # avoid Keras 3 after TF 2.15 + if model_path_override: + self.override_models((model_category, model_variant, model_path_override)) + model_path = self.model_path(model_category, model_variant) + + if model_path.is_dir() and (model_path / "keras_metadata.pb").exists(): + # Keras model + model = self._load_keras_model(model_category, model_path, device=device) + elif model_path.is_dir(): + # TF-Serving model + model = self._load_serving_model(model_category, model_path, device=device) + elif model_path.suffix == '.onnx': + # ONNX model + model = self._load_onnx_model(model_category, model_path, device=device) + else: + raise ValueError("unknown model type for '%s'" % str(model_path)) + model._name = model_category + return model + + def get(self, model_category: str) -> Union[Predictor, AnyModel]: + if model_category not in self._loaded: + raise ValueError(f'Model "{model_category}" not previously loaded with "load_model(..)"') + return self._loaded[model_category] + + def _configure_tf_device(self, model_category, device=''): from ocrd_utils import tf_disable_interactive_logs tf_disable_interactive_logs() - import tensorflow as tf - from tensorflow.keras.models import load_model - from tensorflow.keras.models import Model as KerasModel - from ..patch_encoder import ( - PatchEncoder, - Patches, - wrap_layout_model_patched, - wrap_layout_model_resized, - ) cuda = False try: gpus = tf.config.list_physical_devices('GPU') @@ -175,18 +206,8 @@ class EynollahModelZoo: # (for small GPUs); so try hard (calibrated) limits instead: tf.config.set_logical_device_configuration( device, - [tf.config.LogicalDeviceConfiguration(memory_limit={ - "binarization": 868, # due to bs 5 - "enhancement": 980, # due to bs 3 - "col_classifier": 210, - "page": 618, - "textline": 1680, # 954 for bs 1 - "region_1_2": 1580, - "region_fl_np": 1756, - "table": 1818, - "reading_order": 632, - "ocr": 850, - }[model_category])]) + [tf.config.LogicalDeviceConfiguration( + memory_limit=MODEL_VRAM_LIMITS[model_category])]) vendor_name = ( tf.config.experimental.get_device_details(device) .get('device_name', 'unknown')) @@ -194,52 +215,124 @@ class EynollahModelZoo: self.logger.info("using GPU %s (%s) for model %s", device.name, vendor_name, - model_category + ( - "_patched" if patched else - "_resized" if resized else "")) + model_category # + ( + # "_patched" if patched else + # "_resized" if resized else "") + ) except RuntimeError: self.logger.exception("cannot configure GPU devices") if not cuda: self.logger.warning("no GPU device available") - if model_path_override: - self.override_models((model_category, model_variant, model_path_override)) - model_path = self.model_path(model_category, model_variant) - try: - if model_path.is_dir() and not (model_path / "keras_metadata.pb").exists(): - # short-cut to avoid warning for exported models - raise ValueError() - model = load_model(model_path, compile=False) - model.make_predict_function() - except (AttributeError, ValueError): - model = tf.saved_model.load(model_path) - model.predict_on_batch = model.serve - model.input_shape = tuple(model.signatures.get('serving_default').inputs[0].shape) - model._name = model_category - if resized: - model = wrap_layout_model_resized(model) - model._name = model_category + '_resized' - elif patched: - model = wrap_layout_model_patched(model) - model._name = model_category + '_patched' - else: - # increases required VRAM, does not always work - # (depending on CUDA/libcudnn/TF version): - #model.jit_compile = True - pass + def _load_keras_model(self, model_category, model_path, device=''): + os.environ['TF_USE_LEGACY_KERAS'] = '1' # avoid Keras 3 after TF 2.15 + from ocrd_utils import tf_disable_interactive_logs + tf_disable_interactive_logs() + + from tensorflow.keras.models import load_model + from tensorflow.keras.models import Model as KerasModel + + self._configure_tf_device(model_category, device=device) + + model = load_model(model_path, compile=False) + + # from ..patch_encoder import ( + # wrap_layout_model_patched, + # wrap_layout_model_resized, + # ) + # if resized: + # model = wrap_layout_model_resized(model) + # model._name = model_category + '_resized' + # elif patched: + # model = wrap_layout_model_patched(model) + # model._name = model_category + '_patched' if model_category == 'ocr': - model = KerasModel( - model.get_layer(name="image").input, # type: ignore - model.get_layer(name="dense2").output, # type: ignore - ) + # cnn-rnn-ocr task model may not be in inference mode, yet + try: + model.get_layer(name='ctc_loss') + except ValueError: + pass + else: + model = KerasModel( + model.get_layer(name="image").input, # type: ignore + model.get_layer(name="dense2").output, # type: ignore + ) + + model.make_predict_function() return model - def get(self, model_category: str) -> Union[Predictor, AnyModel]: - if model_category not in self._loaded: - raise ValueError(f'Model "{model_category}" not previously loaded with "load_model(..)"') - return self._loaded[model_category] + def _load_serving_model(self, model_category, model_path, device=''): + from ocrd_utils import tf_disable_interactive_logs + tf_disable_interactive_logs() + import tensorflow as tf + + self._configure_tf_device(model_category, device=device) + model = tf.saved_model.load(model_path) + model.predict_on_batch = model.serve + model.input_shape = tuple(model.signatures.get('serving_default').inputs[0].shape) + + return model + + def _load_onnx_model(self, model_category, model_path, device=''): + import onnxruntime as ort + import numpy as np + + providers = ort.get_available_providers() + if device: + if ':' in device: + for spec in device.split(','): + cat, dev = spec.split(':') + if fnmatchcase(model_category, cat): + device = dev + break + if device == 'CPU': + gpu = -1 + else: + assert device.startswith('GPU') + gpu = int(device[3:] or "0") + else: + gpu = 0 # try first allowable + # configure and prioritise + if 'CUDAExecutionProvider' in providers: + providers.remove('CUDAExecutionProvider') + if gpu >= 0: + providers = [('CUDAExecutionProvider', { + 'device_id': gpu, + # 'arena_extend_strategy': 'kNextPowerOfTwo', + 'gpu_mem_limit': MODEL_VRAM_LIMITS[model_category] * 1024 * 1024, + # 'cudnn_conv_algo_search': 'EXHAUSTIVE', + # 'do_copy_in_default_stream': True, + # ... + })] + providers + if 'TensorrtExecutionProvider' in providers: + providers.remove('TensorrtExecutionProvider') + if gpu >= 0: + providers = [('TensorrtExecutionProvider', { + 'device_id': gpu, + 'trt_max_workspace_size': MODEL_VRAM_LIMITS[model_category] * 1024 * 1024, + # 'trt_fp16_enable': True, + # 'trt_engine_cache_enable': True, + # 'trt_timing_cache_enable': True, + # ... + })] + providers + model = ort.InferenceSession( + model_path, + providers=providers) + # FIXME: notify about selected provider/device + input_name = model.get_inputs()[0].name + output_name = model.get_outputs()[0].name + def predict_onnx(inputs): + # models expect data_type() == 'tensor(float)', but np.float16 is 'tensor(float16)' + # FIXME: do this dynamically (but how to convert .type to np.dtype?) + inputs = inputs.astype(np.float32) + return model.run( + [output_name], {input_name: inputs})[0] + model.predict_on_batch = predict_onnx + model.input_shape = model.get_inputs()[0].shape + + return model def _load_ocr_model(self, variant: str, device: str = "") -> AnyModel: """ From c79b73dcc8f31e8c27655e165a61413aca5fedb0 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 2 Jun 2026 20:26:42 +0200 Subject: [PATCH 039/121] =?UTF-8?q?cnn-rnn-ocr:=20move=20CTC=20decoder=20a?= =?UTF-8?q?nd=20string=20decoder=20to=20inference=20model=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ModelZoo: drop `num_to_char` and `characters` model types, also drop `_load_characters()` and `_load_num_to_char()` loaders - `ModelZoo.load_models()`: use Predictor for `ocr` models, too - `ModelZoo.load_model()`: delegate runtime/inference conversion of OCR models to `eynollah.training.models.cnn_rnn_ocr_model4inference` - `training.models`: add (purely functional) Keras layer `CTCDecoder` for inference on top of softmax output, but using TF backend function instead of (broken) `Keras.backend.ctc_decode()`, while switching to beam search (instead of greedy) and also returning decoded path probability - `training.models.cnn_rnn_ocr_model()` w/ `inference=True`: * add kwarg `characters_txt_file` for file path of character set * configure secondary tensor path on OCR graph for binarized input (additional input `image_bin`, averaging softmax outputs) * use new `CTCDecoder` layer and inverse `StringLookup` layer to decode from softmax output to tf.string; so inference models now have 2 inputs (RGB, binarized) and 2 outputs (text, prob) * since `np.dtype=object` cannot be handled by SharedMemory (as needed by Predictor queues), also replace tf.string by tf.uint8 arrays * use this for `training convert` for OCR models w/ `--rebuild` - `training.models.cnn_rnn_ocr_model4inference`: * new function which does the same but loads an existing OCR model in training configuration (i.e. without prior `inference=True`) * use this for `training convert` for OCR models w/o `--rebuild` --- src/eynollah/model_zoo/default_specs.py | 16 ----- src/eynollah/model_zoo/model_zoo.py | 42 ++--------- src/eynollah/training/convert.py | 12 ++-- src/eynollah/training/models.py | 93 ++++++++++++++++++++++++- 4 files changed, 100 insertions(+), 63 deletions(-) diff --git a/src/eynollah/model_zoo/default_specs.py b/src/eynollah/model_zoo/default_specs.py index dc725e4..170d944 100644 --- a/src/eynollah/model_zoo/default_specs.py +++ b/src/eynollah/model_zoo/default_specs.py @@ -208,22 +208,6 @@ DEFAULT_MODEL_SPECS = EynollahModelSpecSet([ type='Keras', ), - EynollahModelSpec( - category="num_to_char", - variant='', - filename="characters_org.txt", - dist_url=dist_url("ocr"), - type='decoder', - ), - - EynollahModelSpec( - category="characters", - variant='', - filename="characters_org.txt", - dist_url=dist_url("ocr"), - type='List[str]', - ), - EynollahModelSpec( category="ocr", variant='tr', diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 0a68203..e7d21aa 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -123,12 +123,8 @@ class EynollahModelZoo: model_category = model_category[:-8] load_kwargs["patched"] = True - if model_category == 'ocr': + if model_category == 'ocr' and model_variant == 'tr': model = self._load_ocr_model(variant=model_variant, device=device) - elif model_category == 'num_to_char': - model = self._load_num_to_char() - elif model_category == 'characters': - model = self._load_characters() elif model_category == 'trocr_processor': from transformers import TrOCRProcessor model_path = self.model_path(model_category, model_variant) @@ -232,9 +228,12 @@ class EynollahModelZoo: from tensorflow.keras.models import load_model from tensorflow.keras.models import Model as KerasModel + from ..training.models import cnn_rnn_ocr_model4inference + self._configure_tf_device(model_category, device=device) model = load_model(model_path, compile=False) + assert isinstance(model, KerasModel) # from ..patch_encoder import ( # wrap_layout_model_patched, @@ -249,15 +248,7 @@ class EynollahModelZoo: if model_category == 'ocr': # cnn-rnn-ocr task model may not be in inference mode, yet - try: - model.get_layer(name='ctc_loss') - except ValueError: - pass - else: - model = KerasModel( - model.get_layer(name="image").input, # type: ignore - model.get_layer(name="dense2").output, # type: ignore - ) + model = cnn_rnn_ocr_model4inference(model, model_path) model.make_predict_function() @@ -369,29 +360,6 @@ class EynollahModelZoo: return self.load_model('ocr', model_variant=variant, device=device) - def _load_characters(self) -> List[str]: - """ - Load encoding for OCR - """ - with open(self.model_path('num_to_char'), "r") as config_file: - return json.load(config_file) - - def _load_num_to_char(self) -> 'StringLookup': - """ - Load decoder for OCR - """ - os.environ['TF_USE_LEGACY_KERAS'] = '1' # avoid Keras 3 after TF 2.15 - from ocrd_utils import tf_disable_interactive_logs - tf_disable_interactive_logs() - - from tensorflow.keras.layers import StringLookup - - characters = self._load_characters() - # Mapping characters to integers. - char_to_num = StringLookup(vocabulary=characters, mask_token=None) - # Mapping integers back to original characters. - return StringLookup(vocabulary=char_to_num.get_vocabulary(), mask_token=None, invert=True) - def __str__(self): return tabulate( [ diff --git a/src/eynollah/training/convert.py b/src/eynollah/training/convert.py index dd4271f..140079e 100644 --- a/src/eynollah/training/convert.py +++ b/src/eynollah/training/convert.py @@ -2,6 +2,7 @@ import os from pathlib import Path from shutil import copy2 import logging +import json import click @@ -74,18 +75,13 @@ def convert_cli(rebuild, format_, in_, out): model = get_model(config, logging.root) model.load_weights(model_path).assert_existing_objects_matched().expect_partial() else: + from .models import cnn_rnn_ocr_model4inference + model = load_model(model_path, compile=False) if isinstance(model, KerasModel): # cnn-rnn-ocr task deviates between training and inference - try: - model.get_layer(name='ctc_loss') - except ValueError: - pass - else: - model = KerasModel( - model.get_layer(name='image').input, - model.get_layer(name='dense2').output) + model = cnn_rnn_ocr_model4inference(model, model_path) if format_ in ["hdf5", "keras", "tf"]: kwargs = {"save_format": {"hdf5": "h5"}.get(format_, format_)} diff --git a/src/eynollah/training/models.py b/src/eynollah/training/models.py index 83058ee..528c848 100644 --- a/src/eynollah/training/models.py +++ b/src/eynollah/training/models.py @@ -1,4 +1,5 @@ import os +import json os.environ['TF_USE_LEGACY_KERAS'] = '1' # avoid Keras 3 after TF 2.15 import tensorflow as tf @@ -23,6 +24,7 @@ from tensorflow.keras.layers import ( Reshape, UpSampling2D, ZeroPadding2D, + StringLookup, add, concatenate ) @@ -57,6 +59,50 @@ class CTCLayer(Layer): # At test time, just return the computed predictions. return y_pred + +class CTCDecoder(Layer): + def call(self, inputs): + n_samples = tf.shape(inputs)[0] + n_steps = inputs.shape[1] + n_classes = inputs.shape[2] + lengths = tf.ones(n_samples, dtype=tf.int32) * n_steps + ## Keras beam search seems to mess with double letters + ## but Keras greedy sometimes removes arbitrary letters + # outputs, logits = tf.keras.backend.ctc_decode(inputs, + # lengths, + # beam_width=20 + # greedy=False, # True, + # # backend does not allow these kwargs + # #merge_repeated=False, + # #mask_index=inputs.shape[2]-1, + # ) + # tf.nn.ctc_*_decoder (in contrast to tf.keras.backend.ctc_decode) + # needs logits instead of probs and time-major (batch 2nd dim) + inputs = tf.math.log( + tf.transpose(inputs, perm=[1, 0, 2]) + tf.keras.backend.epsilon() + ) + # tf.nn.ctc_greedy_decoder() is not as precise + # tf.compat.v1.nn.ctc_beam_search_decoder() also needs merge_repeated=False + decoded, logits = tf.nn.ctc_beam_search_decoder( + inputs, + lengths, + beam_width=10, + top_paths=2, + ) + # get top path for all sequences in batch + decoded = decoded[0] + logits = logits[:, 0] - logits[:, 1] + # convert to dense + outputs = tf.SparseTensor(decoded.indices, decoded.values, + (n_samples, n_steps)) + outputs = tf.sparse.to_dense(sp_input=outputs, default_value=-1) + # # drop non-tokens (-1) and OOV (0) + # result = [] + # for output in outputs: + # result.append(tf.gather(output, tf.where(output > 0))) + # outputs = tf.stack(result) + probs = tf.exp(-logits) + return outputs, probs def mlp(x, hidden_units, dropout_rate): for units in hidden_units: @@ -422,7 +468,7 @@ def machine_based_reading_order_model(n_classes,input_height=224,input_width=224 return model -def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_len=None, inference=False): +def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_len=None, inference=False, characters_txt_file=None): inputs = Input(shape=(image_height, image_width, 3), name="image") labels = Input(name="label", shape=(None,)) @@ -492,13 +538,56 @@ def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_l out = Dense(n_classes, activation="softmax", name="dense2")(out) if inference: - return Model(inputs, out) + # add second path for binarization + inputs_bin = Input(shape=(image_height, image_width, 3), name="image_bin") + out_bin = Model(inputs, out)(inputs_bin) + # ensemble raw results + out = 0.5 * (out + out_bin) + # get tf.string batch + out, prob = CTCDecoder()(out) + # decode int to str + with open(characters_txt_file, "r") as voc_file: + voc = json.load(voc_file) + char2num = StringLookup(vocabulary=voc) + voc = char2num.get_vocabulary() + num2char = StringLookup(vocabulary=voc, invert=True) + output = num2char(out) + # avoid output tf.dtype=string → np.dtype=object (which cannot be shm-ed) + output = tf.io.decode_raw(output, tf.uint8, fixed_length=max(map(len, voc))) + + return Model((inputs, inputs_bin), (output, prob)) # Add CTC layer for calculating CTC loss at each step. out = CTCLayer(name="ctc_loss")(labels, out) return Model((inputs, labels), out) +def cnn_rnn_ocr_model4inference(model, model_path): + """convert trained cnn-rnn-ocr model to inference model post-hoc""" + try: + model.get_layer(name='ctc_loss') + except ValueError: + # likely already converted + return model + else: + inputs = model.get_layer(name='image').input + output = model.get_layer(name='dense2').output + inputs_bin = Input(inputs.shape[1:], name='image_bin') + output_bin = Model(inputs, output)(inputs_bin) + output = 0.5 * (output + output_bin) + output, prob = CTCDecoder()(output) + with open(model_path / "characters_org.txt", "r") as voc_file: + voc = json.load(voc_file) + char2num = StringLookup(vocabulary=voc) + voc = char2num.get_vocabulary() + num2char = StringLookup(vocabulary=voc, invert=True) + output = num2char(output) + # avoid output tf.dtype=string → np.dtype=object (which cannot be shm-ed) + output = tf.io.decode_raw(output, tf.uint8, fixed_length=max(map(len, voc))) + inputs = (inputs, inputs_bin) + outputs = (output, prob) + return Model(inputs, outputs) + def get_model(config, logger): from sacred.config import create_captured_function From a391ee24e68b9dbced39efe50de1a70a5dea115d Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 2 Jun 2026 21:18:22 +0200 Subject: [PATCH 040/121] Predictor: handle multi-input and/or multi-output cases --- src/eynollah/predictor.py | 61 ++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/src/eynollah/predictor.py b/src/eynollah/predictor.py index 3c6890e..141d3f0 100644 --- a/src/eynollah/predictor.py +++ b/src/eynollah/predictor.py @@ -1,5 +1,5 @@ from contextlib import ExitStack -from typing import List, Dict +from typing import List, Dict, Tuple, Union import logging import logging.handlers import multiprocessing as mp @@ -8,6 +8,7 @@ import numpy as np from .utils.shm import share_ndarray, ndarray_shared QSIZE = 200 +ArrayT = Union[np.ndarray, Tuple[np.ndarray]] class Predictor(mp.context.SpawnProcess): @@ -40,10 +41,10 @@ class Predictor(mp.context.SpawnProcess): def input_shape(self): return self({}) - def predict(self, data: dict, verbose=0): + def predict(self, data: ArrayT, verbose=0) -> ArrayT: return self(data) - def __call__(self, data: dict): + def __call__(self, data: Union[ArrayT, Dict]) -> Union[ArrayT, Tuple]: # unusable as per python/cpython#79967 #with self.jobid.get_lock(): # would work, but not public: @@ -55,7 +56,15 @@ class Predictor(mp.context.SpawnProcess): self.taskq.put((jobid, data)) #self.logger.debug("sent shape query task '%d' for model '%s'", jobid, self.name) return self.result(jobid) - with share_ndarray(data) as shared_data: + with ExitStack() as stack: + if isinstance(data, tuple): + # multi-input + shared_data = [] + for data0 in data: + shared_data.append(stack.enter_context(share_ndarray(data0))) + shared_data = tuple(shared_data) + else: + shared_data = stack.enter_context(share_ndarray(data)) self.taskq.put((jobid, shared_data)) #self.logger.debug("sent prediction task '%d' for model '%s': %s", jobid, self.name, shared_data) return self.result(jobid) @@ -67,6 +76,14 @@ class Predictor(mp.context.SpawnProcess): result = self.results.pop(jobid) if isinstance(result, Exception): raise Exception(f"predictor {self.name} failed for {jobid}") from result + elif isinstance(result, tuple) and isinstance(result[0], dict): + # multi-output + result1 = [] + for result0 in result: + with ndarray_shared(result0) as shared_result0: + result1.append(np.copy(shared_result0)) + result = result1 + self.closable.append(jobid) elif isinstance(result, dict): with ndarray_shared(result) as shared_result: result = np.copy(shared_result) @@ -111,6 +128,7 @@ class Predictor(mp.context.SpawnProcess): "binarization": 4, "enhancement": 4, "reading_order": 4, + "ocr": 8, # medium size (672x672x3)... "textline": 2, # large models... @@ -126,8 +144,13 @@ class Predictor(mp.context.SpawnProcess): self.resultq.put((jobid, result)) #self.logger.debug("sent result for '%d': %s", jobid, result) else: + if isinstance(shared_data, tuple): + multi_input = True + batch_size = shared_data[0]['shape'][0] + else: + multi_input = False + batch_size = shared_data['shape'][0] tasks = [(jobid, shared_data)] - batch_size = shared_data['shape'][0] while (not self.taskq.empty() and # climb to target batch size batch_size * len(tasks) < REBATCH_SIZE): @@ -136,7 +159,7 @@ class Predictor(mp.context.SpawnProcess): # add to our batch tasks.append((jobid0, shared_data0)) else: - # immediately anser + # immediately answer self.resultq.put((jobid0, self.model.input_shape)) if len(tasks) > 1: self.logger.debug("rebatching %d '%s' tasks of batch size %d", @@ -147,12 +170,26 @@ class Predictor(mp.context.SpawnProcess): for jobid, shared_data in tasks: #self.logger.debug("predicting '%d' with model '%s': %s", jobid, self.name, shared_data) jobs.append(jobid) - data.append(stack.enter_context(ndarray_shared(shared_data))) - data = np.concatenate(data) + if multi_input: + data.append(tuple(stack.enter_context(ndarray_shared(shared_data0)) + for shared_data0 in shared_data)) + else: + data.append(stack.enter_context(ndarray_shared(shared_data))) + if multi_input: + data = tuple(np.concatenate(data0) + for data0 in zip(*data)) + else: + data = np.concatenate(data) #result = self.model.predict(data, verbose=0) # faster, less VRAM result = self.model.predict_on_batch(data) - results = np.split(result, len(jobs)) + if isinstance(result, tuple): + multi_output = True + results = zip(*(np.split(result0, len(jobs)) + for result0 in result)) + else: + multi_output = False + results = np.split(result, len(jobs)) #self.logger.debug("sharing result array for '%d'", jobid) with ExitStack() as stack: for jobid, result in zip(jobs, results): @@ -160,7 +197,11 @@ class Predictor(mp.context.SpawnProcess): # but don't want to wait either, so track closing # context per job, and wait for closable signal # from client - result = stack.enter_context(share_ndarray(result)) + if multi_output: + result = tuple(stack.enter_context(share_ndarray(result0)) + for result0 in result) + else: + result = stack.enter_context(share_ndarray(result)) closing[jobid] = stack.pop_all() self.resultq.put((jobid, result)) #self.logger.debug("sent result for '%d': %s", jobid, result) From 8ffc4ed8d377fea54174cbc606ce0df38b46bf8b Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 2 Jun 2026 21:20:06 +0200 Subject: [PATCH 041/121] =?UTF-8?q?Eynollah=5Focr:=20adapt=20to=20inferenc?= =?UTF-8?q?e=20model,=20improve=20and=20simplify=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - drop `end_character` mechanics and `characters` model type for decoding output probability (not needed) - drop `decode_batch_predictions()` and `num_to_char` model type (part of inference model) - drop roughshot confidence estimation calculation (returned precisely by inference model) - adapt model prediction to inference model: just omit zeros, map to bytes, filter OOV tokens and decode UTF-8 to str - if no binarization input was provided, then compute it on the fly using `binarization` model - also apply `min_conf_value_of_textline_text` (as for TrOCR) - batching over entire page instead of region-wise (which underfilled batches) - simplify and avoid copied redundant code - rename `extracted_conf_value_merged` → `extracted_confs_merged` - move `batched()` from `utils.utils_ocr` to `utils` - drop `utils_ocr.distortion_free_resize()` (not needed) - simplify `utils_ocr.break_curved_line_into_small_pieces_and_then_merge()` - drop `utils_ocr.return_textline_contour_with_added_box_coordinate()` and `utils_ocr.return_rnn_cnn_ocr_of_given_textlines()` (not needed) --- src/eynollah/eynollah_ocr.py | 512 ++++++++++---------------------- src/eynollah/utils/__init__.py | 6 + src/eynollah/utils/utils_ocr.py | 319 +++----------------- 3 files changed, 206 insertions(+), 631 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index b94853b..40cbeaa 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -19,27 +19,29 @@ from ocrd_utils import polygon_from_points, xywh_from_polygon from .eynollah import Eynollah from .model_zoo import EynollahModelZoo -from .utils import is_image_filename +from .utils import ( + is_image_filename, + batched, + pairwise, +) from .utils.font import get_font from .utils.xml import etree_namespace_for_element_tag from .utils.resize import resize_image from .utils.utils_ocr import ( break_curved_line_into_small_pieces_and_then_merge, - decode_batch_predictions, fit_text_single_line, get_contours_and_bounding_boxes, get_orientation_moments, preprocess_and_resize_image_for_ocrcnn_model, return_textlines_split_if_needed, rotate_image_with_padding, - batched, ) # TODO: refine typing @dataclass class EynollahOcrResult: extracted_texts_merged: List - extracted_conf_value_merged: Optional[List] + extracted_confs_merged: Optional[List] cropped_lines_region_indexer: List total_bb_coordinates:List @@ -73,10 +75,8 @@ class Eynollah_ocr(Eynollah): device=device) else: self.model_zoo.load_models('ocr', - 'num_to_char', - 'characters', + 'binarization', device=device) - self.end_character = len(self.model_zoo.get('characters')) + 2 @property def device(self): @@ -95,8 +95,6 @@ class Eynollah_ocr(Eynollah): cropped_lines = [] cropped_lines_region_indexer = [] cropped_lines_meging_indexing = [] - extracted_texts = [] - extracted_confs = [] for n_region, region in enumerate(page_tree.getroot().iter('{%s}TextRegion' % page_ns)): for n_line, line in enumerate(region.iter('{%s}TextLine' % page_ns)): @@ -139,7 +137,8 @@ class Eynollah_ocr(Eynollah): cropped_lines.append(img_crop) cropped_lines_meging_indexing.append(0) - + extracted_texts = [] + extracted_confs = [] self.logger.debug("processing %d lines for %d regions", len(cropped_lines), len(set(cropped_lines_region_indexer))) for imgs in batched(cropped_lines, self.b_s): @@ -157,6 +156,10 @@ class Eynollah_ocr(Eynollah): conf = output.sequences_scores.exp().clamp(0.0, 1.0).tolist() else: conf = [1.0] * len(output.sequences) + if conf < self.min_conf_value_of_textline_text: + extracted_confs.extend(0) + extracted_texts.extend("") + continue text = self.model_zoo.get('trocr_processor').batch_decode( output.sequences, skip_special_tokens=True, @@ -179,7 +182,7 @@ class Eynollah_ocr(Eynollah): return EynollahOcrResult( extracted_texts_merged=extracted_texts_merged, - extracted_conf_value_merged=extracted_confs_merged, + extracted_confs_merged=extracted_confs_merged, cropped_lines_region_indexer=cropped_lines_region_indexer, total_bb_coordinates=total_bb_coordinates, ) @@ -196,362 +199,163 @@ class Eynollah_ocr(Eynollah): ) -> EynollahOcrResult: total_bb_coordinates = [] - - cropped_lines = [] - img_crop_bin = None - imgs_bin = None - imgs_bin_ver_flipped = None + cropped_lines_rgb = [] cropped_lines_bin = [] cropped_lines_ver_index = [] cropped_lines_region_indexer = [] cropped_lines_meging_indexing = [] - - indexer_text_region = 0 - for nn in page_tree.getroot().iter(f'{{{page_ns}}}TextRegion'): - try: - type_textregion = nn.attrib['type'] - except: - type_textregion = 'paragraph' - for child_textregion in nn: - if child_textregion.tag.endswith("TextLine"): - for child_textlines in child_textregion: - if child_textlines.tag.endswith("Coords"): - cropped_lines_region_indexer.append(indexer_text_region) - p_h=child_textlines.attrib['points'].split(' ') - textline_coords = np.array( [ [int(x.split(',')[0]), - int(x.split(',')[1]) ] - for x in p_h] ) - - x,y,w,h = cv2.boundingRect(textline_coords) - - angle_radians = math.atan2(h, w) - # Convert to degrees - angle_degrees = math.degrees(angle_radians) - if type_textregion=='drop-capital': - angle_degrees = 0 - - total_bb_coordinates.append([x,y,w,h]) - - w_scaled = w * image_height/float(h) - - img_poly_on_img = np.copy(img) - if img_bin: - img_poly_on_img_bin = np.copy(img_bin) - img_crop_bin = img_poly_on_img_bin[y:y+h, x:x+w, :] - - mask_poly = np.zeros(img.shape) - mask_poly = cv2.fillPoly(mask_poly, pts=[textline_coords], color=(1, 1, 1)) - - - mask_poly = mask_poly[y:y+h, x:x+w, :] - img_crop = img_poly_on_img[y:y+h, x:x+w, :] - - # print(file_name, angle_degrees, w*h, - # mask_poly[:,:,0].sum(), - # mask_poly[:,:,0].sum() /float(w*h) , - # 'didi') - - if angle_degrees > 3: - better_des_slope = get_orientation_moments(textline_coords) - - img_crop = rotate_image_with_padding(img_crop, better_des_slope) - if img_bin: - img_crop_bin = rotate_image_with_padding(img_crop_bin, better_des_slope) - - mask_poly = rotate_image_with_padding(mask_poly, better_des_slope) - mask_poly = mask_poly.astype('uint8') - - #new bounding box - x_n, y_n, w_n, h_n = get_contours_and_bounding_boxes(mask_poly[:,:,0]) - - mask_poly = mask_poly[y_n:y_n+h_n, x_n:x_n+w_n, :] - img_crop = img_crop[y_n:y_n+h_n, x_n:x_n+w_n, :] - - if not self.do_not_mask_with_textline_contour: - img_crop[mask_poly==0] = 255 - if img_bin: - img_crop_bin = img_crop_bin[y_n:y_n+h_n, x_n:x_n+w_n, :] - if not self.do_not_mask_with_textline_contour: - img_crop_bin[mask_poly==0] = 255 - - if mask_poly[:,:,0].sum() /float(w_n*h_n) < 0.50 and w_scaled > 90: - if img_bin: - img_crop, img_crop_bin = \ - break_curved_line_into_small_pieces_and_then_merge( - img_crop, mask_poly, img_crop_bin) - else: - img_crop, _ = \ - break_curved_line_into_small_pieces_and_then_merge( - img_crop, mask_poly) - else: - better_des_slope = 0 - if not self.do_not_mask_with_textline_contour: - img_crop[mask_poly==0] = 255 - if img_bin: - if not self.do_not_mask_with_textline_contour: - img_crop_bin[mask_poly==0] = 255 - if type_textregion=='drop-capital': - pass - else: - if mask_poly[:,:,0].sum() /float(w*h) < 0.50 and w_scaled > 90: - if img_bin: - img_crop, img_crop_bin = \ - break_curved_line_into_small_pieces_and_then_merge( - img_crop, mask_poly, img_crop_bin) - else: - img_crop, _ = \ - break_curved_line_into_small_pieces_and_then_merge( - img_crop, mask_poly) - - if w_scaled < 750:#1.5*image_width: - img_fin = preprocess_and_resize_image_for_ocrcnn_model( - img_crop, image_height, image_width) - cropped_lines.append(img_fin) - if abs(better_des_slope) > 45: - cropped_lines_ver_index.append(1) - else: - cropped_lines_ver_index.append(0) - - cropped_lines_meging_indexing.append(0) - if img_bin: - img_fin = preprocess_and_resize_image_for_ocrcnn_model( - img_crop_bin, image_height, image_width) - cropped_lines_bin.append(img_fin) - else: - splited_images, splited_images_bin = return_textlines_split_if_needed( - img_crop, img_crop_bin if img_bin else None) - if splited_images: - img_fin = preprocess_and_resize_image_for_ocrcnn_model( - splited_images[0], image_height, image_width) - cropped_lines.append(img_fin) - cropped_lines_meging_indexing.append(1) - - if abs(better_des_slope) > 45: - cropped_lines_ver_index.append(1) - else: - cropped_lines_ver_index.append(0) - - img_fin = preprocess_and_resize_image_for_ocrcnn_model( - splited_images[1], image_height, image_width) - - cropped_lines.append(img_fin) - cropped_lines_meging_indexing.append(-1) - - if abs(better_des_slope) > 45: - cropped_lines_ver_index.append(1) - else: - cropped_lines_ver_index.append(0) - - if img_bin: - img_fin = preprocess_and_resize_image_for_ocrcnn_model( - splited_images_bin[0], image_height, image_width) - cropped_lines_bin.append(img_fin) - img_fin = preprocess_and_resize_image_for_ocrcnn_model( - splited_images_bin[1], image_height, image_width) - cropped_lines_bin.append(img_fin) - - else: - img_fin = preprocess_and_resize_image_for_ocrcnn_model( - img_crop, image_height, image_width) - cropped_lines.append(img_fin) - cropped_lines_meging_indexing.append(0) - - if abs(better_des_slope) > 45: - cropped_lines_ver_index.append(1) - else: - cropped_lines_ver_index.append(0) - - if img_bin: - img_fin = preprocess_and_resize_image_for_ocrcnn_model( - img_crop_bin, image_height, image_width) - cropped_lines_bin.append(img_fin) - + img_rgb = img # cosmetic + if img_bin is None: + # run ad-hoc binarization + self.logger.info("running binarization for ensemble input") + img_bin = self.do_prediction(True, img, self.model_zoo.get("binarization"), + n_batch_inference=5) + img_bin = np.repeat(img_bin[:, :, np.newaxis], 3, axis=2) + img_bin = 255 * (img_bin == 0).astype(np.uint8) + + for n_region, region in enumerate(page_tree.getroot().iter('{%s}TextRegion' % page_ns)): + type_textregion = region.attrib.get('type', 'paragraph') + for n_line, line in enumerate(region.iter('{%s}TextLine' % page_ns)): + cropped_lines_region_indexer.append(n_region) + + coords = line.find('{%s}Coords' % page_ns) + if coords is None: + self.logger.warning("region '%s' line '%s' has no Coords", region.attrib['id'], line.attrib['id']) + continue + poly = np.array(polygon_from_points(coords.attrib['points'])).astype(int) + cont = poly[:, np.newaxis] + xywh = xywh_from_polygon(poly) + x, y, w, h = xywh['x'], xywh['y'], xywh['w'], xywh['h'] + + angle_radians = math.atan2(h, w) + angle_degrees = math.degrees(angle_radians) + if type_textregion=='drop-capital': + angle_degrees = 0 + + total_bb_coordinates.append([x, y, w, h]) + + w_scaled = w * image_height / float(h) + + img_crop_rgb = img_rgb[y: y + h, x: x + w] + img_crop_bin = img_bin[y: y + h, x: x + w] + + mask_poly = np.zeros(img_crop_rgb.shape[:2], dtype=np.uint8) + mask_poly = cv2.fillPoly(mask_poly, pts=[cont - [x, y]], color=1) + + if angle_degrees > 3: + better_des_slope = get_orientation_moments(cont) + img_crop_rgb = rotate_image_with_padding(img_crop_rgb, better_des_slope) + img_crop_bin = rotate_image_with_padding(img_crop_bin, better_des_slope) + mask_poly = rotate_image_with_padding(mask_poly, better_des_slope) + # get new bounding box + x_n, y_n, w_n, h_n = get_contours_and_bounding_boxes(mask_poly) + img_crop_rgb = img_crop_rgb[y_n: y_n + h_n, x_n: x_n + w_n] + img_crop_bin = img_crop_bin[y_n: y_n + h_n, x_n: x_n + w_n] + mask_poly = mask_poly[y_n: y_n + h_n, x_n: x_n + w_n] + else: + better_des_slope = 0 + + if not self.do_not_mask_with_textline_contour: + img_crop_rgb[mask_poly == 0] = 255 # FIXME: or median color? + img_crop_bin[mask_poly == 0] = 255 + + if (type_textregion !='drop-capital' and + mask_poly.sum() < 0.50 * mask_poly.size and + w_scaled > 90): + + img_crop_rgb, img_crop_bin = \ + break_curved_line_into_small_pieces_and_then_merge( + img_crop_rgb, img_crop_bin, mask_poly) + + if w_scaled < 750:#1.5*image_width: + img_crop_split_rgb = img_crop_split_bin = None + else: + img_crop_split_rgb, img_crop_split_bin = return_textlines_split_if_needed( + img_crop_rgb, img_crop_bin) + if img_crop_split_rgb: + cropped_lines_rgb.extend(img_crop_split_rgb) + cropped_lines_bin.extend(img_crop_split_bin) + if abs(better_des_slope) > 45: + cropped_lines_ver_index.append(1) + cropped_lines_ver_index.append(1) + else: + cropped_lines_ver_index.append(0) + cropped_lines_ver_index.append(0) + cropped_lines_meging_indexing.append(1) + cropped_lines_meging_indexing.append(-1) + else: + cropped_lines_rgb.append(img_crop_rgb) + cropped_lines_bin.append(img_crop_bin) + if abs(better_des_slope) > 45: + cropped_lines_ver_index.append(1) + else: + cropped_lines_ver_index.append(0) + cropped_lines_meging_indexing.append(0) + + cropped_lines_rgb = [preprocess_and_resize_image_for_ocrcnn_model(img, image_height, image_width) + for img in cropped_lines_rgb] + cropped_lines_bin = [preprocess_and_resize_image_for_ocrcnn_model(img, image_height, image_width) + for img in cropped_lines_bin] - indexer_text_region = indexer_text_region +1 - extracted_texts = [] - extracted_conf_value = [] + extracted_confs = [] + self.logger.debug("processing %d lines for %d regions", + len(cropped_lines_rgb), len(set(cropped_lines_region_indexer))) + cropped_lines = zip(cropped_lines_rgb, cropped_lines_bin, cropped_lines_ver_index) + for batch in batched(cropped_lines, self.b_s): + imgs_rgb, imgs_bin, ver_index = zip(*batch) + ver_index = np.array(ver_index) + imgs_rgb = np.stack(imgs_rgb) + imgs_bin = np.stack(imgs_bin) + imgs_rgb_ver = imgs_rgb[ver_index > 0, ::-1, ::-1] + imgs_bin_ver = imgs_bin[ver_index > 0, ::-1, ::-1] - n_iterations = math.ceil(len(cropped_lines) / self.b_s) - - # FIXME: copy pasta - for i in range(n_iterations): - if i==(n_iterations-1): - n_start = i*self.b_s - imgs = cropped_lines[n_start:] - imgs = np.array(imgs) - imgs = imgs.reshape(imgs.shape[0], image_height, image_width, 3) - - ver_imgs = np.array( cropped_lines_ver_index[n_start:] ) - indices_ver = np.where(ver_imgs == 1)[0] - - #print(indices_ver, 'indices_ver') - if len(indices_ver)>0: - imgs_ver_flipped = imgs[indices_ver, : ,: ,:] - imgs_ver_flipped = imgs_ver_flipped[:,::-1,::-1,:] - #print(imgs_ver_flipped, 'imgs_ver_flipped') - - else: - imgs_ver_flipped = None - - if img_bin: - imgs_bin = cropped_lines_bin[n_start:] - imgs_bin = np.array(imgs_bin) - imgs_bin = imgs_bin.reshape(imgs_bin.shape[0], image_height, image_width, 3) - - if len(indices_ver)>0: - imgs_bin_ver_flipped = imgs_bin[indices_ver, : ,: ,:] - imgs_bin_ver_flipped = imgs_bin_ver_flipped[:,::-1,::-1,:] - #print(imgs_ver_flipped, 'imgs_ver_flipped') - - else: - imgs_bin_ver_flipped = None - else: - n_start = i*self.b_s - n_end = (i+1)*self.b_s - imgs = cropped_lines[n_start:n_end] - imgs = np.array(imgs).reshape(self.b_s, image_height, image_width, 3) - - ver_imgs = np.array( cropped_lines_ver_index[n_start:n_end] ) - indices_ver = np.where(ver_imgs == 1)[0] - #print(indices_ver, 'indices_ver') - - if len(indices_ver)>0: - imgs_ver_flipped = imgs[indices_ver, : ,: ,:] - imgs_ver_flipped = imgs_ver_flipped[:,::-1,::-1,:] - #print(imgs_ver_flipped, 'imgs_ver_flipped') - else: - imgs_ver_flipped = None - - - if img_bin: - imgs_bin = cropped_lines_bin[n_start:n_end] - imgs_bin = np.array(imgs_bin).reshape(self.b_s, image_height, image_width, 3) - - - if len(indices_ver)>0: - imgs_bin_ver_flipped = imgs_bin[indices_ver, : ,: ,:] - imgs_bin_ver_flipped = imgs_bin_ver_flipped[:,::-1,::-1,:] - #print(imgs_ver_flipped, 'imgs_ver_flipped') - else: - imgs_bin_ver_flipped = None - - - self.logger.debug("processing next %d lines", len(imgs)) - preds = self.model_zoo.get('ocr').predict(imgs, verbose=0) + # inference model now yields (char-bytes, line-prob) instead of vocidx-softmax + # (so ctc_decode and inverse StringLookup are included) + # also, the model now expects a secondary binary input image + preds, probs = self.model_zoo.get('ocr').predict((imgs_rgb, imgs_bin), verbose=0) - if len(indices_ver)>0: - preds_flipped = self.model_zoo.get('ocr').predict(imgs_ver_flipped, verbose=0) - preds_max_fliped = np.max(preds_flipped, axis=2 ) - preds_max_args_flipped = np.argmax(preds_flipped, axis=2 ) - pred_max_not_unk_mask_bool_flipped = preds_max_args_flipped[:,:]!=self.end_character - masked_means_flipped = \ - np.sum(preds_max_fliped * pred_max_not_unk_mask_bool_flipped, axis=1) / \ - np.sum(pred_max_not_unk_mask_bool_flipped, axis=1) - masked_means_flipped[np.isnan(masked_means_flipped)] = 0 - - preds_max = np.max(preds, axis=2 ) - preds_max_args = np.argmax(preds, axis=2 ) - pred_max_not_unk_mask_bool = preds_max_args[:,:]!=self.end_character - - masked_means = \ - np.sum(preds_max * pred_max_not_unk_mask_bool, axis=1) / \ - np.sum(pred_max_not_unk_mask_bool, axis=1) - masked_means[np.isnan(masked_means)] = 0 - - masked_means_ver = masked_means[indices_ver] - #print(masked_means_ver, 'pred_max_not_unk') - - indices_where_flipped_conf_value_is_higher = \ - np.where(masked_means_flipped > masked_means_ver)[0] - - #print(indices_where_flipped_conf_value_is_higher, 'indices_where_flipped_conf_value_is_higher') - if len(indices_where_flipped_conf_value_is_higher)>0: - indices_to_be_replaced = indices_ver[indices_where_flipped_conf_value_is_higher] - preds[indices_to_be_replaced,:,:] = \ - preds_flipped[indices_where_flipped_conf_value_is_higher, :, :] + if ver_index.any(): + preds_ver, probs_ver = self.model_zoo.get('ocr').predict((imgs_rgb_ver, imgs_bin_ver), verbose=0) + flipped_ver_is_better = np.flatnonzero(probs_ver > probs[ver_index > 0]) + if len(flipped_ver_is_better): + self.logger.info("%d skewed lines perform better when flipped", len(flipped_ver_is_better)) + preds[ver_index > 0][flipped_ver_is_better] = preds_ver[flipped_ver_is_better] + probs[ver_index > 0][flipped_ver_is_better] = probs_ver[flipped_ver_is_better] - if img_bin: - preds_bin = self.model_zoo.get('ocr').predict(imgs_bin, verbose=0) - - if len(indices_ver)>0: - preds_flipped = self.model_zoo.get('ocr').predict(imgs_bin_ver_flipped, verbose=0) - preds_max_fliped = np.max(preds_flipped, axis=2 ) - preds_max_args_flipped = np.argmax(preds_flipped, axis=2 ) - pred_max_not_unk_mask_bool_flipped = preds_max_args_flipped[:,:]!=self.end_character - masked_means_flipped = \ - np.sum(preds_max_fliped * pred_max_not_unk_mask_bool_flipped, axis=1) / \ - np.sum(pred_max_not_unk_mask_bool_flipped, axis=1) - masked_means_flipped[np.isnan(masked_means_flipped)] = 0 - - preds_max = np.max(preds, axis=2 ) - preds_max_args = np.argmax(preds, axis=2 ) - pred_max_not_unk_mask_bool = preds_max_args[:,:]!=self.end_character - - masked_means = \ - np.sum(preds_max * pred_max_not_unk_mask_bool, axis=1) / \ - np.sum(pred_max_not_unk_mask_bool, axis=1) - masked_means[np.isnan(masked_means)] = 0 - - masked_means_ver = masked_means[indices_ver] - #print(masked_means_ver, 'pred_max_not_unk') - - indices_where_flipped_conf_value_is_higher = \ - np.where(masked_means_flipped > masked_means_ver)[0] - - #print(indices_where_flipped_conf_value_is_higher, 'indices_where_flipped_conf_value_is_higher') - if len(indices_where_flipped_conf_value_is_higher)>0: - indices_to_be_replaced = indices_ver[indices_where_flipped_conf_value_is_higher] - preds_bin[indices_to_be_replaced,:,:] = \ - preds_flipped[indices_where_flipped_conf_value_is_higher, :, :] - - preds = (preds + preds_bin) / 2. - - pred_texts = decode_batch_predictions(preds, self.model_zoo.get('num_to_char')) - - preds_max = np.max(preds, axis=2 ) - preds_max_args = np.argmax(preds, axis=2 ) - pred_max_not_unk_mask_bool = preds_max_args[:,:]!=self.end_character - masked_means = \ - np.sum(preds_max * pred_max_not_unk_mask_bool, axis=1) / \ - np.sum(pred_max_not_unk_mask_bool, axis=1) - - for ib in range(imgs.shape[0]): - pred_texts_ib = pred_texts[ib].replace("[UNK]", "") - if masked_means[ib] >= self.min_conf_value_of_textline_text: - extracted_texts.append(pred_texts_ib) - extracted_conf_value.append(masked_means[ib]) - else: + def nooov(x): + return x != b'[UNK]' + for pred, prob in zip(preds, probs): + if prob < self.min_conf_value_of_textline_text: extracted_texts.append("") - extracted_conf_value.append(0) - del cropped_lines + extracted_confs.append(0) + else: + text = b''.join( + filter(nooov, + map(bytes, + (filter(None, char) + for char in pred.tolist())))).decode('utf-8') + extracted_texts.append(text) + extracted_confs.append(prob) + del cropped_lines_rgb del cropped_lines_bin gc.collect() extracted_texts_merged = [extracted_texts[ind] - if cropped_lines_meging_indexing[ind]==0 - else extracted_texts[ind]+" "+extracted_texts[ind+1] - if cropped_lines_meging_indexing[ind]==1 - else None - for ind in range(len(cropped_lines_meging_indexing))] - - extracted_conf_value_merged = [extracted_conf_value[ind] # type: ignore - if cropped_lines_meging_indexing[ind]==0 - else (extracted_conf_value[ind]+extracted_conf_value[ind+1])/2. - if cropped_lines_meging_indexing[ind]==1 - else None - for ind in range(len(cropped_lines_meging_indexing))] - - extracted_conf_value_merged: List[float] = [extracted_conf_value_merged[ind_cfm] - for ind_cfm in range(len(extracted_texts_merged)) - if extracted_texts_merged[ind_cfm] is not None] - - extracted_texts_merged = [ind for ind in extracted_texts_merged if ind is not None] + if cropped_lines_meging_indexing[ind] == 0 + else extracted_texts[ind] + " " + extracted_texts[ind + 1] + for ind in range(len(cropped_lines_meging_indexing)) + if cropped_lines_meging_indexing[ind] >= 0] + extracted_confs_merged = [extracted_confs[ind] + if cropped_lines_meging_indexing[ind] == 0 + else 0.5 * (extracted_confs[ind] + extracted_confs[ind + 1]) + for ind in range(len(cropped_lines_meging_indexing)) + if cropped_lines_meging_indexing[ind] >= 0] return EynollahOcrResult( extracted_texts_merged=extracted_texts_merged, - extracted_conf_value_merged=extracted_conf_value_merged, + extracted_confs_merged=extracted_confs_merged, cropped_lines_region_indexer=cropped_lines_region_indexer, total_bb_coordinates=total_bb_coordinates, ) @@ -569,7 +373,7 @@ class Eynollah_ocr(Eynollah): cropped_lines_region_indexer = result.cropped_lines_region_indexer total_bb_coordinates = result.total_bb_coordinates extracted_texts_merged = result.extracted_texts_merged - extracted_conf_value_merged = result.extracted_conf_value_merged + extracted_confs_merged = result.extracted_confs_merged unique_cropped_lines_region_indexer = np.unique(cropped_lines_region_indexer) if out_image_with_text: @@ -646,8 +450,8 @@ class Eynollah_ocr(Eynollah): if not is_textline_text: text_subelement = ET.SubElement(child_textregion, 'TextEquiv') - if extracted_conf_value_merged: - text_subelement.set('conf', f"{extracted_conf_value_merged[indexer]:.2f}") + if extracted_confs_merged: + text_subelement.set('conf', f"{extracted_confs_merged[indexer]:.2f}") unicode_textline = ET.SubElement(text_subelement, 'Unicode') unicode_textline.text = extracted_texts_merged[indexer] else: @@ -655,8 +459,8 @@ class Eynollah_ocr(Eynollah): if childtest3.tag.endswith("TextEquiv"): for child_uc in childtest3: if child_uc.tag.endswith("Unicode"): - if extracted_conf_value_merged: - childtest3.set('conf', f"{extracted_conf_value_merged[indexer]:.2f}") + if extracted_confs_merged: + childtest3.set('conf', f"{extracted_confs_merged[indexer]:.2f}") child_uc.text = extracted_texts_merged[indexer] indexer = indexer + 1 diff --git a/src/eynollah/utils/__init__.py b/src/eynollah/utils/__init__.py index 47a765c..621b9ec 100644 --- a/src/eynollah/utils/__init__.py +++ b/src/eynollah/utils/__init__.py @@ -2,6 +2,7 @@ from typing import Iterable, List, Tuple from logging import getLogger import time import math +from itertools import islice try: import matplotlib.pyplot as plt @@ -33,6 +34,11 @@ def pairwise(iterable): yield a, b a = b +def batched(iterable, n): + iterator = iter(iterable) + while batch := tuple(islice(iterator, n)): + yield batch + def return_multicol_separators_x_start_end( regions_without_separators, peak_points, top, bot, x_min_hor_some, x_max_hor_some, cy_hor_some, y_min_hor_some, y_max_hor_some): diff --git a/src/eynollah/utils/utils_ocr.py b/src/eynollah/utils/utils_ocr.py index 817406c..6fc81fb 100644 --- a/src/eynollah/utils/utils_ocr.py +++ b/src/eynollah/utils/utils_ocr.py @@ -1,6 +1,5 @@ import math import copy -from itertools import islice import numpy as np import cv2 @@ -11,6 +10,7 @@ from scipy.signal import find_peaks from scipy.ndimage import gaussian_filter1d from PIL import Image, ImageDraw, ImageFont +from . import pairwise from .resize import resize_image @@ -41,45 +41,6 @@ def decode_batch_predictions(pred, num_to_char, max_len = 128): d = d.numpy().decode("utf-8") output.append(d) return output - - -def distortion_free_resize(image, img_size): - import tensorflow as tf - - w, h = img_size - image = tf.image.resize(image, size=(h, w), preserve_aspect_ratio=True) - - # Check tha amount of padding needed to be done. - pad_height = h - tf.shape(image)[0] - pad_width = w - tf.shape(image)[1] - - # Only necessary if you want to do same amount of padding on both sides. - if pad_height % 2 != 0: - height = pad_height // 2 - pad_height_top = height + 1 - pad_height_bottom = height - else: - pad_height_top = pad_height_bottom = pad_height // 2 - - if pad_width % 2 != 0: - width = pad_width // 2 - pad_width_left = width + 1 - pad_width_right = width - else: - pad_width_left = pad_width_right = pad_width // 2 - - image = tf.pad( - image, - paddings=[ - [pad_height_top, pad_height_bottom], - [pad_width_left, pad_width_right], - [0, 0], - ], - ) - - image = tf.transpose(image, (1, 0, 2)) - image = tf.image.flip_left_right(image) - return image def return_start_and_end_of_common_text_of_textline_ocr_without_common_section(textline_image): width = np.shape(textline_image)[1] @@ -263,254 +224,58 @@ def return_splitting_point_of_image(image_to_spliited): return np.sort(peaks_sort_4) -def break_curved_line_into_small_pieces_and_then_merge(img_curved, mask_curved, img_bin_curved=None): - peaks_4 = return_splitting_point_of_image(img_curved) - if len(peaks_4)>0: +def break_curved_line_into_small_pieces_and_then_merge(img_rgb_curved, img_bin_curved, mask_curved): + peaks_4 = return_splitting_point_of_image(img_rgb_curved) + if len(peaks_4): imgs_tot = [] - - for ind in range(len(peaks_4)+1): - if ind==0: - img = img_curved[:, :peaks_4[ind], :] - if img_bin_curved is not None: - img_bin = img_bin_curved[:, :peaks_4[ind], :] - mask = mask_curved[:, :peaks_4[ind], :] - elif ind==len(peaks_4): - img = img_curved[:, peaks_4[ind-1]:, :] - if img_bin_curved is not None: - img_bin = img_bin_curved[:, peaks_4[ind-1]:, :] - mask = mask_curved[:, peaks_4[ind-1]:, :] - else: - img = img_curved[:, peaks_4[ind-1]:peaks_4[ind], :] - if img_bin_curved is not None: - img_bin = img_bin_curved[:, peaks_4[ind-1]:peaks_4[ind], :] - mask = mask_curved[:, peaks_4[ind-1]:peaks_4[ind], :] - + for left, right in pairwise([None] + peaks_4 + [None]): + img_rgb = img_rgb_curved[:, left: right] + img_bin = img_bin_curved[:, left: right] + mask = mask_curved[:, left: right] or_ma = get_orientation_moments_of_mask(mask) - - if img_bin_curved is not None: - imgs_tot.append([img, mask, or_ma, img_bin] ) - else: - imgs_tot.append([img, mask, or_ma] ) - + imgs_tot.append([img_rgb, img_bin, mask, or_ma]) w_tot_des_list = [] - w_tot_des = 0 - imgs_deskewed_list = [] + imgs_rgb_deskewed_list = [] imgs_bin_deskewed_list = [] - for ind in range(len(imgs_tot)): - img_in = imgs_tot[ind][0] - mask_in = imgs_tot[ind][1] - ori_in = imgs_tot[ind][2] - if img_bin_curved is not None: - img_bin_in = imgs_tot[ind][3] - - if abs(ori_in)<45: - img_in_des = rotate_image_with_padding(img_in, ori_in, border_value=(255,255,255) ) - if img_bin_curved is not None: - img_bin_in_des = rotate_image_with_padding(img_bin_in, ori_in, border_value=(255,255,255) ) + for img_rgb_in, img_bin_in, mask_in, ori_in in imgs_tot: + if abs(ori_in) < 45: + img_rgb_in_des = rotate_image_with_padding(img_rgb_in, ori_in, border_value=(255,255,255) ) + img_bin_in_des = rotate_image_with_padding(img_bin_in, ori_in, border_value=(255,255,255) ) mask_in_des = rotate_image_with_padding(mask_in, ori_in) - mask_in_des = mask_in_des.astype('uint8') - - #new bounding box - x_n, y_n, w_n, h_n = get_contours_and_bounding_boxes(mask_in_des[:,:,0]) - - if w_n==0 or h_n==0: - img_in_des = np.copy(img_in) - if img_bin_curved is not None: - img_bin_in_des = np.copy(img_bin_in) - w_relative = int(32 * img_in_des.shape[1]/float(img_in_des.shape[0]) ) - if w_relative==0: - w_relative = img_in_des.shape[1] - img_in_des = resize_image(img_in_des, 32, w_relative) - if img_bin_curved is not None: - img_bin_in_des = resize_image(img_bin_in_des, 32, w_relative) + # get new bounding box + x_n, y_n, w_n, h_n = get_contours_and_bounding_boxes(mask_in_des) + if w_n and h_n: + img_rgb_in_des = img_rgb_in_des[y_n: y_n + h_n, x_n: x_n + w_n] + img_bin_in_des = img_bin_in_des[y_n: y_n + h_n, x_n: x_n + w_n] else: - mask_in_des = mask_in_des[y_n:y_n+h_n, x_n:x_n+w_n, :] - img_in_des = img_in_des[y_n:y_n+h_n, x_n:x_n+w_n, :] - if img_bin_curved is not None: - img_bin_in_des = img_bin_in_des[y_n:y_n+h_n, x_n:x_n+w_n, :] - - w_relative = int(32 * img_in_des.shape[1]/float(img_in_des.shape[0]) ) - if w_relative==0: - w_relative = img_in_des.shape[1] - img_in_des = resize_image(img_in_des, 32, w_relative) - if img_bin_curved is not None: - img_bin_in_des = resize_image(img_bin_in_des, 32, w_relative) - - - else: - img_in_des = np.copy(img_in) - if img_bin_curved is not None: + img_rgb_in_des = np.copy(img_rgb_in) img_bin_in_des = np.copy(img_bin_in) - w_relative = int(32 * img_in_des.shape[1]/float(img_in_des.shape[0]) ) - if w_relative==0: - w_relative = img_in_des.shape[1] - img_in_des = resize_image(img_in_des, 32, w_relative) - if img_bin_curved is not None: - img_bin_in_des = resize_image(img_bin_in_des, 32, w_relative) - - w_tot_des+=img_in_des.shape[1] - w_tot_des_list.append(img_in_des.shape[1]) - imgs_deskewed_list.append(img_in_des) - if img_bin_curved is not None: - imgs_bin_deskewed_list.append(img_bin_in_des) - - - + else: + img_rgb_in_des = np.copy(img_rgb_in) + img_bin_in_des = np.copy(img_bin_in) - img_final_deskewed = np.zeros((32, w_tot_des, 3))+255 - if img_bin_curved is not None: - img_bin_final_deskewed = np.zeros((32, w_tot_des, 3))+255 - else: - img_bin_final_deskewed = None + h, w = img_rgb_in_des.shape[:2] + new_h = 32 + new_w = 32 * w // h + new_w = new_w or w + img_rgb_in_des = resize_image(img_rgb_in_des, new_h, new_w) + img_bin_in_des = resize_image(img_bin_in_des, new_h, new_w) + + w_tot_des_list.append(new_w) + imgs_rgb_deskewed_list.append(img_rgb_in_des) + imgs_bin_deskewed_list.append(img_bin_in_des) + + img_rgb_final_deskewed = np.ones((new_h, sum(w_tot_des_list), 3)) * 255 + img_bin_final_deskewed = np.ones((new_h, sum(w_tot_des_list), 3)) * 255 w_indexer = 0 for ind in range(len(w_tot_des_list)): - img_final_deskewed[:,w_indexer:w_indexer+w_tot_des_list[ind],:] = imgs_deskewed_list[ind][:,:,:] - if img_bin_curved is not None: - img_bin_final_deskewed[:,w_indexer:w_indexer+w_tot_des_list[ind],:] = imgs_bin_deskewed_list[ind][:,:,:] - w_indexer = w_indexer+w_tot_des_list[ind] - return img_final_deskewed, img_bin_final_deskewed + w_indexer2 = w_indexer + w_tot_des_list[ind] + img_rgb_final_deskewed[:, w_indexer: w_indexer2] = imgs_rgb_deskewed_list[ind] + img_bin_final_deskewed[:, w_indexer: w_indexer2] = imgs_bin_deskewed_list[ind] + w_indexer = w_indexer2 + return img_rgb_final_deskewed, img_bin_final_deskewed else: - return img_curved, img_bin_curved - -def return_textline_contour_with_added_box_coordinate(textline_contour, box_ind): - textline_contour[:,:,0] += box_ind[2] - textline_contour[:,:,1] += box_ind[0] - return textline_contour - - -def return_rnn_cnn_ocr_of_given_textlines(image, - all_found_textline_polygons, - all_box_coord, - prediction_model, - b_s_ocr, num_to_char, - curved_line=False): - max_len = 512 - padding_token = 299 - image_width = 512#max_len * 4 - image_height = 32 - ind_tot = 0 - #cv2.imwrite('./img_out.png', image_page) - ocr_all_textlines = [] - cropped_lines_region_indexer = [] - cropped_lines_meging_indexing = [] - cropped_lines = [] - indexer_text_region = 0 - - for indexing, ind_poly_first in enumerate(all_found_textline_polygons): - #ocr_textline_in_textregion = [] - if len(ind_poly_first)==0: - cropped_lines_region_indexer.append(indexer_text_region) - cropped_lines_meging_indexing.append(0) - img_fin = np.ones((image_height, image_width, 3))*1 - cropped_lines.append(img_fin) - - else: - for indexing2, ind_poly in enumerate(ind_poly_first): - cropped_lines_region_indexer.append(indexer_text_region) - if not curved_line: - ind_poly = copy.deepcopy(ind_poly) - box_ind = all_box_coord[indexing] - - ind_poly = return_textline_contour_with_added_box_coordinate(ind_poly, box_ind) - #print(ind_poly_copy) - ind_poly[ind_poly<0] = 0 - x, y, w, h = cv2.boundingRect(ind_poly) - - w_scaled = w * image_height/float(h) - - mask_poly = np.zeros(image.shape) - - img_poly_on_img = np.copy(image) - - mask_poly = cv2.fillPoly(mask_poly, pts=[ind_poly], color=(1, 1, 1)) - - - - mask_poly = mask_poly[y:y+h, x:x+w, :] - img_crop = img_poly_on_img[y:y+h, x:x+w, :] - - img_crop[mask_poly==0] = 255 - - if w_scaled < 640:#1.5*image_width: - img_fin = preprocess_and_resize_image_for_ocrcnn_model(img_crop, image_height, image_width) - cropped_lines.append(img_fin) - cropped_lines_meging_indexing.append(0) - else: - splited_images, splited_images_bin = return_textlines_split_if_needed(img_crop, None) - - if splited_images: - img_fin = preprocess_and_resize_image_for_ocrcnn_model(splited_images[0], - image_height, - image_width) - cropped_lines.append(img_fin) - cropped_lines_meging_indexing.append(1) - - img_fin = preprocess_and_resize_image_for_ocrcnn_model(splited_images[1], - image_height, - image_width) - - cropped_lines.append(img_fin) - cropped_lines_meging_indexing.append(-1) - - else: - img_fin = preprocess_and_resize_image_for_ocrcnn_model(img_crop, - image_height, - image_width) - cropped_lines.append(img_fin) - cropped_lines_meging_indexing.append(0) - - indexer_text_region+=1 - - extracted_texts = [] - - n_iterations = math.ceil(len(cropped_lines) / b_s_ocr) - - for i in range(n_iterations): - if i==(n_iterations-1): - n_start = i*b_s_ocr - imgs = cropped_lines[n_start:] - imgs = np.array(imgs) - imgs = imgs.reshape(imgs.shape[0], image_height, image_width, 3) - - - else: - n_start = i*b_s_ocr - n_end = (i+1)*b_s_ocr - imgs = cropped_lines[n_start:n_end] - imgs = np.array(imgs).reshape(b_s_ocr, image_height, image_width, 3) - - - preds = prediction_model.predict(imgs, verbose=0) - - pred_texts = decode_batch_predictions(preds, num_to_char) - - for ib in range(imgs.shape[0]): - pred_texts_ib = pred_texts[ib].replace("[UNK]", "") - extracted_texts.append(pred_texts_ib) - - extracted_texts_merged = [extracted_texts[ind] - if cropped_lines_meging_indexing[ind]==0 - else extracted_texts[ind]+" "+extracted_texts[ind+1] - if cropped_lines_meging_indexing[ind]==1 - else None - for ind in range(len(cropped_lines_meging_indexing))] - - extracted_texts_merged = [ind for ind in extracted_texts_merged if ind is not None] - unique_cropped_lines_region_indexer = np.unique(cropped_lines_region_indexer) - - ocr_all_textlines = [] - for ind in unique_cropped_lines_region_indexer: - ocr_textline_in_textregion = [] - extracted_texts_merged_un = np.array(extracted_texts_merged)[np.array(cropped_lines_region_indexer)==ind] - for it_ind, text_textline in enumerate(extracted_texts_merged_un): - ocr_textline_in_textregion.append(text_textline) - ocr_all_textlines.append(ocr_textline_in_textregion) - return ocr_all_textlines - -def batched(iterable, n): - iterator = iter(iterable) - while batch := tuple(islice(iterator, n)): - yield batch + return img_rgb_curved, img_bin_curved From d2f2a1e06b3632b11b0dbf7c40ed59bc50ee5d44 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 3 Jun 2026 00:43:46 +0200 Subject: [PATCH 042/121] =?UTF-8?q?Eynollah=5Focr:=20correctly=20handle=20?= =?UTF-8?q?min=5Fconf,=20improve=20writer=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `min_conf_value_of_textline_text`: apply by skipping lines below threshold (instead of writing empty text), and delete their TextEquiv (if existing) - `write_ocr()`: simplify, and ensure consistency between line and region level text correctly --- src/eynollah/eynollah_ocr.py | 137 +++++++++++++---------------------- 1 file changed, 50 insertions(+), 87 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 40cbeaa..aeaabfe 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -41,7 +41,7 @@ from .utils.utils_ocr import ( @dataclass class EynollahOcrResult: extracted_texts_merged: List - extracted_confs_merged: Optional[List] + extracted_confs_merged: List cropped_lines_region_indexer: List total_bb_coordinates:List @@ -156,10 +156,6 @@ class Eynollah_ocr(Eynollah): conf = output.sequences_scores.exp().clamp(0.0, 1.0).tolist() else: conf = [1.0] * len(output.sequences) - if conf < self.min_conf_value_of_textline_text: - extracted_confs.extend(0) - extracted_texts.extend("") - continue text = self.model_zoo.get('trocr_processor').batch_decode( output.sequences, skip_special_tokens=True, @@ -327,17 +323,13 @@ class Eynollah_ocr(Eynollah): def nooov(x): return x != b'[UNK]' for pred, prob in zip(preds, probs): - if prob < self.min_conf_value_of_textline_text: - extracted_texts.append("") - extracted_confs.append(0) - else: - text = b''.join( - filter(nooov, - map(bytes, - (filter(None, char) - for char in pred.tolist())))).decode('utf-8') - extracted_texts.append(text) - extracted_confs.append(prob) + text = b''.join( + filter(nooov, + map(bytes, + (filter(None, char) + for char in pred.tolist())))).decode('utf-8') + extracted_texts.append(text) + extracted_confs.append(prob) del cropped_lines_rgb del cropped_lines_bin gc.collect() @@ -375,7 +367,6 @@ class Eynollah_ocr(Eynollah): extracted_texts_merged = result.extracted_texts_merged extracted_confs_merged = result.extracted_confs_merged - unique_cropped_lines_region_indexer = np.unique(cropped_lines_region_indexer) if out_image_with_text: image_text = Image.new("RGB", (img.shape[1], img.shape[0]), "white") draw = ImageDraw.Draw(image_text) @@ -403,78 +394,50 @@ class Eynollah_ocr(Eynollah): draw.text((text_x, text_y), extracted_texts_merged[indexer_text], fill="black", font=font) image_text.save(out_image_with_text) - text_by_textregion = [] - for ind in unique_cropped_lines_region_indexer: - ind = np.array(cropped_lines_region_indexer)==ind - extracted_texts_merged_un = np.array(extracted_texts_merged)[ind] - if len(extracted_texts_merged_un)>1: - text_by_textregion_ind = "" - next_glue = "" - for indt in range(len(extracted_texts_merged_un)): - if (extracted_texts_merged_un[indt].endswith('⸗') or - extracted_texts_merged_un[indt].endswith('-') or - extracted_texts_merged_un[indt].endswith('¬')): - text_by_textregion_ind += next_glue + extracted_texts_merged_un[indt][:-1] - next_glue = "" - else: - text_by_textregion_ind += next_glue + extracted_texts_merged_un[indt] - next_glue = " " - text_by_textregion.append(text_by_textregion_ind) - else: - text_by_textregion.append(" ".join(extracted_texts_merged_un)) + cropped_lines_region_indexer = np.array(cropped_lines_region_indexer) + for n_region, region in enumerate(page_tree.getroot().iter('{%s}TextRegion' % page_ns)): + lines_indexer = np.flatnonzero(cropped_lines_region_indexer == n_region) + if not len(lines_indexer): + continue - indexer = 0 - indexer_textregion = 0 - for nn in page_tree.getroot().iter(f'{{{page_ns}}}TextRegion'): - - is_textregion_text = False - for childtest in nn: - if childtest.tag.endswith("TextEquiv"): - is_textregion_text = True - - if not is_textregion_text: - text_subelement_textregion = ET.SubElement(nn, 'TextEquiv') - unicode_textregion = ET.SubElement(text_subelement_textregion, 'Unicode') - - - has_textline = False - for child_textregion in nn: - # FIXME: should remove Word level, if it already exists - if child_textregion.tag.endswith("TextLine"): - - is_textline_text = False - for childtest2 in child_textregion: - if childtest2.tag.endswith("TextEquiv"): - is_textline_text = True - - - if not is_textline_text: - text_subelement = ET.SubElement(child_textregion, 'TextEquiv') - if extracted_confs_merged: - text_subelement.set('conf', f"{extracted_confs_merged[indexer]:.2f}") - unicode_textline = ET.SubElement(text_subelement, 'Unicode') - unicode_textline.text = extracted_texts_merged[indexer] - else: - for childtest3 in child_textregion: - if childtest3.tag.endswith("TextEquiv"): - for child_uc in childtest3: - if child_uc.tag.endswith("Unicode"): - if extracted_confs_merged: - childtest3.set('conf', f"{extracted_confs_merged[indexer]:.2f}") - child_uc.text = extracted_texts_merged[indexer] - - indexer = indexer + 1 - has_textline = True - if has_textline: - if is_textregion_text: - for child4 in nn: - if child4.tag.endswith("TextEquiv"): - for childtr_uc in child4: - if childtr_uc.tag.endswith("Unicode"): - childtr_uc.text = text_by_textregion[indexer_textregion] + text_region = "" + next_glue = "" + for line_idx in lines_indexer: + if extracted_confs_merged[line_idx] < self.min_conf_value_of_textline_text: + continue + text_line = extracted_texts_merged[line_idx] + if (text_line.endswith(('⸗', '-', '¬')) and + # last line of a region can still be wrapped + # around columns or pages + line_idx < len(lines_indexer) - 1): + text_region += next_glue + text_line[:-1] + next_glue = "" else: - unicode_textregion.text = text_by_textregion[indexer_textregion] - indexer_textregion = indexer_textregion + 1 + text_region += next_glue + text_line + next_glue = " " + + region_textequiv = region.find('{%s}TextEquiv' % page_ns) + if region_textequiv is None: + region_textequiv = ET.SubElement(region, 'TextEquiv') + region_teunicode = region_textequiv.find('{%s}Unicode' % page_ns) + if region_teunicode is None: + region_teunicode = ET.SubElement(region_textequiv, 'Unicode') + region_teunicode.text = text_region + + for n_line, line in enumerate(region.iter('{%s}TextLine' % page_ns)): + line_textequiv = line.find('{%s}TextEquiv' % page_ns) + if line_textequiv is None: + line_textequiv = ET.SubElement(line, 'TextEquiv') + line_teunicode = line_textequiv.find('{%s}Unicode' % page_ns) + if line_teunicode is None: + line_teunicode = ET.SubElement(line_textequiv, 'Unicode') + + line_idx = lines_indexer[n_line] + if extracted_confs_merged[line_idx] < self.min_conf_value_of_textline_text: + line.remove(line_textequiv) + else: + line_textequiv.set('conf', str(round(extracted_confs_merged[line_idx], 2))) + line_teunicode.text = extracted_texts_merged[line_idx] ET.register_namespace("",page_ns) self.logger.info("output filename: '%s'", out_file_ocr) From f447a9f248d0d48ea6f4a6fc7185ae82484c8ac3 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 3 Jun 2026 03:41:44 +0200 Subject: [PATCH 043/121] =?UTF-8?q?trocr:=20move=20preprocessor=20and=20de?= =?UTF-8?q?coder=20into=20model=20object,=20too=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ModelZoo: drop `trocr_processor` model type - `ModelZoo.load_models()`: use Predictor for `ocr_tr` models, too - `ModelZoo.load_model()`: for `ocr_tr`, load processor and model, then define a function object as stand-in for the common model interface based on Keras (w/ `.predict_on_batch()`) - Predictor: allow multi-input without actual batch dimension for `ocr_tr` models (because the model takes a list of original image arrays and resizes them to model shape internally) - Eynollah_ocr: adapt (replacing preprocessing, prediction and decoding steps by a single `.predict()` call) --- src/eynollah/eynollah_ocr.py | 22 +---- src/eynollah/model_zoo/default_specs.py | 16 ---- src/eynollah/model_zoo/model_zoo.py | 121 ++++++++++++++---------- src/eynollah/predictor.py | 15 ++- 4 files changed, 88 insertions(+), 86 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index aeaabfe..1dfe177 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -70,8 +70,7 @@ class Eynollah_ocr(Eynollah): def setup_models(self, device=''): if self.tr_ocr: - self.model_zoo.load_models('trocr_processor', - ('ocr', 'tr'), + self.model_zoo.load_models(('ocr', 'tr'), device=device) else: self.model_zoo.load_models('ocr', @@ -142,24 +141,7 @@ class Eynollah_ocr(Eynollah): self.logger.debug("processing %d lines for %d regions", len(cropped_lines), len(set(cropped_lines_region_indexer))) for imgs in batched(cropped_lines, self.b_s): - pixel_values = self.model_zoo.get('trocr_processor')( - imgs, return_tensors="pt").pixel_values - output = self.model_zoo.get('ocr').generate( - pixel_values.to(self.device), - # beam search instead of greedy decoding: - num_beams=4, - # also return probability - output_scores=True, - return_dict_in_generate=True) - if output.sequences_scores is not None: - # log-prob averaged over length - conf = output.sequences_scores.exp().clamp(0.0, 1.0).tolist() - else: - conf = [1.0] * len(output.sequences) - text = self.model_zoo.get('trocr_processor').batch_decode( - output.sequences, - skip_special_tokens=True, - clean_up_tokenization_spaces=False) + text, conf = self.model_zoo.get('ocr').predict(imgs) extracted_confs.extend(conf) extracted_texts.extend(text) del cropped_lines diff --git a/src/eynollah/model_zoo/default_specs.py b/src/eynollah/model_zoo/default_specs.py index 170d944..18bf093 100644 --- a/src/eynollah/model_zoo/default_specs.py +++ b/src/eynollah/model_zoo/default_specs.py @@ -217,20 +217,4 @@ DEFAULT_MODEL_SPECS = EynollahModelSpecSet([ type='Keras', ), - EynollahModelSpec( - category="trocr_processor", - variant='', - filename="models_eynollah/model_eynollah_ocr_trocr_20250919", - dist_url=dist_url("ocr"), - type='TrOCRProcessor', - ), - - EynollahModelSpec( - category="trocr_processor", - variant='htr', - filename="models_eynollah/microsoft/trocr-base-handwritten", - dist_url=dist_url("extra"), - type='TrOCRProcessor', - ), - ]) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index e7d21aa..0dd24a8 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -116,22 +116,15 @@ class EynollahModelZoo: model_category, model_variant = load_args load_kwargs["model_variant"] = model_variant - if model_category.endswith('_resized'): - model_category = model_category[:-8] - load_kwargs["resized"] = True - elif model_category.endswith('_patched'): - model_category = model_category[:-8] - load_kwargs["patched"] = True + # if model_category.endswith('_resized'): + # model_category = model_category[:-8] + # load_kwargs["resized"] = True + # elif model_category.endswith('_patched'): + # model_category = model_category[:-8] + # load_kwargs["patched"] = True - if model_category == 'ocr' and model_variant == 'tr': - model = self._load_ocr_model(variant=model_variant, device=device) - elif model_category == 'trocr_processor': - from transformers import TrOCRProcessor - model_path = self.model_path(model_category, model_variant) - model = TrOCRProcessor.from_pretrained(model_path) - else: - model = Predictor(self.logger, self) - model.load_model(model_category, **load_kwargs) + model = Predictor(self.logger, self) + model.load_model(model_category, **load_kwargs) ret[model_category] = model self._loaded.update(ret) @@ -142,8 +135,8 @@ class EynollahModelZoo: model_category: str, model_variant: str = '', model_path_override: Optional[str] = None, - patched: bool = False, - resized: bool = False, + # patched: bool = False, + # resized: bool = False, device: str = '', ) -> AnyModel: """ @@ -153,7 +146,9 @@ class EynollahModelZoo: self.override_models((model_category, model_variant, model_path_override)) model_path = self.model_path(model_category, model_variant) - if model_path.is_dir() and (model_path / "keras_metadata.pb").exists(): + if model_category == 'ocr' and model_variant == 'tr': + model = self._load_trocr_model(model_path, device=device) + elif model_path.is_dir() and (model_path / "keras_metadata.pb").exists(): # Keras model model = self._load_keras_model(model_category, model_path, device=device) elif model_path.is_dir(): @@ -220,6 +215,30 @@ class EynollahModelZoo: if not cuda: self.logger.warning("no GPU device available") + def _configure_torch_device(self, model_category, device=''): + import torch + + device0 = torch.device('cpu') + if not device and torch.cuda.is_available(): + device = 'GPU' # try + if device and ':' in device: + for spec in device.split(','): + cat, dev = spec.split(':') + if fnmatchcase('ocr', cat): + device = dev + break + if device and device.startswith('GPU'): + try: + device0 = torch.device('cuda', int(device[3:] or 0)) + name = torch.cuda.get_device_name(device0) + self.logger.info("using GPU %s (%s) for model ocr:tr", device0, name) + except: + self.logger.exception("cannot configure GPU device") + device0 = torch.device('cpu') + if device0.type != 'cuda': + self.logger.warning("no GPU device available") + return device0 + def _load_keras_model(self, model_category, model_path, device=''): os.environ['TF_USE_LEGACY_KERAS'] = '1' # avoid Keras 3 after TF 2.15 from ocrd_utils import tf_disable_interactive_logs @@ -325,40 +344,46 @@ class EynollahModelZoo: return model - def _load_ocr_model(self, variant: str, device: str = "") -> AnyModel: + def _load_trocr_model(self, model_path, device: str = "") -> AnyModel: """ Load OCR model """ - model_dir = self.model_path('ocr', variant) - if variant == 'tr': - from transformers import VisionEncoderDecoderModel - import torch - model = VisionEncoderDecoderModel.from_pretrained(model_dir) - assert isinstance(model, VisionEncoderDecoderModel) - device0 = torch.device('cpu') - if not device and torch.cuda.is_available(): - device = 'GPU' # try - if device and ':' in device: - for spec in device.split(','): - cat, dev = spec.split(':') - if fnmatchcase('ocr', cat): - device = dev - break - if device and device.startswith('GPU'): - try: - device0 = torch.device('cuda', int(device[3:] or 0)) - name = torch.cuda.get_device_name(device0) - self.logger.info("using GPU %s (%s) for model ocr:tr", device0, name) - except: - self.logger.exception("cannot configure GPU device") - device0 = torch.device('cpu') - if device0.type == 'cuda': - model.to(device0) - else: - self.logger.warning("no GPU device available") - return model + from transformers import VisionEncoderDecoderModel, TrOCRProcessor + import numpy as np - return self.load_model('ocr', model_variant=variant, device=device) + device = self._configure_torch_device('ocr', device=device) + proc = TrOCRProcessor.from_pretrained(model_path) + model = VisionEncoderDecoderModel.from_pretrained(model_path) + assert isinstance(model, VisionEncoderDecoderModel) + + model.to(device) + def predict_torch(inputs): + output = model.generate( + proc(inputs, return_tensors="pt").pixel_values.to(device), + # beam search instead of greedy decoding: + num_beams=4, + # also return probability + output_scores=True, + return_dict_in_generate=True) + if output.sequences_scores is not None: + # log-prob averaged over length + conf = output.sequences_scores.exp().clamp(0.0, 1.0).cpu().numpy() + else: + conf = np.ones(len(output.sequences), dtype=float) + text = proc.batch_decode( + output.sequences, + skip_special_tokens=True, + clean_up_tokenization_spaces=False) + # we must convert to ndarray for Predictor resultq to work + text = np.array(text) + return text, conf + model.predict_on_batch = predict_torch + # not actually needed (image processor does resize itself) + model.input_shape = (None, + proc.image_processor.size.height, + proc.image_processor.size.width, + len(proc.image_processor.image_mean)) + return model def __str__(self): return tabulate( diff --git a/src/eynollah/predictor.py b/src/eynollah/predictor.py index 141d3f0..23cc36f 100644 --- a/src/eynollah/predictor.py +++ b/src/eynollah/predictor.py @@ -129,6 +129,7 @@ class Predictor(mp.context.SpawnProcess): "enhancement": 4, "reading_order": 4, "ocr": 8, + "ocr_tr": 2, # medium size (672x672x3)... "textline": 2, # large models... @@ -144,7 +145,14 @@ class Predictor(mp.context.SpawnProcess): self.resultq.put((jobid, result)) #self.logger.debug("sent result for '%d': %s", jobid, result) else: - if isinstance(shared_data, tuple): + if self.name == 'ocr_tr': + # this model takes a list of (image) tensors + # of heterogeneous shape as input, + # resizing them internally; + # so this looks like multi-input + multi_input = True + batch_size = len(shared_data) + elif isinstance(shared_data, tuple): multi_input = True batch_size = shared_data[0]['shape'][0] else: @@ -215,8 +223,11 @@ class Predictor(mp.context.SpawnProcess): def load_model(self, *load_args, **load_kwargs): assert len(load_args) self.name = '_'.join(list(load_args[:1]) + + list(load_kwargs[key] for key in load_kwargs + if key == 'model_variant') + list(key for key in load_kwargs - if key != 'device')) + if key in ['patched', 'resized'] + and load_kwargs[key])) self.load_args = load_args self.load_kwargs = load_kwargs self.start() # call run() in subprocess From 4e7e1c06b95e6a761f2232350fa4946452e93be2 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 3 Jun 2026 20:51:56 +0200 Subject: [PATCH 044/121] =?UTF-8?q?trocr=20viarant=20for=20Predictor=20run?= =?UTF-8?q?time:=20no=20model=20size=20for=20input=5Fshape=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because transformers v4 and v5 API for image preprocessor differs, and the model-internal image input sizes are actually irrelevant, because the preprocessor will resize them anyway, and there is no batch dimension (because the input images will have different shapes), do not advertise this information in `.input_shape`. --- src/eynollah/model_zoo/model_zoo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 0dd24a8..49ed8e1 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -379,9 +379,9 @@ class EynollahModelZoo: return text, conf model.predict_on_batch = predict_torch # not actually needed (image processor does resize itself) + # no batch dimension (images passed as list w/ varying shapes) model.input_shape = (None, - proc.image_processor.size.height, - proc.image_processor.size.width, + None, len(proc.image_processor.image_mean)) return model From 38fe4d33add7de3bf819d9378cc1d1e2266ea1ab Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 3 Jun 2026 20:56:00 +0200 Subject: [PATCH 045/121] =?UTF-8?q?Predictor=20for=20multi-input=20models:?= =?UTF-8?q?=20present=20as=20list=20instead=20of=20tuple=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (because TF-Serving expects that and cannot cast) --- src/eynollah/predictor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eynollah/predictor.py b/src/eynollah/predictor.py index 23cc36f..6790676 100644 --- a/src/eynollah/predictor.py +++ b/src/eynollah/predictor.py @@ -184,8 +184,8 @@ class Predictor(mp.context.SpawnProcess): else: data.append(stack.enter_context(ndarray_shared(shared_data))) if multi_input: - data = tuple(np.concatenate(data0) - for data0 in zip(*data)) + data = list(np.concatenate(data0) + for data0 in zip(*data)) else: data = np.concatenate(data) #result = self.model.predict(data, verbose=0) From 27ca9733db22d9a406b25d69f20654f4b9f44743 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 3 Jun 2026 20:57:02 +0200 Subject: [PATCH 046/121] ModelZoo ONNX backend for inference: support multi-input or -output --- src/eynollah/model_zoo/model_zoo.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 49ed8e1..51ce909 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -331,14 +331,25 @@ class EynollahModelZoo: model_path, providers=providers) # FIXME: notify about selected provider/device - input_name = model.get_inputs()[0].name - output_name = model.get_outputs()[0].name + model_inputs = [model_input.name + for model_input in model.get_inputs()] + model_outputs = [model_output.name + for model_output in model.get_outputs()] def predict_onnx(inputs): - # models expect data_type() == 'tensor(float)', but np.float16 is 'tensor(float16)' - # FIXME: do this dynamically (but how to convert .type to np.dtype?) - inputs = inputs.astype(np.float32) - return model.run( - [output_name], {input_name: inputs})[0] + if len(model_inputs) == 1: + inputs = [inputs] + outputs = model.run(model_outputs, { + model_input: + input_data.astype( + # models expect data_type() == 'tensor(float)', but np.float16 is 'tensor(float16)' + # FIXME: do this dynamically (but how to convert .type to np.dtype?) + np.float32 if input_data.dtype in [np.float16, np.float64] else + input_data.dtype) + for model_input, input_data in zip(model_inputs, inputs) + }) + if len(model_outputs) == 1: + outputs = outputs[0] + return outputs model.predict_on_batch = predict_onnx model.input_shape = model.get_inputs()[0].shape From 24c7d4c277402524f15c703d021c00fc81d53956 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 3 Jun 2026 20:58:05 +0200 Subject: [PATCH 047/121] update trocr smoke test, add cnnrnn ocr smoke test --- tests/test_model_zoo.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/test_model_zoo.py b/tests/test_model_zoo.py index 341bc21..2902bfe 100644 --- a/tests/test_model_zoo.py +++ b/tests/test_model_zoo.py @@ -1,16 +1,28 @@ from eynollah.model_zoo import EynollahModelZoo +from eynollah.predictor import Predictor def test_trocr1( model_dir, ): model_zoo = EynollahModelZoo(model_dir) try: - from transformers import TrOCRProcessor, VisionEncoderDecoderModel - model_zoo.load_models('trocr_processor', - ('ocr', 'tr')) - proc = model_zoo.get('trocr_processor') - assert isinstance(proc, TrOCRProcessor) + model_zoo.load_models(('ocr', 'tr')) model = model_zoo.get('ocr') - assert isinstance(model, VisionEncoderDecoderModel) + assert isinstance(model, Predictor) + shape = model.input_shape + assert len(shape) == 3 + except ImportError: + pass + +def test_cnnrnnocr1( + model_dir, +): + model_zoo = EynollahModelZoo(model_dir) + try: + model_zoo.load_models('ocr') + model = model_zoo.get('ocr') + assert isinstance(model, Predictor) + shape = model.input_shape + assert len(shape) == 4 except ImportError: pass From 348ac95ad37fe82c86413e8aa54bf781a813ade2 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 3 Jun 2026 20:59:00 +0200 Subject: [PATCH 048/121] =?UTF-8?q?Eynollah=5Focr:=20drop=20fixed=20input?= =?UTF-8?q?=20sizes=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tr-ocr: no need to resize images in advance (done by model, anyway) - cnn-rnn-ocr: get model size from model's input shape --- src/eynollah/eynollah_ocr.py | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 1dfe177..76e54a7 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -87,9 +87,8 @@ class Eynollah_ocr(Eynollah): img: MatLike, page_tree: ET.ElementTree, page_ns, - tr_ocr_input_height_and_width, ) -> EynollahOcrResult: - + total_bb_coordinates = [] cropped_lines = [] cropped_lines_region_indexer = [] @@ -117,20 +116,14 @@ class Eynollah_ocr(Eynollah): img_crop[mask_poly == 0] = 255 # FIXME: or median color? if h > 0.1 * w: - cropped_lines.append(resize_image(img_crop, - tr_ocr_input_height_and_width, - tr_ocr_input_height_and_width) ) + cropped_lines.append(img_crop) cropped_lines_meging_indexing.append(0) else: splited_images, _ = return_textlines_split_if_needed(img_crop, None) if splited_images: - cropped_lines.append(resize_image(splited_images[0], - tr_ocr_input_height_and_width, - tr_ocr_input_height_and_width)) + cropped_lines.append(splited_images[0]) + cropped_lines.append(splited_images[1]) cropped_lines_meging_indexing.append(1) - cropped_lines.append(resize_image(splited_images[1], - tr_ocr_input_height_and_width, - tr_ocr_input_height_and_width)) cropped_lines_meging_indexing.append(-1) else: cropped_lines.append(img_crop) @@ -172,10 +165,9 @@ class Eynollah_ocr(Eynollah): img_bin: Optional[MatLike], page_tree: ET.ElementTree, page_ns, - image_width, - image_height, ) -> EynollahOcrResult: - + _, image_height, image_width, _ = self.model_zoo.get('ocr').input_shape + total_bb_coordinates = [] cropped_lines_rgb = [] cropped_lines_bin = [] @@ -482,18 +474,13 @@ class Eynollah_ocr(Eynollah): img=img, page_tree=page_tree, page_ns=page_ns, - - tr_ocr_input_height_and_width = 384 ) else: result = self.run_cnn( img=img, page_tree=page_tree, page_ns=page_ns, - img_bin=img_bin, - image_width=512, - image_height=32, ) self.write_ocr( From 4181e03bc9798dd796e4aabe440f7791d49ffe90 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 12 Jun 2026 14:48:47 +0200 Subject: [PATCH 049/121] =?UTF-8?q?`training=20convert=20--rebuild`=20for?= =?UTF-8?q?=20cnn-rnn-ocr:=20override=20charset=20file=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when rebuilding the inference model for cnn-rnn-ocr, - open the old `characters_org.txt` file for the charset - use it to pass the actual `n_classes` (overriding the config) - use its path to pass the `characters_txt_file` (overriding the config) --- src/eynollah/training/convert.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/eynollah/training/convert.py b/src/eynollah/training/convert.py index 140079e..d2d7b49 100644 --- a/src/eynollah/training/convert.py +++ b/src/eynollah/training/convert.py @@ -68,6 +68,12 @@ def convert_cli(rebuild, format_, in_, out): ex.add_config(str(config_path)) # some models deviate between training and inference ex.add_config(inference=True) + # make sure the local vocab file gets re-used + characters_txt_file = model_path / "characters_org.txt" + with open(characters_txt_file, "r") as voc_file: + voc = json.load(voc_file) + ex.add_config(characters_txt_file=characters_txt_file) + ex.add_config(n_classes=len(voc) + 3) # just retrieve final config (via pseudo-run) ex.main(lambda: 0) config = ex.run(options={'--loglevel': 'ERROR'}).config From 9d2412080fe72e171458907d70a29f7e2fa3f886 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 12 Jun 2026 14:52:23 +0200 Subject: [PATCH 050/121] =?UTF-8?q?training.models=20for=20cnn-rnn-ocr:=20?= =?UTF-8?q?fix=20config=20names=20for=20height/width=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - rename `image_height` → `input_height` - rename `image_width` → `input_width` --- src/eynollah/training/models.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/eynollah/training/models.py b/src/eynollah/training/models.py index 528c848..b3d811a 100644 --- a/src/eynollah/training/models.py +++ b/src/eynollah/training/models.py @@ -92,16 +92,16 @@ class CTCDecoder(Layer): # get top path for all sequences in batch decoded = decoded[0] logits = logits[:, 0] - logits[:, 1] + probs = tf.exp(-logits) # convert to dense outputs = tf.SparseTensor(decoded.indices, decoded.values, (n_samples, n_steps)) - outputs = tf.sparse.to_dense(sp_input=outputs, default_value=-1) + outputs = tf.sparse.to_dense(sp_input=outputs, default_value=n_classes-1) # # drop non-tokens (-1) and OOV (0) # result = [] # for output in outputs: # result.append(tf.gather(output, tf.where(output > 0))) # outputs = tf.stack(result) - probs = tf.exp(-logits) return outputs, probs def mlp(x, hidden_units, dropout_rate): @@ -468,8 +468,8 @@ def machine_based_reading_order_model(n_classes,input_height=224,input_width=224 return model -def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_len=None, inference=False, characters_txt_file=None): - inputs = Input(shape=(image_height, image_width, 3), name="image") +def cnn_rnn_ocr_model(input_height=None, input_width=None, n_classes=None, max_len=None, inference=False, characters_txt_file=None): + inputs = Input(shape=(input_height, input_width, 3), name="image") labels = Input(name="label", shape=(None,)) x = Conv2D(64,kernel_size=(3,3),padding="same")(inputs) @@ -496,10 +496,10 @@ def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_l x = Activation("relu", name="relu6")(x) x = MaxPooling2D(pool_size=(2,2),strides=(2,2))(x) - x = Conv2D(image_width,kernel_size=(3,3),padding="same")(x) + x = Conv2D(input_width,kernel_size=(3,3),padding="same")(x) x = BatchNormalization(name="bn7")(x) x = Activation("relu", name="relu7")(x) - x = Conv2D(image_width,kernel_size=(16,1))(x) + x = Conv2D(input_width,kernel_size=(16,1))(x) x = BatchNormalization(name="bn8")(x) x = Activation("relu", name="relu8")(x) x2d = MaxPooling2D(pool_size=(1,2),strides=(1,2))(x) @@ -513,9 +513,9 @@ def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_l x2d = Reshape(new_shape2, name="reshape2")(x2d) x4d = Reshape(new_shape4, name="reshape4")(x4d) - xrnnorg = Bidirectional(LSTM(image_width, return_sequences=True, dropout=0.25))(x) - xrnn2d = Bidirectional(LSTM(image_width, return_sequences=True, dropout=0.25))(x2d) - xrnn4d = Bidirectional(LSTM(image_width, return_sequences=True, dropout=0.25))(x4d) + xrnnorg = Bidirectional(LSTM(input_width, return_sequences=True, dropout=0.25))(x) + xrnn2d = Bidirectional(LSTM(input_width, return_sequences=True, dropout=0.25))(x2d) + xrnn4d = Bidirectional(LSTM(input_width, return_sequences=True, dropout=0.25))(x4d) xrnn2d = Reshape((1, xrnn2d.shape[1], xrnn2d.shape[2]), name="reshape6")(xrnn2d) xrnn4d = Reshape((1, xrnn4d.shape[1], xrnn4d.shape[2]), name="reshape8")(xrnn4d) @@ -528,7 +528,7 @@ def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_l addition = Add()([xrnnorg, xrnn2dup, xrnn4dup]) - addition_rnn = Bidirectional(LSTM(image_width, return_sequences=True, dropout=0.25))(addition) + addition_rnn = Bidirectional(LSTM(input_width, return_sequences=True, dropout=0.25))(addition) out = Conv1D(max_len, 1, data_format="channels_first")(addition_rnn) out = BatchNormalization(name="bn9")(out) @@ -539,7 +539,7 @@ def cnn_rnn_ocr_model(image_height=None, image_width=None, n_classes=None, max_l if inference: # add second path for binarization - inputs_bin = Input(shape=(image_height, image_width, 3), name="image_bin") + inputs_bin = Input(shape=(input_height, input_width, 3), name="image_bin") out_bin = Model(inputs, out)(inputs_bin) # ensemble raw results out = 0.5 * (out + out_bin) From 08946067acc881680eddd18c58495f44a5f03fe1 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 12 Jun 2026 14:54:51 +0200 Subject: [PATCH 051/121] ModelZoo ONNX backend: handle multiple inputs, too --- src/eynollah/model_zoo/model_zoo.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 51ce909..c66c349 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -313,6 +313,7 @@ class EynollahModelZoo: # 'arena_extend_strategy': 'kNextPowerOfTwo', 'gpu_mem_limit': MODEL_VRAM_LIMITS[model_category] * 1024 * 1024, # 'cudnn_conv_algo_search': 'EXHAUSTIVE', + #'cudnn_conv_use_max_workspace': 0, # 'do_copy_in_default_stream': True, # ... })] + providers @@ -351,7 +352,12 @@ class EynollahModelZoo: outputs = outputs[0] return outputs model.predict_on_batch = predict_onnx - model.input_shape = model.get_inputs()[0].shape + input_spec = model.get_inputs() + input_spec = [i.shape for i in input_spec] + if len(input_spec) > 1: + model.input_shape = tuple(input_spec) + else: + model.input_shape = input_spec[0] return model From 45c92eada27fb3f6f3b8471b798101c26d13a63a Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 12 Jun 2026 14:55:46 +0200 Subject: [PATCH 052/121] models w/ multiple inputs yield a tuple for `.input_shape` --- src/eynollah/eynollah_ocr.py | 3 ++- tests/test_model_zoo.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 76e54a7..9ed02d4 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -166,7 +166,8 @@ class Eynollah_ocr(Eynollah): page_tree: ET.ElementTree, page_ns, ) -> EynollahOcrResult: - _, image_height, image_width, _ = self.model_zoo.get('ocr').input_shape + input_shape, _ = self.model_zoo.get('ocr').input_shape + _, image_height, image_width, _ = input_shape total_bb_coordinates = [] cropped_lines_rgb = [] diff --git a/tests/test_model_zoo.py b/tests/test_model_zoo.py index 2902bfe..082f2a7 100644 --- a/tests/test_model_zoo.py +++ b/tests/test_model_zoo.py @@ -23,6 +23,7 @@ def test_cnnrnnocr1( model = model_zoo.get('ocr') assert isinstance(model, Predictor) shape = model.input_shape - assert len(shape) == 4 + assert len(shape) == 2 + assert len(shape[0]) == 4 except ImportError: pass From 94082bc64a914c74062be491f10e5f4b64ddcae1 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 12 Jun 2026 14:56:44 +0200 Subject: [PATCH 053/121] =?UTF-8?q?ModelZoo=20TF-Serving=20backend:=20deal?= =?UTF-8?q?=20with=20buggy=20.inputs=20signature=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit work around TF bug that adds captured/unknown inputs to function signature --- src/eynollah/model_zoo/model_zoo.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index c66c349..d028004 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -281,7 +281,19 @@ class EynollahModelZoo: self._configure_tf_device(model_category, device=device) model = tf.saved_model.load(model_path) model.predict_on_batch = model.serve - model.input_shape = tuple(model.signatures.get('serving_default').inputs[0].shape) + spec = model.signatures['serving_default'] + # some models receive lots of additional/internal + # (unknown) captured inputs polluting .inputs + # TF>=2.16 has spec.function_type.flat_inputs + # this non-public API works: + # input_spec = spec.inputs[:len(spec._arg_keywords)] + # but perhaps this is most reliable: + input_spec = tf.nest.flatten(spec.structured_input_signature, True) + input_spec = [tuple(i.shape) for i in input_spec] + if len(input_spec) > 1: + model.input_shape = tuple(input_spec) + else: + model.input_shape = input_spec[0] return model From 60c9f4786c6668fe8aa8a8eaa4a78d4088481642 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 12 Jun 2026 14:58:32 +0200 Subject: [PATCH 054/121] =?UTF-8?q?ModelZoo=20device=20selection:=20warn?= =?UTF-8?q?=20if=20model=20category=20still=20unmatched=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (and try GPU) --- src/eynollah/model_zoo/model_zoo.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index d028004..13656f4 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -182,7 +182,10 @@ class EynollahModelZoo: if fnmatchcase(model_category, cat): device = dev break - if device == 'CPU': + if ':' in device: + self.logger.warning("missing device specification for model type %s", model_category) + gpus = gpus[:1] + elif device == 'CPU': gpus = [] else: assert device.startswith('GPU') @@ -227,6 +230,9 @@ class EynollahModelZoo: if fnmatchcase('ocr', cat): device = dev break + if ':' in device: + self.logger.warning("missing device specification for model type %s", model_category) + device = 'GPU' if device and device.startswith('GPU'): try: device0 = torch.device('cuda', int(device[3:] or 0)) @@ -309,7 +315,10 @@ class EynollahModelZoo: if fnmatchcase(model_category, cat): device = dev break - if device == 'CPU': + if ':' in device: + self.logger.warning("missing device specification for model type %s", model_category) + gpu = 0 + elif device == 'CPU': gpu = -1 else: assert device.startswith('GPU') From 19504cb9328d2fa06065e260c759f84caeb59c79 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 12 Jun 2026 14:59:38 +0200 Subject: [PATCH 055/121] makefile to reload models: add target for SavedModel Keras format --- src/eynollah/training/reload-models-v0.8.mk | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/eynollah/training/reload-models-v0.8.mk b/src/eynollah/training/reload-models-v0.8.mk index 9855f0f..b2f4db9 100644 --- a/src/eynollah/training/reload-models-v0.8.mk +++ b/src/eynollah/training/reload-models-v0.8.mk @@ -26,17 +26,19 @@ CURRENT_MODELS += eynollah-enhancement_20210425 all: tf-serving tf-serving: $(CURRENT_MODELS:%=$(MODELS_DST)/%) +tf: $(CURRENT_MODELS:%=$(MODELS_DST)/%) keras: $(CURRENT_MODELS:%=$(MODELS_DST)/%.keras) hdf5: $(CURRENT_MODELS:%=$(MODELS_DST)/%.h5) onnx: $(CURRENT_MODELS:%=$(MODELS_DST)/%.onnx) +$(MODELS_DST)/%: FORMAT = $(or $(filter tf,$(MAKECMDGOALS)), tf-serving) $(MODELS_DST)/%: $(MODELS_SRC)/% eynollah-training convert \ $(and $(wildcard $&1 | tee $(notdir $<).tf-serving.log + 2>&1 | tee $(notdir $<).$(FORMAT).log $(MODELS_DST)/%.keras: $(MODELS_SRC)/% eynollah-training convert \ From e9839a8b54d4e7ef9dc4b2677485501084653e54 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 12 Jun 2026 15:00:36 +0200 Subject: [PATCH 056/121] makefile to reload models: avoid ONNX conversion for cnn-rnn-ocr too --- src/eynollah/training/reload-models-v0.8.mk | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/eynollah/training/reload-models-v0.8.mk b/src/eynollah/training/reload-models-v0.8.mk index b2f4db9..ef74bdc 100644 --- a/src/eynollah/training/reload-models-v0.8.mk +++ b/src/eynollah/training/reload-models-v0.8.mk @@ -58,7 +58,10 @@ $(MODELS_DST)/%.h5: $(MODELS_SRC)/% $(MODELS_DST)/%.onnx: $(MODELS_SRC)/% if jq -e '.task == "segmentation" and .backbone_type == "transformer"' $/dev/null; then \ - echo skipping $@: vision transformer architecture currently does not work with ONNX; else \ + echo skipping $@: vision transformer architecture currently does not work with ONNX; \ + elif jq -e '.task == "cnn-rnn-ocr"' $/dev/null || test x$(findstring _ocr,$@) = x_ocr; then \ + echo skipping $@: OCR CTC decoder does not work with ONNX; \ + else \ eynollah-training convert \ $(and $(wildcard $ Date: Tue, 3 Feb 2026 19:45:50 +0100 Subject: [PATCH 057/121] integrating transformer ocr --- src/eynollah/training/train.py | 86 +++++++++++++++++++++++++++++++++- train/config_params_trocr.json | 82 ++++++++++++++++++++++++++++++++ train/requirements.txt | 3 ++ 3 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 train/config_params_trocr.json diff --git a/src/eynollah/training/train.py b/src/eynollah/training/train.py index 62d8e51..da2cbdb 100644 --- a/src/eynollah/training/train.py +++ b/src/eynollah/training/train.py @@ -3,6 +3,7 @@ import sys import io import json import click +from typing import Optional from tqdm import tqdm import requests @@ -397,7 +398,7 @@ def run(_config, f1_threshold_classification=None, classification_classes_name=None, ## if task=cnn-rnn-ocr - characters_txt_file=None, + characters_txt_file: Optional[str]=None, color_padding_rotation=False, thetha_padd=None, bin_deg=False, @@ -698,6 +699,89 @@ def run(_config, callbacks=callbacks, initial_epoch=index_start) + elif task=="transformer-ocr": + import torch + from torch.utils.data import Dataset as TorchDataset + from transformers import TrOCRProcessor, VisionEncoderDecoderModel, Seq2SeqTrainer, Seq2SeqTrainingArguments, default_data_collator + dir_img, dir_lab = get_dirs_or_files(dir_train) + + if continue_training: + model = VisionEncoderDecoderModel.from_pretrained(dir_of_start_model) + else: + model = VisionEncoderDecoderModel.from_pretrained("microsoft/trocr-base-printed") + + processor = TrOCRProcessor.from_pretrained("microsoft/trocr-base-printed") + + + # Create a DataLoader + class TransformerOCRTorchDataset(TorchDataset): + """ + Wraps preprocess_imgs in a format consumable by torch + """ + def __init__(self, config, dir_img, dir_lab, char_to_num): + self.samples = list( + preprocess_imgs( + config, + dir_img, + dir_lab, + char_to_num=char_to_num + ) + ) + + def __len__(self): + return len(self.samples) + + def __getitem__(self, idx): + image, label = self.samples[idx] + + return { + "image": torch.as_tensor(image, dtype=torch.float32), + "label": torch.as_tensor(label, dtype=torch.long), + } + assert characters_txt_file + with open(characters_txt_file, 'r') as char_txt_f: + characters = json.load(char_txt_f) + char_to_num = StringLookup(vocabulary=list(characters), mask_token=None) + dataset = TransformerOCRTorchDataset(_config, dir_img, dir_lab, char_to_num) + data_loader = torch.utils.data.DataLoader(dataset, batch_size=1) + train_dataset = data_loader.dataset + + # set special tokens used for creating the decoder_input_ids from the labels + model.config.decoder_start_token_id = processor.tokenizer.cls_token_id + model.config.pad_token_id = processor.tokenizer.pad_token_id + # make sure vocab size is set correctly + model.config.vocab_size = model.config.decoder.vocab_size + + # set beam search parameters + model.config.eos_token_id = processor.tokenizer.sep_token_id + model.config.max_length = max_len + model.config.early_stopping = True + model.config.no_repeat_ngram_size = 3 + model.config.length_penalty = 2.0 + model.config.num_beams = 4 + + + training_args = Seq2SeqTrainingArguments( + predict_with_generate=True, + num_train_epochs=n_epochs, + learning_rate=learning_rate, + per_device_train_batch_size=n_batch, + fp16=True, + output_dir=dir_output, + logging_steps=2, + save_steps=save_interval, + ) + + # instantiate trainer + trainer = Seq2SeqTrainer( + model=model, + tokenizer=processor.feature_extractor, + args=training_args, + train_dataset=train_dataset, + data_collator=default_data_collator, + ) + trainer.train() + elif task=='classification': if continue_training: model = load_model(dir_of_start_model, compile=False) diff --git a/train/config_params_trocr.json b/train/config_params_trocr.json new file mode 100644 index 0000000..34c6376 --- /dev/null +++ b/train/config_params_trocr.json @@ -0,0 +1,82 @@ +{ + "backbone_type" : "transformer", + "task": "transformer-ocr", + "n_classes" : 2, + "max_len": 192, + "n_epochs" : 1, + "input_height" : 32, + "input_width" : 512, + "weight_decay" : 1e-6, + "n_batch" : 1, + "learning_rate": 1e-5, + "save_interval": 1500, + "patches" : false, + "pretraining" : false, + "augmentation" : true, + "flip_aug" : false, + "blur_aug" : true, + "scaling" : false, + "adding_rgb_background": true, + "adding_rgb_foreground": true, + "add_red_textlines": true, + "white_noise_strap": true, + "textline_right_in_depth": true, + "textline_left_in_depth": true, + "textline_up_in_depth": true, + "textline_down_in_depth": true, + "textline_right_in_depth_bin": true, + "textline_left_in_depth_bin": true, + "textline_up_in_depth_bin": true, + "textline_down_in_depth_bin": true, + "bin_deg": true, + "textline_skewing": true, + "textline_skewing_bin": true, + "channels_shuffling": true, + "degrading": true, + "brightening": true, + "binarization" : true, + "pepper_aug": true, + "pepper_bin_aug": true, + "image_inversion": true, + "scaling_bluring" : false, + "scaling_binarization" : false, + "scaling_flip" : false, + "rotation": false, + "color_padding_rotation": true, + "padding_white": true, + "rotation_not_90": true, + "transformer_num_patches_xy": [56, 56], + "transformer_patchsize_x": 4, + "transformer_patchsize_y": 4, + "transformer_projection_dim": 64, + "transformer_mlp_head_units": [128, 64], + "transformer_layers": 1, + "transformer_num_heads": 1, + "transformer_cnn_first": false, + "blur_k" : ["blur","gauss","median"], + "padd_colors" : ["white", "black"], + "scales" : [0.6, 0.7, 0.8, 0.9], + "brightness" : [1.3, 1.5, 1.7, 2], + "degrade_scales" : [0.2, 0.4], + "pepper_indexes": [0.01, 0.005], + "skewing_amplitudes" : [5, 8], + "flip_index" : [0, 1, -1], + "shuffle_indexes" : [ [0,2,1], [1,2,0], [1,0,2] , [2,1,0]], + "thetha" : [0.1, 0.2, -0.1, -0.2], + "thetha_padd": [-0.6, -1, -1.4, -1.8, 0.6, 1, 1.4, 1.8], + "white_padds" : [0.1, 0.3, 0.5, 0.7, 0.9], + "number_of_backgrounds_per_image": 2, + "continue_training": false, + "index_start" : 0, + "dir_of_start_model" : " ", + "weighted_loss": false, + "is_loss_soft_dice": false, + "data_is_provided": false, + "dir_train": "/home/vahid/extracted_lines/1919_bin/train", + "dir_eval": "/home/vahid/Documents/test/sbb_pixelwise_segmentation/test_label/pageextractor_test/eval_new", + "dir_output": "/home/vahid/extracted_lines/1919_bin/output", + "dir_rgb_backgrounds": "/home/vahid/Documents/1_2_test_eynollah/set_rgb_background", + "dir_rgb_foregrounds": "/home/vahid/Documents/1_2_test_eynollah/out_set_rgb_foreground", + "dir_img_bin": "/home/vahid/extracted_lines/1919_bin/images_bin" + +} diff --git a/train/requirements.txt b/train/requirements.txt index 090bc50..03994f8 100644 --- a/train/requirements.txt +++ b/train/requirements.txt @@ -8,3 +8,6 @@ tensorflow-addons # for connected_components, depublished and only compatible wi tensorflow < 2.16 # for tensorflow-addons, so only needed in training tf_data < 2.16 # for tensorflow-addons, so only needed in training protobuf < 5 # for tensorflow-addons, so only needed in training +torch +transformers <= 4.30.2 ; python_version < '3.10' +transformers >= 5 ; python_version >= '3.10' From aba0138216d7e2d454ecf246eeb0f45ac3f6f107 Mon Sep 17 00:00:00 2001 From: vahidrezanezhad Date: Tue, 3 Feb 2026 20:20:20 +0100 Subject: [PATCH 058/121] generate or update list of characters in the case of cnn-rnn ocr training --- src/eynollah/training/cli.py | 2 + ...te_or_update_cnn_rnn_ocr_character_list.py | 59 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/eynollah/training/generate_or_update_cnn_rnn_ocr_character_list.py diff --git a/src/eynollah/training/cli.py b/src/eynollah/training/cli.py index ccabb82..4b1b391 100644 --- a/src/eynollah/training/cli.py +++ b/src/eynollah/training/cli.py @@ -11,6 +11,7 @@ from .train import train_cli from .convert import convert_cli from .extract_line_gt import linegt_cli from .weights_ensembling import ensemble_cli +from .generate_or_update_cnn_rnn_ocr_character_list import main as update_ocr_characters_cli @click.group('training') def main(): @@ -23,3 +24,4 @@ main.add_command(train_cli, 'train') main.add_command(convert_cli, 'convert') main.add_command(linegt_cli, 'export_textline_images_and_text') main.add_command(ensemble_cli, 'ensembling') +main.add_command(update_ocr_characters_cli, 'generate_or_update_cnn_rnn_ocr_character_list') diff --git a/src/eynollah/training/generate_or_update_cnn_rnn_ocr_character_list.py b/src/eynollah/training/generate_or_update_cnn_rnn_ocr_character_list.py new file mode 100644 index 0000000..8620515 --- /dev/null +++ b/src/eynollah/training/generate_or_update_cnn_rnn_ocr_character_list.py @@ -0,0 +1,59 @@ +import os +import numpy as np +import json +import click +import logging + + + +def run_character_list_update(dir_labels, out, current_character_list): + ls_labels = os.listdir(dir_labels) + ls_labels = [ind for ind in ls_labels if ind.endswith('.txt')] + + if current_character_list: + with open(current_character_list, 'r') as f_name: + characters = json.load(f_name) + + characters = set(characters) + else: + characters = set() + + + for ind in ls_labels: + label = open(os.path.join(dir_labels,ind),'r').read().split('\n')[0] + + for char in label: + characters.add(char) + + + characters = sorted(list(set(characters))) + + with open(out, 'w') as f_name: + json.dump(characters, f_name) + + +@click.command() +@click.option( + "--dir_labels", + "-dl", + help="directory of labels which are .txt files", + type=click.Path(exists=True, file_okay=False), + required=True, +) +@click.option( + "--current_character_list", + "-ccl", + help="existing character list in a .txt file that needs to be updated with a set of labels", + type=click.Path(exists=True, file_okay=True), + required=False, +) +@click.option( + "--out", + "-o", + help="An output .txt file where the generated or updated character list will be written", + type=click.Path(exists=False, file_okay=True), +) + +def main(dir_labels, out, current_character_list): + run_character_list_update(dir_labels, out, current_character_list) + From 4776ea9fc464517538b107ac185eff4860cd6de5 Mon Sep 17 00:00:00 2001 From: vahidrezanezhad Date: Wed, 4 Feb 2026 21:16:08 +0100 Subject: [PATCH 059/121] torch model ensembling is integrated --- src/eynollah/training/train.py | 2 +- src/eynollah/training/weights_ensembling.py | 73 +++++++++++++++------ 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/eynollah/training/train.py b/src/eynollah/training/train.py index da2cbdb..93b1588 100644 --- a/src/eynollah/training/train.py +++ b/src/eynollah/training/train.py @@ -825,7 +825,7 @@ def run(_config, usable_checkpoints = [os.path.join(dir_output, 'model_{epoch:02d}'.format(epoch=epoch + 1)) for epoch in usable_checkpoints] ens_path = os.path.join(dir_output, 'model_ens_avg') - run_ensembling(usable_checkpoints, ens_path) + run_ensembling(usable_checkpoints, ens_path, framework='tensorflow') _log.info("ensemble model saved under '%s'", ens_path) elif task=='reading_order': diff --git a/src/eynollah/training/weights_ensembling.py b/src/eynollah/training/weights_ensembling.py index f651c56..1c175c0 100644 --- a/src/eynollah/training/weights_ensembling.py +++ b/src/eynollah/training/weights_ensembling.py @@ -1,4 +1,5 @@ import os +from typing import Optional from warnings import catch_warnings, simplefilter import click @@ -11,33 +12,56 @@ from ocrd_utils import tf_disable_interactive_logs tf_disable_interactive_logs() import tensorflow as tf from tensorflow.keras.models import load_model +import torch +from transformers import VisionEncoderDecoderModel from ..patch_encoder import ( PatchEncoder, Patches, ) -def run_ensembling(model_dirs, out_dir): - all_weights = [] - - for model_dir in model_dirs: - assert os.path.isdir(model_dir), model_dir - model = load_model(model_dir, compile=False, - custom_objects=dict(PatchEncoder=PatchEncoder, - Patches=Patches)) - all_weights.append(model.get_weights()) +def run_ensembling(dir_models, out, framework): + ls_models = os.listdir(dir_models) + # model: Optional[VisionEncoderDecoderModel] = None + # model_name: Optional[str] = None + if framework=="torch": + models = [] + sd_models = [] - new_weights = [] - for layer_weights in zip(*all_weights): - layer_weights = np.array([np.array(weights).mean(axis=0) - for weights in zip(*layer_weights)]) - new_weights.append(layer_weights) + for model_name in ls_models: + model = VisionEncoderDecoderModel.from_pretrained(os.path.join(dir_models, model_name)) + models.append(model) + sd_models.append(model.state_dict()) + for key in sd_models[0]: + sd_models[0][key] = sum(sd[key] for sd in sd_models) / len(sd_models) + + model.load_state_dict(sd_models[0]) + os.system("mkdir "+out) + torch.save(model.state_dict(), os.path.join(out, "pytorch_model.bin")) + os.system('cp ' + os.path.join(os.path.join(dir_models, model_name), "config.json") + " " + out) + + else: + weights=[] - #model = tf.keras.models.clone_model(model) - model.set_weights(new_weights) + for model_name in ls_models: + model = load_model(os.path.join(dir_models, model_name), compile=False, custom_objects={'PatchEncoder':PatchEncoder, 'Patches': Patches}) + weights.append(model.get_weights()) + + new_weights = list() - model.save(out_dir) - os.system('cp ' + os.path.join(model_dirs[0], "config.json ") + out_dir + "/") + for weights_list_tuple in zip(*weights): + new_weights.append( + [np.array(weights_).mean(axis=0)\ + for weights_ in zip(*weights_list_tuple)]) + + + + new_weights = [np.array(x) for x in new_weights] + + model.set_weights(new_weights) + model.save(out) + os.system('cp '+os.path.join(os.path.join(dir_models, model_name), "config.json") + " " + out) + os.system('cp '+os.path.join(os.path.join(dir_models, model_name), "characters_org.txt") + " " + out) @click.command() @click.option( @@ -56,12 +80,19 @@ def run_ensembling(model_dirs, out_dir): required=True, type=click.Path(exists=False, file_okay=False), ) -def ensemble_cli(in_, out): +@click.option( + "--framework", + "-fw", + help="this parameter gets tensorflow or torch as model framework", + type=click.Choice(['torch', 'tensorflow']), + default="tensorflow" +) + +def ensemble_cli(in_, out, framework): """ mix multiple model weights Load a sequence of models and mix them into a single ensemble model by averaging their weights. Write the resulting model. """ - run_ensembling(in_, out) - + run_ensembling(in_, out, framework) From d0b3bb419f2afd98fc0ca8a773ced6975e8f0c63 Mon Sep 17 00:00:00 2001 From: vahidrezanezhad Date: Tue, 10 Feb 2026 14:32:23 +0100 Subject: [PATCH 060/121] extracting ocr textline images and text: vertical lines threshold has changed to 1.4 --- src/eynollah/training/extract_line_gt.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/eynollah/training/extract_line_gt.py b/src/eynollah/training/extract_line_gt.py index 58fc253..fe9f60d 100644 --- a/src/eynollah/training/extract_line_gt.py +++ b/src/eynollah/training/extract_line_gt.py @@ -50,6 +50,12 @@ from ..utils import is_image_filename is_flag=True, help="if this parameter set to true, cropped textline images will not be masked with textline contour.", ) +@click.option( + "--exclude_vertical_lines", + "-exv", + is_flag=True, + help="if this parameter set to true, vertical textline images will be excluded.", +) def linegt_cli( image, dir_in, @@ -57,6 +63,7 @@ def linegt_cli( dir_out, pref_of_dataset, do_not_mask_with_textline_contour, + exclude_vertical_lines, ): assert bool(dir_in) ^ bool(image), "Set --dir-in or --image-filename, not both" if dir_in: @@ -100,6 +107,9 @@ def linegt_cli( x, y, w, h = cv2.boundingRect(textline_coords) + if exclude_vertical_lines and h > 2 * w: + continue + total_bb_coordinates.append([x, y, w, h]) img_poly_on_img = np.copy(img) From a11c833fc1b5f97b59ec5bed546d79138fdfdd15 Mon Sep 17 00:00:00 2001 From: vahidrezanezhad Date: Mon, 16 Feb 2026 11:50:39 +0100 Subject: [PATCH 061/121] bug fix: layout visualization --- src/eynollah/training/generate_gt_for_training.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eynollah/training/generate_gt_for_training.py b/src/eynollah/training/generate_gt_for_training.py index cc5a1b2..963aa9d 100644 --- a/src/eynollah/training/generate_gt_for_training.py +++ b/src/eynollah/training/generate_gt_for_training.py @@ -515,7 +515,7 @@ def visualize_layout_segmentation(xml_file, dir_xml, dir_out, dir_imgs): co_text, co_graphic, co_sep, co_img, co_table, co_map, co_noise, y_len, x_len = get_layout_contours_for_visualization(xml_file) - added_image = visualize_image_from_contours_layout(co_text['paragraph'], co_text['header']+co_text['heading'], co_text['drop-capital'], co_sep, co_img, co_text['marginalia'], co_table, img) + added_image = visualize_image_from_contours_layout(co_text['paragraph'], co_text['header']+co_text['heading'], co_text['drop-capital'], co_sep, co_img, co_text['marginalia'], co_table, co_map, img) cv2.imwrite(os.path.join(dir_out, f_name+'.png'), added_image) From 499e3d0715b88120227f62f1f40df2b9cdaa1846 Mon Sep 17 00:00:00 2001 From: vahidrezanezhad Date: Wed, 18 Feb 2026 15:04:54 +0100 Subject: [PATCH 062/121] trocr inference is integrated - works on CPU cause seg fault on GPU --- src/eynollah/eynollah_ocr.py | 2 +- .../training/generate_gt_for_training.py | 9 +++--- src/eynollah/training/gt_gen_utils.py | 8 ++--- src/eynollah/training/inference.py | 29 +++++++++++++++++-- src/eynollah/utils/font.py | 4 +-- 5 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 9ed02d4..ee10a69 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -345,7 +345,7 @@ class Eynollah_ocr(Eynollah): if out_image_with_text: image_text = Image.new("RGB", (img.shape[1], img.shape[0]), "white") draw = ImageDraw.Draw(image_text) - font = get_font() + font = get_font(font_size=40) for indexer_text, bb_ind in enumerate(total_bb_coordinates): x_bb = bb_ind[0] diff --git a/src/eynollah/training/generate_gt_for_training.py b/src/eynollah/training/generate_gt_for_training.py index 963aa9d..2e3dd54 100644 --- a/src/eynollah/training/generate_gt_for_training.py +++ b/src/eynollah/training/generate_gt_for_training.py @@ -6,6 +6,7 @@ from pathlib import Path from PIL import Image, ImageDraw, ImageFont import cv2 import numpy as np +from eynollah.utils.font import get_font from .gt_gen_utils import ( filter_contours_area_of_image, @@ -552,8 +553,8 @@ def visualize_ocr_text(xml_file, dir_xml, dir_out): else: xml_files_ind = [xml_file] - font_path = "Charis-7.000/Charis-Regular.ttf" # Make sure this file exists! - font = ImageFont.truetype(font_path, 40) + ###font_path = "Charis-7.000/Charis-Regular.ttf" # Make sure this file exists! + font = get_font(font_size=40)#ImageFont.truetype(font_path, 40) for ind_xml in tqdm(xml_files_ind): indexer = 0 @@ -590,11 +591,11 @@ def visualize_ocr_text(xml_file, dir_xml, dir_out): is_vertical = h > 2*w # Check orientation - font = fit_text_single_line(draw, ocr_texts[index], font_path, w, int(h*0.4) ) + font = fit_text_single_line(draw, ocr_texts[index], w, int(h*0.4) ) if is_vertical: - vertical_font = fit_text_single_line(draw, ocr_texts[index], font_path, h, int(w * 0.8)) + vertical_font = fit_text_single_line(draw, ocr_texts[index], h, int(w * 0.8)) text_img = Image.new("RGBA", (h, w), (255, 255, 255, 0)) # Note: dimensions are swapped text_draw = ImageDraw.Draw(text_img) diff --git a/src/eynollah/training/gt_gen_utils.py b/src/eynollah/training/gt_gen_utils.py index 796e896..1d29598 100644 --- a/src/eynollah/training/gt_gen_utils.py +++ b/src/eynollah/training/gt_gen_utils.py @@ -8,7 +8,7 @@ from shapely import geometry from pathlib import Path from PIL import ImageFont from ocrd_utils import bbox_from_points - +from eynollah.utils.font import get_font KERNEL = np.ones((5, 5), np.uint8) NS = { 'pc': 'http://schema.primaresearch.org/PAGE/gts/pagecontent/2019-07-15' @@ -352,11 +352,11 @@ def get_textline_contours_and_ocr_text(xml_file): ocr_textlines.append(ocr_text_in[0]) return co_use_case, y_len, x_len, ocr_textlines -def fit_text_single_line(draw, text, font_path, max_width, max_height): +def fit_text_single_line(draw, text, max_width, max_height): initial_font_size = 50 font_size = initial_font_size while font_size > 10: # Minimum font size - font = ImageFont.truetype(font_path, font_size) + font = get_font(font_size=font_size)# ImageFont.truetype(font_path, font_size) text_bbox = draw.textbbox((0, 0), text, font=font) # Get text bounding box text_width = text_bbox[2] - text_bbox[0] text_height = text_bbox[3] - text_bbox[1] @@ -366,7 +366,7 @@ def fit_text_single_line(draw, text, font_path, max_width, max_height): font_size -= 2 # Reduce font size and retry - return ImageFont.truetype(font_path, 10) # Smallest font fallback + return get_font(font_size=10)#ImageFont.truetype(font_path, 10) # Smallest font fallback def get_layout_contours_for_visualization(xml_file): tree1 = ET.parse(xml_file, parser = ET.XMLParser(encoding='utf-8')) diff --git a/src/eynollah/training/inference.py b/src/eynollah/training/inference.py index 2be937d..58ccb22 100644 --- a/src/eynollah/training/inference.py +++ b/src/eynollah/training/inference.py @@ -132,15 +132,31 @@ class SBBPredict: self.model = Model( self.model.get_layer(name = "image").input, self.model.get_layer(name = "dense2").output) + assert isinstance(self.model, Model) + elif self.task == "transformer-ocr": + import torch + from transformers import VisionEncoderDecoderModel, TrOCRProcessor + + self.model = VisionEncoderDecoderModel.from_pretrained(self.model_dir) + self.processor = TrOCRProcessor.from_pretrained(self.model_dir) + + if self.cpu: + self.device = torch.device('cpu') + else: + self.device = torch.device('cuda:0') + + self.model.to(self.device) + + assert isinstance(self.model, torch.nn.Module) else: self.model = load_model(self.model_dir, compile=False, custom_objects={"PatchEncoder": PatchEncoder, "Patches": Patches}) + assert isinstance(self.model, Model) ##if self.weights_dir!=None: ##self.model.load_weights(self.weights_dir) - assert isinstance(self.model, Model) if self.task != 'classification' and self.task != 'reading_order': last = self.model.layers[-1] self.img_height = last.output_shape[1] @@ -230,6 +246,13 @@ class SBBPredict: pred_texts = decode_batch_predictions(preds, num_to_char) pred_texts = pred_texts[0].replace("[UNK]", "") return pred_texts + + elif self.task == "transformer-ocr": + from PIL import Image + image = Image.open(image_dir).convert("RGB") + pixel_values = self.processor(image, return_tensors="pt").pixel_values + generated_ids = self.model.generate(pixel_values.to(self.device)) + return self.processor.batch_decode(generated_ids, skip_special_tokens=True)[0] elif self.task == 'reading_order': @@ -566,6 +589,8 @@ class SBBPredict: cv2.imwrite(self.save,res) elif self.task == "cnn-rnn-ocr": print(f"Detected text: {res}") + elif self.task == "transformer-ocr": + print(f"Detected text: {res}") else: img_seg_overlayed, only_layout = self.visualize_model_output(res, self.img_org, self.task) if self.save: @@ -672,7 +697,7 @@ def main(image, dir_in, model, patches, save, save_layout, ground_truth, xml_fil with open(os.path.join(model,'config.json')) as f: config_params_model = json.load(f) task = config_params_model['task'] - if task not in ['classification', 'reading_order', "cnn-rnn-ocr"]: + if task not in ['classification', 'reading_order', "cnn-rnn-ocr", "transformer-ocr"]: assert not image or save, "For segmentation or binarization, an input single image -i also requires an output filename -s" assert not dir_in or out, "For segmentation or binarization, an input directory -di also requires an output directory -o" x = SBBPredict(image, dir_in, model, task, config_params_model, diff --git a/src/eynollah/utils/font.py b/src/eynollah/utils/font.py index 939933e..0354317 100644 --- a/src/eynollah/utils/font.py +++ b/src/eynollah/utils/font.py @@ -9,8 +9,8 @@ else: import importlib.resources as importlib_resources -def get_font(): +def get_font(font_size): #font_path = "Charis-7.000/Charis-Regular.ttf" # Make sure this file exists! font = importlib_resources.files(__package__) / "../Charis-Regular.ttf" with importlib_resources.as_file(font) as font: - return ImageFont.truetype(font=font, size=40) + return ImageFont.truetype(font=font, size=font_size) From d2123a2746473f3adbc0572dd6803c06f0b1dc7b Mon Sep 17 00:00:00 2001 From: vahidrezanezhad Date: Tue, 24 Feb 2026 01:39:12 +0100 Subject: [PATCH 063/121] FIXME: get label for decoration without type attribute --- .../training/generate_gt_for_training.py | 47 +++++++++++-------- src/eynollah/training/gt_gen_utils.py | 19 +++++--- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/eynollah/training/generate_gt_for_training.py b/src/eynollah/training/generate_gt_for_training.py index 2e3dd54..d819944 100644 --- a/src/eynollah/training/generate_gt_for_training.py +++ b/src/eynollah/training/generate_gt_for_training.py @@ -394,11 +394,15 @@ def visualize_reading_order(xml_file, dir_xml, dir_out, dir_imgs): layout = np.zeros( (y_len,x_len,3) ) layout = cv2.fillPoly(layout, pts =co_text_all, color=(1,1,1)) - img_file_name_with_format = find_format_of_given_filename_in_dir(dir_imgs, f_name) - img = cv2.imread(os.path.join(dir_imgs, img_file_name_with_format)) - - overlayed = overlay_layout_on_image(layout, img, cx_ordered, cy_ordered, color, thickness) - cv2.imwrite(os.path.join(dir_out, f_name+'.png'), overlayed) + try: + img_file_name_with_format = find_format_of_given_filename_in_dir(dir_imgs, f_name) + img = cv2.imread(os.path.join(dir_imgs, img_file_name_with_format)) + + overlayed = overlay_layout_on_image(layout, img, cx_ordered, cy_ordered, color, thickness) + cv2.imwrite(os.path.join(dir_out, f_name+'.png'), overlayed) + except: + pass + else: img = np.zeros( (y_len,x_len,3) ) @@ -453,14 +457,17 @@ def visualize_textline_segmentation(xml_file, dir_xml, dir_out, dir_imgs): xml_file = os.path.join(dir_xml,ind_xml ) f_name = Path(ind_xml).stem - img_file_name_with_format = find_format_of_given_filename_in_dir(dir_imgs, f_name) - img = cv2.imread(os.path.join(dir_imgs, img_file_name_with_format)) + try: + img_file_name_with_format = find_format_of_given_filename_in_dir(dir_imgs, f_name) + img = cv2.imread(os.path.join(dir_imgs, img_file_name_with_format)) + + co_tetxlines, y_len, x_len = get_textline_contours_for_visualization(xml_file) - co_tetxlines, y_len, x_len = get_textline_contours_for_visualization(xml_file) - - added_image = visualize_image_from_contours(co_tetxlines, img) - - cv2.imwrite(os.path.join(dir_out, f_name+'.png'), added_image) + added_image = visualize_image_from_contours(co_tetxlines, img) + + cv2.imwrite(os.path.join(dir_out, f_name+'.png'), added_image) + except: + pass @@ -510,15 +517,17 @@ def visualize_layout_segmentation(xml_file, dir_xml, dir_out, dir_imgs): f_name = Path(ind_xml).stem print(f_name, 'f_name') - img_file_name_with_format = find_format_of_given_filename_in_dir(dir_imgs, f_name) - img = cv2.imread(os.path.join(dir_imgs, img_file_name_with_format)) + try: + img_file_name_with_format = find_format_of_given_filename_in_dir(dir_imgs, f_name) + img = cv2.imread(os.path.join(dir_imgs, img_file_name_with_format)) + + co_text, co_graphic, co_sep, co_img, co_table, co_map, co_noise, y_len, x_len = get_layout_contours_for_visualization(xml_file) - co_text, co_graphic, co_sep, co_img, co_table, co_map, co_noise, y_len, x_len = get_layout_contours_for_visualization(xml_file) - - - added_image = visualize_image_from_contours_layout(co_text['paragraph'], co_text['header']+co_text['heading'], co_text['drop-capital'], co_sep, co_img, co_text['marginalia'], co_table, co_map, img) + added_image = visualize_image_from_contours_layout(co_text['paragraph'], co_text['header']+co_text['heading'], co_text['drop-capital'], co_sep, co_img, co_text['marginalia'], co_table, co_map, img) - cv2.imwrite(os.path.join(dir_out, f_name+'.png'), added_image) + cv2.imwrite(os.path.join(dir_out, f_name+'.png'), added_image) + except: + pass diff --git a/src/eynollah/training/gt_gen_utils.py b/src/eynollah/training/gt_gen_utils.py index 1d29598..f21ee53 100644 --- a/src/eynollah/training/gt_gen_utils.py +++ b/src/eynollah/training/gt_gen_utils.py @@ -966,19 +966,21 @@ def get_images_of_ground_truth( if "rest_as_decoration" in types_graphic: types_graphic_without_decoration = [element for element in types_graphic if element!='rest_as_decoration' and element!='decoration'] if len(types_graphic_without_decoration) == 0: - if "type" in nn.attrib: - c_t_in_graphic['decoration'].append( np.array( [ [ int(x.split(',')[0]) , int(x.split(',')[1]) ] for x in p_h] ) ) + #if "type" in nn.attrib: + c_t_in_graphic['decoration'].append( np.array( [ [ int(x.split(',')[0]) , int(x.split(',')[1]) ] for x in p_h] ) ) elif len(types_graphic_without_decoration) >= 1: if "type" in nn.attrib: if nn.attrib['type'] in types_graphic_without_decoration: c_t_in_graphic[nn.attrib['type']].append( np.array( [ [ int(x.split(',')[0]) , int(x.split(',')[1]) ] for x in p_h] ) ) else: c_t_in_graphic['decoration'].append( np.array( [ [ int(x.split(',')[0]) , int(x.split(',')[1]) ] for x in p_h] ) ) - + else: + c_t_in_graphic['decoration'].append( np.array( [ [ int(x.split(',')[0]) , int(x.split(',')[1]) ] for x in p_h] ) ) else: if "type" in nn.attrib: if nn.attrib['type'] in all_defined_graphic_types: - c_t_in_graphic[nn.attrib['type']].append( np.array( [ [ int(x.split(',')[0]) , int(x.split(',')[1]) ] for x in p_h] ) ) + c_t_in_graphic[nn.attrib['type']].append( np.array( [ [ int(x.split(',')[0]) , int(x.split(',')[1]) ] for x in p_h] ) ) + break else: @@ -989,9 +991,9 @@ def get_images_of_ground_truth( if "rest_as_decoration" in types_graphic: types_graphic_without_decoration = [element for element in types_graphic if element!='rest_as_decoration' and element!='decoration'] if len(types_graphic_without_decoration) == 0: - if "type" in nn.attrib: - c_t_in_graphic['decoration'].append( [ int(float(vv.attrib['x'])) , int(float(vv.attrib['y'])) ] ) - sumi+=1 + #if "type" in nn.attrib: + c_t_in_graphic['decoration'].append( [ int(float(vv.attrib['x'])) , int(float(vv.attrib['y'])) ] ) + sumi+=1 elif len(types_graphic_without_decoration) >= 1: if "type" in nn.attrib: if nn.attrib['type'] in types_graphic_without_decoration: @@ -1000,6 +1002,9 @@ def get_images_of_ground_truth( else: c_t_in_graphic['decoration'].append( [ int(float(vv.attrib['x'])) , int(float(vv.attrib['y'])) ] ) sumi+=1 + else: + c_t_in_graphic['decoration'].append( [ int(float(vv.attrib['x'])) , int(float(vv.attrib['y'])) ] ) + sumi+=1 else: if "type" in nn.attrib: From 303bdfe0e7b855044c6df07a050f1d4acf73e58a Mon Sep 17 00:00:00 2001 From: vahidrezanezhad Date: Tue, 24 Feb 2026 13:55:45 +0100 Subject: [PATCH 064/121] Amiri font which works for both arabic and latin --- src/eynollah/Amiri-Regular.ttf | Bin 0 -> 421196 bytes src/eynollah/Charis-Regular.ttf | Bin 878076 -> 0 bytes src/eynollah/utils/font.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 src/eynollah/Amiri-Regular.ttf delete mode 100644 src/eynollah/Charis-Regular.ttf diff --git a/src/eynollah/Amiri-Regular.ttf b/src/eynollah/Amiri-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..df5e1df7305d0b9dd851d43ec1a032be34a44e67 GIT binary patch literal 421196 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 PEl3jEkR@J#B^Ve0Q;Rx4 literal 0 HcmV?d00001 diff --git a/src/eynollah/Charis-Regular.ttf b/src/eynollah/Charis-Regular.ttf deleted file mode 100644 index a4e75a450996c466d8eaa8528943c77590820bac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 878076 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 I(wn_2BExlWc) zu@Rj;9TJk*C@zAECb}MSC-L?#ATd9rU zZB!i5dwug#8{yk&F%|tUa_5NN6xWp9@OPv*|Bk~<9iWJQlI^En#FeV*q5 z+rjS&esbKI+@kCFYZdj@cJRB#9T)%_R(L+SxGHWvspwJd-nPb=>-49AzZd+I-laL0 zujAwVZ`}y~Uhm8Spo4a_xXDA|BX3=Vo!T7 zw)cDsGeGS|1)g1=93+y48x=Ub887&vmlaAwwaBexo=dTaL9(Lcc{Z~ByV_B)RF2vY9o$D@o2PquQ~=t z@&-pmZBp-3$Khx^kDAr{)d@IyfJYNC#>BOxKE$J`_=@s096idT>G+y*3yvP+kzcKs zcT=eiJQ}XvrH;VS-8`DCKCHrvtB>GFi(ZPmDjz5xDjz8yEB{h1DW57=RSos#R>M$6 zX(*X2H6LYAj5F8O5Y(ZeKB;~O?+N>^x8#fpc#*Ic-qnpv)xXI{Y%*I{mxyC$m zp}ELhY~o&0^b%#L+DA=Bt@TG4m!o8dD(@(#l=o1JDJp9Zd1JjHf2=Vz5BX$mA+M|_ z)K8^ag?gz~>r|GA+Mu#tj4nnv?uy@HL>jjmQOHj;aukC+#UfWeBi=|r-g+T-iO64X zgN4ZBij;|QM?f8Rhx;su2=$)70sBy4XqYqTir*)o;?1^?rE9_yL|9UeGVKN_B!@C_B-Bn&2-Ijeb1fa&T|*Ii`*sdGUG+#H^y&`myF*jTGUEq zqS{BV|Gz8$ubuh)AJog&%%;>{tCcHpE92J2wZ(0Y+a9+&Zhze2xZ`oB3z`ew!t#f)H~Zz<*jYkh5)4Q%)Ajvj~Ez~T5t-*MmR=!wx&qNhjC zjGhxcKYCI0(&!b@tE1ONw?%J?-Wt6ldUy1`=!4NmqK`+Piar~CA^LKR5)%?*#DvFm zkBN-&#w5n{i%E;gi7ASyh^dVk6f-PlWXzbD=9oz_Q)60UX2r~nSrD^0W?9V2m^Cr$ zV>ZNWj@cHoGiHzPe9ZosLor8VPQ;v!ITv#==1Qy<>yEWzBVv2RM#cJKlVVe1Gh*{% zOJXZy{jo!0hsQR?HpNbeog6zYc1C=2?CjWiu?u6D#4e9r6}vXJHFjg{me}pFyJGjo z9*8|0do1>3?3vi}v6p<8yRD6FkJr2ougBXt-qZDvx2w0OH^!UbP4=dGv%Ce~GH7C=9?_K0w>RsVo?Oo?>^KSBP_3rTQ_U`i@^d9jZ_nz{e z^hvE~EjTyv*+(0twe z&~o6(SH4wc4YuyGriEP&`^>o0m|;F-uCn5dT=T`SV_}!fXU%Qa*s$ZqtgttY#m3|K zNNSkb#SAs)nG4MM*3H(f)-7R2!`=!zfhTgO!cK?1ZFs}Zgq;mL7j{1ELfHFZ7sLJ) z_DR^MVOPSg%FBrG33}ad7)~R^a2uWQ1TWMGGYrEtETglLW8@k6e4bZi6dNT*nNe;G zG%AcrV~|m0R2wx$EnZMxZ`4_eF~qpt7;4;M3^RrscNtB_2;**Jq;Zci3eN)XHO3f| zjQfoV#skJg<3Zz5W3usx@vt$~_?9uv7-x((9y4YdPZ&=c-!YytzH5BXm~T97EHJ)r zEHr*#EHZv*{K!~h{20#@pD~sh&l<~(pBO(iRvOP4tBju+tBp0rFO0Ru^LX0$OJlw9 zE92KjoACmkJpSI;X1r`{H+C31jXxQ?j8}}^#-EKn#$Mwu#t~z`@rH5Kc+>dUcneP` z-!@Jd?-(bIca2lV8RLE9tnq9W@o4xQ1G|5ah`v+;a1*UU5X%>uK~EHaDD60_7SGt12yv%(x`R+`mjm04@n znSQh0Y%qtIx0^%FJIrC`NOO>Rr#alb%N$|eZQf%xnxo9o=Dp_qW|MiJInEr9C#Vz5 ziROdmBy+O)usOwi#GGn=%ba0OGpC!6n={QP%qPuR<{a}o=4|u3=J(8}&F`B(Fn?(N z$o#Rn)O^NVW-d2>Vy-ZMYOXY&Gk<2THh*reF@IsMHJ>-vnZGhy&0m`v%#G%6%uVKR z&CTXZ<`(mJczXML^JR0p`3Lh)<}ULUbGP|tbC0>#{HwX&e9e5-{ENBIJYfFK{JVMB zJY@dEJYv3K9yQ-IkC|_o$IZ9R6Xr?tUGtRrPxG|-o_WT6-~7NlXP!4dGB21Pn-|T0 znU~B@%**Dd<`qjf+s)4`)w6@J1tJE2x1O+OSx;KC@tpiS)*NfD^<8V8^|bYUYoYZ$YrgdZYmxOsYl-!&g}LAekM^gq zzlR+P`$yQ}|CQ%?J?u!>oA5Tr;e#&OUdVXdm}M-6w+W@cc?kYyK0MAU^I7F-iJ5(0RHGhc%+ZulRo~EPg1Y*N-p@N zQ23<|uha>C=|A<|asSIvRfn&$JzXh%9X{VgZ}$aXht&;(zSi3{Uh{Wj;n$k4^Ldlt z^B#iFn*z^g`@Qk-gVT*~U-Nyl;r-^o|ILL5ocCY(z!gC+`17D2{P|aS!WWDU7;$aH zm}?V8U7IoP+G70fI^Xz*ukenq!awfAXlDPv-5q`%_lJ)Jz2?9B%=hG)y3TWU_|1>6 z^PHc2rSJTA&lz!@@4Ok_Q|^S{67-<4@U-4*el&P*{7e2+ylLA1!k-TQFFfj)>pbfH zU+Gh)nUBJ!e%ov@AA^6r-nY(%cYW%bcU=hox(FV2F?{Tj|H{kao<2Qo+c)^y9p)du z;BQ}r#}!X&dtKY#inl%d75?^}|B=VN8uYpNOV@QiH|(0%?Q+fU-bl~e&FcO?@VlvB z>v;>TLipYidf$rw%Kwgl{~bvWJc>S8{B4IHzTcVvKRglsc#`$df8~Y0V=aIee(G!d z@M3u4A6Y-PmRiqP%WR)~-$7?6ee(ZiBnUr#ZTxtQBgZd~8#!tW6VLs{aU({MLErtg zK3g3se!F9IDgOJLMwp%-gdMeK$(UPyjCti%`0!7^;HSUnr90-~ws-EBjsK^)E@ryd z?gBh0BT>6Y3HBggH;VoGF>HrlFUO}N>DA?l_Vr^}`1OF%G0y$UQLZuQYsa~bT7=e> zzJH7{_N#jbEh8xZV!!a^KHqHGszr4 z`lf&H%{t}*U!Da7doRab|K5Mex(fDS|8M6D|HJu(W2ZToGmAUFG^_aH%;VqZ9b-82 z_a6BOUZ2N>*rI~diQUbab4f{cg(l0_53c^wVwa0 zW@i8UdD)jo2f6?C*y7)3bh(y2qOfOk-#oYb=K0 z|ij^_u)iqCtyuej^G5A&q=&_4{uOrXObiND!lp2ax1 zi}#Pv zXF8gC(kPc>?c2t~vUe?Z;S3c`|Dz$#LN(_jCe}$fr#K&Uev8Ksn|P|EXdw&CcV+MD zVRe$dJHfgiIT(rU9^?W~RW1B`*E18_6WAWdHV1l+(BFo>?(v0{E4n?~Qk+e@oAx)& zX`0`(NO1@@Ep5X3fTm?lE1T9dtw#!TTu%X4yMB%{e+6fab$uZ&Y@s;&9gz54^@=9G z0QPn5W-Z6KSJ50+=%>KWM9YJ9@{Z0#%Y(dW;n*Uub;Xv3Ed%9*XKqSJ=*yvRhrZ_t z^(1>Lajb?yTXDD_bWf6dI6}K>U3IQ$7D^UpGdA?;&}Tg;=}DNS9+wvRoHqa{uMAA4^Z-By-#4c~+JUP+e2%*^R9 zIt&goGc$7>W@ct)W@cvSFm)L1FoVBcCot)me%|%0kABT)&5)DbPU)=t2F`pWt27I#azW!=i=Z+P7E2(N&vf?pNYNZt9+^kwS$H;IJxe!B*7Pdg-^NYzH2H+D3N?MzbVbwlO$*^G zX8GJr`0MO&FT2;=o1Vm;WS*4XLf)cY^FDYd;B|%hjSTMHV|YxzmwuPci^&Vh(f57r z11kElA1uceu5x{@-;L--cBA_7X|x=+o0k-&L@ls19l_?p;cRhlY%i)Kyh*(&E&NsC z0{#bgu6h2S+|KmZ+UeeMA9|E0m8S@PPP@C^+wNnJ?MdT-8{AUndWE@OWv*9x&fq=d zp2uFii}YWF_l(}D9k_YjW_b7DBf#tIckjEQ9ID zK6antClr2{i=Rt{-tZ~f>-v=IJw_=4ZjWwBBlbbb# zSyR$gkv|&m3%xrY^X>%V*+MuaxD`w*KHc=VyjiQ7HHleMm^F=AGnzF!tt_p1e1}%- z3HrC?%$mWh)y66tNFkPprDB*esCZ-n!Fx961kdIcQko8Ua(gZGZ%uiD&dXLYi=TfME7 z)^Y2!b>6zj(zEI!ROMAo>`nGD`<)Zd`xlhnzFjV%gORkkEog|4w!)Cq$ikv^B24e6`|NJ;H&v7>ByXFzFMH3s%Lf{yS2kSW1#Tl z^v#AI0lwa!iGKT!Jx@C@rui(#`|VkC5}406ed>aAC40TS$L;Qpa;Lis-IeZf_l5h} zBRmm2kvx$-(LFIxM;6Bu*OSqc)sxMW&r{Y@!Bf>!-BZ(3*VD|?!qeK*-qX?3+0)Gv z;_2-f=b7l4o01sGxl+2dP+Uh_0er z=^Hk)Q`&v(D>$X}?kV@Qd&WKMo^#K;f4VQ-SMF=~jr-Pp=e~D8xF6lG?l%w0hdqIwh@PmPn4b8a z1fEQu?4JCd0-l1NLY~5?S^w2>1pTb;OXj_?#<$DA*69WdqCM{SCIl;1YNRm%ux?1U{jg7lWh5W*+z-vcUV01CED$tN^?YaFf6> z(2xyagV51?7^Yy2W#idIK9Nu6Q~7j$g({n1aJVwwi3bL6W2=Xz2UuK zrGwh>)=Cd$^pjP@`^D#2#eGS9NvtZkj#xjggLW}9SHsQKIOb|xb2T1wHC}TyK65n@ z%+*9PR};ltO*C^gvCY-QHCGegTunlAHHppDq%)^PdUHzrVXiEfxw72m$|{&Et75LK zp*ceu;fm_v`ikMiC}UN#YT^2tTP>`XRx7+~9dTlHwz^nztV_7EQ2dRbn`@)@iQc7e zRye{2TvWzAg?EXrimr(qQYe-1~x!!}^K=&3NCiN%&WqEyP-c?Lw41 z?kj=EMS;7yuO2WW_ca2FrpI(Wjew@PuRW*V-jSEW+6l;<`v!7)oxvQ=B;Qaz4eM|| z1M6%KCjq_-A2+^x9qUlcsHN}zg>^Q6jdiW?VBH|-eb|KQvD~*sM8>*JM8moR)04SK z>f41irEfQ$*(cIr-7o&YdO&2vdPvYaa9C8vdPLyU?>j20Vm&6RVLdLYV?7~iU_B{n zV?8a(V!dV_ze3i+#d6;_Jo5w2^9!iu^$v(oeP z9D079L(eZa&!cY6Jf`QDo9EY?=hvI(QCElO;T^>a|EO8%=XaUsQFn*O)4d$)Jg>mI z+>7gU)_d_@IlH{LT6`COS$x;Qy4;Ju4!(=eExzkDEB@Aa2A^Sk*N=6cHvsE&FDirZ zGk9dn03==Z%DQI;wKG_r5Oy)`z}?SReZmVSVaLjPr-ApSFD2I3zEoJ> z`cmUlYWs51r`LDV5~!yfi+*JSRh3hmnYf>i8p;K@Ux7->mAK!4ddiKs-;JuuJ-9!D z+RCH2KZ^=W)B>UY@=xaq?r%BQoWCsLy#YVy9o*l7v#lEL|01-A`#W$Mq6dNZ6TF5^ zaQ^|0LwHQR;qV={!2NBw4r}24F1&}eaDN|X*h}0$#3}X`_m6Rop}x@j6ernNOZbw& zk%*oL-e+)<*1>%!3u1L~{~QiQ)Tnu1!cU1BB=2iD6rQ7 z$(V{^Q|1xI5`+{0_iTH&6<-PjuUl9gu(aIfHVY=e87s*-T; zu*xhA?oXkgc}&Jl_mp!Bf3V>br1zfnoMi&$klqK@YxrZ|;L#)NJtj(j#G@zH zXSig);87T&Qyx|ik6yqjnh#!9i4)fW7HNP-ZxF8vv4)n!$s3!{uO$O|{Nxu{G(_n8 z)&oT7N7iGOl2w5lwJYnz`m*J01KY~>QD#9Z5IfDzvGeRAyTq=tYwQNQ#qP0p>^=L! zzOrvzb3gLlC_E14%H~IpmP)(|ufyx{2D}k(!CUb*d=wwe$MCUy3*U;~EW7z$et;k0 z$N4Fa>2&WU_!ndl^Xie6%b7%j$%@nQ<)H>6jHwPL;4 zAU283V!PNO4vQnm1|Et>;xV!Tsu_!pUM&e_5}5-1TGGfoCOar4%bNawP+BkRis zlqZpzAUntb=nOVlPLXrvJh=$5`H(y;FUjljmb@z;$mjC4d@J9}FY=qRR16hUC00pQ za+OM@Q@K@;Dxu1%N~)%+r5dP4s-qg92C6~m2fbXaP;1qCbwC|gC)H`qz&F@BP)E=a zbz+@VC)X)-DxF%V(?xV?T~SxojdeTSNq5oRbPwH6570C9LcK^Y*6a0ly<1;i*vah_b^*I2xDxO)c1632UCnN9_p*E2gYEHPp(fi??dkSRdzL-N z-eB*w588+A3-(3(l6~2}XWzG9+OO=_j^lV7pW}BTJ5iiCPEIG-DdjYFx;uTGz7F^$ zeBS=4LYmN3@&6C;RrJ3htbfPb6$x|wJKAOo*ly&-55UL71{;%`7vm*)&tDjP^-mPe zU;PcOB|`rE9kCS=MMbbEh7Lg`1o0@PMH%eZ>Bz1R{tsx*0iMf=y!&5>?t;9c9Mws6 z`Gxf<-#qdY@l)mr_9>PgqwgbAkA*ChxUSZA_wPuMxIrhy?|6`SF>qAG_Wa#SyKhH!zY`h!ZZI5&Q3-w&72(HG1%49M;HSX~#PP=U z#`DGp=aI~t+?&Fi@;|Z=?K>6FgV;CVvqDx8Mda&dr;6R?CV5i?Z+EE%wJ3YLnQx&WM%KfK^S9M$bv z2i6hOWV^se-<$Pg8`&v#mi@^tuq*5)yN~`*ird`bF8A^P%$|*iPEk>LG#;JD;4#rN zDmIUYzERnDc3zXWUWqdhb!B-;tU⋙N7Q=0fp6rS_-4L?@8rAq9=?z7 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 K|DZ?>ggYV;{0D8ErN57pKMEyKQv; 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%0A2$S-JV&Siih@#>9y;#%^zG+fMvTUM$W> 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 z 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

oP#>71t_4~XJ0(%Cb2CY=t_hXh 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 diff --git a/src/eynollah/utils/font.py b/src/eynollah/utils/font.py index 0354317..3e9e588 100644 --- a/src/eynollah/utils/font.py +++ b/src/eynollah/utils/font.py @@ -11,6 +11,6 @@ else: def get_font(font_size): #font_path = "Charis-7.000/Charis-Regular.ttf" # Make sure this file exists! - font = importlib_resources.files(__package__) / "../Charis-Regular.ttf" + font = importlib_resources.files(__package__) / "../Amiri-Regular.ttf" with importlib_resources.as_file(font) as font: return ImageFont.truetype(font=font, size=font_size) From 89ce9de6fac39beda626ffb13287b8791b2ab706 Mon Sep 17 00:00:00 2001 From: vahidrezanezhad Date: Tue, 24 Feb 2026 15:46:15 +0100 Subject: [PATCH 065/121] musicregion is added to pagexml to label --- .../training/generate_gt_for_training.py | 4 +- src/eynollah/training/gt_gen_utils.py | 72 ++++++++++++++++++- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/eynollah/training/generate_gt_for_training.py b/src/eynollah/training/generate_gt_for_training.py index d819944..a848b65 100644 --- a/src/eynollah/training/generate_gt_for_training.py +++ b/src/eynollah/training/generate_gt_for_training.py @@ -521,9 +521,9 @@ def visualize_layout_segmentation(xml_file, dir_xml, dir_out, dir_imgs): img_file_name_with_format = find_format_of_given_filename_in_dir(dir_imgs, f_name) img = cv2.imread(os.path.join(dir_imgs, img_file_name_with_format)) - co_text, co_graphic, co_sep, co_img, co_table, co_map, co_noise, y_len, x_len = get_layout_contours_for_visualization(xml_file) + co_text, co_graphic, co_sep, co_img, co_table, co_map, co_music, co_noise, y_len, x_len = get_layout_contours_for_visualization(xml_file) - added_image = visualize_image_from_contours_layout(co_text['paragraph'], co_text['header']+co_text['heading'], co_text['drop-capital'], co_sep, co_img, co_text['marginalia'], co_table, co_map, img) + added_image = visualize_image_from_contours_layout(co_text['paragraph'], co_text['header']+co_text['heading'], co_text['drop-capital'], co_sep, co_img, co_text['marginalia'], co_table, co_map, co_music, img) cv2.imwrite(os.path.join(dir_out, f_name+'.png'), added_image) except: diff --git a/src/eynollah/training/gt_gen_utils.py b/src/eynollah/training/gt_gen_utils.py index f21ee53..e085fa0 100644 --- a/src/eynollah/training/gt_gen_utils.py +++ b/src/eynollah/training/gt_gen_utils.py @@ -18,7 +18,7 @@ with warnings.catch_warnings(): warnings.simplefilter("ignore") -def visualize_image_from_contours_layout(co_par, co_header, co_drop, co_sep, co_image, co_marginal, co_table, co_map, img): +def visualize_image_from_contours_layout(co_par, co_header, co_drop, co_sep, co_image, co_marginal, co_table, co_map, co_music, img): alpha = 0.5 blank_image = np.ones( (img.shape[:]), dtype=np.uint8) * 255 @@ -32,6 +32,7 @@ def visualize_image_from_contours_layout(co_par, co_header, co_drop, co_sep, co_ col_marginal = (106, 90, 205) col_table = (0, 90, 205) col_map = (90, 90, 205) + col_music = (90, 90, 0) if len(co_image)>0: cv2.drawContours(blank_image, co_image, -1, col_image, thickness=cv2.FILLED) # Fill the contour @@ -59,6 +60,9 @@ def visualize_image_from_contours_layout(co_par, co_header, co_drop, co_sep, co_ if len(co_map)>0: cv2.drawContours(blank_image, co_map, -1, col_map, thickness=cv2.FILLED) # Fill the contour + + if len(co_music)>0: + cv2.drawContours(blank_image, co_music, -1, col_music, thickness=cv2.FILLED) # Fill the contour img_final =cv2.cvtColor(blank_image, cv2.COLOR_BGR2RGB) @@ -389,6 +393,7 @@ def get_layout_contours_for_visualization(xml_file): co_img=[] co_table=[] co_map=[] + co_music=[] co_noise=[] types_text = [] @@ -630,6 +635,31 @@ def get_layout_contours_for_visualization(xml_file): elif vv.tag!=link+'Point' and sumi>=1: break co_map.append(np.array(c_t_in)) + + if tag.endswith('}MusicRegion') or tag.endswith('}musicregion'): + #print('sth') + for nn in root1.iter(tag): + c_t_in=[] + sumi=0 + for vv in nn.iter(): + # check the format of coords + if vv.tag==link+'Coords': + coords=bool(vv.attrib) + if coords: + p_h=vv.attrib['points'].split(' ') + c_t_in.append( np.array( [ [ int(x.split(',')[0]) , int(x.split(',')[1]) ] for x in p_h] ) ) + break + else: + pass + + + if vv.tag==link+'Point': + c_t_in.append([ int(float(vv.attrib['x'])) , int(float(vv.attrib['y'])) ]) + sumi+=1 + #print(vv.tag,'in') + elif vv.tag!=link+'Point' and sumi>=1: + break + co_music.append(np.array(c_t_in)) if tag.endswith('}NoiseRegion') or tag.endswith('}noiseregion'): @@ -656,7 +686,7 @@ def get_layout_contours_for_visualization(xml_file): elif vv.tag!=link+'Point' and sumi>=1: break co_noise.append(np.array(c_t_in)) - return co_text, co_graphic, co_sep, co_img, co_table, co_map, co_noise, y_len, x_len + return co_text, co_graphic, co_sep, co_img, co_table, co_map, co_music, co_noise, y_len, x_len def get_images_of_ground_truth( gt_list, @@ -870,7 +900,7 @@ def get_images_of_ground_truth( types_graphic_label = list(types_graphic_dict.values()) - labels_rgb_color = [ (0,0,0), (255,0,0), (255,125,0), (255,0,125), (125,255,125), (125,125,0), (0,125,255), (0,125,0), (125,125,125), (255,0,255), (125,0,125), (0,255,0),(0,0,255), (0,255,255), (255,125,125), (0,125,125), (0,255,125), (255,125,255), (125,255,0), (125,255,255)] + labels_rgb_color = [ (0,0,0), (255,0,0), (255,125,0), (255,0,125), (125,255,125), (125,125,0), (0,125,255), (0,125,0), (125,125,125), (255,0,255), (125,0,125), (0,255,0),(0,0,255), (0,255,255), (255,125,125), (0,125,125), (0,255,125), (255,125,255), (125,255,0), (125,255,255), (125,125,255)] region_tags=np.unique([x for x in alltags if x.endswith('Region')]) @@ -882,6 +912,7 @@ def get_images_of_ground_truth( co_img=[] co_table=[] co_map=[] + co_music=[] co_noise=[] for tag in region_tags: @@ -1123,6 +1154,32 @@ def get_images_of_ground_truth( elif vv.tag!=link+'Point' and sumi>=1: break co_map.append(np.array(c_t_in)) + + if 'musicregion' in keys: + if tag.endswith('}MusicRegion') or tag.endswith('}musicregion'): + #print('sth') + for nn in root1.iter(tag): + c_t_in=[] + sumi=0 + for vv in nn.iter(): + # check the format of coords + if vv.tag==link+'Coords': + coords=bool(vv.attrib) + if coords: + p_h=vv.attrib['points'].split(' ') + c_t_in.append( np.array( [ [ int(x.split(',')[0]) , int(x.split(',')[1]) ] for x in p_h] ) ) + break + else: + pass + + + if vv.tag==link+'Point': + c_t_in.append([ int(float(vv.attrib['x'])) , int(float(vv.attrib['y'])) ]) + sumi+=1 + #print(vv.tag,'in') + elif vv.tag!=link+'Point' and sumi>=1: + break + co_music.append(np.array(c_t_in)) if 'noiseregion' in keys: if tag.endswith('}NoiseRegion') or tag.endswith('}noiseregion'): @@ -1200,6 +1257,10 @@ def get_images_of_ground_truth( erosion_rate = 0#2 dilation_rate = 3#4 co_map, img_boundary = update_region_contours(co_map, img_boundary, erosion_rate, dilation_rate, y_len, x_len ) + if "musicregion" in elements_with_artificial_class: + erosion_rate = 0#2 + dilation_rate = 3#4 + co_music, img_boundary = update_region_contours(co_music, img_boundary, erosion_rate, dilation_rate, y_len, x_len ) @@ -1227,6 +1288,8 @@ def get_images_of_ground_truth( img_poly=cv2.fillPoly(img, pts =co_table, color=labels_rgb_color[ config_params['tableregion']]) if 'mapregion' in keys: img_poly=cv2.fillPoly(img, pts =co_map, color=labels_rgb_color[ config_params['mapregion']]) + if 'musicregion' in keys: + img_poly=cv2.fillPoly(img, pts =co_music, color=labels_rgb_color[ config_params['musicregion']]) if 'noiseregion' in keys: img_poly=cv2.fillPoly(img, pts =co_noise, color=labels_rgb_color[ config_params['noiseregion']]) @@ -1291,6 +1354,9 @@ def get_images_of_ground_truth( if 'mapregion' in keys: color_label = config_params['mapregion'] img_poly=cv2.fillPoly(img, pts =co_map, color=(color_label,color_label,color_label)) + if 'musicregion' in keys: + color_label = config_params['musicregion'] + img_poly=cv2.fillPoly(img, pts =co_music, color=(color_label,color_label,color_label)) if 'noiseregion' in keys: color_label = config_params['noiseregion'] img_poly=cv2.fillPoly(img, pts =co_noise, color=(color_label,color_label,color_label)) From dfa651ef8a1589aebd28357d95436beb74f37186 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 12 Jun 2026 22:21:06 +0200 Subject: [PATCH 066/121] predictor: show full stacktrace before passing the exception over --- src/eynollah/predictor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eynollah/predictor.py b/src/eynollah/predictor.py index 6790676..526223a 100644 --- a/src/eynollah/predictor.py +++ b/src/eynollah/predictor.py @@ -214,7 +214,7 @@ class Predictor(mp.context.SpawnProcess): self.resultq.put((jobid, result)) #self.logger.debug("sent result for '%d': %s", jobid, result) except Exception as e: - self.logger.error("prediction for %s failed: %s", self.name, e.__class__.__name__) + self.logger.exception("prediction for %s failed: %s", self.name, e.__class__.__name__) result = e self.resultq.put((jobid, result)) close_all() From eb4cae9dee0ab625e08df3a23cd931dcebfae67f Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 16 Jun 2026 17:28:10 +0200 Subject: [PATCH 067/121] training.models for cnn-rnn-ocr: avoid `Conv1D(..channels_first..)` --- src/eynollah/training/models.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/eynollah/training/models.py b/src/eynollah/training/models.py index b3d811a..9111225 100644 --- a/src/eynollah/training/models.py +++ b/src/eynollah/training/models.py @@ -21,6 +21,7 @@ from tensorflow.keras.layers import ( LSTM, MaxPooling2D, MultiHeadAttention, + Permute, Reshape, UpSampling2D, ZeroPadding2D, @@ -70,7 +71,7 @@ class CTCDecoder(Layer): ## but Keras greedy sometimes removes arbitrary letters # outputs, logits = tf.keras.backend.ctc_decode(inputs, # lengths, - # beam_width=20 + # beam_width=20, # greedy=False, # True, # # backend does not allow these kwargs # #merge_repeated=False, @@ -530,10 +531,12 @@ def cnn_rnn_ocr_model(input_height=None, input_width=None, n_classes=None, max_l addition_rnn = Bidirectional(LSTM(input_width, return_sequences=True, dropout=0.25))(addition) - out = Conv1D(max_len, 1, data_format="channels_first")(addition_rnn) + #out = Conv1D(max_len, 1, data_format="channels_first")(addition_rnn) + out = Permute((2, 1))(addition_rnn) + out = Conv1D(max_len, 1, data_format="channels_last")(out) + out = Permute((2, 1))(out) out = BatchNormalization(name="bn9")(out) out = Activation("relu", name="relu9")(out) - #out = Conv1D(n_classes, 1, activation='relu', data_format="channels_last")(out) out = Dense(n_classes, activation="softmax", name="dense2")(out) From 0bfbbfdc801de3ea339f1a187f42c9625037ee5f Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 19 Jun 2026 22:01:02 +0200 Subject: [PATCH 068/121] training.metrics: allow module init without TFA --- src/eynollah/training/metrics.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/eynollah/training/metrics.py b/src/eynollah/training/metrics.py index caa0e65..2b4fc4f 100644 --- a/src/eynollah/training/metrics.py +++ b/src/eynollah/training/metrics.py @@ -5,7 +5,10 @@ import tensorflow as tf from tensorflow.keras import backend as K from tensorflow.keras.metrics import Metric, MeanMetricWrapper, get from tensorflow.keras.initializers import Zeros -from tensorflow_addons.image import connected_components +try: + from tensorflow_addons.image import connected_components +except ModuleNotFoundError: + pass # n/a beyond TF 2.15 (and only needed for training) ... import numpy as np From 42a3751e6392f295c79fc3e6a1d8112a0d909df7 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 19 Jun 2026 22:01:39 +0200 Subject: [PATCH 069/121] ModelZoo ONNX: avoid verbose logging --- src/eynollah/model_zoo/model_zoo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 13656f4..232a28a 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -307,6 +307,8 @@ class EynollahModelZoo: import onnxruntime as ort import numpy as np + ort.set_default_logger_severity(3) + providers = ort.get_available_providers() if device: if ':' in device: From ef47f0ef09f2fa7993bc7251ad12250215d2a253 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 26 Jun 2026 02:11:59 +0200 Subject: [PATCH 070/121] training convert: only add characters_org.txt if it exists --- src/eynollah/training/convert.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/eynollah/training/convert.py b/src/eynollah/training/convert.py index d2d7b49..5bf30a1 100644 --- a/src/eynollah/training/convert.py +++ b/src/eynollah/training/convert.py @@ -68,12 +68,13 @@ def convert_cli(rebuild, format_, in_, out): ex.add_config(str(config_path)) # some models deviate between training and inference ex.add_config(inference=True) - # make sure the local vocab file gets re-used + # OCR models: make sure the local vocab file gets re-used, if available characters_txt_file = model_path / "characters_org.txt" - with open(characters_txt_file, "r") as voc_file: - voc = json.load(voc_file) - ex.add_config(characters_txt_file=characters_txt_file) - ex.add_config(n_classes=len(voc) + 3) + if characters_txt_file.exists(): + with open(characters_txt_file, "r") as voc_file: + voc = json.load(voc_file) + ex.add_config(characters_txt_file=characters_txt_file) + ex.add_config(n_classes=len(voc) + 3) # just retrieve final config (via pseudo-run) ex.main(lambda: 0) config = ex.run(options={'--loglevel': 'ERROR'}).config From 45168178dcb6bce14c0d65ccfaebf4cfa74bd899 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 26 Jun 2026 02:13:39 +0200 Subject: [PATCH 071/121] ModelZoo ONNX backend: log configured top provider (backend) --- src/eynollah/model_zoo/model_zoo.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 232a28a..d6b5e38 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -351,10 +351,15 @@ class EynollahModelZoo: # 'trt_timing_cache_enable': True, # ... })] + providers + provider0 = providers[0] + if isinstance(provider0, tuple): + provider0 = provider0[0] + self.logger.info("using %s with ONNX provider %s for model %s", + "GPU %d" % gpu if gpu >= 0 else "CPU", + provider0[:-17], model_category) model = ort.InferenceSession( model_path, providers=providers) - # FIXME: notify about selected provider/device model_inputs = [model_input.name for model_input in model.get_inputs()] model_outputs = [model_output.name From 948d841a7d58d7737b2abd58bbdced4a9f9d222d Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 26 Jun 2026 02:21:47 +0200 Subject: [PATCH 072/121] =?UTF-8?q?training.models=20for=20cnn-rnn-ocr:=20?= =?UTF-8?q?make=20ONNX=20convertible=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `training.models.CTCDecoder`: switch back from `tf.nn.ctc_beam_search_decoder()` to `tf.nn.ctc_greedy_decoder()` (because ONNX only implements `CTCGreedyDecoder`) - `training.models.cnn_rnn_ocr_model(inference=True)` and `training.models.cnn_rnn_ocr_model4inference`: drop layer `tf.io.decode_raw()` (because ONNX does not implement `DecodePaddedRaw`) - `Eynollah_ocr.run_cnn()`: expect bytes arrays from predictor instead of uint8 - `predictor`: to prevent segfaults when sending `tf.string` results via `shared_memory`, convert `np.object` to `np.bytes_` directly --- src/eynollah/eynollah_ocr.py | 10 ++++------ src/eynollah/predictor.py | 12 +++++++++--- src/eynollah/training/models.py | 15 +++++---------- src/eynollah/training/reload-models-v0.8.mk | 2 -- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 9ed02d4..9cc2c9a 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -296,13 +296,11 @@ class Eynollah_ocr(Eynollah): probs[ver_index > 0][flipped_ver_is_better] = probs_ver[flipped_ver_is_better] def nooov(x): - return x != b'[UNK]' + if x == b'[UNK]': + return b'' + return x for pred, prob in zip(preds, probs): - text = b''.join( - filter(nooov, - map(bytes, - (filter(None, char) - for char in pred.tolist())))).decode('utf-8') + text = b''.join(map(nooov, pred.tolist())).decode('utf-8') extracted_texts.append(text) extracted_confs.append(prob) del cropped_lines_rgb diff --git a/src/eynollah/predictor.py b/src/eynollah/predictor.py index 526223a..6b7fe96 100644 --- a/src/eynollah/predictor.py +++ b/src/eynollah/predictor.py @@ -191,13 +191,19 @@ class Predictor(mp.context.SpawnProcess): #result = self.model.predict(data, verbose=0) # faster, less VRAM result = self.model.predict_on_batch(data) - if isinstance(result, tuple): + def make_shareable(x): + # convert tf.string/np.object to fixed-length bytes + # (because object segfaults in shm) + if x.dtype is np.dtype(object): + return x.astype(bytes) + return x + if isinstance(result, (list, tuple)): multi_output = True - results = zip(*(np.split(result0, len(jobs)) + results = zip(*(np.split(make_shareable(result0), len(jobs)) for result0 in result)) else: multi_output = False - results = np.split(result, len(jobs)) + results = np.split(make_shareable(result), len(jobs)) #self.logger.debug("sharing result array for '%d'", jobid) with ExitStack() as stack: for jobid, result in zip(jobs, results): diff --git a/src/eynollah/training/models.py b/src/eynollah/training/models.py index 9111225..eda6b0f 100644 --- a/src/eynollah/training/models.py +++ b/src/eynollah/training/models.py @@ -82,18 +82,17 @@ class CTCDecoder(Layer): inputs = tf.math.log( tf.transpose(inputs, perm=[1, 0, 2]) + tf.keras.backend.epsilon() ) - # tf.nn.ctc_greedy_decoder() is not as precise # tf.compat.v1.nn.ctc_beam_search_decoder() also needs merge_repeated=False - decoded, logits = tf.nn.ctc_beam_search_decoder( + # tf.nn.ctc_beam_search_decoder() is not supported by ONNX, yet + # tf.nn.ctc_greedy_decoder() is not as precise, though: + decoded, logits = tf.nn.ctc_greedy_decoder( inputs, lengths, - beam_width=10, - top_paths=2, ) # get top path for all sequences in batch decoded = decoded[0] - logits = logits[:, 0] - logits[:, 1] - probs = tf.exp(-logits) + logits = logits[:, 0] + probs = tf.exp(-logits / n_steps) # convert to dense outputs = tf.SparseTensor(decoded.indices, decoded.values, (n_samples, n_steps)) @@ -555,8 +554,6 @@ def cnn_rnn_ocr_model(input_height=None, input_width=None, n_classes=None, max_l voc = char2num.get_vocabulary() num2char = StringLookup(vocabulary=voc, invert=True) output = num2char(out) - # avoid output tf.dtype=string → np.dtype=object (which cannot be shm-ed) - output = tf.io.decode_raw(output, tf.uint8, fixed_length=max(map(len, voc))) return Model((inputs, inputs_bin), (output, prob)) @@ -585,8 +582,6 @@ def cnn_rnn_ocr_model4inference(model, model_path): voc = char2num.get_vocabulary() num2char = StringLookup(vocabulary=voc, invert=True) output = num2char(output) - # avoid output tf.dtype=string → np.dtype=object (which cannot be shm-ed) - output = tf.io.decode_raw(output, tf.uint8, fixed_length=max(map(len, voc))) inputs = (inputs, inputs_bin) outputs = (output, prob) return Model(inputs, outputs) diff --git a/src/eynollah/training/reload-models-v0.8.mk b/src/eynollah/training/reload-models-v0.8.mk index ef74bdc..022603e 100644 --- a/src/eynollah/training/reload-models-v0.8.mk +++ b/src/eynollah/training/reload-models-v0.8.mk @@ -59,8 +59,6 @@ $(MODELS_DST)/%.h5: $(MODELS_SRC)/% $(MODELS_DST)/%.onnx: $(MODELS_SRC)/% if jq -e '.task == "segmentation" and .backbone_type == "transformer"' $/dev/null; then \ echo skipping $@: vision transformer architecture currently does not work with ONNX; \ - elif jq -e '.task == "cnn-rnn-ocr"' $/dev/null || test x$(findstring _ocr,$@) = x_ocr; then \ - echo skipping $@: OCR CTC decoder does not work with ONNX; \ else \ eynollah-training convert \ $(and $(wildcard $ Date: Fri, 26 Jun 2026 02:42:51 +0200 Subject: [PATCH 073/121] =?UTF-8?q?predictor=20for=20OCR=20models:=20work?= =?UTF-8?q?=20around=20ONNX=20bug=20=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (ONNX converted models already return `np.dtype=object` arrays of `np.str_` instead of `np.bytes_`; so undo this) --- src/eynollah/predictor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/eynollah/predictor.py b/src/eynollah/predictor.py index 6b7fe96..9121ffe 100644 --- a/src/eynollah/predictor.py +++ b/src/eynollah/predictor.py @@ -195,6 +195,10 @@ class Predictor(mp.context.SpawnProcess): # convert tf.string/np.object to fixed-length bytes # (because object segfaults in shm) if x.dtype is np.dtype(object): + # ONNX conversion for some reason decodes bytes into str already + # so here we undo this, too + if x[0].dtype is np.dtype(object) and isinstance(x[0, 0], str): + x = np.char.encode(x.astype(str), 'utf-8') return x.astype(bytes) return x if isinstance(result, (list, tuple)): From 5e531ab00619688d0020834d8e159a74c7866da3 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 1 Jul 2026 18:27:46 +0200 Subject: [PATCH 074/121] get_textlines_of_textregion_sorted: fix be61875 --- src/eynollah/eynollah.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eynollah/eynollah.py b/src/eynollah/eynollah.py index 9db47ce..8cc17a1 100644 --- a/src/eynollah/eynollah.py +++ b/src/eynollah/eynollah.py @@ -901,7 +901,7 @@ class Eynollah: if N > 1: mean_y_diff = np.median(diff_cy) mean_x_diff = np.median(diff_cx) - count_hor = np.count_nonzero(np.diff(w_h_textline) > 0) + count_hor = np.count_nonzero(np.diff(w_h_textline, axis=0) > 0) count_ver = N - count_hor else: mean_y_diff = 0 From 1b27c7390f7311492bd746dfb83185f58dfb63db Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 2 Jul 2026 20:56:55 +0200 Subject: [PATCH 075/121] training.convert/ONNX: run strict shape inference and check model --- src/eynollah/training/convert.py | 5 +++++ src/eynollah/training/reload-models-v0.8.mk | 11 ++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/eynollah/training/convert.py b/src/eynollah/training/convert.py index 5bf30a1..7351356 100644 --- a/src/eynollah/training/convert.py +++ b/src/eynollah/training/convert.py @@ -99,7 +99,12 @@ def convert_cli(rebuild, format_, in_, out): model.export(out) elif format_ == "onnx": import tf2onnx + import onnx tf2onnx.convert.from_keras(model, opset=18, output_path=out) + model = onnx.load(out) + model = onnx.shape_inference.infer_shapes(model, strict_mode=True) + onnx.checker.check_model(model, full_check=True) + onnx.save(model, out) else: raise ValueError("unknown output format '%s'" % format_) diff --git a/src/eynollah/training/reload-models-v0.8.mk b/src/eynollah/training/reload-models-v0.8.mk index 022603e..342d211 100644 --- a/src/eynollah/training/reload-models-v0.8.mk +++ b/src/eynollah/training/reload-models-v0.8.mk @@ -38,7 +38,7 @@ $(MODELS_DST)/%: $(MODELS_SRC)/% --in $< \ --format $(FORMAT) \ --out $@ \ - 2>&1 | tee $(notdir $<).$(FORMAT).log + > $(notdir $<).$(FORMAT).log 2>&1 || { cat $(notdir $<).$(FORMAT).log; false; } $(MODELS_DST)/%.keras: $(MODELS_SRC)/% eynollah-training convert \ @@ -46,7 +46,7 @@ $(MODELS_DST)/%.keras: $(MODELS_SRC)/% --in $< \ --format keras \ --out $@ \ - 2>&1 | tee $(notdir $<).keras.log + > $(notdir $<).keras.log 2>&1 || { cat $(notdir $<).keras.log; false; } $(MODELS_DST)/%.h5: $(MODELS_SRC)/% eynollah-training convert \ @@ -54,18 +54,15 @@ $(MODELS_DST)/%.h5: $(MODELS_SRC)/% --in $< \ --format hdf5 \ --out $@ \ - 2>&1 | tee $(notdir $<).hdf5.log + > $(notdir $<).hdf5.log 2>&1 || { cat $(notdir $<).hdf5.log; false; } $(MODELS_DST)/%.onnx: $(MODELS_SRC)/% - if jq -e '.task == "segmentation" and .backbone_type == "transformer"' $/dev/null; then \ - echo skipping $@: vision transformer architecture currently does not work with ONNX; \ - else \ eynollah-training convert \ $(and $(wildcard $&1 | tee $(notdir $<).onnx.log; fi + > $(notdir $<).onnx.log 2>&1 || { cat $(notdir $<).onnx.log; false; } compare: for i in `find $(MODELS_DST) -mindepth 2`;do \ From 16943f70b4c08db914b64a768a8820cee79e7370 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Thu, 2 Jul 2026 20:58:22 +0200 Subject: [PATCH 076/121] =?UTF-8?q?models=20(ViT=20backbone)=20iterate=20`?= =?UTF-8?q?extract=5Fpatches`=20over=20batch=20dim=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Patches.call`: use `tf.map_fn` instead of running entire batch through `tf.image.extract_patches` (faster, less VRAM, allows ONNX conversion to work) --- src/eynollah/model_zoo/model_zoo.py | 33 ++++++++++++++++++++++++++--- src/eynollah/patch_encoder.py | 13 +++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index d6b5e38..3fca088 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -19,7 +19,7 @@ MODEL_VRAM_LIMITS = { "enhancement": 980, # due to bs 3 "col_classifier": 210, "page": 618, - "textline": 1680, # 954 for bs 1 + "textline": 1880, # 954 for bs 1 "region_1_2": 1580, "region_fl_np": 1756, "table": 1818, @@ -338,6 +338,14 @@ class EynollahModelZoo: # 'cudnn_conv_algo_search': 'EXHAUSTIVE', #'cudnn_conv_use_max_workspace': 0, # 'do_copy_in_default_stream': True, + # enable_cuda_graph + # cudnn_conv1d_pad_to_nc1d + # prefer_nhwc + # tunable_op_enable + # tunable_op_tuning_enable + # tunable_op_max_tuning_duration_ms + # use_ep_level_unified_stream + # enable_skip_layer_norm_strict_mode # ... })] + providers if 'TensorrtExecutionProvider' in providers: @@ -347,9 +355,28 @@ class EynollahModelZoo: 'device_id': gpu, 'trt_max_workspace_size': MODEL_VRAM_LIMITS[model_category] * 1024 * 1024, # 'trt_fp16_enable': True, - # 'trt_engine_cache_enable': True, - # 'trt_timing_cache_enable': True, + # trt_bf16_enable + 'trt_engine_cache_enable': True, + 'trt_timing_cache_enable': True, # ... + # trt_engine_hw_compatible + # trt_engine_cache_path + # trt_engine_cache_prefix + # trt_timing_cache_path + # trt_onnx_model_folder_path + # trt_ep_context_file_path + # trt_cuda_graph_enable + # trt_profile_opt_shapes + # trt_profile_min_shapes + # trt_profile_max_shapes + # trt_builder_optimization_level + # trt_build_heuristics_enable + # trt_sparsity_enable + # trt_weight_stripped_engine_enable + # trt_dla_core + # trt_dla_enable + # trt_min_subgraph_size + # trt_ep_context_embed_mode })] + providers provider0 = providers[0] if isinstance(provider0, tuple): diff --git a/src/eynollah/patch_encoder.py b/src/eynollah/patch_encoder.py index 610f0b4..556a4c9 100644 --- a/src/eynollah/patch_encoder.py +++ b/src/eynollah/patch_encoder.py @@ -29,7 +29,13 @@ class Patches(layers.Layer): self.patch_size_y = patch_size_y def call(self, images): - batch_size = tf.shape(images)[0] + #batch_size = tf.shape(images)[0] + return tf.map_fn(self.call_single, images) + + def call_single(self, image): + # avoid batched extract_patches: too much memory, + # and variable batch dim not supported by ONNX implementation + images = tf.expand_dims(image, axis=0) patches = tf.image.extract_patches( images=images, sizes=[1, self.patch_size_y, self.patch_size_x, 1], @@ -37,8 +43,9 @@ class Patches(layers.Layer): rates=[1, 1, 1, 1], padding="VALID", ) - patch_dims = patches.shape[-1] - return tf.reshape(patches, [batch_size, -1, patch_dims]) + _, n_rows, n_cols, patch_dims = patches.shape + n_tiles = patches.shape[1] * patches.shape[2] #-1 + return tf.reshape(patches, [1, n_tiles, patch_dims]) def get_config(self): return dict(patch_size_x=self.patch_size_x, From 1567df1379b457c1247568d5a7bfeb703ed82a8a Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 7 Jul 2026 22:09:46 +0200 Subject: [PATCH 077/121] remove numba dependency (previously used to free CUDA memory) --- requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index d79853f..5bfb3b9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,7 @@ # ocrd includes opencv, numpy, shapely, click ocrd >= 3.3.0 -numpy < 2.0 scikit-learn >= 0.23.2 tensorflow tf-keras # avoid keras 3 (also needs TF_USE_LEGACY_KERAS=1) -numba <= 0.58.1 scikit-image tabulate From 171a8a3161c13ff4747a39286b175db3de3a1d62 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 7 Jul 2026 22:56:36 +0200 Subject: [PATCH 078/121] move TF+Keras dependencies to `training` extra, replace by ONNX+TRT --- requirements.txt | 4 ++-- train/requirements.txt | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 5bfb3b9..a707407 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # ocrd includes opencv, numpy, shapely, click ocrd >= 3.3.0 +onnxruntime-gpu[cuda,cudnn] # w/ .onnx models +tensorrt_cu12 < 11 # 11 incompatible with CUDA libs from onnxruntime-gpu[cuda,cudnn] scikit-learn >= 0.23.2 -tensorflow -tf-keras # avoid keras 3 (also needs TF_USE_LEGACY_KERAS=1) scikit-image tabulate diff --git a/train/requirements.txt b/train/requirements.txt index 090bc50..1734b67 100644 --- a/train/requirements.txt +++ b/train/requirements.txt @@ -6,5 +6,6 @@ imutils scipy tensorflow-addons # for connected_components, depublished and only compatible with tensorflow < 2.16 tensorflow < 2.16 # for tensorflow-addons, so only needed in training -tf_data < 2.16 # for tensorflow-addons, so only needed in training +tf-keras # avoid keras 3 (also needs TF_USE_LEGACY_KERAS=1) +tf-data < 2.16 # for tensorflow-addons, so only needed in training protobuf < 5 # for tensorflow-addons, so only needed in training From 32568a590f928a41c0a3c749d25945918d8a83a2 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 8 Jul 2026 02:54:34 +0200 Subject: [PATCH 079/121] Docker: update to ONNX base image --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index a15776e..f267b85 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ LABEL \ org.opencontainers.image.documentation="https://github.com/qurator-spk/eynollah/blob/${VCS_REF}/README.md" \ org.opencontainers.image.revision=$VCS_REF \ org.opencontainers.image.created=$BUILD_DATE \ - org.opencontainers.image.base.name=ocrd/core-cuda-tf2 + org.opencontainers.image.base.name=ocrd/core-cuda-onnx ENV DEBIAN_FRONTEND=noninteractive # set proper locales @@ -40,8 +40,8 @@ RUN ocrd ocrd-tool ocrd-tool.json dump-tools > $(dirname $(ocrd bashlib filename RUN ocrd ocrd-tool ocrd-tool.json dump-module-dirs > $(dirname $(ocrd bashlib filename))/ocrd-all-module-dir.json # install everything and reduce image size RUN make install EXTRAS=OCR && rm -rf /build/eynollah -# fixup for broken cuDNN installation (Torch pulls in 8.5.0, which is incompatible with Tensorflow) -RUN pip install nvidia-cudnn-cu11==8.6.0.163 +# fixup for broken cuDNN installation (Torch may pull in version which is incompatible with Tensorflow) +# but most recent Torch versions pull cu13 variants, which are not conflicting here # smoke test RUN eynollah --help From 8cc8c2847167c33585815f1e3fb9d1ca7c12710d Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 8 Jul 2026 02:55:25 +0200 Subject: [PATCH 080/121] ModelZoo for ONNX backend: configure TRT cache path from XDG env --- src/eynollah/model_zoo/model_zoo.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 3fca088..a0e6052 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -306,6 +306,7 @@ class EynollahModelZoo: def _load_onnx_model(self, model_category, model_path, device=''): import onnxruntime as ort import numpy as np + from ocrd_utils import config ort.set_default_logger_severity(3) @@ -358,11 +359,11 @@ class EynollahModelZoo: # trt_bf16_enable 'trt_engine_cache_enable': True, 'trt_timing_cache_enable': True, + 'trt_engine_cache_path': config.XDG_CONFIG_HOME, + 'trt_timing_cache_path': config.XDG_CONFIG_HOME, # ... # trt_engine_hw_compatible - # trt_engine_cache_path # trt_engine_cache_prefix - # trt_timing_cache_path # trt_onnx_model_folder_path # trt_ep_context_file_path # trt_cuda_graph_enable From 5354583913a87ffc157ea82bb6f70b17c72af2a0 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 8 Jul 2026 02:56:11 +0200 Subject: [PATCH 081/121] update readme --- README.md | 99 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 7e9a90a..bf80bca 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,12 @@ documents using a combination of multiple deep learning models and heuristics; therefore processing can be slow. ## Installation -Python `3.8-3.11` with Tensorflow `<2.13` on Linux are currently supported. -For (limited) GPU support the CUDA toolkit needs to be installed. -A working config is CUDA `11.8` with cuDNN `8.6`. + +Python `3.8-3.11` with ONNX Runtime on Linux are currently supported. + +For GPU support, NVidia drivers supporting CUDA 12 must be installed. +The runtime dependencies will pull in ONNX, TensorRT and CUDA runtime +libraries (including cuDNN) from PyPI. You can either install from PyPI @@ -52,6 +55,13 @@ pip install "eynollah[OCR]" make install EXTRAS=OCR ``` +> **Note**: Requirements for OCR are more involved, +> as they may need Tensorflow (with tf-keras) and/or +> Torch (with transformers). Those two frameworks may +> also have conflicting CUDA dependencies. An ONNX +> conversion for these models may be achieved soon. +> :construction: + ### Docker Use @@ -64,7 +74,12 @@ When using Eynollah with Docker, see [`docker.md`](https://github.com/qurator-sp ## Models -Pretrained models can be downloaded from [Zenodo](https://zenodo.org/records/17727267) or [Hugging Face](https://huggingface.co/SBB?search_models=eynollah). +Pretrained models can be downloaded from [Zenodo](https://zenodo.org/records/17727267) or [Hugging Face](https://huggingface.co/SBB?search_models=eynollah). + +For fast runtime inference, download the ONNX models. + +For finetuning training, download the original (Tensorflow / Torch) models +(and install the `[training]` extra). For model documentation and model cards, see [`models.md`](https://github.com/qurator-spk/eynollah/tree/main/docs/models.md). @@ -83,18 +98,26 @@ Eynollah supports five use cases: Some example outputs can be found in [`examples.md`](https://github.com/qurator-spk/eynollah/tree/main/docs/examples.md). +The **generic options** shared by all subcommands are: +```sh + -m + -mv + -D + -l +``` + ### Layout Analysis -The layout analysis module is responsible for detecting layout elements, identifying text lines, and determining reading -order using heuristic methods or a [pretrained model](https://github.com/qurator-spk/eynollah#machine-based-reading-order). +Detects layout elements, i.e. regions of various types and text lines, +and determines their reading order using either heuristic methods or a +[pretrained model](https://github.com/qurator-spk/eynollah#machine-based-reading-order). The command-line interface for layout analysis can be called like this: ```sh -eynollah layout \ +eynollah [GENERIC_OPTIONS] layout \ -i | -di \ -o \ - -m \ [OPTIONS] ``` @@ -106,7 +129,7 @@ The following options can be used to further configure the processing: | `-tab` | apply table detection | | `-ae` | apply enhancement (the resulting image is saved to the output directory) | | `-as` | apply scaling | -| `-cl` | apply contour detection for curved text lines instead of bounding boxes | +| `-cl` | apply contour detection for curved text lines, deskewing all regions independently | | `-ib` | apply binarization (the resulting image is saved to the output directory) | | `-ep` | enable plotting (MUST always be used with `-sl`, `-sd`, `-sa`, `-si` or `-ae`) | | `-ho` | ignore headers for reading order dectection | @@ -115,79 +138,93 @@ The following options can be used to further configure the processing: | `-sl ` | save layout prediction as plot to this directory | | `-sp ` | save cropped page image to this directory | | `-sa ` | save all (plot, enhanced/binary image, layout) to this directory | -| `-thart` | threshold of artifical class in the case of textline detection. The default value is 0.1 | -| `-tharl` | threshold of artifical class in the case of layout detection. The default value is 0.1 | +| `-thart` | confidence threshold of artifical boundary class during textline detection | +| `-tharl` | confidence threshold of artifical boundary class during region detection | | `-ncu` | upper limit of columns in document image | | `-ncl` | lower limit of columns in document image | | `-slro` | skip layout detection and reading order | | `-romb` | apply machine based reading order detection | | `-ipe` | ignore page extraction | +| `-j` | number of CPU jobs to run parallel (useful with -di) | +| `-H` | when to halt when some jobs fail | +The default is to only perform layout detection of main regions +(background, text, images, separators and marginals). -If no further option is set, the tool performs layout detection of main regions (background, text, images, separators -and marginals). -The best output quality is achieved when RGB images are used as input rather than greyscale or binarized images. +The best output quality is achieved when RGB images are used as input +rather than greyscale or binarized images. -Additional documentation can be found in [`usage.md`](https://github.com/qurator-spk/eynollah/tree/main/docs/usage.md). +Additional documentation can be found in +[`usage.md`](https://github.com/qurator-spk/eynollah/tree/main/docs/usage.md). ### Binarization -The binarization module performs document image binarization using pretrained pixelwise segmentation models. +Performs document image binarization (thresholding) +using pretrained pixelwise segmentation models. The command-line interface for binarization can be called like this: ```sh -eynollah binarization \ +eynollah [GENERIC_OPTIONS] binarization \ -i | -di \ -o \ - -m + [OPTIONS] ``` ### Image Enhancement -TODO + +This enlarges and enhances images. Useful in case the scan quality is low. + +```sh +eynollah [GENERIC_OPTIONS] enhancement \ + -i | -di \ + -o \ + [OPTIONS] +``` + +| option | description | +|-------------------|:--------------------------------------------------------------------------------------------| +| `-sos` | save the enhanced image in original image size | +| `-ncu` | upper limit of columns in document image | +| `-ncl` | lower limit of columns in document image | ### OCR -The OCR module performs text recognition using either a CNN-RNN model or a Transformer model. +Performs text recognition using either a CNN-RNN model or a Transformer model. +Needs a PAGE-XML input file. The command-line interface for OCR can be called like this: ```sh -eynollah ocr \ +eynollah [GENERIC_OPTIONS] ocr \ -i | -di \ -dx \ -o \ - -m | --model_name ``` The following options can be used to further configure the ocr processing: | option | description | |-------------------|:-------------------------------------------------------------------------------------------| +| `-trocr` | use transformer OCR model instead of CNN-RNN model | | `-dib` | directory of binarized images (file type must be '.png'), prediction with both RGB and bin | | `-doit` | directory for output images rendered with the predicted text | -| `--model_name` | file path to use specific model for OCR | -| `-trocr` | use transformer ocr model (otherwise cnn_rnn model is used) | -| `-etit` | export textline images and text in xml to output dir (OCR training data) | | `-nmtc` | cropped textline images will not be masked with textline contour | | `-bs` | ocr inference batch size. Default batch size is 2 for trocr and 8 for cnn_rnn models | -| `-ds_pref` | add an abbrevation of dataset name to generated training data | | `-min_conf` | minimum OCR confidence value. OCR with textline conf lower than this will be ignored | ### Reading Order Detection -Reading order detection can be performed either as part of layout analysis based on image input, or, currently under -development, based on pre-existing layout analysis data in PAGE-XML format as input. -The reading order detection module employs a pretrained model to identify the reading order from layouts represented in PAGE-XML files. +Reading order can be detected either during layout analysis, +or as a separate module, which requires a PAGE-XML input file. The command-line interface for machine based reading order can be called like this: ```sh -eynollah machine-based-reading-order \ +eynollah [GENERIC_OPTIONS] machine-based-reading-order \ -i | -di \ -xml | -dx \ - -m \ -o ``` From 98e28ca9f43d73fc8b1423ea5095ecd34c2accd8 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 8 Jul 2026 03:05:07 +0200 Subject: [PATCH 082/121] Docker: fixup cuDNN installation after OCR extra --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f267b85..d0b41a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,7 +41,7 @@ RUN ocrd ocrd-tool ocrd-tool.json dump-module-dirs > $(dirname $(ocrd bashlib fi # install everything and reduce image size RUN make install EXTRAS=OCR && rm -rf /build/eynollah # fixup for broken cuDNN installation (Torch may pull in version which is incompatible with Tensorflow) -# but most recent Torch versions pull cu13 variants, which are not conflicting here +RUN pip install "nvidia-cudnn-cu12<9.10.2.21" # smoke test RUN eynollah --help From c9c14ed83d48b5d75e4c014b553e68a81b691349 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 8 Jul 2026 14:04:19 +0200 Subject: [PATCH 083/121] Docker: update to ONNX base image (in Makefile, too) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f54cf5b..e4ac5c6 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ PYTHON ?= python3 PIP ?= pip3 EXTRAS ?= -DOCKER_BASE_IMAGE ?= docker.io/ocrd/core-cuda-tf2:v3.13.0 +DOCKER_BASE_IMAGE ?= docker.io/ocrd/core-cuda-onnx:v3.13.1 DOCKER_TAG ?= ocrd/eynollah DOCKER ?= docker WGET = wget -O From b9ba43b44499bd19609e78f7026c159c99daf13e Mon Sep 17 00:00:00 2001 From: kba Date: Thu, 9 Jul 2026 14:37:51 +0200 Subject: [PATCH 084/121] training: require tf2onnx and pin ml_dtypes >= 0.5 --- .gitignore | 2 ++ train/requirements.txt | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 49835a7..e3356ea 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ output.html *.sw? TAGS uv.lock +/ignore +*.log diff --git a/train/requirements.txt b/train/requirements.txt index 1734b67..dbc4f76 100644 --- a/train/requirements.txt +++ b/train/requirements.txt @@ -6,6 +6,8 @@ imutils scipy tensorflow-addons # for connected_components, depublished and only compatible with tensorflow < 2.16 tensorflow < 2.16 # for tensorflow-addons, so only needed in training -tf-keras # avoid keras 3 (also needs TF_USE_LEGACY_KERAS=1) +tf-keras < 2.16 # avoid keras 3 (also needs TF_USE_LEGACY_KERAS=1) tf-data < 2.16 # for tensorflow-addons, so only needed in training protobuf < 5 # for tensorflow-addons, so only needed in training +tf2onnx +ml_dtypes >= 0.5 From 492fcbacb758fd954af7cdfad821fb977d10483b Mon Sep 17 00:00:00 2001 From: kba Date: Thu, 9 Jul 2026 15:17:11 +0200 Subject: [PATCH 085/121] switch to fork of tf2onnx --- train/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/train/requirements.txt b/train/requirements.txt index dbc4f76..02cba9f 100644 --- a/train/requirements.txt +++ b/train/requirements.txt @@ -9,5 +9,5 @@ tensorflow < 2.16 # for tensorflow-addons, so only needed in training tf-keras < 2.16 # avoid keras 3 (also needs TF_USE_LEGACY_KERAS=1) tf-data < 2.16 # for tensorflow-addons, so only needed in training protobuf < 5 # for tensorflow-addons, so only needed in training -tf2onnx +eynollah-fork-tf2onnx == 1.17.0.post1 ml_dtypes >= 0.5 From b1f2f430519567b60e9837ce79597d84eca451bc Mon Sep 17 00:00:00 2001 From: kba Date: Thu, 9 Jul 2026 17:30:48 +0200 Subject: [PATCH 086/121] upgrade tf2onnx fork dep, remove spurious tf-data dep --- train/requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/train/requirements.txt b/train/requirements.txt index 02cba9f..392581c 100644 --- a/train/requirements.txt +++ b/train/requirements.txt @@ -7,7 +7,6 @@ scipy tensorflow-addons # for connected_components, depublished and only compatible with tensorflow < 2.16 tensorflow < 2.16 # for tensorflow-addons, so only needed in training tf-keras < 2.16 # avoid keras 3 (also needs TF_USE_LEGACY_KERAS=1) -tf-data < 2.16 # for tensorflow-addons, so only needed in training protobuf < 5 # for tensorflow-addons, so only needed in training -eynollah-fork-tf2onnx == 1.17.0.post1 +eynollah-fork-tf2onnx == 1.17.0.post2 ml_dtypes >= 0.5 From affddd6c85d5ea9d0aed532d472948d322f2c77b Mon Sep 17 00:00:00 2001 From: kba Date: Thu, 9 Jul 2026 19:12:09 +0200 Subject: [PATCH 087/121] train: make preprocess_imgs_ocr work for transformer-ocr --- src/eynollah/training/train.py | 3 +- src/eynollah/training/utils.py | 71 ++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/eynollah/training/train.py b/src/eynollah/training/train.py index 93b1588..304dc87 100644 --- a/src/eynollah/training/train.py +++ b/src/eynollah/training/train.py @@ -724,7 +724,8 @@ def run(_config, config, dir_img, dir_lab, - char_to_num=char_to_num + char_to_num=char_to_num, + processor=processor, ) ) diff --git a/src/eynollah/training/utils.py b/src/eynollah/training/utils.py index 33a1fd2..e84ac66 100644 --- a/src/eynollah/training/utils.py +++ b/src/eynollah/training/utils.py @@ -789,7 +789,7 @@ def preprocess_imgs(config, lab = cv2.imread(os.path.join(dir_lab, img_name + '.png')) elif config['task'] == "enhancement": lab = cv2.imread(os.path.join(dir_lab, img)) - elif config['task'] == "cnn-rnn-ocr": + elif config['task'] in ["cnn-rnn-ocr", "transformer-ocr"]: # assert lab == 'img_name + '.txt' with open(os.path.join(dir_lab, img_name + '.txt'), 'r') as f: lab = f.read().split('\n')[0] @@ -797,7 +797,7 @@ def preprocess_imgs(config, lab = None try: - if config['task'] == "cnn-rnn-ocr": + if config['task'] in ["cnn-rnn-ocr", "transformer-ocr"]: yield from preprocess_img_ocr(img, img_name, lab, **config) continue else: @@ -1116,14 +1116,25 @@ def preprocess_img_ocr( number_of_backgrounds_per_image=None, list_all_possible_background_images=None, list_all_possible_foreground_rgbs=None, + task=None, + processor=None, **kwargs ): def scale_image(img): return scale_padd_image_for_ocr(img, input_height, input_width).astype(np.float32) / 255. #lab = vectorize_label(lab, char_to_num, padding_token, max_len) # now padded at Dataset.padded_batch - lab = char_to_num(tf.strings.unicode_split(lab, input_encoding="UTF-8")) - yield scale_image(img), lab + if task == 'cnn-rnn-ocr': + assert char_to_num, 'task is cnn-rnn-ocr, so preprocess_imgs_ocr should be passed "char_to_num"' + lab = char_to_num(tf.strings.unicode_split(lab, input_encoding="UTF-8")) + yield_encoder = lambda x: x + elif task == 'transformer-ocr': + assert processor, 'task is transformer-ocr, so preprocess_imgs_ocr should be passed "processor"' + # TODO make max_length configurable again, if deemed sensible + lab = [l if l != self.processor.tokenizer.pad_token_id else -100 + for l in processor.tokenizer(lab, padding="max_length", max_length=128).input_ids] + yield_encoder = lambda img_, lab_: {"pixel_values": processor(Image.fromarray(img_), return_tensors="pt").pixel_values.squeeze(), "labels": torch.tensor(lab_)} + yield yield_encoder(scale_image(img), lab) #to_yield = {"image": ret_x, "label": ret_y} if dir_img_bin: @@ -1139,32 +1150,32 @@ def preprocess_img_ocr( for padd_col in padd_colors: img_pad = do_padding_for_ocr(img, 1.2, padd_col) img_rot = rotation_not_90_func_single_image(img_pad, thetha_ind) - yield scale_image(img_rot), lab + yield yield_encoder(scale_image(img_rot), lab) if rotation_not_90: for thetha_ind in thetha: img_rot = rotation_not_90_func_single_image(img, thetha_ind) - yield scale_image(img_rot), lab + yield yield_encoder(scale_image(img_rot), lab) if blur_aug: for blur_type in blur_k: img_blur = bluring(img, blur_type) - yield scale_image(img_blur), lab + yield yield_encoder(scale_image(img_blur), lab) if degrading: for deg_scale_ind in degrade_scales: img_deg = do_degrading(img, deg_scale_ind) - yield scale_image(img_deg), lab + yield yield_encoder(scale_image(img_deg), lab) if bin_deg: for deg_scale_ind in degrade_scales: img_deg = do_degrading(img_bin_corr, deg_scale_ind) - yield scale_image(img_deg), lab + yield yield_encoder(scale_image(img_deg), lab) if brightening: for bright_scale_ind in brightness: img_bright = do_brightening(img, bright_scale_ind) - yield scale_image(img_bright), lab + yield yield_encoder(scale_image(img_bright), lab) if padding_white: for padding_size in white_padds: for padd_col in padd_colors: img_pad = do_padding_for_ocr(img, padding_size, padd_col) - yield scale_image(img_pad), lab + yield yield_encoder(scale_image(img_pad), lab) if adding_rgb_foreground: for i_n in range(number_of_backgrounds_per_image): background_image_chosen_name = random.choice(list_all_possible_background_images) @@ -1178,7 +1189,7 @@ def preprocess_img_ocr( img_fg = \ return_binary_image_with_given_rgb_background_and_given_foreground_rgb( img_bin_corr, img_rgb_background_chosen, foreground_rgb_chosen) - yield scale_image(img_fg), lab + yield yield_encoder(scale_image(img_fg), lab) if adding_rgb_background: for i_n in range(number_of_backgrounds_per_image): background_image_chosen_name = random.choice(list_all_possible_background_images) @@ -1186,59 +1197,59 @@ def preprocess_img_ocr( cv2.imread(dir_rgb_backgrounds + '/' + background_image_chosen_name) img_bg = \ return_binary_image_with_given_rgb_background(img_bin_corr, img_rgb_background_chosen) - yield scale_image(img_bg), lab + yield yield_encoder(scale_image(img_bg), lab) if binarization: - yield scale_image(img_bin_corr), lab + yield yield_encoder(scale_image(img_bin_corr), lab) if image_inversion: img_inv = invert_image(img_bin_corr) - yield scale_image(img_inv), lab + yield yield_encoder(scale_image(img_inv), lab) if channels_shuffling: for shuffle_index in shuffle_indexes: img_shuf = return_shuffled_channels(img, shuffle_index) - yield scale_image(img_shuf), lab + yield yield_encoder(scale_image(img_shuf), lab) if add_red_textlines: img_red = return_image_with_red_elements(img, img_bin_corr) - yield scale_image(img_red), lab + yield yield_encoder(scale_image(img_red), lab) if white_noise_strap: img_noisy = return_image_with_strapped_white_noises(img) - yield scale_image(img_noisy), lab + yield yield_encoder(scale_image(img_noisy), lab) if textline_skewing: for des_scale_ind in skewing_amplitudes: img_rot = do_deskewing(img, des_scale_ind) - yield scale_image(img_rot), lab + yield yield_encoder(scale_image(img_rot), lab) if textline_skewing_bin: for des_scale_ind in skewing_amplitudes: img_rot = do_deskewing(img_bin_corr, des_scale_ind) - yield scale_image(img_rot), lab + yield yield_encoder(scale_image(img_rot), lab) if textline_left_in_depth: img_warp = do_direction_in_depth(img, 'left') - yield scale_image(img_warp), lab + yield yield_encoder(scale_image(img_warp), lab) if textline_left_in_depth_bin: img_warp = do_direction_in_depth(img_bin_corr, 'left') - yield scale_image(img_warp), lab + yield yield_encoder(scale_image(img_warp), lab) if textline_right_in_depth: img_warp = do_direction_in_depth(img, 'right') - yield scale_image(img_warp), lab + yield yield_encoder(scale_image(img_warp), lab) if textline_right_in_depth_bin: img_warp = do_direction_in_depth(img_bin_corr, 'right') - yield scale_image(img_warp), lab + yield yield_encoder(scale_image(img_warp), lab) if textline_up_in_depth: img_warp = do_direction_in_depth(img, 'up') - yield scale_image(img_warp), lab + yield yield_encoder(scale_image(img_warp), lab) if textline_up_in_depth_bin: img_warp = do_direction_in_depth(img_bin_corr, 'up') - yield scale_image(img_warp), lab + yield yield_encoder(scale_image(img_warp), lab) if textline_down_in_depth: img_warp = do_direction_in_depth(img, 'down') - yield scale_image(img_warp), lab + yield yield_encoder(scale_image(img_warp), lab) if textline_down_in_depth_bin: img_warp = do_direction_in_depth(img_bin_corr, 'down') - yield scale_image(img_warp), lab + yield yield_encoder(scale_image(img_warp), lab) if pepper_aug: for pepper_ind in pepper_indexes: img_noisy = add_salt_and_pepper_noise(img, pepper_ind, pepper_ind) - yield scale_image(img_noisy), lab + yield yield_encoder(scale_image(img_noisy), lab) if pepper_bin_aug: for pepper_ind in pepper_indexes: img_noisy = add_salt_and_pepper_noise(img_bin_corr, pepper_ind, pepper_ind) - yield scale_image(img_noisy), lab + yield yield_encoder(scale_image(img_noisy), lab) From 39e054e718f91c2dfa44bc626119480bf975917c Mon Sep 17 00:00:00 2001 From: kba Date: Thu, 9 Jul 2026 19:23:21 +0200 Subject: [PATCH 088/121] train: remove tf-specifics and weird __getitem__ def from transformer-ocr setup --- src/eynollah/training/train.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/eynollah/training/train.py b/src/eynollah/training/train.py index 304dc87..a6b5331 100644 --- a/src/eynollah/training/train.py +++ b/src/eynollah/training/train.py @@ -718,32 +718,21 @@ def run(_config, """ Wraps preprocess_imgs in a format consumable by torch """ - def __init__(self, config, dir_img, dir_lab, char_to_num): - self.samples = list( - preprocess_imgs( + def __init__(self, config, dir_img, dir_lab): + self.samples = preprocess_imgs( config, dir_img, dir_lab, - char_to_num=char_to_num, processor=processor, ) - ) def __len__(self): return len(self.samples) - def __getitem__(self, idx): - image, label = self.samples[idx] + def __iter__(self): + yield from self.samples - return { - "image": torch.as_tensor(image, dtype=torch.float32), - "label": torch.as_tensor(label, dtype=torch.long), - } - assert characters_txt_file - with open(characters_txt_file, 'r') as char_txt_f: - characters = json.load(char_txt_f) - char_to_num = StringLookup(vocabulary=list(characters), mask_token=None) - dataset = TransformerOCRTorchDataset(_config, dir_img, dir_lab, char_to_num) + dataset = TransformerOCRTorchDataset(_config, dir_img, dir_lab) data_loader = torch.utils.data.DataLoader(dataset, batch_size=1) train_dataset = data_loader.dataset From c680dae2d1095609138bc024415199a4873a85e5 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 10 Jul 2026 17:22:50 +0200 Subject: [PATCH 089/121] ModelZoo for ONNX backend: allow setting execution providers via env --- .dockerignore | 4 ++++ src/eynollah/model_zoo/model_zoo.py | 5 +++++ src/eynollah/predictor.py | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index 562fb6f..5fafd44 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,5 +2,9 @@ tests dist build env* +venv* *.egg-info models_eynollah* +reloaded +*.h5 +config_files* diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index a0e6052..1d15ee2 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -328,6 +328,11 @@ class EynollahModelZoo: gpu = int(device[3:] or "0") else: gpu = 0 # try first allowable + # make runtime-configurable + if override_providers := os.environ.get('EYNOLLAH_ONNX_EP', ''): + override_providers = override_providers.split(',') + providers = [provider for provider in providers + if provider[:-17] in override_providers] # configure and prioritise if 'CUDAExecutionProvider' in providers: providers.remove('CUDAExecutionProvider') diff --git a/src/eynollah/predictor.py b/src/eynollah/predictor.py index 9121ffe..2d892c7 100644 --- a/src/eynollah/predictor.py +++ b/src/eynollah/predictor.py @@ -208,9 +208,9 @@ class Predictor(mp.context.SpawnProcess): else: multi_output = False results = np.split(make_shareable(result), len(jobs)) - #self.logger.debug("sharing result array for '%d'", jobid) with ExitStack() as stack: for jobid, result in zip(jobs, results): + #self.logger.debug("sharing result array for '%d'", jobid) # we don't know when the result will be received, # but don't want to wait either, so track closing # context per job, and wait for closable signal From 12be9834872f511f61a2c17b836ce2a8988d2898 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Sun, 12 Jul 2026 03:49:41 +0200 Subject: [PATCH 090/121] =?UTF-8?q?cnn-rnn-ocr=20inference:=20switch=20bac?= =?UTF-8?q?k=20to=20beam=20search,=20only=20run=20in=20TF=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - training.models.CTCDecoder: prefer beam search over greedy (because it is more accurate) - training reload makefile: skip ONNX and TF-Serving conversion for cnn-rnn-ocr models (because these would not work) - training reload makefile: default to onnx and tf conversions for all models (tf for training and onnx for inference) instead of tf-serving export --- src/eynollah/training/models.py | 4 +++- src/eynollah/training/reload-models-v0.8.mk | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/eynollah/training/models.py b/src/eynollah/training/models.py index eda6b0f..9076f0a 100644 --- a/src/eynollah/training/models.py +++ b/src/eynollah/training/models.py @@ -85,9 +85,11 @@ class CTCDecoder(Layer): # tf.compat.v1.nn.ctc_beam_search_decoder() also needs merge_repeated=False # tf.nn.ctc_beam_search_decoder() is not supported by ONNX, yet # tf.nn.ctc_greedy_decoder() is not as precise, though: - decoded, logits = tf.nn.ctc_greedy_decoder( + decoded, logits = tf.nn.ctc_beam_search_decoder( inputs, lengths, + beam_width=10, + top_paths=1 ) # get top path for all sequences in batch decoded = decoded[0] diff --git a/src/eynollah/training/reload-models-v0.8.mk b/src/eynollah/training/reload-models-v0.8.mk index 342d211..eab5ffb 100644 --- a/src/eynollah/training/reload-models-v0.8.mk +++ b/src/eynollah/training/reload-models-v0.8.mk @@ -23,7 +23,9 @@ CURRENT_MODELS += eynollah-binarization_20210425 CURRENT_MODELS += eynollah-column-classifier_20210425 CURRENT_MODELS += eynollah-enhancement_20210425 -all: tf-serving +# tf (SavedModel format) for training +# onnx conversion for fast inference +all: tf onnx tf-serving: $(CURRENT_MODELS:%=$(MODELS_DST)/%) tf: $(CURRENT_MODELS:%=$(MODELS_DST)/%) @@ -31,8 +33,11 @@ keras: $(CURRENT_MODELS:%=$(MODELS_DST)/%.keras) hdf5: $(CURRENT_MODELS:%=$(MODELS_DST)/%.h5) onnx: $(CURRENT_MODELS:%=$(MODELS_DST)/%.onnx) -$(MODELS_DST)/%: FORMAT = $(or $(filter tf,$(MAKECMDGOALS)), tf-serving) +# distinguish tf from tf-serving: target pattern is the same, +# so check if either is current goal, otherwise assumg tf +$(MODELS_DST)/%: FORMAT = $(or $(filter tf-serving,$(MAKECMDGOALS)), tf) $(MODELS_DST)/%: $(MODELS_SRC)/% + $(if $(and $(filter tf-serving,$(FORMAT)),$(findstring _ocr,$@)),$(warning skipping $@: OCR CTC decoder fails in TF-Serving) : )\ eynollah-training convert \ $(and $(wildcard $ $(notdir $<).hdf5.log 2>&1 || { cat $(notdir $<).hdf5.log; false; } $(MODELS_DST)/%.onnx: $(MODELS_SRC)/% + $(if $(findstring _ocr,$@),$(warning skipping $@: OCR CTC decoder is buggy in ONNX) : )\ eynollah-training convert \ $(and $(wildcard $ Date: Tue, 14 Jul 2026 15:58:00 +0200 Subject: [PATCH 091/121] Dockerfile: do not install OCR (as Torch and ONNX clash over CUDA) --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d0b41a5..a041807 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,7 +39,8 @@ RUN ocrd ocrd-tool ocrd-tool.json dump-tools > $(dirname $(ocrd bashlib filename # prepackage ocrd-all-module-dir.json RUN ocrd ocrd-tool ocrd-tool.json dump-module-dirs > $(dirname $(ocrd bashlib filename))/ocrd-all-module-dir.json # install everything and reduce image size -RUN make install EXTRAS=OCR && rm -rf /build/eynollah +# FIXME: EXTRAS=OCR (should become extra Dockerfile based on ocrd/core-cuda-tf2 and ocrd/core-cuda-torch) +RUN make install && rm -rf /build/eynollah # fixup for broken cuDNN installation (Torch may pull in version which is incompatible with Tensorflow) RUN pip install "nvidia-cudnn-cu12<9.10.2.21" # smoke test From 1591d2091c069e488a8fde0b3488a4d8148f1831 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Tue, 14 Jul 2026 16:22:47 +0200 Subject: [PATCH 092/121] update docs --- docs/ocrd.md | 4 ++-- docs/usage.md | 66 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/docs/ocrd.md b/docs/ocrd.md index 9e7e268..fc68a31 100644 --- a/docs/ocrd.md +++ b/docs/ocrd.md @@ -5,7 +5,7 @@ formally described in [`ocrd-tool.json`](https://github.com/qurator-spk/eynollah When using Eynollah in OCR-D, the source image file group with (preferably) RGB images should be used as input like this: - ocrd-eynollah-segment -I OCR-D-IMG -O OCR-D-SEG -P models eynollah_layout_v0_5_0 + ocrd-eynollah-segment -I OCR-D-IMG -O OCR-D-SEG -P models eynollah_layout_v0_9_0 If the input file group is PAGE-XML (from a previous OCR-D workflow step), Eynollah behaves as follows: - existing regions are kept and ignored (i.e. in effect they might overlap segments from Eynollah results) @@ -17,7 +17,7 @@ If the input file group is PAGE-XML (from a previous OCR-D workflow step), Eynol (because some other preprocessing step was in effect like `denoised`), then the output PAGE-XML will be based on that as new top-level (`@imageFilename`) - ocrd-eynollah-segment -I OCR-D-XYZ -O OCR-D-SEG -P models eynollah_layout_v0_5_0 + ocrd-eynollah-segment -I OCR-D-XYZ -O OCR-D-SEG -P models eynollah_layout_v0_9_0 In general, it makes more sense to add other workflow steps **after** Eynollah. diff --git a/docs/usage.md b/docs/usage.md index da164de..2f9a688 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -3,49 +3,69 @@ The command-line interface can be called like this: ```sh eynollah \ + [GENERIC_OPTIONS] \ + layout \ -i | -di \ -o \ - -m \ - [OPTIONS] + [LAYOUT_OPTIONS] ``` -## Processing options -The following options can be used to further configure the processing: +## Generic options +Pass any of the following options: -| option | description | +| **option** | **description** | +|---------------------------------------------------|:----------------------------------------------------------------| +| -m | override default directory `$PWD/models_eynollah` | +| -mv | override specific models, e.g. `region_1_2 '' /path/to/my.onnx` | +| -D | allocate models to GPUs, e.g. `col*:CPU,page:GPU1,*:GPU0` | +| -l | override default `INFO` log level e.g. `DEBUG` | + +## Processing options +The following options can be used to further control layout analysis: + +| **option** | **description** | |-------------------|:-------------------------------------------------------------------------------| -| `-fl` | full layout analysis including all steps and segmentation classes | -| `-light` | lighter and faster but simpler method for main region detection and deskewing | +| `-fl` | full layout analysis including all steps and segmentation classes (recommended)| | `-tab` | apply table detection | | `-ae` | apply enhancement (the resulting image is saved to the output directory) | | `-as` | apply scaling | -| `-cl` | apply contour detection for curved text lines instead of bounding boxes | +| `-cl` | apply contour detection for curved text lines, deskewing regions independently | | `-ib` | apply binarization (the resulting image is saved to the output directory) | | `-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 ` | save image regions detected to this directory | | `-sd ` | save deskewed image to this directory | | `-sl ` | save layout prediction as plot to this directory | | `-sp ` | save cropped page image to this directory | | `-sa ` | save all (plot, enhanced/binary image, layout) to this directory | +| `-thart` | confidence threshold of artifical boundary class during textline detection | +| `-tharl` | confidence threshold of artifical boundary class during region detection | +| `-ncu` | upper limit of columns in document image | +| `-ncl` | lower limit of columns in document image | +| `-slro` | skip layout detection and reading order | +| `-romb` | apply machine based reading order detection | +| `-ipe` | ignore page extraction | +| `-j` | number of CPU jobs to run parallel (useful with -di) | +| `-H` | when to halt if some jobs fail, e.g. `0.1` for 10% or `3` for 3 pages | If no option is set, the tool performs detection of main regions (background, text, images, separators and marginals). -### `--full-layout` vs `--no-full-layout` +### `--full-layout` vs shallow -Here are the difference in elements detected depending on the `--full-layout`/`--no-full-layout` command line flags: +Here are the differences in segment types detected: -| | `--full-layout` | `--no-full-layout` | -|--------------------------|-----------------|--------------------| -| reading order | x | x | -| header regions | x | - | -| text regions | x | x | -| text regions / text line | x | x | -| drop-capitals | x | - | -| marginals | x | x | -| marginals / text line | x | x | -| image region | x | x | +| | `-fl` | without | +|--------------------------|-------|---------| +| reading order | x | x | +| header regions | x | - | +| text regions | x | x | +| text regions / textlines | x | x | +| drop-capitals | x | - | +| marginals | x | x | +| marginals / textlines | x | x | +| image regions | x | x | + +(Note: No marginals are detected for pages with 3 columns or more.) ## Use as OCR-D processor Eynollah ships with a CLI interface to be used as [OCR-D](https://ocr-d.de) processor that is described in @@ -54,13 +74,13 @@ Eynollah ships with a CLI interface to be used as [OCR-D](https://ocr-d.de) proc The source image file group with (preferably) RGB images should be used as input for Eynollah like this: ``` -ocrd-eynollah-segment -I OCR-D-IMG -O SEG-LINE -P models +ocrd-eynollah-segment -I OCR-D-IMG -O SEG-LINE -P full_layout true ``` Any image referenced by `@imageFilename` in PAGE-XML is passed on directly to Eynollah as a processor, so that e.g. ``` -ocrd-eynollah-segment -I OCR-D-IMG-BIN -O SEG-LINE -P models +ocrd-eynollah-segment -I OCR-D-IMG-BIN -O SEG-LINE -P full_layout true ``` uses the original (RGB) image despite any binarization that may have occured in previous OCR-D processing steps. From c43e2198584031fc2065e2eaceae52c305548037 Mon Sep 17 00:00:00 2001 From: kba Date: Tue, 14 Jul 2026 19:02:52 +0200 Subject: [PATCH 093/121] model packaging/uploading --- .gitignore | 5 ++ models/Makefile | 142 ++++++++++++++++++++++++++++++++ models/scripts/convert.sh | 6 ++ models/scripts/plot.py | 62 ++++++++++++++ models/scripts/run.sh | 34 ++++++++ models/scripts/zenodo_upload.sh | 71 ++++++++++++++++ 6 files changed, 320 insertions(+) create mode 100644 models/Makefile create mode 100644 models/scripts/convert.sh create mode 100644 models/scripts/plot.py create mode 100644 models/scripts/run.sh create mode 100755 models/scripts/zenodo_upload.sh diff --git a/.gitignore b/.gitignore index e3356ea..5aa9023 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,8 @@ TAGS uv.lock /ignore *.log +.env +models/reloaded +models/packages +models/models_eynollah +models/pretrained_model diff --git a/models/Makefile b/models/Makefile new file mode 100644 index 0000000..c80571a --- /dev/null +++ b/models/Makefile @@ -0,0 +1,142 @@ +SHELL = bash -e + +VERSION = v0_9_0 +MODELS_SRC = models_eynollah +MODELS_DST = reloaded/models_eynollah + + +# eynollah-main-regions-aug-rotation_20210425 +# eynollah-main-regions-aug-scaling_20210425 +# eynollah-main-regions-ensembled_20210425 +# eynollah-main-regions_20220314 +# eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 +# eynollah-tables_20210319 + +CURRENT_MODELS := +CURRENT_MODELS += eynollah-main-regions-aug-rotation_20210425 +CURRENT_MODELS += eynollah-main-regions-aug-scaling_20210425 +CURRENT_MODELS += eynollah-main-regions-ensembled_20210425 +CURRENT_MODELS += eynollah-main-regions_20220314 +CURRENT_MODELS += eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 +CURRENT_MODELS += eynollah-tables_20210319 +CURRENT_MODELS += eynollah-main-regions_20220314 +CURRENT_MODELS += model_eynollah_page_extraction_20250915 +CURRENT_MODELS += model_eynollah_reading_order_20250824 +CURRENT_MODELS += modelens_e_l_all_sp_0_1_2_3_4_171024 +CURRENT_MODELS += modelens_full_lay_1__4_3_091124 +CURRENT_MODELS += modelens_table_0t4_201124 +CURRENT_MODELS += modelens_textline_0_1__2_4_16092024 +CURRENT_MODELS += model_eynollah_ocr_cnnrnn_20250930 +CURRENT_MODELS += eynollah-binarization_20210425 +CURRENT_MODELS += eynollah-column-classifier_20210425 +CURRENT_MODELS += eynollah-enhancement_20210425 + +help: + @echo "Targets:" + @echo "" + @echo "Model conversion:" + @echo " all Convert all current models to TensorFlow SavedModel (default)" + @echo " tf[-serving] Convert all current models to TensorFlow SavedModel" + @echo " keras Convert all current models to Keras format (.keras)" + @echo " hdf5 Convert all current models to HDF5 format (.h5)" + @echo " onnx Convert all eligible current models to ONNX (.onnx)" + @echo " reload Reload selected pre-v0.8 models without Lambda layers" + @echo "" + @echo "Model reloading:" + @echo " compare Compare original and v0.8 reloaded models" + @echo "" + @echo "Packaging:" + @echo " packages Build all Zenodo distribution archives" + @echo " upload Upload all distribution archives to Zenodo" + @echo "" + @echo "Cleanup:" + @echo " clean Remove all generated files" + @echo " clean-reload Remove reloaded model directories" + @echo " clean-packages Remove generated package archives" + @echo "" + @echo "Variables:" + @echo " VERSION=$(VERSION)" + + +# tf (SavedModel format) for training +# onnx conversion for fast inference +all: tf onnx + +tf-serving: $(CURRENT_MODELS:%=$(MODELS_DST)/%) +tf: $(CURRENT_MODELS:%=$(MODELS_DST)/%) +keras: $(CURRENT_MODELS:%=$(MODELS_DST)/%.keras) +hdf5: $(CURRENT_MODELS:%=$(MODELS_DST)/%.h5) +onnx: $(CURRENT_MODELS:%=$(MODELS_DST)/%.onnx) + +# distinguish tf from tf-serving: target pattern is the same, +# so check if either is current goal, otherwise assumg tf +$(MODELS_DST)/%: FORMAT = $(or $(filter tf-serving,$(MAKECMDGOALS)), tf) +$(MODELS_DST)/%: $(MODELS_SRC)/% + $(if $(and $(filter tf-serving,$(FORMAT)),$(findstring _ocr,$@)),$(warning skipping $@: OCR CTC decoder fails in TF-Serving) : )\ + eynollah-training convert \ + $(and $(wildcard $ $(notdir $<).$(FORMAT).log 2>&1 || { cat $(notdir $<).$(FORMAT).log; false; } + +$(MODELS_DST)/%.keras: $(MODELS_SRC)/% + eynollah-training convert \ + $(and $(wildcard $ $(notdir $<).keras.log 2>&1 || { cat $(notdir $<).keras.log; false; } + +$(MODELS_DST)/%.h5: $(MODELS_SRC)/% + eynollah-training convert \ + $(and $(wildcard $ $(notdir $<).hdf5.log 2>&1 || { cat $(notdir $<).hdf5.log; false; } + +$(MODELS_DST)/%.onnx: $(MODELS_SRC)/% + $(if $(findstring _ocr,$@),$(warning skipping $@: OCR CTC decoder is buggy in ONNX) : )\ + eynollah-training convert \ + $(and $(wildcard $ $(notdir $<).onnx.log 2>&1 || { cat $(notdir $<).onnx.log; false; } + +compare: + for i in `find $(MODELS_DST) -mindepth 2`;do \ + n=$(MODELS_SRC)$${i#$(MODELS_DST)}; \ + du -bs $$n $$i ; \ + done + +clean: clean-reload clean-packages + +clean-reload: + rm -rf $(RELOADABLE_MODELS) + +clean-packages: + rm -rf $(PACKAGES_DIR) + +# NB: this works using symlinks +PACKAGES_DIR = packages +BUNDLES = inference training +CATEGORIES = layout ocr extra all + +packages: $(foreach B,$(BUNDLES),$(foreach C,$(CATEGORIES),$(PACKAGES_DIR)/models_$(B)_$(C)_$(VERSION).zip)) + +$(PACKAGES_DIR)/models_%.zip: + @mkdir -p $(PACKAGES_DIR) ;\ + bundle=$(word 1,$(subst _, ,$*)); \ + category=$(word 2,$(subst _, ,$*)); \ + echo "Packaging $$bundle/$$category to $(notdir $@)" ;\ + cd "dist/$$bundle/$$category" && \ + zip -vqr "$$OLDPWD/$@" models_eynollah + + +upload: $(foreach B,$(BUNDLES),$(foreach C,$(CATEGORIES),upload/$(B)_$(C))) + +upload/%: + @source .env ;\ + bash scripts/zenodo_upload.sh $$ZENODO_ID $(PACKAGES_DIR)/models_$*_$(VERSION).zip --verbose diff --git a/models/scripts/convert.sh b/models/scripts/convert.sh new file mode 100644 index 0000000..225785f --- /dev/null +++ b/models/scripts/convert.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +for i in */models_eynollah/*;do + rslv=$(readlink $i|sed 's,models_eynollah,reloaded/models_eynollah,'); + ln -srf $rslv $i; +done diff --git a/models/scripts/plot.py b/models/scripts/plot.py new file mode 100644 index 0000000..2543860 --- /dev/null +++ b/models/scripts/plot.py @@ -0,0 +1,62 @@ +import os +import sys +import more_itertools +import numpy as np +import argparse + +parser = argparse.ArgumentParser( + prog='plot', + description='Print metrics', + epilog='...') +parser.add_argument('stem1', default='nohup.out.eynollah-206-noautosize-keepseps-refactoring9-crop-after2') +parser.add_argument('stem2', default='eval-gt2-onlyfg-pixel-f1-v0.5-206-noautosize-keepseps-refactoring9-crop-after2') + +def main(): + args = parser.parse_args() + + prefix = os.path.dirname(sys.argv[0]) + files = [path for path in os.listdir(prefix) + if path.startswith(args.stem1) + and path.endswith('.txt')] + + for path in files: + name = path[len(args.stem1):] + metric = name.split('.')[-2] + series = name[:-(len(metric) + 5)] + path = os.path.join(prefix, path) + + if metric == 'peak-vram': + cat, peak = np.loadtxt(path, unpack=True, dtype=np.dtype([("category", "U15"), ("peak", float)])) + peak //= 1024 + peak //= 1024 + print(metric, f"{series: <52}", dict(zip(cat.tolist(), peak.tolist()))) + else: + val = np.loadtxt(path) + print(metric, f"{series: <52}", {'µ': float(np.round(np.mean(val), 2)), + 'M': float(np.round(np.median(val), 2)), + 'min': float(np.min(val)), + 'max': float(np.max(val))}) + + + files = [path for path in os.listdir(prefix) + if path.startswith(args.stem2) + and path.endswith('.log')] + + + for path in files: + name = path[len(args.stem2):] + series = name[:-4] + path = os.path.join(prefix, path) + with open(path, 'r') as fd: + lines = fd.readlines() + result_v0_5 = {} + result_v206 = {} + for metric, v0_5, v206 in more_itertools.chunked(lines, n=3, strict=True): + metric = metric[len(prefix) + 1:-1] + result_v0_5[metric] = float(np.round(float(v0_5.strip()), 3)) + result_v206[metric] = float(np.round(float(v206.strip()), 3)) + print("pixel-f1", f"{series: <52}", result_v206) + + print("pixel-f1", "v0.5 ", result_v0_5) + + diff --git a/models/scripts/run.sh b/models/scripts/run.sh new file mode 100644 index 0000000..79cd4e6 --- /dev/null +++ b/models/scripts/run.sh @@ -0,0 +1,34 @@ +for variant in reloaded.*; do + +for dir in Korrigierte_Layout_GT/*/; do test $dir = ${dir/.} || continue; out=${dir%/}.eynollah-206-noautosize; mkdir -p $out; nohup /usr/bin +/time eynollah -D GPU0 -m $variant layout --dir_in $dir -o + $out -fl -H 1 -O || break; done + +logfile=Korrigierte_Layout_GT/nohup.out.eynollah-206-noautosize-$variant + +mv nohup.out $logfile + +fgrep peaked $logfile | sort -u | cut -d\ -f2,5 > $logfile.peak-vram.txt +fgrep initialization $logfile | sed "s/^.* (\([0-9.]*\)s)/\1/" > $logfile.secs-init.txt +fgrep "Job done in" $logfile | sed "s/^.* //;s/s$//" > $logfile.secs-jobs.txt +fgrep %CPU $logfile | sed "s/^.* \([0-9]*\)%CPU.*/\1/" > $logfile.prct-cpu.txt + +for dir in Korrigierte_Layout_GT/*.eynollah*/; do + test $dir = ${dir/-eval} || continue; + test $dir != ${dir/eynollah-206} || continue; + mets=${dir%.eynollah*}.mets.xml; + dir=$(basename $dir); + gt=${dir%.eynollah*}.gt2; + echo $mets: $dir; + ocrd-segment-evaluate -m $mets -I $gt,$dir -O $dir-eval,$dir-eval2 -P level-of-operation region -P ignore-subtype true -P only-fg true --overwrite; +done + +for mets in Korrigierte_Layout_GT/*.mets.xml; do dir=${mets%.mets.xml}; echo $dir; jq '."by-category".TextRegion."pixel-f1".avg' $dir.eynollah-v0.5-eval/*.json $dir.eynollah-206-noautosize-eval/*.json; done > Korrigierte_Layout_GT/eval-gt2-onlyfg-pixel-f1-v0.5-206-noautosize-$variant.log + + +done + + + +python Korrigierte_Layout_GT/plot.py | sort -V + diff --git a/models/scripts/zenodo_upload.sh b/models/scripts/zenodo_upload.sh new file mode 100755 index 0000000..da67298 --- /dev/null +++ b/models/scripts/zenodo_upload.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# +# Upload big files to Zenodo. +# +# usage: ./zenodo_upload.sh [deposition id] [filename] [--verbose|-v] +# +# Taken from https://github.com/jhpoelen/zenodo-upload +# +# Copyright (c) 2019 Jorrit Poelen +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +set -e + +VERBOSE=0 +if [ "$3" == "--verbose" ] || [ "$3" == "-v" ]; then + VERBOSE=1 +fi + +# strip deposition url prefix if provided; see https://github.com/jhpoelen/zenodo-upload/issues/2#issuecomment-797657717 +DEPOSITION=$( echo $1 | sed 's+^http[s]*://zenodo.org/deposit/++g' ) +FILEPATH="$2" +FILENAME=$(echo $FILEPATH | sed 's+.*/++g') +FILENAME=${FILENAME// /%20} +ZENODO_ENDPOINT=${ZENODO_ENDPOINT:-https://zenodo.org} + +DEPOSIT_URL=${ZENODO_ENDPOINT}/api/deposit/depositions/"$DEPOSITION"?access_token="$ZENODO_TOKEN" +BUCKET=$(curl $DEPOSIT_URL | jq --raw-output .links.bucket) + +if [ "$VERBOSE" -eq 1 ]; then + echo "Deposition ID: $DEPOSITION" + echo "File path: $FILEPATH" + echo "File name: $FILENAME" + echo "Bucket URL: $BUCKET" + echo "Upload URL: $BUCKET/$FILENAME" + echo "Deposit URL: $DEPOSIT_URL" + echo "Uploading file..." +fi + +set -x +curl --progress-bar \ + --retry 5 \ + --retry-delay 5 \ + -o ${FILENAME}.upload.log \ + -H "Authorization: Bearer $ZENODO_TOKEN" \ + --upload-file "$FILEPATH" \ + "$BUCKET/$FILENAME" + +# curl -i \ +# -H "Authorization: Bearer $ZENODO_TOKEN" \ +# -X POST \ +# -F name=$FILENAME \ +# -F file=@$FILEPATH \ +# https://zenodo.org/api/deposit/depositions/$DEPOSITION/files From b4165114ea6edc478597668c65b7ef224419f237 Mon Sep 17 00:00:00 2001 From: kba Date: Tue, 14 Jul 2026 21:00:09 +0200 Subject: [PATCH 094/121] update zenodo links to v0_9_0 models --- Makefile | 2 +- README.md | 6 +++--- src/eynollah/model_zoo/default_specs.py | 4 ++-- src/eynollah/ocrd-tool.json | 16 ++++++++++++++++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index e4ac5c6..eb6353a 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ WGET = wget -O #SEG_MODEL := https://github.com/qurator-spk/eynollah/releases/download/v0.3.0/models_eynollah.tar.gz #SEG_MODEL := https://github.com/qurator-spk/eynollah/releases/download/v0.3.1/models_eynollah.tar.gz #SEG_MODEL := https://zenodo.org/records/17194824/files/models_layout_v0_5_0.tar.gz?download=1 -EYNOLLAH_MODELS_URL := https://zenodo.org/records/17727267/files/models_all_v0_8_0.zip +EYNOLLAH_MODELS_URL := https://zenodo.org/records/21362927/files/models_inference_all_v0_9_0.zip EYNOLLAH_MODELS_ZIP = $(notdir $(EYNOLLAH_MODELS_URL)) EYNOLLAH_MODELS_DIR = $(EYNOLLAH_MODELS_ZIP:%.zip=%) diff --git a/README.md b/README.md index bf80bca..be21a7a 100644 --- a/README.md +++ b/README.md @@ -74,11 +74,11 @@ When using Eynollah with Docker, see [`docker.md`](https://github.com/qurator-sp ## Models -Pretrained models can be downloaded from [Zenodo](https://zenodo.org/records/17727267) or [Hugging Face](https://huggingface.co/SBB?search_models=eynollah). +Pretrained models can be downloaded from [Zenodo](https://doi.org/10.5281/zenodo.17194823) or [Hugging Face](https://huggingface.co/SBB?search_models=eynollah). -For fast runtime inference, download the ONNX models. +For fast runtime inference, download the ONNX models distributed as `models_inference_...zip`. -For finetuning training, download the original (Tensorflow / Torch) models +For finetuning training, download the original (Tensorflow / Torch) models distributed as `models_training...zip` (and install the `[training]` extra). For model documentation and model cards, see [`models.md`](https://github.com/qurator-spk/eynollah/tree/main/docs/models.md). diff --git a/src/eynollah/model_zoo/default_specs.py b/src/eynollah/model_zoo/default_specs.py index 18bf093..2b1196b 100644 --- a/src/eynollah/model_zoo/default_specs.py +++ b/src/eynollah/model_zoo/default_specs.py @@ -1,8 +1,8 @@ from .specs import EynollahModelSpec, EynollahModelSpecSet # NOTE: This needs to change whenever models/versions change -ZENODO = "https://zenodo.org/records/17727267" -MODELS_VERSION = "v0_8_0" +ZENODO = "https://zenodo.org/records/21362927" +MODELS_VERSION = "v0_9_0" def dist_url(dist_name: str="layout") -> str: return f'{ZENODO}/models_{dist_name}_{MODELS_VERSION}.zip' diff --git a/src/eynollah/ocrd-tool.json b/src/eynollah/ocrd-tool.json index 89ed0da..38a129d 100644 --- a/src/eynollah/ocrd-tool.json +++ b/src/eynollah/ocrd-tool.json @@ -84,6 +84,22 @@ } }, "resources": [ + { + "url": "https://zenodo.org/records/21362927/files/models_inference_all_v0_9_0.zip", + "name": "models_inference_all_v0_9_0", + "type": "archive", + "size": 5177204769, + "description": "Models for layout detection, reading order detection, textline detection, page extraction, column classification, table detection, binarization and image enhancement", + "version_range": ">= v0.9.0" + }, + { + "url": "https://zenodo.org/records/21362927/files/models_inference_all_v0_9_0.zip", + "name": "models_inference_all_v0_9_0", + "type": "archive", + "size": 5177204769, + "description": "Models for layout detection, reading order detection, textline detection, page extraction, column classification, table detection, binarization and image enhancement", + "version_range": ">= v0.9.0" + }, { "url": "https://zenodo.org/records/17727267/files/models_all_v0_8_0.zip", "name": "models_all_v0_8_0", From c5713e010e0caf61e647b5aa308b24705ffd6d68 Mon Sep 17 00:00:00 2001 From: kba Date: Tue, 14 Jul 2026 21:04:06 +0200 Subject: [PATCH 095/121] deps: OCR requires explicit dep on tensorflow/keras now --- requirements-ocr.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements-ocr.txt b/requirements-ocr.txt index dad26f4..767a9a6 100644 --- a/requirements-ocr.txt +++ b/requirements-ocr.txt @@ -1,3 +1,5 @@ torch transformers <= 4.30.2 ; python_version < '3.10' transformers >= 5 ; python_version >= '3.10' +tensorflow < 2.16 # for tensorflow-addons, so only needed in training +tf-keras < 2.16 # avoid keras 3 (also needs TF_USE_LEGACY_KERAS=1) From 839d7b9a7e1340cbc01bc0dd8ba03c1a02ab2408 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 15 Jul 2026 04:09:33 +0200 Subject: [PATCH 096/121] ModelZoo: avoid Azure EP (displacing CPU) --- src/eynollah/model_zoo/model_zoo.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 1d15ee2..5a0a867 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -334,6 +334,8 @@ class EynollahModelZoo: providers = [provider for provider in providers if provider[:-17] in override_providers] # configure and prioritise + if 'AzureExecutionProvider' in providers: + providers.remove('AzureExecutionProvider') if 'CUDAExecutionProvider' in providers: providers.remove('CUDAExecutionProvider') if gpu >= 0: From efe4aa8b0c1f9f57774be54d498451244e5f26b3 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 15 Jul 2026 04:10:47 +0200 Subject: [PATCH 097/121] mbreorder: fix init (wrong way to load model) --- src/eynollah/mb_ro_on_layout.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/eynollah/mb_ro_on_layout.py b/src/eynollah/mb_ro_on_layout.py index 6c0477b..5bab608 100644 --- a/src/eynollah/mb_ro_on_layout.py +++ b/src/eynollah/mb_ro_on_layout.py @@ -42,7 +42,6 @@ class Reorder(Eynollah): self.logger = logger or logging.getLogger('eynollah.mbreorder') self.model_zoo = model_zoo - self.model_zoo.load_model('reading_order') self.setup_models(device=device) def setup_models(self, device=''): From 25865372d0a7b3f7fabacd68fb02ab8689bc94db Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 15 Jul 2026 04:11:29 +0200 Subject: [PATCH 098/121] dependencies for `[OCR]`: add TF, avoid newer Torch (pulling CUDA 13) --- requirements-ocr.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements-ocr.txt b/requirements-ocr.txt index dad26f4..0825e18 100644 --- a/requirements-ocr.txt +++ b/requirements-ocr.txt @@ -1,3 +1,5 @@ -torch +torch < 2.11 # avoid pull CUDA 13 (which will clash with 12) transformers <= 4.30.2 ; python_version < '3.10' transformers >= 5 ; python_version >= '3.10' +tensorflow[and-cuda] +tf-keras # avoid keras 3 (also needs TF_USE_LEGACY_KERAS=1) From b89e1b42965e18b4544d91a79954e8bced2c4ab6 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 15 Jul 2026 04:24:33 +0200 Subject: [PATCH 099/121] ocrd-tool.json: differentiate inference-all and inference-layout --- src/eynollah/ocrd-tool.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/eynollah/ocrd-tool.json b/src/eynollah/ocrd-tool.json index 38a129d..48c1740 100644 --- a/src/eynollah/ocrd-tool.json +++ b/src/eynollah/ocrd-tool.json @@ -89,14 +89,14 @@ "name": "models_inference_all_v0_9_0", "type": "archive", "size": 5177204769, - "description": "Models for layout detection, reading order detection, textline detection, page extraction, column classification, table detection, binarization and image enhancement", + "description": "Models for layout detection, reading order detection, textline detection, page extraction, column classification, table detection, binarization and image enhancement, as well as OCR", "version_range": ">= v0.9.0" }, { - "url": "https://zenodo.org/records/21362927/files/models_inference_all_v0_9_0.zip", - "name": "models_inference_all_v0_9_0", + "url": "https://zenodo.org/records/21362927/files/models_inference_layout_v0_9_0.zip", + "name": "models_inference_layout_v0_9_0", "type": "archive", - "size": 5177204769, + "size": 1572255489, "description": "Models for layout detection, reading order detection, textline detection, page extraction, column classification, table detection, binarization and image enhancement", "version_range": ">= v0.9.0" }, From 0ab6e19f33abbe021a37221a948b3fd81931a301 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 15 Jul 2026 12:46:13 +0200 Subject: [PATCH 100/121] configure ruff search path --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index e6821a5..fdd41db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,6 +67,7 @@ source = ["eynollah"] [tool.ruff] line-length = 120 +include = ["pyproject.toml", "src/eynollah/**/*.py"] [tool.ruff.lint] ignore = [ From 4d97e3bf7ff3de7e6aa8a31ff8cdaf61a583ce33 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 15 Jul 2026 12:46:33 +0200 Subject: [PATCH 101/121] training: fix typos found by ruff --- src/eynollah/training/utils.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/eynollah/training/utils.py b/src/eynollah/training/utils.py index e84ac66..d04a17b 100644 --- a/src/eynollah/training/utils.py +++ b/src/eynollah/training/utils.py @@ -1127,13 +1127,21 @@ def preprocess_img_ocr( if task == 'cnn-rnn-ocr': assert char_to_num, 'task is cnn-rnn-ocr, so preprocess_imgs_ocr should be passed "char_to_num"' lab = char_to_num(tf.strings.unicode_split(lab, input_encoding="UTF-8")) - yield_encoder = lambda x: x + def yield_encoder(x): + return x elif task == 'transformer-ocr': + import torch assert processor, 'task is transformer-ocr, so preprocess_imgs_ocr should be passed "processor"' # TODO make max_length configurable again, if deemed sensible - lab = [l if l != self.processor.tokenizer.pad_token_id else -100 - for l in processor.tokenizer(lab, padding="max_length", max_length=128).input_ids] - yield_encoder = lambda img_, lab_: {"pixel_values": processor(Image.fromarray(img_), return_tensors="pt").pixel_values.squeeze(), "labels": torch.tensor(lab_)} + lab = [tok if tok != processor.tokenizer.pad_token_id else -100 + for tok in processor.tokenizer(lab, + padding="max_length", + max_length=128 + ).input_ids] + def yield_encoder(img_, lab_): + return {"pixel_values": processor(Image.fromarray(img_), + return_tensors="pt").pixel_values.squeeze(), + "labels": torch.tensor(lab_)} yield yield_encoder(scale_image(img), lab) #to_yield = {"image": ret_x, "label": ret_y} From 9804d736ac06e6aa99764559b4f25fe4dc48428c Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Wed, 15 Jul 2026 13:59:43 +0200 Subject: [PATCH 102/121] CI: avoid CUDA dependencies here --- .github/workflows/test-eynollah.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test-eynollah.yml b/.github/workflows/test-eynollah.yml index 82de94d..44910ce 100644 --- a/.github/workflows/test-eynollah.yml +++ b/.github/workflows/test-eynollah.yml @@ -64,6 +64,9 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip + # preempt CUDA dependencies (which need core's recipe) + pip install onnxruntime tensorflow tf-keras "torch<2.11" + sed -i '/onnxruntime-gpu/d;/tensorrt/d;/torch/d;/tensorflow/d' requirements*.txt make install-dev EXTRAS=OCR,plotting make deps-test EXTRAS=OCR,plotting From 03915b24a53ca9123dac4e7493f9808edcac0931 Mon Sep 17 00:00:00 2001 From: kba Date: Wed, 15 Jul 2026 16:02:49 +0200 Subject: [PATCH 103/121] models: symlinks for the model packages for zenodo upload --- .gitignore | 5 +---- models/dist/inference/all/models_eynollah/characters_org.txt | 1 + .../all/models_eynollah/eynollah-binarization_20210309 | 1 + .../all/models_eynollah/eynollah-binarization_20210425.onnx | 1 + .../models_eynollah/eynollah-column-classifier_20210425.onnx | 1 + .../all/models_eynollah/eynollah-enhancement_20210425.onnx | 1 + .../eynollah-main-regions-aug-rotation_20210425.onnx | 1 + .../eynollah-main-regions-aug-scaling_20210425.onnx | 1 + .../eynollah-main-regions-ensembled_20210425.onnx | 1 + .../all/models_eynollah/eynollah-main-regions_20220314.onnx | 1 + ...lah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx | 1 + .../all/models_eynollah/eynollah-tables_20210319.onnx | 1 + models/dist/inference/all/models_eynollah/microsoft | 1 + .../all/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 | 1 + .../model_eynollah_ocr_cnnrnn__degraded_20250805 | 1 + .../all/models_eynollah/model_eynollah_ocr_trocr_20250919 | 1 + .../model_eynollah_reading_order_20250824.onnx | 1 + .../modelens_e_l_all_sp_0_1_2_3_4_171024.onnx | 1 + .../all/models_eynollah/modelens_full_lay_1__4_3_091124.onnx | 1 + .../all/models_eynollah/modelens_table_0t4_201124.onnx | 1 + .../models_eynollah/modelens_textline_0_1__2_4_16092024.onnx | 1 + .../extra/models_eynollah/eynollah-binarization_20210309 | 1 + .../models_eynollah/eynollah-binarization_20210425.onnx | 1 + .../eynollah-main-regions-aug-scaling_20210425.onnx | 1 + .../extra/models_eynollah/eynollah-tables_20210319.onnx | 1 + models/dist/inference/extra/models_eynollah/microsoft | 1 + .../models_eynollah/modelens_textline_0_1__2_4_16092024.onnx | 1 + .../models_eynollah/eynollah-column-classifier_20210425.onnx | 1 + .../models_eynollah/eynollah-enhancement_20210425.onnx | 1 + .../eynollah-main-regions-aug-rotation_20210425.onnx | 1 + .../eynollah-main-regions-aug-scaling_20210425.onnx | 1 + .../eynollah-main-regions-ensembled_20210425.onnx | 1 + .../models_eynollah/eynollah-main-regions_20220314.onnx | 1 + ...lah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx | 1 + .../model_eynollah_reading_order_20250824.onnx | 1 + .../modelens_e_l_all_sp_0_1_2_3_4_171024.onnx | 1 + .../models_eynollah/modelens_full_lay_1__4_3_091124.onnx | 1 + .../layout/models_eynollah/modelens_table_0t4_201124.onnx | 1 + .../models_eynollah/modelens_textline_0_1__2_4_16092024.onnx | 1 + models/dist/inference/ocr/models_eynollah/characters_org.txt | 1 + .../ocr/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 | 1 + .../model_eynollah_ocr_cnnrnn__degraded_20250805 | 1 + .../ocr/models_eynollah/model_eynollah_ocr_trocr_20250919 | 1 + models/dist/training/all/models_eynollah/characters_org.txt | 1 + .../models_eynollah/eynollah-binarization-hybrid_20230504 | 1 + .../all/models_eynollah/eynollah-binarization_20210309 | 1 + .../all/models_eynollah/eynollah-binarization_20210425 | 1 + .../all/models_eynollah/eynollah-column-classifier_20210425 | 1 + .../all/models_eynollah/eynollah-enhancement_20210425 | 1 + .../eynollah-main-regions-aug-rotation_20210425 | 1 + .../eynollah-main-regions-aug-scaling_20210425 | 1 + .../models_eynollah/eynollah-main-regions-ensembled_20210425 | 1 + .../all/models_eynollah/eynollah-main-regions_20220314 | 1 + ...eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 | 1 + .../training/all/models_eynollah/eynollah-tables_20210319 | 1 + .../all/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 | 1 + .../model_eynollah_ocr_cnnrnn__degraded_20250805 | 1 + .../all/models_eynollah/model_eynollah_ocr_trocr_20250919 | 1 + .../models_eynollah/model_eynollah_page_extraction_20250915 | 1 + .../models_eynollah/model_eynollah_reading_order_20250824 | 1 + .../all/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 | 1 + .../all/models_eynollah/modelens_full_lay_1__4_3_091124 | 1 + .../training/all/models_eynollah/modelens_table_0t4_201124 | 1 + .../all/models_eynollah/modelens_textline_0_1__2_4_16092024 | 1 + .../extra/models_eynollah/eynollah-binarization_20210309 | 1 + .../extra/models_eynollah/eynollah-binarization_20210425 | 1 + .../eynollah-main-regions-aug-scaling_20210425 | 1 + .../training/extra/models_eynollah/eynollah-tables_20210319 | 1 + models/dist/training/extra/models_eynollah/microsoft | 1 + .../models_eynollah/modelens_textline_0_1__2_4_16092024 | 1 + .../models_eynollah/eynollah-column-classifier_20210425 | 1 + .../layout/models_eynollah/eynollah-enhancement_20210425 | 1 + .../eynollah-main-regions-aug-rotation_20210425 | 1 + .../eynollah-main-regions-aug-scaling_20210425 | 1 + .../models_eynollah/eynollah-main-regions-ensembled_20210425 | 1 + .../layout/models_eynollah/eynollah-main-regions_20220314 | 1 + ...eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 | 1 + .../models_eynollah/model_eynollah_reading_order_20250824 | 1 + .../models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 | 1 + .../layout/models_eynollah/modelens_full_lay_1__4_3_091124 | 1 + .../layout/models_eynollah/modelens_table_0t4_201124 | 1 + .../models_eynollah/modelens_textline_0_1__2_4_16092024 | 1 + models/dist/training/ocr/models_eynollah/characters_org.txt | 1 + .../ocr/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 | 1 + .../model_eynollah_ocr_cnnrnn__degraded_20250805 | 1 + .../ocr/models_eynollah/model_eynollah_ocr_trocr_20250919 | 1 + 86 files changed, 86 insertions(+), 4 deletions(-) create mode 120000 models/dist/inference/all/models_eynollah/characters_org.txt create mode 120000 models/dist/inference/all/models_eynollah/eynollah-binarization_20210309 create mode 120000 models/dist/inference/all/models_eynollah/eynollah-binarization_20210425.onnx create mode 120000 models/dist/inference/all/models_eynollah/eynollah-column-classifier_20210425.onnx create mode 120000 models/dist/inference/all/models_eynollah/eynollah-enhancement_20210425.onnx create mode 120000 models/dist/inference/all/models_eynollah/eynollah-main-regions-aug-rotation_20210425.onnx create mode 120000 models/dist/inference/all/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx create mode 120000 models/dist/inference/all/models_eynollah/eynollah-main-regions-ensembled_20210425.onnx create mode 120000 models/dist/inference/all/models_eynollah/eynollah-main-regions_20220314.onnx create mode 120000 models/dist/inference/all/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx create mode 120000 models/dist/inference/all/models_eynollah/eynollah-tables_20210319.onnx create mode 120000 models/dist/inference/all/models_eynollah/microsoft create mode 120000 models/dist/inference/all/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 create mode 120000 models/dist/inference/all/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 create mode 120000 models/dist/inference/all/models_eynollah/model_eynollah_ocr_trocr_20250919 create mode 120000 models/dist/inference/all/models_eynollah/model_eynollah_reading_order_20250824.onnx create mode 120000 models/dist/inference/all/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024.onnx create mode 120000 models/dist/inference/all/models_eynollah/modelens_full_lay_1__4_3_091124.onnx create mode 120000 models/dist/inference/all/models_eynollah/modelens_table_0t4_201124.onnx create mode 120000 models/dist/inference/all/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx create mode 120000 models/dist/inference/extra/models_eynollah/eynollah-binarization_20210309 create mode 120000 models/dist/inference/extra/models_eynollah/eynollah-binarization_20210425.onnx create mode 120000 models/dist/inference/extra/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx create mode 120000 models/dist/inference/extra/models_eynollah/eynollah-tables_20210319.onnx create mode 120000 models/dist/inference/extra/models_eynollah/microsoft create mode 120000 models/dist/inference/extra/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx create mode 120000 models/dist/inference/layout/models_eynollah/eynollah-column-classifier_20210425.onnx create mode 120000 models/dist/inference/layout/models_eynollah/eynollah-enhancement_20210425.onnx create mode 120000 models/dist/inference/layout/models_eynollah/eynollah-main-regions-aug-rotation_20210425.onnx create mode 120000 models/dist/inference/layout/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx create mode 120000 models/dist/inference/layout/models_eynollah/eynollah-main-regions-ensembled_20210425.onnx create mode 120000 models/dist/inference/layout/models_eynollah/eynollah-main-regions_20220314.onnx create mode 120000 models/dist/inference/layout/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx create mode 120000 models/dist/inference/layout/models_eynollah/model_eynollah_reading_order_20250824.onnx create mode 120000 models/dist/inference/layout/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024.onnx create mode 120000 models/dist/inference/layout/models_eynollah/modelens_full_lay_1__4_3_091124.onnx create mode 120000 models/dist/inference/layout/models_eynollah/modelens_table_0t4_201124.onnx create mode 120000 models/dist/inference/layout/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx create mode 120000 models/dist/inference/ocr/models_eynollah/characters_org.txt create mode 120000 models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 create mode 120000 models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 create mode 120000 models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_trocr_20250919 create mode 120000 models/dist/training/all/models_eynollah/characters_org.txt create mode 120000 models/dist/training/all/models_eynollah/eynollah-binarization-hybrid_20230504 create mode 120000 models/dist/training/all/models_eynollah/eynollah-binarization_20210309 create mode 120000 models/dist/training/all/models_eynollah/eynollah-binarization_20210425 create mode 120000 models/dist/training/all/models_eynollah/eynollah-column-classifier_20210425 create mode 120000 models/dist/training/all/models_eynollah/eynollah-enhancement_20210425 create mode 120000 models/dist/training/all/models_eynollah/eynollah-main-regions-aug-rotation_20210425 create mode 120000 models/dist/training/all/models_eynollah/eynollah-main-regions-aug-scaling_20210425 create mode 120000 models/dist/training/all/models_eynollah/eynollah-main-regions-ensembled_20210425 create mode 120000 models/dist/training/all/models_eynollah/eynollah-main-regions_20220314 create mode 120000 models/dist/training/all/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 create mode 120000 models/dist/training/all/models_eynollah/eynollah-tables_20210319 create mode 120000 models/dist/training/all/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 create mode 120000 models/dist/training/all/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 create mode 120000 models/dist/training/all/models_eynollah/model_eynollah_ocr_trocr_20250919 create mode 120000 models/dist/training/all/models_eynollah/model_eynollah_page_extraction_20250915 create mode 120000 models/dist/training/all/models_eynollah/model_eynollah_reading_order_20250824 create mode 120000 models/dist/training/all/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 create mode 120000 models/dist/training/all/models_eynollah/modelens_full_lay_1__4_3_091124 create mode 120000 models/dist/training/all/models_eynollah/modelens_table_0t4_201124 create mode 120000 models/dist/training/all/models_eynollah/modelens_textline_0_1__2_4_16092024 create mode 120000 models/dist/training/extra/models_eynollah/eynollah-binarization_20210309 create mode 120000 models/dist/training/extra/models_eynollah/eynollah-binarization_20210425 create mode 120000 models/dist/training/extra/models_eynollah/eynollah-main-regions-aug-scaling_20210425 create mode 120000 models/dist/training/extra/models_eynollah/eynollah-tables_20210319 create mode 120000 models/dist/training/extra/models_eynollah/microsoft create mode 120000 models/dist/training/extra/models_eynollah/modelens_textline_0_1__2_4_16092024 create mode 120000 models/dist/training/layout/models_eynollah/eynollah-column-classifier_20210425 create mode 120000 models/dist/training/layout/models_eynollah/eynollah-enhancement_20210425 create mode 120000 models/dist/training/layout/models_eynollah/eynollah-main-regions-aug-rotation_20210425 create mode 120000 models/dist/training/layout/models_eynollah/eynollah-main-regions-aug-scaling_20210425 create mode 120000 models/dist/training/layout/models_eynollah/eynollah-main-regions-ensembled_20210425 create mode 120000 models/dist/training/layout/models_eynollah/eynollah-main-regions_20220314 create mode 120000 models/dist/training/layout/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 create mode 120000 models/dist/training/layout/models_eynollah/model_eynollah_reading_order_20250824 create mode 120000 models/dist/training/layout/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 create mode 120000 models/dist/training/layout/models_eynollah/modelens_full_lay_1__4_3_091124 create mode 120000 models/dist/training/layout/models_eynollah/modelens_table_0t4_201124 create mode 120000 models/dist/training/layout/models_eynollah/modelens_textline_0_1__2_4_16092024 create mode 120000 models/dist/training/ocr/models_eynollah/characters_org.txt create mode 120000 models/dist/training/ocr/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 create mode 120000 models/dist/training/ocr/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 create mode 120000 models/dist/training/ocr/models_eynollah/model_eynollah_ocr_trocr_20250919 diff --git a/.gitignore b/.gitignore index 5aa9023..3b4f340 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,14 @@ *.egg-info __pycache__ sbb_newspapers_org_image/pylint.log -models_eynollah* -models_ocr* -models_layout* default-2021-03-09 output.html /build -/dist *.tif *.sw? TAGS uv.lock +/dist /ignore *.log .env diff --git a/models/dist/inference/all/models_eynollah/characters_org.txt b/models/dist/inference/all/models_eynollah/characters_org.txt new file mode 120000 index 0000000..9b62c85 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/characters_org.txt @@ -0,0 +1 @@ +../../../../models_eynollah/characters_org.txt \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/eynollah-binarization_20210309 b/models/dist/inference/all/models_eynollah/eynollah-binarization_20210309 new file mode 120000 index 0000000..a043883 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/eynollah-binarization_20210309 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-binarization_20210309 \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/eynollah-binarization_20210425.onnx b/models/dist/inference/all/models_eynollah/eynollah-binarization_20210425.onnx new file mode 120000 index 0000000..eb64495 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/eynollah-binarization_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-binarization_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/eynollah-column-classifier_20210425.onnx b/models/dist/inference/all/models_eynollah/eynollah-column-classifier_20210425.onnx new file mode 120000 index 0000000..d7a154f --- /dev/null +++ b/models/dist/inference/all/models_eynollah/eynollah-column-classifier_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-column-classifier_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/eynollah-enhancement_20210425.onnx b/models/dist/inference/all/models_eynollah/eynollah-enhancement_20210425.onnx new file mode 120000 index 0000000..1e400f3 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/eynollah-enhancement_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-enhancement_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/eynollah-main-regions-aug-rotation_20210425.onnx b/models/dist/inference/all/models_eynollah/eynollah-main-regions-aug-rotation_20210425.onnx new file mode 120000 index 0000000..e1aea76 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/eynollah-main-regions-aug-rotation_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-main-regions-aug-rotation_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx b/models/dist/inference/all/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx new file mode 120000 index 0000000..921f8f3 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/eynollah-main-regions-ensembled_20210425.onnx b/models/dist/inference/all/models_eynollah/eynollah-main-regions-ensembled_20210425.onnx new file mode 120000 index 0000000..6c23442 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/eynollah-main-regions-ensembled_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-main-regions-ensembled_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/eynollah-main-regions_20220314.onnx b/models/dist/inference/all/models_eynollah/eynollah-main-regions_20220314.onnx new file mode 120000 index 0000000..e15fbd9 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/eynollah-main-regions_20220314.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-main-regions_20220314.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx b/models/dist/inference/all/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx new file mode 120000 index 0000000..9034c95 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/eynollah-tables_20210319.onnx b/models/dist/inference/all/models_eynollah/eynollah-tables_20210319.onnx new file mode 120000 index 0000000..912a400 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/eynollah-tables_20210319.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-tables_20210319.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/microsoft b/models/dist/inference/all/models_eynollah/microsoft new file mode 120000 index 0000000..5516955 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/microsoft @@ -0,0 +1 @@ +../../../../models_eynollah/microsoft \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 b/models/dist/inference/all/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 new file mode 120000 index 0000000..9875a6a --- /dev/null +++ b/models/dist/inference/all/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 b/models/dist/inference/all/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 new file mode 120000 index 0000000..0982ba7 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/model_eynollah_ocr_trocr_20250919 b/models/dist/inference/all/models_eynollah/model_eynollah_ocr_trocr_20250919 new file mode 120000 index 0000000..d5dc0b1 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/model_eynollah_ocr_trocr_20250919 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_ocr_trocr_20250919 \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/model_eynollah_reading_order_20250824.onnx b/models/dist/inference/all/models_eynollah/model_eynollah_reading_order_20250824.onnx new file mode 120000 index 0000000..2db28c0 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/model_eynollah_reading_order_20250824.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/model_eynollah_reading_order_20250824.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024.onnx b/models/dist/inference/all/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024.onnx new file mode 120000 index 0000000..c5828f6 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/modelens_full_lay_1__4_3_091124.onnx b/models/dist/inference/all/models_eynollah/modelens_full_lay_1__4_3_091124.onnx new file mode 120000 index 0000000..758c260 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/modelens_full_lay_1__4_3_091124.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/modelens_full_lay_1__4_3_091124.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/modelens_table_0t4_201124.onnx b/models/dist/inference/all/models_eynollah/modelens_table_0t4_201124.onnx new file mode 120000 index 0000000..5768a79 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/modelens_table_0t4_201124.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/modelens_table_0t4_201124.onnx \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx b/models/dist/inference/all/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx new file mode 120000 index 0000000..2b842b4 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx \ No newline at end of file diff --git a/models/dist/inference/extra/models_eynollah/eynollah-binarization_20210309 b/models/dist/inference/extra/models_eynollah/eynollah-binarization_20210309 new file mode 120000 index 0000000..a043883 --- /dev/null +++ b/models/dist/inference/extra/models_eynollah/eynollah-binarization_20210309 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-binarization_20210309 \ No newline at end of file diff --git a/models/dist/inference/extra/models_eynollah/eynollah-binarization_20210425.onnx b/models/dist/inference/extra/models_eynollah/eynollah-binarization_20210425.onnx new file mode 120000 index 0000000..eb64495 --- /dev/null +++ b/models/dist/inference/extra/models_eynollah/eynollah-binarization_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-binarization_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/extra/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx b/models/dist/inference/extra/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx new file mode 120000 index 0000000..921f8f3 --- /dev/null +++ b/models/dist/inference/extra/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/extra/models_eynollah/eynollah-tables_20210319.onnx b/models/dist/inference/extra/models_eynollah/eynollah-tables_20210319.onnx new file mode 120000 index 0000000..912a400 --- /dev/null +++ b/models/dist/inference/extra/models_eynollah/eynollah-tables_20210319.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-tables_20210319.onnx \ No newline at end of file diff --git a/models/dist/inference/extra/models_eynollah/microsoft b/models/dist/inference/extra/models_eynollah/microsoft new file mode 120000 index 0000000..5516955 --- /dev/null +++ b/models/dist/inference/extra/models_eynollah/microsoft @@ -0,0 +1 @@ +../../../../models_eynollah/microsoft \ No newline at end of file diff --git a/models/dist/inference/extra/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx b/models/dist/inference/extra/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx new file mode 120000 index 0000000..2b842b4 --- /dev/null +++ b/models/dist/inference/extra/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/eynollah-column-classifier_20210425.onnx b/models/dist/inference/layout/models_eynollah/eynollah-column-classifier_20210425.onnx new file mode 120000 index 0000000..d7a154f --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/eynollah-column-classifier_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-column-classifier_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/eynollah-enhancement_20210425.onnx b/models/dist/inference/layout/models_eynollah/eynollah-enhancement_20210425.onnx new file mode 120000 index 0000000..1e400f3 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/eynollah-enhancement_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-enhancement_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/eynollah-main-regions-aug-rotation_20210425.onnx b/models/dist/inference/layout/models_eynollah/eynollah-main-regions-aug-rotation_20210425.onnx new file mode 120000 index 0000000..e1aea76 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/eynollah-main-regions-aug-rotation_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-main-regions-aug-rotation_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx b/models/dist/inference/layout/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx new file mode 120000 index 0000000..921f8f3 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-main-regions-aug-scaling_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/eynollah-main-regions-ensembled_20210425.onnx b/models/dist/inference/layout/models_eynollah/eynollah-main-regions-ensembled_20210425.onnx new file mode 120000 index 0000000..6c23442 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/eynollah-main-regions-ensembled_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-main-regions-ensembled_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/eynollah-main-regions_20220314.onnx b/models/dist/inference/layout/models_eynollah/eynollah-main-regions_20220314.onnx new file mode 120000 index 0000000..e15fbd9 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/eynollah-main-regions_20220314.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-main-regions_20220314.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx b/models/dist/inference/layout/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx new file mode 120000 index 0000000..9034c95 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/model_eynollah_reading_order_20250824.onnx b/models/dist/inference/layout/models_eynollah/model_eynollah_reading_order_20250824.onnx new file mode 120000 index 0000000..2db28c0 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/model_eynollah_reading_order_20250824.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/model_eynollah_reading_order_20250824.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024.onnx b/models/dist/inference/layout/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024.onnx new file mode 120000 index 0000000..c5828f6 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/modelens_full_lay_1__4_3_091124.onnx b/models/dist/inference/layout/models_eynollah/modelens_full_lay_1__4_3_091124.onnx new file mode 120000 index 0000000..758c260 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/modelens_full_lay_1__4_3_091124.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/modelens_full_lay_1__4_3_091124.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/modelens_table_0t4_201124.onnx b/models/dist/inference/layout/models_eynollah/modelens_table_0t4_201124.onnx new file mode 120000 index 0000000..5768a79 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/modelens_table_0t4_201124.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/modelens_table_0t4_201124.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx b/models/dist/inference/layout/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx new file mode 120000 index 0000000..2b842b4 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/modelens_textline_0_1__2_4_16092024.onnx \ No newline at end of file diff --git a/models/dist/inference/ocr/models_eynollah/characters_org.txt b/models/dist/inference/ocr/models_eynollah/characters_org.txt new file mode 120000 index 0000000..9b62c85 --- /dev/null +++ b/models/dist/inference/ocr/models_eynollah/characters_org.txt @@ -0,0 +1 @@ +../../../../models_eynollah/characters_org.txt \ No newline at end of file diff --git a/models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 b/models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 new file mode 120000 index 0000000..9875a6a --- /dev/null +++ b/models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 \ No newline at end of file diff --git a/models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 b/models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 new file mode 120000 index 0000000..0982ba7 --- /dev/null +++ b/models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 \ No newline at end of file diff --git a/models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_trocr_20250919 b/models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_trocr_20250919 new file mode 120000 index 0000000..d5dc0b1 --- /dev/null +++ b/models/dist/inference/ocr/models_eynollah/model_eynollah_ocr_trocr_20250919 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_ocr_trocr_20250919 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/characters_org.txt b/models/dist/training/all/models_eynollah/characters_org.txt new file mode 120000 index 0000000..9b62c85 --- /dev/null +++ b/models/dist/training/all/models_eynollah/characters_org.txt @@ -0,0 +1 @@ +../../../../models_eynollah/characters_org.txt \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/eynollah-binarization-hybrid_20230504 b/models/dist/training/all/models_eynollah/eynollah-binarization-hybrid_20230504 new file mode 120000 index 0000000..0d90379 --- /dev/null +++ b/models/dist/training/all/models_eynollah/eynollah-binarization-hybrid_20230504 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-binarization-hybrid_20230504 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/eynollah-binarization_20210309 b/models/dist/training/all/models_eynollah/eynollah-binarization_20210309 new file mode 120000 index 0000000..a043883 --- /dev/null +++ b/models/dist/training/all/models_eynollah/eynollah-binarization_20210309 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-binarization_20210309 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/eynollah-binarization_20210425 b/models/dist/training/all/models_eynollah/eynollah-binarization_20210425 new file mode 120000 index 0000000..dc3ebc7 --- /dev/null +++ b/models/dist/training/all/models_eynollah/eynollah-binarization_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-binarization_20210425 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/eynollah-column-classifier_20210425 b/models/dist/training/all/models_eynollah/eynollah-column-classifier_20210425 new file mode 120000 index 0000000..66c19d1 --- /dev/null +++ b/models/dist/training/all/models_eynollah/eynollah-column-classifier_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-column-classifier_20210425 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/eynollah-enhancement_20210425 b/models/dist/training/all/models_eynollah/eynollah-enhancement_20210425 new file mode 120000 index 0000000..1287961 --- /dev/null +++ b/models/dist/training/all/models_eynollah/eynollah-enhancement_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-enhancement_20210425 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/eynollah-main-regions-aug-rotation_20210425 b/models/dist/training/all/models_eynollah/eynollah-main-regions-aug-rotation_20210425 new file mode 120000 index 0000000..d66338f --- /dev/null +++ b/models/dist/training/all/models_eynollah/eynollah-main-regions-aug-rotation_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-main-regions-aug-rotation_20210425 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/eynollah-main-regions-aug-scaling_20210425 b/models/dist/training/all/models_eynollah/eynollah-main-regions-aug-scaling_20210425 new file mode 120000 index 0000000..9e11f44 --- /dev/null +++ b/models/dist/training/all/models_eynollah/eynollah-main-regions-aug-scaling_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-main-regions-aug-scaling_20210425 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/eynollah-main-regions-ensembled_20210425 b/models/dist/training/all/models_eynollah/eynollah-main-regions-ensembled_20210425 new file mode 120000 index 0000000..19fc248 --- /dev/null +++ b/models/dist/training/all/models_eynollah/eynollah-main-regions-ensembled_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-main-regions-ensembled_20210425 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/eynollah-main-regions_20220314 b/models/dist/training/all/models_eynollah/eynollah-main-regions_20220314 new file mode 120000 index 0000000..6d3d6ca --- /dev/null +++ b/models/dist/training/all/models_eynollah/eynollah-main-regions_20220314 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-main-regions_20220314 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 b/models/dist/training/all/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 new file mode 120000 index 0000000..81d5930 --- /dev/null +++ b/models/dist/training/all/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/eynollah-tables_20210319 b/models/dist/training/all/models_eynollah/eynollah-tables_20210319 new file mode 120000 index 0000000..56d65a6 --- /dev/null +++ b/models/dist/training/all/models_eynollah/eynollah-tables_20210319 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-tables_20210319 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 b/models/dist/training/all/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 new file mode 120000 index 0000000..1e8a14b --- /dev/null +++ b/models/dist/training/all/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_ocr_cnnrnn_20250930 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 b/models/dist/training/all/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 new file mode 120000 index 0000000..0982ba7 --- /dev/null +++ b/models/dist/training/all/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/model_eynollah_ocr_trocr_20250919 b/models/dist/training/all/models_eynollah/model_eynollah_ocr_trocr_20250919 new file mode 120000 index 0000000..d5dc0b1 --- /dev/null +++ b/models/dist/training/all/models_eynollah/model_eynollah_ocr_trocr_20250919 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_ocr_trocr_20250919 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/model_eynollah_page_extraction_20250915 b/models/dist/training/all/models_eynollah/model_eynollah_page_extraction_20250915 new file mode 120000 index 0000000..21ee2d8 --- /dev/null +++ b/models/dist/training/all/models_eynollah/model_eynollah_page_extraction_20250915 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_page_extraction_20250915 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/model_eynollah_reading_order_20250824 b/models/dist/training/all/models_eynollah/model_eynollah_reading_order_20250824 new file mode 120000 index 0000000..232e767 --- /dev/null +++ b/models/dist/training/all/models_eynollah/model_eynollah_reading_order_20250824 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_reading_order_20250824 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 b/models/dist/training/all/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 new file mode 120000 index 0000000..5f4ff0b --- /dev/null +++ b/models/dist/training/all/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 @@ -0,0 +1 @@ +../../../../models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/modelens_full_lay_1__4_3_091124 b/models/dist/training/all/models_eynollah/modelens_full_lay_1__4_3_091124 new file mode 120000 index 0000000..e5f4d77 --- /dev/null +++ b/models/dist/training/all/models_eynollah/modelens_full_lay_1__4_3_091124 @@ -0,0 +1 @@ +../../../../models_eynollah/modelens_full_lay_1__4_3_091124 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/modelens_table_0t4_201124 b/models/dist/training/all/models_eynollah/modelens_table_0t4_201124 new file mode 120000 index 0000000..60c21ae --- /dev/null +++ b/models/dist/training/all/models_eynollah/modelens_table_0t4_201124 @@ -0,0 +1 @@ +../../../../models_eynollah/modelens_table_0t4_201124 \ No newline at end of file diff --git a/models/dist/training/all/models_eynollah/modelens_textline_0_1__2_4_16092024 b/models/dist/training/all/models_eynollah/modelens_textline_0_1__2_4_16092024 new file mode 120000 index 0000000..ec2e51c --- /dev/null +++ b/models/dist/training/all/models_eynollah/modelens_textline_0_1__2_4_16092024 @@ -0,0 +1 @@ +../../../../models_eynollah/modelens_textline_0_1__2_4_16092024 \ No newline at end of file diff --git a/models/dist/training/extra/models_eynollah/eynollah-binarization_20210309 b/models/dist/training/extra/models_eynollah/eynollah-binarization_20210309 new file mode 120000 index 0000000..a043883 --- /dev/null +++ b/models/dist/training/extra/models_eynollah/eynollah-binarization_20210309 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-binarization_20210309 \ No newline at end of file diff --git a/models/dist/training/extra/models_eynollah/eynollah-binarization_20210425 b/models/dist/training/extra/models_eynollah/eynollah-binarization_20210425 new file mode 120000 index 0000000..dc3ebc7 --- /dev/null +++ b/models/dist/training/extra/models_eynollah/eynollah-binarization_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-binarization_20210425 \ No newline at end of file diff --git a/models/dist/training/extra/models_eynollah/eynollah-main-regions-aug-scaling_20210425 b/models/dist/training/extra/models_eynollah/eynollah-main-regions-aug-scaling_20210425 new file mode 120000 index 0000000..9e11f44 --- /dev/null +++ b/models/dist/training/extra/models_eynollah/eynollah-main-regions-aug-scaling_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-main-regions-aug-scaling_20210425 \ No newline at end of file diff --git a/models/dist/training/extra/models_eynollah/eynollah-tables_20210319 b/models/dist/training/extra/models_eynollah/eynollah-tables_20210319 new file mode 120000 index 0000000..56d65a6 --- /dev/null +++ b/models/dist/training/extra/models_eynollah/eynollah-tables_20210319 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-tables_20210319 \ No newline at end of file diff --git a/models/dist/training/extra/models_eynollah/microsoft b/models/dist/training/extra/models_eynollah/microsoft new file mode 120000 index 0000000..5516955 --- /dev/null +++ b/models/dist/training/extra/models_eynollah/microsoft @@ -0,0 +1 @@ +../../../../models_eynollah/microsoft \ No newline at end of file diff --git a/models/dist/training/extra/models_eynollah/modelens_textline_0_1__2_4_16092024 b/models/dist/training/extra/models_eynollah/modelens_textline_0_1__2_4_16092024 new file mode 120000 index 0000000..ec2e51c --- /dev/null +++ b/models/dist/training/extra/models_eynollah/modelens_textline_0_1__2_4_16092024 @@ -0,0 +1 @@ +../../../../models_eynollah/modelens_textline_0_1__2_4_16092024 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/eynollah-column-classifier_20210425 b/models/dist/training/layout/models_eynollah/eynollah-column-classifier_20210425 new file mode 120000 index 0000000..66c19d1 --- /dev/null +++ b/models/dist/training/layout/models_eynollah/eynollah-column-classifier_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-column-classifier_20210425 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/eynollah-enhancement_20210425 b/models/dist/training/layout/models_eynollah/eynollah-enhancement_20210425 new file mode 120000 index 0000000..1287961 --- /dev/null +++ b/models/dist/training/layout/models_eynollah/eynollah-enhancement_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-enhancement_20210425 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/eynollah-main-regions-aug-rotation_20210425 b/models/dist/training/layout/models_eynollah/eynollah-main-regions-aug-rotation_20210425 new file mode 120000 index 0000000..d66338f --- /dev/null +++ b/models/dist/training/layout/models_eynollah/eynollah-main-regions-aug-rotation_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-main-regions-aug-rotation_20210425 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/eynollah-main-regions-aug-scaling_20210425 b/models/dist/training/layout/models_eynollah/eynollah-main-regions-aug-scaling_20210425 new file mode 120000 index 0000000..9e11f44 --- /dev/null +++ b/models/dist/training/layout/models_eynollah/eynollah-main-regions-aug-scaling_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-main-regions-aug-scaling_20210425 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/eynollah-main-regions-ensembled_20210425 b/models/dist/training/layout/models_eynollah/eynollah-main-regions-ensembled_20210425 new file mode 120000 index 0000000..19fc248 --- /dev/null +++ b/models/dist/training/layout/models_eynollah/eynollah-main-regions-ensembled_20210425 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-main-regions-ensembled_20210425 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/eynollah-main-regions_20220314 b/models/dist/training/layout/models_eynollah/eynollah-main-regions_20220314 new file mode 120000 index 0000000..6d3d6ca --- /dev/null +++ b/models/dist/training/layout/models_eynollah/eynollah-main-regions_20220314 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-main-regions_20220314 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 b/models/dist/training/layout/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 new file mode 120000 index 0000000..81d5930 --- /dev/null +++ b/models/dist/training/layout/models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 @@ -0,0 +1 @@ +../../../../models_eynollah/eynollah-main-regions_20231127_672_org_ens_11_13_16_17_18 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/model_eynollah_reading_order_20250824 b/models/dist/training/layout/models_eynollah/model_eynollah_reading_order_20250824 new file mode 120000 index 0000000..232e767 --- /dev/null +++ b/models/dist/training/layout/models_eynollah/model_eynollah_reading_order_20250824 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_reading_order_20250824 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 b/models/dist/training/layout/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 new file mode 120000 index 0000000..5f4ff0b --- /dev/null +++ b/models/dist/training/layout/models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 @@ -0,0 +1 @@ +../../../../models_eynollah/modelens_e_l_all_sp_0_1_2_3_4_171024 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/modelens_full_lay_1__4_3_091124 b/models/dist/training/layout/models_eynollah/modelens_full_lay_1__4_3_091124 new file mode 120000 index 0000000..e5f4d77 --- /dev/null +++ b/models/dist/training/layout/models_eynollah/modelens_full_lay_1__4_3_091124 @@ -0,0 +1 @@ +../../../../models_eynollah/modelens_full_lay_1__4_3_091124 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/modelens_table_0t4_201124 b/models/dist/training/layout/models_eynollah/modelens_table_0t4_201124 new file mode 120000 index 0000000..60c21ae --- /dev/null +++ b/models/dist/training/layout/models_eynollah/modelens_table_0t4_201124 @@ -0,0 +1 @@ +../../../../models_eynollah/modelens_table_0t4_201124 \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/modelens_textline_0_1__2_4_16092024 b/models/dist/training/layout/models_eynollah/modelens_textline_0_1__2_4_16092024 new file mode 120000 index 0000000..ec2e51c --- /dev/null +++ b/models/dist/training/layout/models_eynollah/modelens_textline_0_1__2_4_16092024 @@ -0,0 +1 @@ +../../../../models_eynollah/modelens_textline_0_1__2_4_16092024 \ No newline at end of file diff --git a/models/dist/training/ocr/models_eynollah/characters_org.txt b/models/dist/training/ocr/models_eynollah/characters_org.txt new file mode 120000 index 0000000..9b62c85 --- /dev/null +++ b/models/dist/training/ocr/models_eynollah/characters_org.txt @@ -0,0 +1 @@ +../../../../models_eynollah/characters_org.txt \ No newline at end of file diff --git a/models/dist/training/ocr/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 b/models/dist/training/ocr/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 new file mode 120000 index 0000000..1e8a14b --- /dev/null +++ b/models/dist/training/ocr/models_eynollah/model_eynollah_ocr_cnnrnn_20250930 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_ocr_cnnrnn_20250930 \ No newline at end of file diff --git a/models/dist/training/ocr/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 b/models/dist/training/ocr/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 new file mode 120000 index 0000000..0982ba7 --- /dev/null +++ b/models/dist/training/ocr/models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_ocr_cnnrnn__degraded_20250805 \ No newline at end of file diff --git a/models/dist/training/ocr/models_eynollah/model_eynollah_ocr_trocr_20250919 b/models/dist/training/ocr/models_eynollah/model_eynollah_ocr_trocr_20250919 new file mode 120000 index 0000000..d5dc0b1 --- /dev/null +++ b/models/dist/training/ocr/models_eynollah/model_eynollah_ocr_trocr_20250919 @@ -0,0 +1 @@ +../../../../models_eynollah/model_eynollah_ocr_trocr_20250919 \ No newline at end of file From 32c5e9ae76f40e4762cddd0b4a2cd1f7512867bc Mon Sep 17 00:00:00 2001 From: kba Date: Wed, 15 Jul 2026 17:03:04 +0200 Subject: [PATCH 104/121] add missing models, remove microsoft --- models/dist/inference/all/models_eynollah/microsoft | 1 - .../models_eynollah/model_eynollah_page_extraction_20250915.onnx | 1 + models/dist/inference/extra/models_eynollah/microsoft | 1 - .../layout/models_eynollah/eynollah-binarization_20210425.onnx | 1 + .../models_eynollah/model_eynollah_page_extraction_20250915.onnx | 1 + models/dist/training/extra/models_eynollah/microsoft | 1 - .../models_eynollah/model_eynollah_page_extraction_20250915 | 1 + 7 files changed, 4 insertions(+), 3 deletions(-) delete mode 120000 models/dist/inference/all/models_eynollah/microsoft create mode 120000 models/dist/inference/all/models_eynollah/model_eynollah_page_extraction_20250915.onnx delete mode 120000 models/dist/inference/extra/models_eynollah/microsoft create mode 120000 models/dist/inference/layout/models_eynollah/eynollah-binarization_20210425.onnx create mode 120000 models/dist/inference/layout/models_eynollah/model_eynollah_page_extraction_20250915.onnx delete mode 120000 models/dist/training/extra/models_eynollah/microsoft create mode 120000 models/dist/training/layout/models_eynollah/model_eynollah_page_extraction_20250915 diff --git a/models/dist/inference/all/models_eynollah/microsoft b/models/dist/inference/all/models_eynollah/microsoft deleted file mode 120000 index 5516955..0000000 --- a/models/dist/inference/all/models_eynollah/microsoft +++ /dev/null @@ -1 +0,0 @@ -../../../../models_eynollah/microsoft \ No newline at end of file diff --git a/models/dist/inference/all/models_eynollah/model_eynollah_page_extraction_20250915.onnx b/models/dist/inference/all/models_eynollah/model_eynollah_page_extraction_20250915.onnx new file mode 120000 index 0000000..688b314 --- /dev/null +++ b/models/dist/inference/all/models_eynollah/model_eynollah_page_extraction_20250915.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/model_eynollah_page_extraction_20250915.onnx \ No newline at end of file diff --git a/models/dist/inference/extra/models_eynollah/microsoft b/models/dist/inference/extra/models_eynollah/microsoft deleted file mode 120000 index 5516955..0000000 --- a/models/dist/inference/extra/models_eynollah/microsoft +++ /dev/null @@ -1 +0,0 @@ -../../../../models_eynollah/microsoft \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/eynollah-binarization_20210425.onnx b/models/dist/inference/layout/models_eynollah/eynollah-binarization_20210425.onnx new file mode 120000 index 0000000..eb64495 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/eynollah-binarization_20210425.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/eynollah-binarization_20210425.onnx \ No newline at end of file diff --git a/models/dist/inference/layout/models_eynollah/model_eynollah_page_extraction_20250915.onnx b/models/dist/inference/layout/models_eynollah/model_eynollah_page_extraction_20250915.onnx new file mode 120000 index 0000000..688b314 --- /dev/null +++ b/models/dist/inference/layout/models_eynollah/model_eynollah_page_extraction_20250915.onnx @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/model_eynollah_page_extraction_20250915.onnx \ No newline at end of file diff --git a/models/dist/training/extra/models_eynollah/microsoft b/models/dist/training/extra/models_eynollah/microsoft deleted file mode 120000 index 5516955..0000000 --- a/models/dist/training/extra/models_eynollah/microsoft +++ /dev/null @@ -1 +0,0 @@ -../../../../models_eynollah/microsoft \ No newline at end of file diff --git a/models/dist/training/layout/models_eynollah/model_eynollah_page_extraction_20250915 b/models/dist/training/layout/models_eynollah/model_eynollah_page_extraction_20250915 new file mode 120000 index 0000000..3e1a782 --- /dev/null +++ b/models/dist/training/layout/models_eynollah/model_eynollah_page_extraction_20250915 @@ -0,0 +1 @@ +../../../../reloaded/models_eynollah/model_eynollah_page_extraction_20250915 \ No newline at end of file From 1440d454cc8f84e32d565661ffeb14a31251b5b7 Mon Sep 17 00:00:00 2001 From: kba Date: Wed, 15 Jul 2026 17:18:33 +0200 Subject: [PATCH 105/121] models: bump version --- models/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/Makefile b/models/Makefile index c80571a..ed97039 100644 --- a/models/Makefile +++ b/models/Makefile @@ -1,6 +1,6 @@ SHELL = bash -e -VERSION = v0_9_0 +VERSION = v0_9_1 MODELS_SRC = models_eynollah MODELS_DST = reloaded/models_eynollah From d2755d1e9381b5c2e4efb9a5a0ff33a30c539866 Mon Sep 17 00:00:00 2001 From: kba Date: Wed, 15 Jul 2026 20:05:36 +0200 Subject: [PATCH 106/121] update references to new v0_9_1 model release --- Makefile | 3 ++- src/eynollah/model_zoo/default_specs.py | 4 ++-- src/eynollah/ocrd-tool.json | 16 ++++++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index eb6353a..2734672 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,8 @@ WGET = wget -O #SEG_MODEL := https://github.com/qurator-spk/eynollah/releases/download/v0.3.0/models_eynollah.tar.gz #SEG_MODEL := https://github.com/qurator-spk/eynollah/releases/download/v0.3.1/models_eynollah.tar.gz #SEG_MODEL := https://zenodo.org/records/17194824/files/models_layout_v0_5_0.tar.gz?download=1 -EYNOLLAH_MODELS_URL := https://zenodo.org/records/21362927/files/models_inference_all_v0_9_0.zip +# EYNOLLAH_MODELS_URL := https://zenodo.org/records/21362927/files/models_inference_all_v0_9_0.zip +EYNOLLAH_MODELS_URL := https://zenodo.org/records/21381102/files/models_inference_all_v0_9_1.zip EYNOLLAH_MODELS_ZIP = $(notdir $(EYNOLLAH_MODELS_URL)) EYNOLLAH_MODELS_DIR = $(EYNOLLAH_MODELS_ZIP:%.zip=%) diff --git a/src/eynollah/model_zoo/default_specs.py b/src/eynollah/model_zoo/default_specs.py index 2b1196b..7fe91f8 100644 --- a/src/eynollah/model_zoo/default_specs.py +++ b/src/eynollah/model_zoo/default_specs.py @@ -1,8 +1,8 @@ from .specs import EynollahModelSpec, EynollahModelSpecSet # NOTE: This needs to change whenever models/versions change -ZENODO = "https://zenodo.org/records/21362927" -MODELS_VERSION = "v0_9_0" +ZENODO = "https://zenodo.org/records/21381102" +MODELS_VERSION = "v0_9_1" def dist_url(dist_name: str="layout") -> str: return f'{ZENODO}/models_{dist_name}_{MODELS_VERSION}.zip' diff --git a/src/eynollah/ocrd-tool.json b/src/eynollah/ocrd-tool.json index 48c1740..5e36ecb 100644 --- a/src/eynollah/ocrd-tool.json +++ b/src/eynollah/ocrd-tool.json @@ -85,11 +85,11 @@ }, "resources": [ { - "url": "https://zenodo.org/records/21362927/files/models_inference_all_v0_9_0.zip", - "name": "models_inference_all_v0_9_0", + "url": "https://zenodo.org/records/21381102/files/models_inference_layout_v0_9_1.zip", + "name": "models_inference_layout_v0_9_1", "type": "archive", - "size": 5177204769, - "description": "Models for layout detection, reading order detection, textline detection, page extraction, column classification, table detection, binarization and image enhancement, as well as OCR", + "size": 1847700967, + "description": "Models for layout detection, reading order detection, textline detection, page extraction, column classification, table detection, binarization and image enhancement", "version_range": ">= v0.9.0" }, { @@ -159,6 +159,14 @@ } }, "resources": [ + { + "url": "https://zenodo.org/records/21381102/files/models_inference_all_v0_9_1.zip", + "name": "models_inference_all_v0_9_1", + "type": "archive", + "size": 3761011909, + "description": "Models for layout detection, reading order detection, textline detection, page extraction, column classification, table detection, binarization and image enhancement", + "version_range": ">= v0.9.0" + }, { "url": "https://zenodo.org/records/17727267/files/models_all_v0_8_0.zip", "name": "models_all_v0_8_0", From 0a9b3097f18de9a1b817edc76bbfdc9c30317605 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 17 Jul 2026 12:53:13 +0200 Subject: [PATCH 107/121] processor: avoid writing XML twice (once by writer, once by OCR-D) --- src/eynollah/eynollah.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/eynollah/eynollah.py b/src/eynollah/eynollah.py index 8cc17a1..b1169a6 100644 --- a/src/eynollah/eynollah.py +++ b/src/eynollah/eynollah.py @@ -2142,7 +2142,8 @@ class Eynollah: conf_textregions=[0], ) self.logger.info("Basic processing complete") - writer.write_pagexml(pcgts) + if writer.pcgts is None: + writer.write_pagexml(pcgts) self.logger.info("Job done in %.1fs", time.time() - t0) return @@ -2221,7 +2222,8 @@ class Eynollah: cont_page=cont_page, polygons_seplines=[], ) - writer.write_pagexml(pcgts) + if writer.pcgts is None: + writer.write_pagexml(pcgts) self.logger.info("Job done in %.1fs", time.time() - t0) return @@ -2508,6 +2510,7 @@ class Eynollah: conf_tables=conf_tables, ) - writer.write_pagexml(pcgts) + if writer.pcgts is None: + writer.write_pagexml(pcgts) self.logger.info("Job done in %.1fs", time.time() - t0) return From f579d1286639ce8623e930972e90f04886ba1599 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 17 Jul 2026 12:56:19 +0200 Subject: [PATCH 108/121] processor: resolve `models` path as processor resource --- src/eynollah/processor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eynollah/processor.py b/src/eynollah/processor.py index 47fa770..684b0d5 100644 --- a/src/eynollah/processor.py +++ b/src/eynollah/processor.py @@ -14,7 +14,8 @@ class EynollahProcessor(Processor): def setup(self) -> None: assert self.parameter - model_zoo = EynollahModelZoo(basedir=self.parameter['models']) + basedir = self.resolve_resource(self.parameter['models']) + model_zoo = EynollahModelZoo(basedir) self.eynollah = Eynollah( model_zoo=model_zoo, allow_enhancement=self.parameter['allow_enhancement'], From c1b276fea16ed58c27a63cf99de175f9e0e7d432 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 17 Jul 2026 15:37:39 +0200 Subject: [PATCH 109/121] =?UTF-8?q?processor:=20pass=20on=20more=20Eynolla?= =?UTF-8?q?h=20parameters=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `device` selection - `model_overrides` (as dict; including relative path resolution), e.g. ```JSON { "binarization": { "": "models_inference_layout_v0_9_1/models_eynollah/eynollah-binarization_20210425.onnx" } } ``` - `skip_layout_and_reading_order` - `num_col_upper` - `num_col_lower` - `binarize` (for `input_binary`, which is a misnomer) --- src/eynollah/ocrd-tool.json | 78 +++++++++++++++++++++++++++++++++++-- src/eynollah/processor.py | 20 ++++++++-- 2 files changed, 91 insertions(+), 7 deletions(-) diff --git a/src/eynollah/ocrd-tool.json b/src/eynollah/ocrd-tool.json index 5e36ecb..8d9b901 100644 --- a/src/eynollah/ocrd-tool.json +++ b/src/eynollah/ocrd-tool.json @@ -6,23 +6,61 @@ "ocrd-eynollah-segment": { "executable": "ocrd-eynollah-segment", "categories": ["Layout analysis"], - "description": "Segment page into regions and lines and do reading order detection with eynollah", + "description": "Segment page into regions and lines and detect reading order with Eynollah", "input_file_grp_cardinality": 1, "output_file_grp_cardinality": 1, "steps": ["layout/segmentation/region", "layout/segmentation/line"], "parameters": { + "device": { + "type": "string", + "default": "", + "description": "Allocate models to computation device; can be a single device name for all models, or a comma-delimited, colon-tagged mapping from model category to device name (where model categories are binarization, col_classifier, page, textline, region_1_2, region_fl_np, table, reading_order, but can be abbreviated by glob expressions), e.g. col*:CPU,page:GPU0,*:GPU1. If empty, selects the first available GPU for all models." + }, "models": { "type": "string", "format": "uri", "content-type": "text/directory", "cacheable": true, - "description": "Directory containing models to be used (See https://qurator-data.de/eynollah)", + "description": "Directory containing models to be used (See https://huggingface.co/collections/SBB/eynollah-models)", "required": true }, + "model_overrides": { + "type": "object", + "properties": { + "binarization": { + "type": "object" + }, + "enhancement": { + "type": "object" + }, + "col_classifier": { + "type": "object" + }, + "page": { + "type": "object" + }, + "textline": { + "type": "object" + }, + "region_1_2": { + "type": "object" + }, + "region_fl_np": { + "type": "object" + }, + "table": { + "type": "object" + }, + "reading_order": { + "type": "object" + } + }, + "description": "Map model categories to mappings from model variant to model path, e.g. {'ocr': {'tr': 'path/to/my/trocr-model'}}." + }, "dpi": { "type": "number", "format": "float", - "description": "pixel density in dots per inch (overrides any meta-data in the images); ignored if <= 0 (with fall-back 230)", + "description": "ignored (only for backwards-compatibility)", "default": 0 }, "full_layout": { @@ -57,10 +95,20 @@ "default": false, "description": "if true, do not attempt page frame detection (cropping)" }, + "skip_layout_and_reading_order": { + "type": "boolean", + "default": false, + "description": "skip regions, only run textlines, stuffing them in a single text region for the entire page" + }, + "binarize": { + "type": "boolean", + "default": false, + "description": "run adaptive thresholding on input image with dedicated model before analysis (may be useful for degraded input)." + }, "allow_scaling": { "type": "boolean", "default": false, - "description": "check the resolution against the number of detected columns and if needed, scale the image up or down during layout detection (heuristic to improve quality and performance)" + "description": "ignored (only for backwards-compatibility)" }, "allow_enhancement": { "type": "boolean", @@ -72,6 +120,20 @@ "default": false, "description": "if true, return reading order in right-to-left reading direction." }, + "num_col_upper": { + "type": "number", + "format": "integer", + "minimum": 0, + "default": 0, + "description": "Constrain detection of number of columns by this upper boundary (ignored when zero)." + }, + "num_col_lower": { + "type": "number", + "format": "integer", + "minimum": 0, + "default": 0, + "description": "Constrain detection of number of columns by this lower boundary (ignored when zero)." + }, "headers_off": { "type": "boolean", "default": false, @@ -159,6 +221,14 @@ } }, "resources": [ + { + "url": "https://zenodo.org/records/21381102/files/models_inference_layout_v0_9_1.zip", + "name": "models_inference_layout_v0_9_1", + "type": "archive", + "size": 1847700967, + "description": "Models for layout detection, reading order detection, textline detection, page extraction, column classification, table detection, binarization and image enhancement", + "version_range": ">= v0.9.0" + }, { "url": "https://zenodo.org/records/21381102/files/models_inference_all_v0_9_1.zip", "name": "models_inference_all_v0_9_1", diff --git a/src/eynollah/processor.py b/src/eynollah/processor.py index 684b0d5..bc62ead 100644 --- a/src/eynollah/processor.py +++ b/src/eynollah/processor.py @@ -15,7 +15,12 @@ class EynollahProcessor(Processor): def setup(self) -> None: assert self.parameter basedir = self.resolve_resource(self.parameter['models']) - model_zoo = EynollahModelZoo(basedir) + overrides = [] + for category, override in self.parameter['model_overrides'].items(): + for variant, path in override.items(): + path = self.resolve_resource(path) + overrides.append((category, variant, path)) + model_zoo = EynollahModelZoo(basedir, model_overrides=overrides) self.eynollah = Eynollah( model_zoo=model_zoo, allow_enhancement=self.parameter['allow_enhancement'], @@ -23,10 +28,15 @@ class EynollahProcessor(Processor): right2left=self.parameter['right_to_left'], reading_order_machine_based=self.parameter['reading_order_machine_based'], ignore_page_extraction=self.parameter['ignore_page_extraction'], + skip_layout_and_reading_order=self.parameter['skip_layout_and_reading_order'], full_layout=self.parameter['full_layout'], allow_scaling=self.parameter['allow_scaling'], headers_off=self.parameter['headers_off'], tables=self.parameter['tables'], + device=self.parameter['device'], + input_binary=self.parameter['binarize'], + num_col_upper=self.parameter['num_col_upper'], + num_col_lower=self.parameter['num_col_lower'], logger=self.logger ) self.eynollah.plotter = None @@ -49,13 +59,17 @@ class EynollahProcessor(Processor): \b - If ``tables``, try to detect table blocks and add them as TableRegion. - - If ``full_layout``, then in addition to paragraphs and marginals, also - try to detect drop capitals and headings. + - If ``full_layout`` (the default), then in addition to paragraphs and marginals, + also try to detect drop capitals and headings. + - If ``ignore_page_extraction``, then attempt no cropping of the page. - If ``ignore_page_extraction``, then attempt no cropping of the page. - If ``curved_line``, then compute contour polygons for text lines instead of simple bounding boxes. - If ``reading_order_machine_based``, then detect reading order via data-driven model instead of geometrical heuristics. + - If ``binarize``, then run internal binarization on the raw image. + - If ``num_col_upper`` or ``num_col_lower`` are non-zero, these will + constrain the column detection (upper or lower bound, respectively). Produce a new output file by serialising the resulting hierarchy. """ From 5939845d1d18e40119282f05fa54441afe40851c Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 17 Jul 2026 15:43:47 +0200 Subject: [PATCH 110/121] utils.contour.make_valid: be more robust (avoiding MultiPolygon) --- src/eynollah/utils/contour.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/eynollah/utils/contour.py b/src/eynollah/utils/contour.py index eda60e9..83243ca 100644 --- a/src/eynollah/utils/contour.py +++ b/src/eynollah/utils/contour.py @@ -354,13 +354,7 @@ def contour2polygon(contour: Union[np.ndarray, Sequence[Sequence[Sequence[Number polygon = Polygon([point[0] for point in contour]) if dilate: polygon = polygon.buffer(dilate) - if polygon.geom_type == 'GeometryCollection': - # heterogeneous result: filter zero-area shapes (LineString, Point) - polygon = unary_union([geom for geom in polygon.geoms if geom.area > 0]) - if polygon.geom_type == 'MultiPolygon': - # homogeneous result: construct convex hull to connect - polygon = join_polygons(polygon.geoms) - return make_valid(polygon) + return ensure_polygon(make_valid(polygon)) def polygon2contour(polygon: Polygon) -> np.ndarray: polygon = np.array(polygon.exterior.coords[:-1], dtype=int) @@ -371,16 +365,22 @@ def make_intersection(poly1, poly2): # post-process if interp.is_empty or interp.area == 0.0: return None - if interp.geom_type == 'GeometryCollection': - # heterogeneous result: filter zero-area shapes (LineString, Point) - interp = unary_union([geom for geom in interp.geoms if geom.area > 0]) - if interp.geom_type == 'MultiPolygon': - # homogeneous result: construct convex hull to connect - interp = join_polygons(interp.geoms) - assert interp.geom_type == 'Polygon', interp.wkt + interp = ensure_polygon(interp) interp = make_valid(interp) + interp = ensure_polygon(interp) return interp +def ensure_polygon(geometry): + if geometry.geom_type == 'GeometryCollection': + # heterogeneous result: filter zero-area shapes (LineString, Point) + geometry = unary_union([geom for geom in geometry.geoms if geom.area > 0]) + if geometry.geom_type == 'MultiPolygon': + # homogeneous result: construct convex hull to connect + geometry = join_polygons(geometry.geoms) + poly = Polygon(geometry) + assert poly.geom_type == 'Polygon', poly.wkt + return poly + def make_valid(polygon: Polygon) -> Polygon: """Ensures shapely.geometry.Polygon object is valid by repeated rearrangement/simplification/enlargement.""" def isint(x): From 4b9fa543aed45f2eb4573907834bdf14faa754da Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 17 Jul 2026 16:23:03 +0200 Subject: [PATCH 111/121] processor: fix typo (empty `model_overrides`) --- src/eynollah/processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eynollah/processor.py b/src/eynollah/processor.py index bc62ead..264d364 100644 --- a/src/eynollah/processor.py +++ b/src/eynollah/processor.py @@ -16,7 +16,7 @@ class EynollahProcessor(Processor): assert self.parameter basedir = self.resolve_resource(self.parameter['models']) overrides = [] - for category, override in self.parameter['model_overrides'].items(): + for category, override in self.parameter.get('model_overrides', {}).items(): for variant, path in override.items(): path = self.resolve_resource(path) overrides.append((category, variant, path)) From 5d129dc8c114e9a493bc648dec0434ca6fc4bae9 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 17 Jul 2026 19:02:42 +0200 Subject: [PATCH 112/121] cnn-rnn-ocr: fix typo causing rare failures --- src/eynollah/eynollah_ocr.py | 2 +- src/eynollah/utils/utils_ocr.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 7fbfa5f..ae105ac 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -455,7 +455,7 @@ class Eynollah_ocr(Eynollah): return img = cv2.imread(img_filename) - + self.logger.info(img_filename) page_tree = ET.parse(page_file_in, parser = ET.XMLParser(encoding="utf-8")) page_ns = etree_namespace_for_element_tag(page_tree.getroot().tag) diff --git a/src/eynollah/utils/utils_ocr.py b/src/eynollah/utils/utils_ocr.py index 6fc81fb..d8b87b4 100644 --- a/src/eynollah/utils/utils_ocr.py +++ b/src/eynollah/utils/utils_ocr.py @@ -228,7 +228,7 @@ def break_curved_line_into_small_pieces_and_then_merge(img_rgb_curved, img_bin_c peaks_4 = return_splitting_point_of_image(img_rgb_curved) if len(peaks_4): imgs_tot = [] - for left, right in pairwise([None] + peaks_4 + [None]): + for left, right in pairwise([None] + list(peaks_4) + [None]): img_rgb = img_rgb_curved[:, left: right] img_bin = img_bin_curved[:, left: right] mask = mask_curved[:, left: right] From aace571368787cf739c51af72dc634a4b23f3fb1 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 17 Jul 2026 19:15:54 +0200 Subject: [PATCH 113/121] cnn-rnn-ocr: fix --- src/eynollah/utils/utils_ocr.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/eynollah/utils/utils_ocr.py b/src/eynollah/utils/utils_ocr.py index d8b87b4..14cbb79 100644 --- a/src/eynollah/utils/utils_ocr.py +++ b/src/eynollah/utils/utils_ocr.py @@ -182,10 +182,11 @@ def get_orientation_moments(contour): def get_orientation_moments_of_mask(mask): mask=mask.astype('uint8') - contours, _ = cv2.findContours(mask[:,:,0], cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) - - largest_contour = max(contours, key=cv2.contourArea) if contours else None - + contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + if not len(contours): + return 0 + largest_contour = max(contours, key=cv2.contourArea) moments = cv2.moments(largest_contour) if moments["mu20"] - moments["mu02"] == 0: # Avoid division by zero return 90 if moments["mu11"] > 0 else -90 From 1c8ac38d31e7b21f9e839949531cb0bd7a484beb Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Fri, 17 Jul 2026 19:19:20 +0200 Subject: [PATCH 114/121] OCR w/o `overwrite`: skip one file, not the entire run --- src/eynollah/eynollah_ocr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index ae105ac..16deecf 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -452,7 +452,7 @@ class Eynollah_ocr(Eynollah): self.logger.warning("will overwrite existing output file '%s'", out_file_ocr) else: self.logger.warning("will skip input for existing output file '%s'", out_file_ocr) - return + continue img = cv2.imread(img_filename) self.logger.info(img_filename) From 6840b6796166712d86b7f2c404c417a391d00254 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Sat, 18 Jul 2026 00:24:08 +0200 Subject: [PATCH 115/121] =?UTF-8?q?ocr:=20run=20`dir=5Fin`=20mode=20in=20p?= =?UTF-8?q?arallel=20(like=20layout),=20too=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add CLI options `--num-jobs` and `--halt-fail` - separate `Eynollah_ocr.run_single()` to be scheduled - use ProcessPoolExecutor w/ forking and QueueListener - also skip if input XML file is missing - log processing times per job and overall --- src/eynollah/cli/cli_ocr.py | 18 ++++ src/eynollah/eynollah_ocr.py | 189 ++++++++++++++++++++++++----------- 2 files changed, 151 insertions(+), 56 deletions(-) diff --git a/src/eynollah/cli/cli_ocr.py b/src/eynollah/cli/cli_ocr.py index daeccbe..09c88db 100644 --- a/src/eynollah/cli/cli_ocr.py +++ b/src/eynollah/cli/cli_ocr.py @@ -47,6 +47,20 @@ import click help="overwrite (instead of skipping) if output xml exists", is_flag=True, ) +@click.option( + "--num-jobs", + "-j", + default=0, + type=click.IntRange(min=0), + help="number of parallel images to process (for --dir_in mode; also helps better utilise GPU if available); 0 means based on autodetected number of processor cores", +) +@click.option( + "--halt-fail", + "-H", + default=0, + type=click.FloatRange(min=0), + help="abort when number of failed images exceeds this value (if >=1) or ratio of failed over total images exceeds this value (if <1); 0 means ignore failures", +) @click.option( "--tr_ocr", "-trocr", @@ -83,6 +97,8 @@ def ocr_cli( out, dir_out_image_text, overwrite, + num_jobs, + halt_fail, tr_ocr, do_not_mask_with_textline_contour, batch_size, @@ -108,4 +124,6 @@ def ocr_cli( dir_xmls=dir_xmls, dir_out_image_text=dir_out_image_text, dir_out=out, + num_jobs=num_jobs, + halt_fail=halt_fail, ) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 16deecf..434b865 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -1,13 +1,17 @@ # FIXME: fix all of those... # pyright: reportOptionalSubscript=false -from logging import Logger, getLogger +import logging +import logging.handlers from typing import List, Optional from pathlib import Path import os import gc import math +import time from dataclasses import dataclass +import multiprocessing as mp +from concurrent.futures import ProcessPoolExecutor, as_completed import cv2 from cv2.typing import MatLike @@ -37,6 +41,21 @@ from .utils.utils_ocr import ( rotate_image_with_padding, ) + +_instance = None +def _set_instance(instance): + global _instance + _instance = instance +def _run_single(*args, **kwargs): + logq = kwargs.pop('logq') + # replace all inherited handlers with queue handler + logging.root.handlers.clear() + _instance.logger.handlers.clear() + handler = logging.handlers.QueueHandler(logq) + logging.root.addHandler(handler) + return _instance.run_single(*args, **kwargs) + + # TODO: refine typing @dataclass class EynollahOcrResult: @@ -54,13 +73,13 @@ class Eynollah_ocr(Eynollah): batch_size: int=0, do_not_mask_with_textline_contour: bool=False, min_conf_value_of_textline_text : float=0.3, - logger: Optional[Logger]=None, + logger: Optional[logging.Logger]=None, device: str = '', ): self.tr_ocr = tr_ocr # masking for OCR and GT generation, relevant for skewed lines and bounding boxes self.do_not_mask_with_textline_contour = do_not_mask_with_textline_contour - self.logger = logger if logger else getLogger('eynollah.ocr') + self.logger = logger if logger else logging.getLogger('eynollah.ocr') self.min_conf_value_of_textline_text = min_conf_value_of_textline_text self.b_s = batch_size or 2 if tr_ocr else 8 @@ -416,16 +435,17 @@ class Eynollah_ocr(Eynollah): self.logger.info("output filename: '%s'", out_file_ocr) page_tree.write(out_file_ocr, xml_declaration=True, method='xml', encoding="utf-8", default_namespace=None) - def run( - self, - *, - overwrite: bool = False, - dir_in: Optional[str] = None, - dir_in_bin: Optional[str] = None, - image_filename: Optional[str] = None, - dir_xmls: str, - dir_out_image_text: Optional[str] = None, - dir_out: str, + def run(self, + *, + overwrite: bool = False, + dir_in: str = "", + dir_in_bin: str = "", + image_filename: str = "", + dir_xmls: str, + dir_out_image_text: str = "", + dir_out: str, + num_jobs: int = 0, + halt_fail: float = 0, ): """ Run OCR. @@ -435,58 +455,115 @@ class Eynollah_ocr(Eynollah): dir_in_bin (str): Prediction with RGB and binarized images for selected pages, should not be the default """ if dir_in: + t0_tot = time.time() ls_imgs = [os.path.join(dir_in, image_filename) - for image_filename in filter(is_image_filename, + for image_filename in filter(is_image_filename, os.listdir(dir_in))] + with ProcessPoolExecutor(max_workers=num_jobs or None, + mp_context=mp.get_context('fork'), + initializer=_set_instance, + initargs=(self,) + ) as exe: + jobs = {} + mngr = mp.get_context('fork').Manager() + n_success = n_fail = 0 + for img_filename in ls_imgs: + logq = mngr.Queue() + jobs[exe.submit(_run_single, img_filename, + dir_out=dir_out, + dir_xmls=dir_xmls, + dir_in_bin=dir_in_bin, + dir_out_image_text=dir_out_image_text, + overwrite=overwrite, + logq=logq)] = img_filename, logq + for job in as_completed(list(jobs)): + img_filename, logq = jobs[job] + loglistener = logging.handlers.QueueListener( + logq, *self.logger.handlers, respect_handler_level=False) + try: + loglistener.start() + job.result() + n_success += 1 + except: + self.logger.exception("Job %s failed", img_filename) + n_fail += 1 + if (halt_fail and + n_fail >= halt_fail * (len(jobs) if halt_fail < 1 else 1)): + self.logger.fatal("terminating after %d failures", n_fail) + for job in jobs: + job.cancel() + break + finally: + loglistener.stop() + self.logger.info("%d of %d jobs successful", n_success, len(jobs)) + self.logger.info("All jobs done in %.1fs", time.time() - t0_tot) else: assert image_filename - ls_imgs = [image_filename] + self.run_single(image_filename, + dir_xmls=dir_xmls, + dir_out=dir_out, + dir_in_bin=dir_in_bin, + dir_out_image_text=dir_out_image_text, + overwrite=overwrite) - for img_filename in ls_imgs: - file_stem = Path(img_filename).stem - page_file_in = os.path.join(dir_xmls, file_stem+'.xml') - out_file_ocr = os.path.join(dir_out, file_stem+'.xml') - - if os.path.exists(out_file_ocr): - if overwrite: - self.logger.warning("will overwrite existing output file '%s'", out_file_ocr) - else: - self.logger.warning("will skip input for existing output file '%s'", out_file_ocr) - continue - - img = cv2.imread(img_filename) - self.logger.info(img_filename) - page_tree = ET.parse(page_file_in, parser = ET.XMLParser(encoding="utf-8")) - page_ns = etree_namespace_for_element_tag(page_tree.getroot().tag) + def run_single(self, + img_filename: str, + dir_xmls: str, + dir_out: str = "", + dir_in_bin: str = "", + dir_out_image_text: str = "", + overwrite: bool = False, + ): + file_stem = Path(img_filename).stem + page_file_in = os.path.join(dir_xmls, file_stem + '.xml') + out_file_ocr = os.path.join(dir_out, file_stem + '.xml') - out_image_with_text = None - if dir_out_image_text: - out_image_with_text = os.path.join(dir_out_image_text, file_stem + '.png') - - img_bin = None - if dir_in_bin: - img_bin = cv2.imread(os.path.join(dir_in_bin, file_stem+'.png')) - - - if self.tr_ocr: - result = self.run_trocr( - img=img, - page_tree=page_tree, - page_ns=page_ns, - ) + if os.path.exists(out_file_ocr): + if overwrite: + self.logger.warning("will overwrite existing output file '%s'", out_file_ocr) else: - result = self.run_cnn( - img=img, - page_tree=page_tree, - page_ns=page_ns, - img_bin=img_bin, - ) + self.logger.warning("will skip input for existing output file '%s'", out_file_ocr) + return + if not os.path.exists(page_file_in): + self.logger.error("will skip missing input file '%s'", page_file_in) + return - self.write_ocr( - result=result, + t0 = time.time() + + img = cv2.imread(img_filename) + self.logger.info(img_filename) + page_tree = ET.parse(page_file_in, parser = ET.XMLParser(encoding="utf-8")) + page_ns = etree_namespace_for_element_tag(page_tree.getroot().tag) + + out_image_with_text = None + if dir_out_image_text: + out_image_with_text = os.path.join(dir_out_image_text, file_stem + '.png') + + img_bin = None + if dir_in_bin: + img_bin = cv2.imread(os.path.join(dir_in_bin, file_stem+'.png')) + + + if self.tr_ocr: + result = self.run_trocr( img=img, page_tree=page_tree, page_ns=page_ns, - out_file_ocr=out_file_ocr, - out_image_with_text=out_image_with_text, ) + else: + result = self.run_cnn( + img=img, + page_tree=page_tree, + page_ns=page_ns, + img_bin=img_bin, + ) + + self.write_ocr( + result=result, + img=img, + page_tree=page_tree, + page_ns=page_ns, + out_file_ocr=out_file_ocr, + out_image_with_text=out_image_with_text, + ) + self.logger.info("Job done in %.1fs", time.time() - t0) From 7e776612a48aaab0bfc672f6600f9902738e1fb7 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Sat, 18 Jul 2026 00:35:50 +0200 Subject: [PATCH 116/121] =?UTF-8?q?cnn-rnn-ocr:=20if=20`dir=5Fin=5Fbin=3D?= =?UTF-8?q?=3Ddir=5Fin`,=20then=20split=20PNG=20and=20rest=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (supports common case that binarized images have same stem, but different file name extension) --- src/eynollah/eynollah_ocr.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 434b865..4d82fb7 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -5,6 +5,7 @@ import logging import logging.handlers from typing import List, Optional from pathlib import Path +from itertools import groupby import os import gc import math @@ -459,6 +460,18 @@ class Eynollah_ocr(Eynollah): ls_imgs = [os.path.join(dir_in, image_filename) for image_filename in filter(is_image_filename, os.listdir(dir_in))] + if dir_in_bin and dir_in_bin == dir_in: + # try filtering PNGs from rest + def pathstem(filename): + return os.path.splitext(filename)[0] + def notpng(filenames): + for filename in filenames: + if not filename.lower().endswith(".png"): + return filename + return filenames[0] + ls_imgs = [notpng(files) + for _, files in groupby(sorted(ls_imgs), + key=pathstem)] with ProcessPoolExecutor(max_workers=num_jobs or None, mp_context=mp.get_context('fork'), initializer=_set_instance, From be8b16160788d5520a3797ea489d59abce3585a2 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Sat, 18 Jul 2026 00:37:19 +0200 Subject: [PATCH 117/121] cnn-rnn-ocr: batch flipped line candidates together with rest --- src/eynollah/eynollah_ocr.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 4d82fb7..fe0bb1f 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -299,8 +299,9 @@ class Eynollah_ocr(Eynollah): ver_index = np.array(ver_index) imgs_rgb = np.stack(imgs_rgb) imgs_bin = np.stack(imgs_bin) - imgs_rgb_ver = imgs_rgb[ver_index > 0, ::-1, ::-1] - imgs_bin_ver = imgs_bin[ver_index > 0, ::-1, ::-1] + if ver_index.any(): + imgs_rgb = np.append(imgs_rgb, imgs_rgb[ver_index > 0, ::-1, ::-1], axis=0) + imgs_bin = np.append(imgs_bin, imgs_bin[ver_index > 0, ::-1, ::-1], axis=0) # inference model now yields (char-bytes, line-prob) instead of vocidx-softmax # (so ctc_decode and inverse StringLookup are included) @@ -308,7 +309,8 @@ class Eynollah_ocr(Eynollah): preds, probs = self.model_zoo.get('ocr').predict((imgs_rgb, imgs_bin), verbose=0) if ver_index.any(): - preds_ver, probs_ver = self.model_zoo.get('ocr').predict((imgs_rgb_ver, imgs_bin_ver), verbose=0) + preds, preds_ver = np.split(preds, [-np.count_nonzero(ver_index)], axis=0) + probs, probs_ver = np.split(probs, [-np.count_nonzero(ver_index)], axis=0) flipped_ver_is_better = np.flatnonzero(probs_ver > probs[ver_index > 0]) if len(flipped_ver_is_better): self.logger.info("%d skewed lines perform better when flipped", len(flipped_ver_is_better)) From 79a9bb0128193554352e3ef3e84595b295c263b9 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Sat, 18 Jul 2026 00:59:57 +0200 Subject: [PATCH 118/121] cnn-rnn-ocr: increase default batch size and VRAM limit --- src/eynollah/eynollah_ocr.py | 2 +- src/eynollah/model_zoo/model_zoo.py | 2 +- src/eynollah/predictor.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index fe0bb1f..748d6a2 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -83,7 +83,7 @@ class Eynollah_ocr(Eynollah): self.logger = logger if logger else logging.getLogger('eynollah.ocr') self.min_conf_value_of_textline_text = min_conf_value_of_textline_text - self.b_s = batch_size or 2 if tr_ocr else 8 + self.b_s = batch_size or (2 if tr_ocr else 64) self.model_zoo = model_zoo self.setup_models(device=device) diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index 5a0a867..f230a31 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -24,7 +24,7 @@ MODEL_VRAM_LIMITS = { "region_fl_np": 1756, "table": 1818, "reading_order": 632, - "ocr": 850, + "ocr": 2400, # 850 for bs 8 } class EynollahModelZoo: diff --git a/src/eynollah/predictor.py b/src/eynollah/predictor.py index 2d892c7..0641e38 100644 --- a/src/eynollah/predictor.py +++ b/src/eynollah/predictor.py @@ -1,5 +1,5 @@ from contextlib import ExitStack -from typing import List, Dict, Tuple, Union +from typing import List, Dict, Sequence, Tuple, Union import logging import logging.handlers import multiprocessing as mp @@ -41,10 +41,10 @@ class Predictor(mp.context.SpawnProcess): def input_shape(self): return self({}) - def predict(self, data: ArrayT, verbose=0) -> ArrayT: + def predict(self, data: Union[Sequence[ArrayT], ArrayT], verbose=0) -> Union[Sequence[ArrayT], ArrayT]: return self(data) - def __call__(self, data: Union[ArrayT, Dict]) -> Union[ArrayT, Tuple]: + def __call__(self, data: Union[Sequence[ArrayT], ArrayT, Dict]) -> Union[ArrayT, Tuple]: # unusable as per python/cpython#79967 #with self.jobid.get_lock(): # would work, but not public: From 4f7c5675fc549f879ccacd4aa5632f916e6cc9b9 Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Sat, 18 Jul 2026 18:02:40 +0200 Subject: [PATCH 119/121] ocr: ensure forked logging handlers also work under pytest --- src/eynollah/eynollah_ocr.py | 6 ++++-- src/eynollah/model_zoo/model_zoo.py | 6 ++++-- tests/cli_tests/test_ocr.py | 5 +++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/eynollah/eynollah_ocr.py b/src/eynollah/eynollah_ocr.py index 748d6a2..b5bf6d8 100644 --- a/src/eynollah/eynollah_ocr.py +++ b/src/eynollah/eynollah_ocr.py @@ -51,7 +51,7 @@ def _run_single(*args, **kwargs): logq = kwargs.pop('logq') # replace all inherited handlers with queue handler logging.root.handlers.clear() - _instance.logger.handlers.clear() + _instance.logger.parent.handlers.clear() handler = logging.handlers.QueueHandler(logq) logging.root.addHandler(handler) return _instance.run_single(*args, **kwargs) @@ -494,7 +494,9 @@ class Eynollah_ocr(Eynollah): for job in as_completed(list(jobs)): img_filename, logq = jobs[job] loglistener = logging.handlers.QueueListener( - logq, *self.logger.handlers, respect_handler_level=False) + logq, *self.logger.handlers, + *self.logger.parent.handlers, + respect_handler_level=False) try: loglistener.start() job.result() diff --git a/src/eynollah/model_zoo/model_zoo.py b/src/eynollah/model_zoo/model_zoo.py index f230a31..5291bf8 100644 --- a/src/eynollah/model_zoo/model_zoo.py +++ b/src/eynollah/model_zoo/model_zoo.py @@ -24,7 +24,7 @@ MODEL_VRAM_LIMITS = { "region_fl_np": 1756, "table": 1818, "reading_order": 632, - "ocr": 2400, # 850 for bs 8 + "ocr": 2600, # 850 for bs 8 } class EynollahModelZoo: @@ -390,7 +390,9 @@ class EynollahModelZoo: if isinstance(provider0, tuple): provider0 = provider0[0] self.logger.info("using %s with ONNX provider %s for model %s", - "GPU %d" % gpu if gpu >= 0 else "CPU", + "GPU %d" % gpu if (gpu >= 0 and not + provider0.startswith("CPU")) + else "CPU", provider0[:-17], model_category) model = ort.InferenceSession( model_path, diff --git a/tests/cli_tests/test_ocr.py b/tests/cli_tests/test_ocr.py index cf34e06..c4fba04 100644 --- a/tests/cli_tests/test_ocr.py +++ b/tests/cli_tests/test_ocr.py @@ -30,7 +30,7 @@ def test_run_eynollah_ocr_filename( '-o', str(outfile.parent), ] + options, [ - 'output filename:' + str(infile) ] ) assert outfile.exists() @@ -57,7 +57,8 @@ def test_run_eynollah_ocr_directory( '-o', str(outdir), ], [ - 'output filename:' + 'Job done in', + 'All jobs done in', ] ) assert len(list(outdir.iterdir())) == 2 From d0a55a1fcbe9a973ec95e3fe13cc8d3019406d0b Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Sun, 19 Jul 2026 03:21:38 +0200 Subject: [PATCH 120/121] reading order: drop hsep/head elongation mechanism (too many FP) --- src/eynollah/utils/__init__.py | 35 ++-------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/src/eynollah/utils/__init__.py b/src/eynollah/utils/__init__.py index 621b9ec..15e8110 100644 --- a/src/eynollah/utils/__init__.py +++ b/src/eynollah/utils/__init__.py @@ -1666,37 +1666,6 @@ def return_boxes_of_images_by_order_of_reading_new( #print(peaks_neg_tot,'peaks_neg_tot') peaks_neg_tot_tables.append(peaks_neg_tot) - all_columns = set(range(len(peaks_neg_tot) - 1)) - #print("all_columns", all_columns) - - # elongate horizontal separators+headings as much as possible without overlap - args_nonver = matrix_new[:, 9] != 1 - for i in np.flatnonzero(args_nonver): - xmin, xmax, ymin, ymax, typ = matrix_new[i, [2, 3, 6, 7, 9]] - cut = sep_mask[ymin: ymax] - # dbg_imshow([xmin, xmax, ymin, ymax], "separator %d (%s)" % (i, "heading" if typ else "horizontal")) - starting = xmin - peaks_neg_tot - min_start = np.flatnonzero(starting >= 0)[-1] # last left-of - ending = xmax - peaks_neg_tot - max_end = np.flatnonzero(ending <= 0)[0] # first right-of - # skip elongation unless this is already a multi-column separator/heading: - if not max_end - min_start > 1: - continue - # is there anything left of min_start? - for j in range(min_start): - # dbg_imshow([peaks_neg_tot[j], xmin, ymin, ymax], "start of %d candidate %d" % (i, j)) - if not np.any(cut[:, peaks_neg_tot[j]: xmin]): - # print("elongated sep", i, "typ", typ, "start", xmin, "to", j, peaks_neg_tot[j]) - matrix_new[i, 2] = peaks_neg_tot[j] + 1 # elongate to start of this column - break - # is there anything right of max_end? - for j in range(len(peaks_neg_tot) - 1, max_end, -1): - # dbg_imshow([xmax, peaks_neg_tot[j], ymin, ymax], "end of %d candidate %d" % (i, j)) - if not np.any(cut[:, xmax: peaks_neg_tot[j]]): - # print("elongated sep", i, "typ", typ, "end", xmax, "to", j, peaks_neg_tot[j]) - matrix_new[i, 3] = peaks_neg_tot[j] - 1 # elongate to end of this column - break - args_hor = matrix_new[:, 9] == 0 x_min_hor_some = matrix_new[:, 2][args_hor] x_max_hor_some = matrix_new[:, 3][args_hor] @@ -1827,8 +1796,8 @@ def return_boxes_of_images_by_order_of_reading_new( (peaks_neg_tot[last] - peaks_neg_tot[start])) > 0.1 * l_count # But do allow cutting tiny passages with less 10% of height # (i.e. label is already almost separated by columns) - and sum(text_mask[ - y_top: y_bot, peaks_neg_tot[start + 1]]) > 0.1 * (y_bot - y_top)), + and text_mask[y_top: y_bot, + peaks_neg_tot[start + 1]].sum() > 0.1 * (y_bot - y_top)), # Otherwise advance only 1 column. default=start + 1) def add_sep(cur): From 5e3fde31d9f91adb672f45ec416be98ff9d0032e Mon Sep 17 00:00:00 2001 From: Robert Sachunsky Date: Sun, 19 Jul 2026 03:22:56 +0200 Subject: [PATCH 121/121] calculate_width_height_by_columns: do allow highest enlargement when confident --- src/eynollah/eynollah.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eynollah/eynollah.py b/src/eynollah/eynollah.py index b1169a6..becf802 100644 --- a/src/eynollah/eynollah.py +++ b/src/eynollah/eynollah.py @@ -276,7 +276,7 @@ class Eynollah: img_new = np.copy(img) img_is_resized = False #elif conf_col < 0.8 and img_h_new >= 8000: - elif img_h_new >= 8000: + elif conf_col < 0.9 and img_h_new >= 8000: # don't upsample if too large img_new = np.copy(img) img_is_resized = False