Refactor CLI for consistent logging and late imports

This commit is contained in:
kba 2025-10-17 17:47:59 +02:00
parent 38c028c6b5
commit 6c89888166
9 changed files with 88 additions and 112 deletions

View file

@ -1,15 +1,34 @@
import sys from dataclasses import dataclass
import click
import logging import logging
from ocrd_utils import initLogging, getLevelName, getLogger import sys
from eynollah.eynollah import Eynollah, Eynollah_ocr from typing import Union
from eynollah.sbb_binarize import SbbBinarizer
from eynollah.image_enhancer import Enhancer import click
from eynollah.mb_ro_on_layout import machine_based_reading_order_on_layout
@dataclass
class EynollahCliContext():
log_level : Union[str, None] = 'INFO'
@click.group() @click.group()
def main(): @click.option(
pass "--log_level",
"-l",
type=click.Choice(['OFF', 'DEBUG', 'INFO', 'WARN', 'ERROR']),
help="Override log level globally to this",
)
@click.pass_context
def main(ctx, log_level):
"""
eynollah - Document Layout Analysis, Image Enhancement, OCR
"""
ctx.obj = EynollahCliContext(log_level=log_level)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.NOTSET)
formatter = logging.Formatter('%(asctime)s.%(msecs)03d %(levelname)s %(name)s - %(message)s', datefmt='%H:%M:%S')
console_handler.setFormatter(formatter)
logging.getLogger('eynollah').addHandler(console_handler)
logging.getLogger('eynollah').setLevel(ctx.obj.log_level or logging.INFO)
@main.command() @main.command()
@click.option( @click.option(
@ -38,18 +57,13 @@ def main():
type=click.Path(exists=True, file_okay=False), type=click.Path(exists=True, file_okay=False),
required=True, required=True,
) )
@click.option( def machine_based_reading_order(input, dir_in, out, model):
"--log_level", """
"-l", Generate ReadingOrder with a ML model
type=click.Choice(['OFF', 'DEBUG', 'INFO', 'WARN', 'ERROR']), """
help="Override log level globally to this", from .mb_ro_on_layout import machine_based_reading_order_on_layout
)
def machine_based_reading_order(input, dir_in, out, model, log_level):
assert bool(input) != bool(dir_in), "Either -i (single input) or -di (directory) must be provided, but not both." 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) orderer = machine_based_reading_order_on_layout(model)
if log_level:
orderer.logger.setLevel(getLevelName(log_level))
orderer.run(xml_filename=input, orderer.run(xml_filename=input,
dir_in=dir_in, dir_in=dir_in,
@ -79,17 +93,13 @@ def machine_based_reading_order(input, dir_in, out, model, log_level):
type=click.Path(file_okay=True, dir_okay=True), type=click.Path(file_okay=True, dir_okay=True),
required=True, required=True,
) )
@click.option( def binarization(patches, model_dir, input_image, dir_in, output):
"--log_level", """
"-l", Binarize images with a ML model
type=click.Choice(['OFF', 'DEBUG', 'INFO', 'WARN', 'ERROR']), """
help="Override log level globally to this",
)
def binarization(patches, model_dir, input_image, dir_in, output, log_level):
assert bool(input_image) != bool(dir_in), "Either -i (single input) or -di (directory) must be provided, but not both." assert bool(input_image) != bool(dir_in), "Either -i (single input) or -di (directory) must be provided, but not both."
from .sbb_binarize import SbbBinarizer
binarizer = SbbBinarizer(model_dir) binarizer = SbbBinarizer(model_dir)
if log_level:
binarizer.log.setLevel(getLevelName(log_level))
binarizer.run(image_path=input_image, use_patches=patches, output=output, dir_in=dir_in) binarizer.run(image_path=input_image, use_patches=patches, output=output, dir_in=dir_in)
@ -144,24 +154,18 @@ def binarization(patches, model_dir, input_image, dir_in, output, log_level):
is_flag=True, is_flag=True,
help="if this parameter set to true, this tool will save the enhanced image in org scale.", help="if this parameter set to true, this tool will save the enhanced image in org scale.",
) )
@click.option( def enhancement(image, out, overwrite, dir_in, model, num_col_upper, num_col_lower, save_org_scale):
"--log_level", """
"-l", Enhance image
type=click.Choice(['OFF', 'DEBUG', 'INFO', 'WARN', 'ERROR']), """
help="Override log level globally to this",
)
def enhancement(image, out, overwrite, dir_in, model, num_col_upper, num_col_lower, save_org_scale, log_level):
assert bool(image) != bool(dir_in), "Either -i (single input) or -di (directory) must be provided, but not both." assert bool(image) != bool(dir_in), "Either -i (single input) or -di (directory) must be provided, but not both."
initLogging() from .image_enhancer import Enhancer
enhancer = Enhancer( enhancer = Enhancer(
model, model,
num_col_upper=num_col_upper, num_col_upper=num_col_upper,
num_col_lower=num_col_lower, num_col_lower=num_col_lower,
save_org_scale=save_org_scale, save_org_scale=save_org_scale,
) )
if log_level:
enhancer.logger.setLevel(getLevelName(log_level))
enhancer.run(overwrite=overwrite, enhancer.run(overwrite=overwrite,
dir_in=dir_in, dir_in=dir_in,
image_filename=image, image_filename=image,
@ -366,30 +370,10 @@ def enhancement(image, out, overwrite, dir_in, model, num_col_upper, num_col_low
is_flag=True, is_flag=True,
help="if this parameter set to true, this tool will ignore layout detection and reading order. It means that textline detection will be done within printspace and contours of textline will be written in xml output file.", help="if this parameter set to true, this tool will ignore layout detection and reading order. It means that textline detection will be done within printspace and contours of textline will be written in xml output file.",
) )
# TODO move to top-level CLI context def layout(image, out, overwrite, dir_in, model, model_version, save_images, save_layout, save_deskewed, save_all, extract_only_images, save_page, enable_plotting, allow_enhancement, curved_line, textline_light, full_layout, tables, right2left, input_binary, allow_scaling, headers_off, light_version, reading_order_machine_based, do_ocr, transformer_ocr, batch_size_ocr, num_col_upper, num_col_lower, threshold_art_class_textline, threshold_art_class_layout, skip_layout_and_reading_order, ignore_page_extraction):
@click.option( """
"--log_level", Detect Layout (with optional image enhancement and reading order detection)
"-l", """
type=click.Choice(['OFF', 'DEBUG', 'INFO', 'WARN', 'ERROR']),
help="Override 'eynollah' log level globally to this",
)
#
@click.option(
"--setup-logging",
is_flag=True,
help="Setup a basic console logger",
)
def layout(image, out, overwrite, dir_in, model, model_version, save_images, save_layout, save_deskewed, save_all, extract_only_images, save_page, enable_plotting, allow_enhancement, curved_line, textline_light, full_layout, tables, right2left, input_binary, allow_scaling, headers_off, light_version, reading_order_machine_based, do_ocr, transformer_ocr, batch_size_ocr, num_col_upper, num_col_lower, threshold_art_class_textline, threshold_art_class_layout, skip_layout_and_reading_order, ignore_page_extraction, log_level, setup_logging):
if setup_logging:
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(message)s')
console_handler.setFormatter(formatter)
getLogger('eynollah').addHandler(console_handler)
getLogger('eynollah').setLevel(logging.INFO)
else:
initLogging()
assert enable_plotting or not save_layout, "Plotting with -sl also requires -ep" assert enable_plotting or not save_layout, "Plotting with -sl also requires -ep"
assert enable_plotting or not save_deskewed, "Plotting with -sd also requires -ep" assert enable_plotting or not save_deskewed, "Plotting with -sd also requires -ep"
assert enable_plotting or not save_all, "Plotting with -sa also requires -ep" assert enable_plotting or not save_all, "Plotting with -sa also requires -ep"
@ -409,6 +393,7 @@ def layout(image, out, overwrite, dir_in, model, model_version, save_images, sav
assert not extract_only_images or not right2left, "Image extraction -eoi can not be set alongside right2left -r2l" assert not extract_only_images or not right2left, "Image extraction -eoi can not be set alongside right2left -r2l"
assert not extract_only_images or not headers_off, "Image extraction -eoi can not be set alongside headers_off -ho" assert not extract_only_images or not headers_off, "Image extraction -eoi can not be set alongside headers_off -ho"
assert bool(image) != bool(dir_in), "Either -i (single input) or -di (directory) must be provided, but not both." assert bool(image) != bool(dir_in), "Either -i (single input) or -di (directory) must be provided, but not both."
from .eynollah import Eynollah
eynollah = Eynollah( eynollah = Eynollah(
model, model,
model_versions=model_version, model_versions=model_version,
@ -435,8 +420,6 @@ def layout(image, out, overwrite, dir_in, model, model_version, save_images, sav
threshold_art_class_textline=threshold_art_class_textline, threshold_art_class_textline=threshold_art_class_textline,
threshold_art_class_layout=threshold_art_class_layout, threshold_art_class_layout=threshold_art_class_layout,
) )
if log_level:
eynollah.logger.setLevel(getLevelName(log_level))
eynollah.run(overwrite=overwrite, eynollah.run(overwrite=overwrite,
image_filename=image, image_filename=image,
dir_in=dir_in, dir_in=dir_in,
@ -537,16 +520,11 @@ def layout(image, out, overwrite, dir_in, model, model_version, save_images, sav
"-min_conf", "-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.", 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(
"--log_level",
"-l",
type=click.Choice(['OFF', 'DEBUG', 'INFO', 'WARN', 'ERROR']),
help="Override log level globally to this",
)
def ocr(image, dir_in, dir_in_bin, dir_xmls, out, dir_out_image_text, overwrite, model, model_name, tr_ocr, export_textline_images_and_text, do_not_mask_with_textline_contour, batch_size, dataset_abbrevation, min_conf_value_of_textline_text, log_level): def ocr(image, dir_in, dir_in_bin, dir_xmls, out, dir_out_image_text, overwrite, model, model_name, tr_ocr, export_textline_images_and_text, do_not_mask_with_textline_contour, batch_size, dataset_abbrevation, min_conf_value_of_textline_text):
initLogging() """
Recognize text with a CNN/RNN or transformer ML model.
"""
assert bool(model) != bool(model_name), "Either -m (model directory) or --model_name (specific model name) must be provided." assert bool(model) != bool(model_name), "Either -m (model directory) or --model_name (specific model name) must be provided."
assert not export_textline_images_and_text or not tr_ocr, "Exporting textline and text -etit can not be set alongside transformer ocr -tr_ocr" assert not export_textline_images_and_text or not tr_ocr, "Exporting textline and text -etit can not be set alongside transformer ocr -tr_ocr"
assert not export_textline_images_and_text or not model, "Exporting textline and text -etit can not be set alongside model -m" assert not export_textline_images_and_text or not model, "Exporting textline and text -etit can not be set alongside model -m"
@ -554,6 +532,7 @@ def ocr(image, dir_in, dir_in_bin, dir_xmls, out, dir_out_image_text, overwrite,
assert not export_textline_images_and_text or not dir_in_bin, "Exporting textline and text -etit can not be set alongside directory of bin images -dib" assert not export_textline_images_and_text or not dir_in_bin, "Exporting textline and text -etit can not be set alongside directory of bin images -dib"
assert not export_textline_images_and_text or not dir_out_image_text, "Exporting textline and text -etit can not be set alongside directory of images with predicted text -doit" assert not export_textline_images_and_text or not dir_out_image_text, "Exporting textline and text -etit can not be set alongside directory of images with predicted text -doit"
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 import Eynollah_ocr
eynollah_ocr = Eynollah_ocr( eynollah_ocr = Eynollah_ocr(
dir_models=model, dir_models=model,
model_name=model_name, model_name=model_name,
@ -562,10 +541,7 @@ def ocr(image, dir_in, dir_in_bin, dir_xmls, out, dir_out_image_text, overwrite,
do_not_mask_with_textline_contour=do_not_mask_with_textline_contour, do_not_mask_with_textline_contour=do_not_mask_with_textline_contour,
batch_size=batch_size, batch_size=batch_size,
pref_of_dataset=dataset_abbrevation, pref_of_dataset=dataset_abbrevation,
min_conf_value_of_textline_text=min_conf_value_of_textline_text, min_conf_value_of_textline_text=min_conf_value_of_textline_text)
)
if log_level:
eynollah_ocr.logger.setLevel(getLevelName(log_level))
eynollah_ocr.run(overwrite=overwrite, eynollah_ocr.run(overwrite=overwrite,
dir_in=dir_in, dir_in=dir_in,
dir_in_bin=dir_in_bin, dir_in_bin=dir_in_bin,

View file

@ -7,6 +7,7 @@ document layout analysis (segmentation) with output in PAGE-XML
""" """
# cannot use importlib.resources until we move to 3.9+ forimportlib.resources.files # cannot use importlib.resources until we move to 3.9+ forimportlib.resources.files
import logging
import sys import sys
if sys.version_info < (3, 10): if sys.version_info < (3, 10):
import importlib_resources import importlib_resources
@ -19,8 +20,7 @@ import math
import os import os
import sys import sys
import time import time
from typing import Dict, List, Optional, Tuple from typing import List, Optional, Tuple
import atexit
import warnings import warnings
from functools import partial from functools import partial
from pathlib import Path from pathlib import Path
@ -39,7 +39,7 @@ from scipy.ndimage import gaussian_filter1d
from numba import cuda from numba import cuda
from skimage.morphology import skeletonize from skimage.morphology import skeletonize
from ocrd import OcrdPage from ocrd import OcrdPage
from ocrd_utils import getLogger, tf_disable_interactive_logs from ocrd_utils import tf_disable_interactive_logs
import statistics import statistics
try: try:
@ -60,8 +60,6 @@ tf_disable_interactive_logs()
import tensorflow as tf import tensorflow as tf
from tensorflow.python.keras import backend as K from tensorflow.python.keras import backend as K
from tensorflow.keras.models import load_model from tensorflow.keras.models import load_model
tf.get_logger().setLevel("ERROR")
warnings.filterwarnings("ignore")
# use tf1 compatibility for keras backend # use tf1 compatibility for keras backend
from tensorflow.compat.v1.keras.backend import set_session from tensorflow.compat.v1.keras.backend import set_session
from tensorflow.keras import layers from tensorflow.keras import layers
@ -230,8 +228,9 @@ class Eynollah:
threshold_art_class_layout: Optional[float] = None, threshold_art_class_layout: Optional[float] = None,
threshold_art_class_textline: Optional[float] = None, threshold_art_class_textline: Optional[float] = None,
skip_layout_and_reading_order : bool = False, skip_layout_and_reading_order : bool = False,
logger : Optional[logging.Logger] = None,
): ):
self.logger = getLogger('eynollah') self.logger = logger or logging.getLogger('eynollah')
self.plotter = None self.plotter = None
if skip_layout_and_reading_order: if skip_layout_and_reading_order:
@ -4888,14 +4887,13 @@ class Eynollah_ocr:
do_not_mask_with_textline_contour=False, do_not_mask_with_textline_contour=False,
pref_of_dataset=None, pref_of_dataset=None,
min_conf_value_of_textline_text : Optional[float]=None, min_conf_value_of_textline_text : Optional[float]=None,
logger=None,
): ):
self.model_name = model_name self.model_name = model_name
self.tr_ocr = tr_ocr self.tr_ocr = tr_ocr
self.export_textline_images_and_text = export_textline_images_and_text self.export_textline_images_and_text = export_textline_images_and_text
self.do_not_mask_with_textline_contour = do_not_mask_with_textline_contour self.do_not_mask_with_textline_contour = do_not_mask_with_textline_contour
self.pref_of_dataset = pref_of_dataset self.pref_of_dataset = pref_of_dataset
self.logger = logger if logger else getLogger('eynollah') self.logger = logging.getLogger('eynollah')
if not export_textline_images_and_text: if not export_textline_images_and_text:
if min_conf_value_of_textline_text: if min_conf_value_of_textline_text:

View file

@ -2,7 +2,7 @@
Image enhancer. The output can be written as same scale of input or in new predicted scale. Image enhancer. The output can be written as same scale of input or in new predicted scale.
""" """
from logging import Logger import logging
import os import os
import time import time
from typing import Optional from typing import Optional
@ -11,7 +11,6 @@ import gc
import cv2 import cv2
import numpy as np import numpy as np
from ocrd_utils import getLogger, tf_disable_interactive_logs
import tensorflow as tf import tensorflow as tf
from skimage.morphology import skeletonize from skimage.morphology import skeletonize
from tensorflow.keras.models import load_model from tensorflow.keras.models import load_model
@ -35,7 +34,6 @@ class Enhancer:
num_col_upper : Optional[int] = None, num_col_upper : Optional[int] = None,
num_col_lower : Optional[int] = None, num_col_lower : Optional[int] = None,
save_org_scale : bool = False, save_org_scale : bool = False,
logger : Optional[Logger] = None,
): ):
self.input_binary = False self.input_binary = False
self.light_version = False self.light_version = False
@ -49,7 +47,7 @@ class Enhancer:
else: else:
self.num_col_lower = num_col_lower self.num_col_lower = num_col_lower
self.logger = logger if logger else getLogger('enhancement') self.logger = logging.getLogger('eynollah.enhancement')
self.dir_models = dir_models self.dir_models = dir_models
self.model_dir_of_binarization = dir_models + "/eynollah-binarization_20210425" self.model_dir_of_binarization = dir_models + "/eynollah-binarization_20210425"
self.model_dir_of_enhancement = dir_models + "/eynollah-enhancement_20210425" self.model_dir_of_enhancement = dir_models + "/eynollah-enhancement_20210425"

View file

@ -1,8 +1,8 @@
""" """
Image enhancer. The output can be written as same scale of input or in new predicted scale. Machine learning based reading order detection
""" """
from logging import Logger import logging
import os import os
import time import time
from typing import Optional from typing import Optional
@ -11,7 +11,6 @@ import xml.etree.ElementTree as ET
import cv2 import cv2
import numpy as np import numpy as np
from ocrd_utils import getLogger
import statistics import statistics
import tensorflow as tf import tensorflow as tf
from tensorflow.keras.models import load_model from tensorflow.keras.models import load_model
@ -33,9 +32,9 @@ class machine_based_reading_order_on_layout:
def __init__( def __init__(
self, self,
dir_models : str, dir_models : str,
logger : Optional[Logger] = None, logger : Optional[logging.Logger] = None,
): ):
self.logger = logger if logger else getLogger('mbreorder') self.logger = logger or logging.getLogger('eynollah.mbreorder')
self.dir_models = dir_models self.dir_models = dir_models
self.model_reading_order_dir = dir_models + "/model_eynollah_reading_order_20250824" self.model_reading_order_dir = dir_models + "/model_eynollah_reading_order_20250824"

View file

@ -34,6 +34,7 @@ class SbbBinarizeProcessor(Processor):
Set up the model prior to processing. Set up the model prior to processing.
""" """
# resolve relative path via OCR-D ResourceManager # resolve relative path via OCR-D ResourceManager
assert isinstance(self.parameter, dict)
model_path = self.resolve_resource(self.parameter['model']) model_path = self.resolve_resource(self.parameter['model'])
self.binarizer = SbbBinarizer(model_dir=model_path, logger=self.logger) self.binarizer = SbbBinarizer(model_dir=model_path, logger=self.logger)

View file

@ -32,8 +32,8 @@ class EynollahProcessor(Processor):
allow_scaling=self.parameter['allow_scaling'], allow_scaling=self.parameter['allow_scaling'],
headers_off=self.parameter['headers_off'], headers_off=self.parameter['headers_off'],
tables=self.parameter['tables'], tables=self.parameter['tables'],
logger=self.logger
) )
self.eynollah.logger = self.logger
self.eynollah.plotter = None self.eynollah.plotter = None
def shutdown(self): def shutdown(self):

View file

@ -2,19 +2,16 @@
Tool to load model and binarize a given image. Tool to load model and binarize a given image.
""" """
import sys
from glob import glob from glob import glob
import os import os
import logging import logging
from typing import Optional
import numpy as np import numpy as np
from PIL import Image
import cv2 import cv2
from ocrd_utils import tf_disable_interactive_logs
tf_disable_interactive_logs()
import tensorflow as tf import tensorflow as tf
from tensorflow.keras.models import load_model from keras.models import load_model
from tensorflow.python.keras import backend as tensorflow_backend from keras import backend as tensorflow_backend
from .utils import is_image_filename from .utils import is_image_filename
@ -23,9 +20,13 @@ def resize_image(img_in, input_height, input_width):
class SbbBinarizer: class SbbBinarizer:
def __init__(self, model_dir, logger=None): def __init__(
self,
model_dir,
logger: Optional[logging.Logger] = None,
):
self.model_dir = model_dir self.model_dir = model_dir
self.log = logger if logger else logging.getLogger('SbbBinarizer') self.logger = logger or logging.getLogger('eynollah.binarize')
self.start_new_session() self.start_new_session()
@ -325,7 +326,7 @@ class SbbBinarizer:
image = cv2.imread(image_path) image = cv2.imread(image_path)
img_last = 0 img_last = 0
for n, (model, model_file) in enumerate(zip(self.models, self.model_files)): for n, (model, model_file) in enumerate(zip(self.models, self.model_files)):
self.log.info('Predicting with model %s [%s/%s]' % (model_file, n + 1, len(self.model_files))) self.logger.debug('Binarizing with model %s [%s/%s]' % (model_file, n + 1, len(self.model_files)))
res = self.predict(model, image, use_patches) res = self.predict(model, image, use_patches)
@ -345,17 +346,19 @@ class SbbBinarizer:
img_last[:, :][img_last[:, :] > 0] = 255 img_last[:, :][img_last[:, :] > 0] = 255
img_last = (img_last[:, :] == 0) * 255 img_last = (img_last[:, :] == 0) * 255
if output: if output:
self.logger.info('Writing binarized image to %s', output)
cv2.imwrite(output, img_last) cv2.imwrite(output, img_last)
return img_last return img_last
else: else:
ls_imgs = list(filter(is_image_filename, os.listdir(dir_in))) ls_imgs = list(filter(is_image_filename, os.listdir(dir_in)))
for image_name in ls_imgs: self.logger.info("Found %d image files to binarize in %s", len(ls_imgs), dir_in)
for i, image_name in enumerate(ls_imgs):
image_stem = image_name.split('.')[0] image_stem = image_name.split('.')[0]
print(image_name,'image_name') self.logger.info('Binarizing [%3d/%d] %s', i + 1, len(ls_imgs), image_name)
image = cv2.imread(os.path.join(dir_in,image_name) ) image = cv2.imread(os.path.join(dir_in,image_name) )
img_last = 0 img_last = 0
for n, (model, model_file) in enumerate(zip(self.models, self.model_files)): for n, (model, model_file) in enumerate(zip(self.models, self.model_files)):
self.log.info('Predicting with model %s [%s/%s]' % (model_file, n + 1, len(self.model_files))) self.logger.debug('Predicting with model %s [%s/%s]' % (model_file, n + 1, len(self.model_files)))
res = self.predict(model, image, use_patches) res = self.predict(model, image, use_patches)
@ -375,4 +378,6 @@ class SbbBinarizer:
img_last[:, :][img_last[:, :] > 0] = 255 img_last[:, :][img_last[:, :] > 0] = 255
img_last = (img_last[:, :] == 0) * 255 img_last = (img_last[:, :] == 0) * 255
cv2.imwrite(os.path.join(output, image_stem + '.png'), img_last) output_filename = os.path.join(output, image_stem + '.png')
self.logger.info('Writing binarized image to %s', output_filename)
cv2.imwrite(output_filename, img_last)

View file

@ -19,7 +19,6 @@ from .contour import (contours_in_same_horizon,
find_new_features_of_contours, find_new_features_of_contours,
return_contours_of_image, return_contours_of_image,
return_parent_contours) return_parent_contours)
def pairwise(iterable): def pairwise(iterable):
# pairwise('ABCDEFG') → AB BC CD DE EF FG # pairwise('ABCDEFG') → AB BC CD DE EF FG

View file

@ -2,11 +2,11 @@
# pylint: disable=import-error # pylint: disable=import-error
from pathlib import Path from pathlib import Path
import os.path import os.path
import logging
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from .utils.xml import create_page_xml, xml_reading_order from .utils.xml import create_page_xml, xml_reading_order
from .utils.counter import EynollahIdCounter from .utils.counter import EynollahIdCounter
from ocrd_utils import getLogger
from ocrd_models.ocrd_page import ( from ocrd_models.ocrd_page import (
BorderType, BorderType,
CoordsType, CoordsType,
@ -24,7 +24,7 @@ import numpy as np
class EynollahXmlWriter: class EynollahXmlWriter:
def __init__(self, *, dir_out, image_filename, curved_line,textline_light, pcgts=None): def __init__(self, *, dir_out, image_filename, curved_line,textline_light, pcgts=None):
self.logger = getLogger('eynollah.writer') self.logger = logging.getLogger('eynollah.writer')
self.counter = EynollahIdCounter() self.counter = EynollahIdCounter()
self.dir_out = dir_out self.dir_out = dir_out
self.image_filename = image_filename self.image_filename = image_filename