diff --git a/calamari_ocr/ocr/dataset/datareader/pagexml/reader.py b/calamari_ocr/ocr/dataset/datareader/pagexml/reader.py index 45e9026f..71a4da5d 100644 --- a/calamari_ocr/ocr/dataset/datareader/pagexml/reader.py +++ b/calamari_ocr/ocr/dataset/datareader/pagexml/reader.py @@ -476,6 +476,11 @@ def _bounding_rect_from_points(points: List[Tuple[int, int]]) -> Tuple[int, int, def _coords_for_rectangle(x, y, width, height): return f"{int(x)},{int(y)} {int(x+width)},{int(y)} {int(x+width)},{int(y+height)} {int(x)},{int(y+height)}" + @staticmethod + def _make_subelement(parent, tag, attrib=None): + tag = '{' + parent.nsmap.get(parent.prefix, '') + '}' + tag + return etree.SubElement(parent, tag, attrib=attrib, nsmap=parent.nsmap) + def _store_old_word(self, word_xml, ns): word_xml.set("id", f"{word_xml.get('id')}_old") @@ -496,11 +501,11 @@ def _store_glyph(self, glyph, word_id, word_xml, line_x, line_y, line_height, gl glyph_xml = word_xml.find(f'./ns:Glyph[@id="{glyph_id}"]', namespaces=ns) if glyph_xml is None: - glyph_xml = etree.SubElement(word_xml, "Glyph", attrib={"id": glyph_id}) + glyph_xml = self._make_subelement(word_xml, "Glyph", attrib={"id": glyph_id}) coords_xml = glyph_xml.find("./ns:Coords", namespaces=ns) if coords_xml is None: - coords_xml = etree.SubElement(glyph_xml, "Coords") + coords_xml = self._make_subelement(glyph_xml, "Coords") glyph_x, glyph_y = glyph.global_start + line_x, line_y glyph_width, glyph_height = glyph.global_end - glyph.global_start, line_height @@ -513,7 +518,7 @@ def _store_glyph(self, glyph, word_id, word_xml, line_x, line_y, line_height, gl textequiv_xml = glyph_xml.find(f'./ns:TextEquiv[@index="{glyph_index}"]', namespaces=ns) if textequiv_xml is None: - textequiv_xml = etree.SubElement(glyph_xml, "TextEquiv") + textequiv_xml = self._make_subelement(glyph_xml, "TextEquiv") textequiv_xml.set("index", str(glyph_index)) if self.params.output_confidences: @@ -521,7 +526,7 @@ def _store_glyph(self, glyph, word_id, word_xml, line_x, line_y, line_height, gl u_xml = textequiv_xml.find("./ns:Unicode", namespaces=ns) if u_xml is None: - u_xml = etree.SubElement(textequiv_xml, "Unicode") + u_xml = self._make_subelement(textequiv_xml, "Unicode") u_xml.text = char def _store_words(self, words, line_xml, line_coords, ns) -> float: @@ -554,11 +559,11 @@ def _store_words(self, words, line_xml, line_coords, ns) -> float: word_xml = line_xml.find(f'./ns:Word[@id="{word_id}"]', namespaces=ns) if word_xml is None: # no word with this id, create a new word element - word_xml = etree.SubElement(line_xml, "Word", attrib={"id": word_id}) + word_xml = self._make_subelement(line_xml, "Word", attrib={"id": word_id}) coords_xml = word_xml.find("./ns:Coords", namespaces=ns) if coords_xml is None: - coords_xml = etree.SubElement(word_xml, "Coords") + coords_xml = self._make_subelement(word_xml, "Coords") word_text = "" word_confidence = 1 @@ -575,7 +580,7 @@ def _store_words(self, words, line_xml, line_coords, ns) -> float: textequiv_xml = word_xml.find(f'./ns:TextEquiv[@index="{self.params.text_index}"]', namespaces=ns) if textequiv_xml is None: - textequiv_xml = etree.SubElement(word_xml, "TextEquiv") + textequiv_xml = self._make_subelement(word_xml, "TextEquiv") textequiv_xml.set("index", str(self.params.text_index)) if self.params.output_confidences: @@ -583,7 +588,7 @@ def _store_words(self, words, line_xml, line_coords, ns) -> float: u_xml = textequiv_xml.find("./ns:Unicode", namespaces=ns) if u_xml is None: - u_xml = etree.SubElement(textequiv_xml, "Unicode") + u_xml = self._make_subelement(textequiv_xml, "Unicode") u_xml.text = word_text word_x, word_y = word[0].global_start + line_x, line_y diff --git a/calamari_ocr/ocr/model/graph.py b/calamari_ocr/ocr/model/graph.py index 1c8846c0..6feb723f 100644 --- a/calamari_ocr/ocr/model/graph.py +++ b/calamari_ocr/ocr/model/graph.py @@ -56,6 +56,9 @@ def __init__(self, params: ModelParams, name="CalamariGraph", **kwargs): self.reshape = ToInputDimsLayerParams(dims=3).create() self.logits = KL.Dense(params.classes, name="logits") self.softmax = KL.Softmax(name="softmax") + self.temperature = ( + tf.constant(params.temperature, dtype=tf.float32, name="temperature") if params.temperature > 0 else None + ) def build_graph(self, inputs, training=None): params: ModelParams = self._params @@ -90,6 +93,9 @@ def build_graph(self, inputs, training=None): blank_last_softmax = self.softmax(blank_last_logits) logits = tf.roll(blank_last_logits, shift=1, axis=-1) + if self.temperature != None: + logits = tf.divide(logits, self.temperature) ### TEST scale, seems to work... + softmax = tf.nn.softmax(logits) greedy_decoded = ctc.ctc_greedy_decoder( diff --git a/calamari_ocr/ocr/model/params.py b/calamari_ocr/ocr/model/params.py index 0d9cdfde..dbab446b 100644 --- a/calamari_ocr/ocr/model/params.py +++ b/calamari_ocr/ocr/model/params.py @@ -53,6 +53,7 @@ class ModelParams(ModelBaseParams): classes: int = -1 ctc_merge_repeated: bool = True ensemble: int = 0 # For usage with the ensemble-model graph + temperature: float = field(default=-1, metadata=pai_meta(help="Value to divide logits by (temperature scaling).")) masking_mode: int = False # This parameter is for evaluation only and should not be used in production @staticmethod diff --git a/calamari_ocr/ocr/predict/predictor.py b/calamari_ocr/ocr/predict/predictor.py index f9b72d57..6f5c35fd 100644 --- a/calamari_ocr/ocr/predict/predictor.py +++ b/calamari_ocr/ocr/predict/predictor.py @@ -25,7 +25,7 @@ def from_checkpoint(params: PredictorParams, checkpoint: str, auto_update_checkp predictor = Predictor(params, scenario.create_data()) predictor.set_model( keras.models.load_model( - ckpt.ckpt_path + ".h5", + ckpt.ckpt_path, custom_objects=CalamariScenario.model_cls().all_custom_objects(), ) ) @@ -50,11 +50,12 @@ def from_paths( DeviceConfig(predictor_params.device) checkpoints = [SavedCalamariModel(ckpt, auto_update=auto_update_checkpoints) for ckpt in checkpoints] + multi_predictor = super(MultiPredictor, cls).from_paths( [ckpt.json_path for ckpt in checkpoints], predictor_params, CalamariScenario, - model_paths=[ckpt.ckpt_path + ".h5" for ckpt in checkpoints], + model_paths=[ckpt.ckpt_path for ckpt in checkpoints], predictor_args={"voter_params": voter_params}, ) diff --git a/calamari_ocr/ocr/savedmodel/migrations/version5to6.py b/calamari_ocr/ocr/savedmodel/migrations/version5to6.py new file mode 100644 index 00000000..78db1d99 --- /dev/null +++ b/calamari_ocr/ocr/savedmodel/migrations/version5to6.py @@ -0,0 +1,34 @@ +import logging +import os + +from tensorflow import keras + +from calamari_ocr.ocr.scenario import CalamariScenario +from calamari_ocr.ocr.training.params import TrainerParams + +logger = logging.getLogger(__name__) + + +def update_model(params: dict, path: str): + logger.info(f"Updating model at {path}") + + trainer_params = TrainerParams.from_dict(params) + scenario_params = trainer_params.scenario + scenario = CalamariScenario(scenario_params) + inputs = scenario.data.create_input_layers() + outputs = scenario.graph.predict(inputs) + pred_model = keras.models.Model(inputs, outputs) + pred_model.load_weights(path + ".h5") + + logger.info(f"Writing converted model at {path}.tmp") + pred_model.save(path + ".tmp", include_optimizer=False) + logger.info(f"Attempting to load converted model at {path}.tmp") + keras.models.load_model( + path + ".tmp", + custom_objects=CalamariScenario.model_cls().all_custom_objects(), + ) + logger.info(f"Replacing old model at {path}.h5") + os.remove(path + ".h5") + os.rename(path + ".tmp", path) + logger.info(f"New model successfully written") + keras.backend.clear_session() diff --git a/calamari_ocr/ocr/savedmodel/saved_model.py b/calamari_ocr/ocr/savedmodel/saved_model.py index a6f43a12..60fcfccf 100644 --- a/calamari_ocr/ocr/savedmodel/saved_model.py +++ b/calamari_ocr/ocr/savedmodel/saved_model.py @@ -11,7 +11,7 @@ class SavedCalamariModel: - VERSION = 5 + VERSION = 6 def __init__(self, json_path: str, auto_update=True, dry_run=False): self.json_path = json_path if json_path.endswith(".json") else json_path + ".json" @@ -98,6 +98,12 @@ def _single_upgrade(self): update_model(self.dict, self.ckpt_path) self.version = 5 + elif self.version == 5: + from calamari_ocr.ocr.savedmodel.migrations.version5to6 import update_model + + update_model(self.dict, self.ckpt_path) + self.version = 6 + self._update_json_version() def _update_json_version(self): diff --git a/calamari_ocr/ocr/scenario.py b/calamari_ocr/ocr/scenario.py index b7d73cc9..7a8c566e 100644 --- a/calamari_ocr/ocr/scenario.py +++ b/calamari_ocr/ocr/scenario.py @@ -34,7 +34,7 @@ def default_params(cls): scenario_params = super(CalamariScenarioBase, cls).default_params() scenario_params.export_serve = True scenario_params.export_net_config = False - scenario_params.default_serve_dir = "best.ckpt.h5" + scenario_params.default_serve_dir = "best.ckpt" scenario_params.scenario_params_filename = "scenario_params.json" # should never be written! scenario_params.trainer_params_filename = "best.ckpt.json" return scenario_params diff --git a/calamari_ocr/ocr/training/params.py b/calamari_ocr/ocr/training/params.py index a63cb34f..f9380d52 100644 --- a/calamari_ocr/ocr/training/params.py +++ b/calamari_ocr/ocr/training/params.py @@ -97,7 +97,7 @@ class TrainerParams(AIPTrainerParams[CalamariScenarioParams, CalamariDefaultTrai ) def __post_init__(self): - self.scenario.default_serve_dir = f"{self.best_model_prefix}.ckpt.h5" + self.scenario.default_serve_dir = f"{self.best_model_prefix}.ckpt" self.scenario.trainer_params_filename = f"{self.best_model_prefix}.ckpt.json" self.early_stopping.best_model_name = "" diff --git a/calamari_ocr/ocr/training/trainer.py b/calamari_ocr/ocr/training/trainer.py index 4a3e32d6..b4f34d6e 100644 --- a/calamari_ocr/ocr/training/trainer.py +++ b/calamari_ocr/ocr/training/trainer.py @@ -63,7 +63,7 @@ def __init__(self, params: TrainerParams, scenario, restore=False): self._params.warmstart.model, auto_update=self._params.auto_upgrade_checkpoints, ) - self._params.warmstart.model = self.checkpoint.ckpt_path + ".h5" + self._params.warmstart.model = self.checkpoint.ckpt_path self._params.warmstart.trim_graph_name = False network = self.checkpoint.trainer_params.network if self._params.network != network: diff --git a/calamari_ocr/scripts/ensemble.py b/calamari_ocr/scripts/ensemble.py index 7f2cd7e0..6316287f 100644 --- a/calamari_ocr/scripts/ensemble.py +++ b/calamari_ocr/scripts/ensemble.py @@ -13,7 +13,7 @@ def split(args): ckpt = SavedCalamariModel(args.model) keras_model = keras.models.load_model( - ckpt.ckpt_path + ".h5", + ckpt.ckpt_path, custom_objects={ "Graph": Graph, "EnsembleGraph": EnsembleGraph, @@ -62,7 +62,7 @@ def extract_keras_model(i): path = os.path.join(ckpt.dirname, f"{ckpt.basename}_split_{i}.ckpt") with open(path + ".json", "w") as f: json.dump(ckpt_dict, f, indent=2) - split_model.save(path + ".h5") + split_model.save(path) print(f"Saved {i + 1}/{len(split_models)}") diff --git a/calamari_ocr/test/models/version6/0.ckpt.json b/calamari_ocr/test/models/version6/0.ckpt.json new file mode 100644 index 00000000..a962928c --- /dev/null +++ b/calamari_ocr/test/models/version6/0.ckpt.json @@ -0,0 +1,935 @@ +{ + "scenario": { + "debug_graph_construction": false, + "debug_graph_n_examples": 1, + "print_eval_limit": 10, + "tensorboard_logger_history_size": 5, + "export_serve": true, + "model": { + "layers": [ + { + "name": "conv2d_0", + "filters": 40, + "kernel_size": { + "x": 3, + "y": 3, + "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D" + }, + "strides": { + "x": 1, + "y": 1, + "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D" + }, + "padding": "same", + "activation": "relu", + "__cls__": "calamari_ocr.ocr.model.layers.conv2d:Conv2DLayerParams" + }, + { + "name": "maxpool2d_0", + "pool_size": { + "x": 2, + "y": 2, + "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D" + }, + "strides": null, + "padding": "same", + "__cls__": "calamari_ocr.ocr.model.layers.pool2d:MaxPool2DLayerParams" + }, + { + "name": "conv2d_1", + "filters": 60, + "kernel_size": { + "x": 3, + "y": 3, + "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D" + }, + "strides": { + "x": 1, + "y": 1, + "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D" + }, + "padding": "same", + "activation": "relu", + "__cls__": "calamari_ocr.ocr.model.layers.conv2d:Conv2DLayerParams" + }, + { + "name": "maxpool2d_1", + "pool_size": { + "x": 2, + "y": 2, + "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D" + }, + "strides": null, + "padding": "same", + "__cls__": "calamari_ocr.ocr.model.layers.pool2d:MaxPool2DLayerParams" + }, + { + "name": "lstm_0", + "hidden_nodes": 200, + "merge_mode": "concat", + "__cls__": "calamari_ocr.ocr.model.layers.bilstm:BiLSTMLayerParams" + }, + { + "name": "dropout_0", + "rate": 0.5, + "__cls__": "calamari_ocr.ocr.model.layers.dropout:DropoutLayerParams" + } + ], + "classes": 88, + "ctc_merge_repeated": true, + "ensemble": 0, + "masking_mode": false, + "__cls__": "calamari_ocr.ocr.model.params:ModelParams" + }, + "data": { + "pre_proc": { + "run_parallel": true, + "num_threads": -1, + "max_tasks_per_process": 250, + "processors": [ + { + "modes": [ + "evaluation", + "training", + "prediction" + ], + "__cls__": "calamari_ocr.ocr.dataset.imageprocessors.data_range_normalizer:DataRangeProcessorParams" + }, + { + "modes": [ + "evaluation", + "training", + "prediction" + ], + "extra_params": [ + 4, + 1.0, + 0.3 + ], + "line_height": 48, + "__cls__": "calamari_ocr.ocr.dataset.imageprocessors.center_normalizer:CenterNormalizerProcessorParams" + }, + { + "modes": [ + "evaluation", + "training", + "prediction" + ], + "normalize": true, + "invert": true, + "transpose": true, + "pad": 16, + "pad_value": false, + "__cls__": "calamari_ocr.ocr.dataset.imageprocessors.final_preparation:FinalPreparationProcessorParams" + }, + { + "modes": [ + "training" + ], + "augmenter": { + "__cls__": "calamari_ocr.ocr.augmentation.data_augmenter:DefaultDataAugmenterParams" + }, + "n_augmentations": 0, + "__cls__": "calamari_ocr.ocr.dataset.imageprocessors.augmentation:AugmentationProcessorParams" + }, + { + "modes": [ + "evaluation", + "training", + "targets" + ], + "unicode_normalization": "NFC", + "__cls__": "calamari_ocr.ocr.dataset.textprocessors.text_normalizer:TextNormalizerProcessorParams" + }, + { + "modes": [ + "evaluation", + "training", + "targets" + ], + "replacement_groups": [ + "extended" + ], + "replacements": [ + { + "old": "\u00b5", + "new": "\u03bc", + "regex": false + }, + { + "old": "\u2013\u2014\u2014", + "new": "-", + "regex": false + }, + { + "old": "\u2013\u2014", + "new": "-", + "regex": false + }, + { + "old": "\"", + "new": "''", + "regex": false + }, + { + "old": "`", + "new": "'", + "regex": false + }, + { + "old": "\u201c", + "new": "''", + "regex": false + }, + { + "old": "\u201d", + "new": "''", + "regex": false + }, + { + "old": "\u00b4", + "new": "'", + "regex": false + }, + { + "old": "\u2018", + "new": "'", + "regex": false + }, + { + "old": "\u2019", + "new": "'", + "regex": false + }, + { + "old": "\u201c", + "new": "''", + "regex": false + }, + { + "old": "\u201d", + "new": "''", + "regex": false + }, + { + "old": "\u201c", + "new": "''", + "regex": false + }, + { + "old": "\u201e", + "new": ",,", + "regex": false + }, + { + "old": "\u2026", + "new": "...", + "regex": false + }, + { + "old": "\u2032", + "new": "'", + "regex": false + }, + { + "old": "\u2033", + "new": "''", + "regex": false + }, + { + "old": "\u2034", + "new": "'''", + "regex": false + }, + { + "old": "\u3003", + "new": "''", + "regex": false + }, + { + "old": "\u2160", + "new": "I", + "regex": false + }, + { + "old": "\u2161", + "new": "II", + "regex": false + }, + { + "old": "\u2162", + "new": "III", + "regex": false + }, + { + "old": "\u2163", + "new": "IV", + "regex": false + }, + { + "old": "\u2164", + "new": "V", + "regex": false + }, + { + "old": "\u2165", + "new": "VI", + "regex": false + }, + { + "old": "\u2166", + "new": "VII", + "regex": false + }, + { + "old": "\u2167", + "new": "VIII", + "regex": false + }, + { + "old": "\u2168", + "new": "IX", + "regex": false + }, + { + "old": "\u2169", + "new": "X", + "regex": false + }, + { + "old": "\u216a", + "new": "XI", + "regex": false + }, + { + "old": "\u216b", + "new": "XII", + "regex": false + }, + { + "old": "\u216c", + "new": "L", + "regex": false + }, + { + "old": "\u216d", + "new": "C", + "regex": false + }, + { + "old": "\u216e", + "new": "D", + "regex": false + }, + { + "old": "\u216f", + "new": "M", + "regex": false + }, + { + "old": "\u2170", + "new": "i", + "regex": false + }, + { + "old": "\u2171", + "new": "ii", + "regex": false + }, + { + "old": "\u2172", + "new": "iii", + "regex": false + }, + { + "old": "\u2173", + "new": "iv", + "regex": false + }, + { + "old": "\u2174", + "new": "v", + "regex": false + }, + { + "old": "\u2175", + "new": "vi", + "regex": false + }, + { + "old": "\u2176", + "new": "vii", + "regex": false + }, + { + "old": "\u2177", + "new": "viii", + "regex": false + }, + { + "old": "\u2178", + "new": "ix", + "regex": false + }, + { + "old": "\u2179", + "new": "x", + "regex": false + }, + { + "old": "\u217a", + "new": "xi", + "regex": false + }, + { + "old": "\u217b", + "new": "xii", + "regex": false + }, + { + "old": "\u217c", + "new": "l", + "regex": false + }, + { + "old": "\u217d", + "new": "c", + "regex": false + }, + { + "old": "\u217e", + "new": "d", + "regex": false + }, + { + "old": "\u217f", + "new": "m", + "regex": false + }, + { + "old": "\\s+(?u)", + "new": " ", + "regex": true + }, + { + "old": "\\n(?u)", + "new": "", + "regex": true + }, + { + "old": "^\\s+(?u)", + "new": "", + "regex": true + }, + { + "old": "\\s+$(?u)", + "new": "", + "regex": true + } + ], + "__cls__": "calamari_ocr.ocr.dataset.textprocessors.text_regularizer:TextRegularizerProcessorParams" + }, + { + "modes": [ + "evaluation", + "training", + "targets" + ], + "__cls__": "calamari_ocr.ocr.dataset.textprocessors.basic_text_processors:StripTextProcessorParams" + }, + { + "modes": [ + "evaluation", + "training", + "prediction" + ], + "__cls__": "calamari_ocr.ocr.dataset.imageprocessors.preparesample:PrepareSampleProcessorParams" + } + ], + "__cls__": "tfaip.data.pipeline.processor.params:SequentialProcessorPipelineParams" + }, + "post_proc": { + "run_parallel": true, + "num_threads": -1, + "max_tasks_per_process": 250, + "processors": [ + { + "modes": [ + "evaluation", + "training", + "targets", + "prediction" + ], + "__cls__": "calamari_ocr.ocr.dataset.postprocessors.reshape:ReshapeOutputsProcessorParams" + }, + { + "modes": [ + "evaluation", + "training", + "targets", + "prediction" + ], + "ctc_decoder_params": { + "type": "default", + "blank_index": 0, + "min_p_threshold": 0, + "non_word_chars": [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "[", + "]", + "(", + ")", + "_", + ".", + ":", + ";", + "!", + "?", + "{", + "}", + "-", + "'", + "\"" + ], + "dictionary": [], + "word_separator": " ", + "__cls__": "calamari_ocr.ocr.model.ctcdecoder.ctc_decoder:CTCDecoderParams" + }, + "__cls__": "calamari_ocr.ocr.dataset.postprocessors.ctcdecoder:CTCDecoderProcessorParams" + }, + { + "modes": [ + "evaluation", + "training", + "targets" + ], + "unicode_normalization": "NFC", + "__cls__": "calamari_ocr.ocr.dataset.textprocessors.text_normalizer:TextNormalizerProcessorParams" + }, + { + "modes": [ + "evaluation", + "training", + "targets" + ], + "replacement_groups": [ + "extended" + ], + "replacements": [ + { + "old": "\u00b5", + "new": "\u03bc", + "regex": false + }, + { + "old": "\u2013\u2014\u2014", + "new": "-", + "regex": false + }, + { + "old": "\u2013\u2014", + "new": "-", + "regex": false + }, + { + "old": "\"", + "new": "''", + "regex": false + }, + { + "old": "`", + "new": "'", + "regex": false + }, + { + "old": "\u201c", + "new": "''", + "regex": false + }, + { + "old": "\u201d", + "new": "''", + "regex": false + }, + { + "old": "\u00b4", + "new": "'", + "regex": false + }, + { + "old": "\u2018", + "new": "'", + "regex": false + }, + { + "old": "\u2019", + "new": "'", + "regex": false + }, + { + "old": "\u201c", + "new": "''", + "regex": false + }, + { + "old": "\u201d", + "new": "''", + "regex": false + }, + { + "old": "\u201c", + "new": "''", + "regex": false + }, + { + "old": "\u201e", + "new": ",,", + "regex": false + }, + { + "old": "\u2026", + "new": "...", + "regex": false + }, + { + "old": "\u2032", + "new": "'", + "regex": false + }, + { + "old": "\u2033", + "new": "''", + "regex": false + }, + { + "old": "\u2034", + "new": "'''", + "regex": false + }, + { + "old": "\u3003", + "new": "''", + "regex": false + }, + { + "old": "\u2160", + "new": "I", + "regex": false + }, + { + "old": "\u2161", + "new": "II", + "regex": false + }, + { + "old": "\u2162", + "new": "III", + "regex": false + }, + { + "old": "\u2163", + "new": "IV", + "regex": false + }, + { + "old": "\u2164", + "new": "V", + "regex": false + }, + { + "old": "\u2165", + "new": "VI", + "regex": false + }, + { + "old": "\u2166", + "new": "VII", + "regex": false + }, + { + "old": "\u2167", + "new": "VIII", + "regex": false + }, + { + "old": "\u2168", + "new": "IX", + "regex": false + }, + { + "old": "\u2169", + "new": "X", + "regex": false + }, + { + "old": "\u216a", + "new": "XI", + "regex": false + }, + { + "old": "\u216b", + "new": "XII", + "regex": false + }, + { + "old": "\u216c", + "new": "L", + "regex": false + }, + { + "old": "\u216d", + "new": "C", + "regex": false + }, + { + "old": "\u216e", + "new": "D", + "regex": false + }, + { + "old": "\u216f", + "new": "M", + "regex": false + }, + { + "old": "\u2170", + "new": "i", + "regex": false + }, + { + "old": "\u2171", + "new": "ii", + "regex": false + }, + { + "old": "\u2172", + "new": "iii", + "regex": false + }, + { + "old": "\u2173", + "new": "iv", + "regex": false + }, + { + "old": "\u2174", + "new": "v", + "regex": false + }, + { + "old": "\u2175", + "new": "vi", + "regex": false + }, + { + "old": "\u2176", + "new": "vii", + "regex": false + }, + { + "old": "\u2177", + "new": "viii", + "regex": false + }, + { + "old": "\u2178", + "new": "ix", + "regex": false + }, + { + "old": "\u2179", + "new": "x", + "regex": false + }, + { + "old": "\u217a", + "new": "xi", + "regex": false + }, + { + "old": "\u217b", + "new": "xii", + "regex": false + }, + { + "old": "\u217c", + "new": "l", + "regex": false + }, + { + "old": "\u217d", + "new": "c", + "regex": false + }, + { + "old": "\u217e", + "new": "d", + "regex": false + }, + { + "old": "\u217f", + "new": "m", + "regex": false + }, + { + "old": "\\s+(?u)", + "new": " ", + "regex": true + }, + { + "old": "\\n(?u)", + "new": "", + "regex": true + }, + { + "old": "^\\s+(?u)", + "new": "", + "regex": true + }, + { + "old": "\\s+$(?u)", + "new": "", + "regex": true + } + ], + "__cls__": "calamari_ocr.ocr.dataset.textprocessors.text_regularizer:TextRegularizerProcessorParams" + }, + { + "modes": [ + "evaluation", + "training", + "targets" + ], + "__cls__": "calamari_ocr.ocr.dataset.textprocessors.basic_text_processors:StripTextProcessorParams" + } + ], + "__cls__": "tfaip.data.pipeline.processor.params:SequentialProcessorPipelineParams" + }, + "resource_base_path": ".", + "skip_invalid_gt": true, + "input_channels": 1, + "downscale_factor": 4, + "line_height": 48, + "ensemble": 0, + "codec": { + "charset": [ + "", + " ", + "!", + "#", + "$", + "%", + "&", + "'", + "(", + ")", + "+", + ",", + "-", + ".", + "/", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + ":", + ";", + "<", + "=", + ">", + "?", + "@", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "[", + "]", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "{", + "}" + ], + "__cls__": "calamari_ocr.ocr.dataset.codec:Codec" + }, + "__cls__": "calamari_ocr.ocr.dataset.params:DataParams" + }, + "evaluator": { + "__cls__": "tfaip.evaluator.params:EvaluatorParams" + }, + "export_net_config": true, + "net_config_filename": "net_config.json", + "default_serve_dir": "serve", + "additional_serve_dir": "additional", + "trainer_params_filename": "trainer_params.json", + "scenario_params_filename": "scenario_params.json", + "scenario_base_path": null, + "scenario_id": null, + "id": null, + "tfaip_commit_hash": "8314f5cf516fcda202d1eff40a5e27121bef6cb3", + "tfaip_version": "1.1.1", + "__cls__": "calamari_ocr.ocr.scenario_params:CalamariScenarioParams" + }, + "version": 6 +} \ No newline at end of file diff --git a/calamari_ocr/test/models/version6/0.ckpt/keras_metadata.pb b/calamari_ocr/test/models/version6/0.ckpt/keras_metadata.pb new file mode 100644 index 00000000..20e3ede4 --- /dev/null +++ b/calamari_ocr/test/models/version6/0.ckpt/keras_metadata.pb @@ -0,0 +1,28 @@ + +>root"_tf_keras_network*>{"name": "model", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "must_restore_from_config": false, "class_name": "Functional", "config": {"name": "model", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, null, 48, 1]}, "dtype": "uint8", "sparse": false, "ragged": false, "name": "img"}, "name": "img", "inbound_nodes": []}, {"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 1]}, "dtype": "int32", "sparse": false, "ragged": false, "name": "img_len"}, "name": "img_len", "inbound_nodes": []}, {"class_name": "Lambda", "config": {"name": "lambda", "trainable": true, "dtype": "float32", "function": {"class_name": "__tuple__", "items": ["4wEAAAAAAAAAAAAAAAEAAAACAAAAUwAAAHMIAAAAZAF8AGkBUwCpAk7aB3ByZWRpY3SpAKkB2gF4\ncgMAAAByAwAAAPpTL2hvbWUvYW5iNTFuaC9jYWxhbWFyaTIvYzJ2ZW52L2xpYi9weXRob24zLjkv\nc2l0ZS1wYWNrYWdlcy90ZmFpcC9tb2RlbC9ncmFwaGJhc2UucHnaCDxsYW1iZGE+FAEAAPMAAAAA\n", null, null]}, "function_type": "lambda", "module": "tfaip.model.graphbase", "output_shape": null, "output_shape_type": "raw", "output_shape_module": null, "arguments": {}}, "name": "lambda", "inbound_nodes": [{"img": ["img", 0, 0, {}], "img_len": ["img_len", 0, 0, {}]}]}, {"class_name": "RootGraph", "config": {"name": "root", "trainable": true, "dtype": "float32", "params": {"layers": [{"name": "conv2d_0", "filters": 40, "kernel_size": {"x": 3, "y": 3, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": {"x": 1, "y": 1, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "padding": "same", "activation": "relu", "__cls__": "calamari_ocr.ocr.model.layers.conv2d:Conv2DLayerParams"}, {"name": "maxpool2d_0", "pool_size": {"x": 2, "y": 2, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": null, "padding": "same", "__cls__": "calamari_ocr.ocr.model.layers.pool2d:MaxPool2DLayerParams"}, {"name": "conv2d_1", "filters": 60, "kernel_size": {"x": 3, "y": 3, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": {"x": 1, "y": 1, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "padding": "same", "activation": "relu", "__cls__": "calamari_ocr.ocr.model.layers.conv2d:Conv2DLayerParams"}, {"name": "maxpool2d_1", "pool_size": {"x": 2, "y": 2, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": null, "padding": "same", "__cls__": "calamari_ocr.ocr.model.layers.pool2d:MaxPool2DLayerParams"}, {"name": "lstm_0", "hidden_nodes": 200, "merge_mode": "concat", "__cls__": "calamari_ocr.ocr.model.layers.bilstm:BiLSTMLayerParams"}, {"name": "dropout_0", "rate": 0.5, "__cls__": "calamari_ocr.ocr.model.layers.dropout:DropoutLayerParams"}], "classes": 88, "ctc_merge_repeated": true, "ensemble": 0, "temperature": 1, "masking_mode": false, "__cls__": "calamari_ocr.ocr.model.params:ModelParams"}, "model_kwargs": {}, "graph_kwargs": {}}, "name": "root", "inbound_nodes": [{"predict": {"img": ["lambda", 0, 0, {}], "img_len": ["lambda", 0, 1, {}]}}]}], "input_layers": {"img": ["img", 0, 0], "img_len": ["img_len", 0, 0]}, "output_layers": {"blank_last_logits": ["root", 0, 0], "blank_last_softmax": ["root", 0, 1], "out_len": ["root", 0, 4], "logits": ["root", 0, 3], "softmax": ["root", 0, 5], "decoded": ["root", 0, 2]}}, "shared_object_id": 4, "input_spec": [{"class_name": "InputSpec", "config": {"dtype": null, "shape": {"class_name": "__tuple__", "items": [null, null, 48, 1]}, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {}}}, {"class_name": "InputSpec", "config": {"dtype": null, "shape": {"class_name": "__tuple__", "items": [null, 1]}, "ndim": 2, "max_ndim": null, "min_ndim": null, "axes": {}}}], "build_input_shape": {"img": {"class_name": "TensorShape", "items": [null, null, 48, 1]}, "img_len": {"class_name": "TensorShape", "items": [null, 1]}}, "is_graph_network": true, "full_save_spec": {"class_name": "__tuple__", "items": [[{"img": {"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, null, 48, 1]}, "uint8", "img"]}, "img_len": {"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, 1]}, "int32", "img_len"]}}], {}]}, "save_spec": {"img": {"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, null, 48, 1]}, "uint8", "img"]}, "img_len": {"class_name": "TypeSpec", "type_spec": "tf.TensorSpec", "serialized": [{"class_name": "TensorShape", "items": [null, 1]}, "int32", "img_len"]}}, "keras_version": "2.6.0", "backend": "tensorflow", "model_config": {"class_name": "Functional", "config": {"name": "model", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, null, 48, 1]}, "dtype": "uint8", "sparse": false, "ragged": false, "name": "img"}, "name": "img", "inbound_nodes": [], "shared_object_id": 0}, {"class_name": "InputLayer", "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 1]}, "dtype": "int32", "sparse": false, "ragged": false, "name": "img_len"}, "name": "img_len", "inbound_nodes": [], "shared_object_id": 1}, {"class_name": "Lambda", "config": {"name": "lambda", "trainable": true, "dtype": "float32", "function": {"class_name": "__tuple__", "items": ["4wEAAAAAAAAAAAAAAAEAAAACAAAAUwAAAHMIAAAAZAF8AGkBUwCpAk7aB3ByZWRpY3SpAKkB2gF4\ncgMAAAByAwAAAPpTL2hvbWUvYW5iNTFuaC9jYWxhbWFyaTIvYzJ2ZW52L2xpYi9weXRob24zLjkv\nc2l0ZS1wYWNrYWdlcy90ZmFpcC9tb2RlbC9ncmFwaGJhc2UucHnaCDxsYW1iZGE+FAEAAPMAAAAA\n", null, null]}, "function_type": "lambda", "module": "tfaip.model.graphbase", "output_shape": null, "output_shape_type": "raw", "output_shape_module": null, "arguments": {}}, "name": "lambda", "inbound_nodes": [{"img": ["img", 0, 0, {}], "img_len": ["img_len", 0, 0, {}]}], "shared_object_id": 2}, {"class_name": "RootGraph", "config": {"name": "root", "trainable": true, "dtype": "float32", "params": {"layers": [{"name": "conv2d_0", "filters": 40, "kernel_size": {"x": 3, "y": 3, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": {"x": 1, "y": 1, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "padding": "same", "activation": "relu", "__cls__": "calamari_ocr.ocr.model.layers.conv2d:Conv2DLayerParams"}, {"name": "maxpool2d_0", "pool_size": {"x": 2, "y": 2, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": null, "padding": "same", "__cls__": "calamari_ocr.ocr.model.layers.pool2d:MaxPool2DLayerParams"}, {"name": "conv2d_1", "filters": 60, "kernel_size": {"x": 3, "y": 3, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": {"x": 1, "y": 1, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "padding": "same", "activation": "relu", "__cls__": "calamari_ocr.ocr.model.layers.conv2d:Conv2DLayerParams"}, {"name": "maxpool2d_1", "pool_size": {"x": 2, "y": 2, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": null, "padding": "same", "__cls__": "calamari_ocr.ocr.model.layers.pool2d:MaxPool2DLayerParams"}, {"name": "lstm_0", "hidden_nodes": 200, "merge_mode": "concat", "__cls__": "calamari_ocr.ocr.model.layers.bilstm:BiLSTMLayerParams"}, {"name": "dropout_0", "rate": 0.5, "__cls__": "calamari_ocr.ocr.model.layers.dropout:DropoutLayerParams"}], "classes": 88, "ctc_merge_repeated": true, "ensemble": 0, "temperature": 1, "masking_mode": false, "__cls__": "calamari_ocr.ocr.model.params:ModelParams"}, "model_kwargs": {}, "graph_kwargs": {}}, "name": "root", "inbound_nodes": [{"predict": {"img": ["lambda", 0, 0, {}], "img_len": ["lambda", 0, 1, {}]}}], "shared_object_id": 3}], "input_layers": {"img": ["img", 0, 0], "img_len": ["img_len", 0, 0]}, "output_layers": {"blank_last_logits": ["root", 0, 0], "blank_last_softmax": ["root", 0, 1], "out_len": ["root", 0, 4], "logits": ["root", 0, 3], "softmax": ["root", 0, 5], "decoded": ["root", 0, 2]}}}}2 + root.layer-0"_tf_keras_input_layer*{"class_name": "InputLayer", "name": "img", "dtype": "uint8", "sparse": false, "ragged": false, "batch_input_shape": {"class_name": "__tuple__", "items": [null, null, 48, 1]}, "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, null, 48, 1]}, "dtype": "uint8", "sparse": false, "ragged": false, "name": "img"}}2 + root.layer-1"_tf_keras_input_layer*{"class_name": "InputLayer", "name": "img_len", "dtype": "int32", "sparse": false, "ragged": false, "batch_input_shape": {"class_name": "__tuple__", "items": [null, 1]}, "config": {"batch_input_shape": {"class_name": "__tuple__", "items": [null, 1]}, "dtype": "int32", "sparse": false, "ragged": false, "name": "img_len"}}2 + root.layer-2"_tf_keras_layer*{"name": "lambda", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Lambda", "config": {"name": "lambda", "trainable": true, "dtype": "float32", "function": {"class_name": "__tuple__", "items": ["4wEAAAAAAAAAAAAAAAEAAAACAAAAUwAAAHMIAAAAZAF8AGkBUwCpAk7aB3ByZWRpY3SpAKkB2gF4\ncgMAAAByAwAAAPpTL2hvbWUvYW5iNTFuaC9jYWxhbWFyaTIvYzJ2ZW52L2xpYi9weXRob24zLjkv\nc2l0ZS1wYWNrYWdlcy90ZmFpcC9tb2RlbC9ncmFwaGJhc2UucHnaCDxsYW1iZGE+FAEAAPMAAAAA\n", null, null]}, "function_type": "lambda", "module": "tfaip.model.graphbase", "output_shape": null, "output_shape_type": "raw", "output_shape_module": null, "arguments": {}}, "inbound_nodes": [{"img": ["img", 0, 0, {}], "img_len": ["img_len", 0, 0, {}]}], "shared_object_id": 2}2 +root.layer_with_weights-0"_tf_keras_layer*{"name": "root", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "RootGraph", "config": {"name": "root", "trainable": true, "dtype": "float32", "params": {"layers": [{"name": "conv2d_0", "filters": 40, "kernel_size": {"x": 3, "y": 3, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": {"x": 1, "y": 1, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "padding": "same", "activation": "relu", "__cls__": "calamari_ocr.ocr.model.layers.conv2d:Conv2DLayerParams"}, {"name": "maxpool2d_0", "pool_size": {"x": 2, "y": 2, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": null, "padding": "same", "__cls__": "calamari_ocr.ocr.model.layers.pool2d:MaxPool2DLayerParams"}, {"name": "conv2d_1", "filters": 60, "kernel_size": {"x": 3, "y": 3, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": {"x": 1, "y": 1, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "padding": "same", "activation": "relu", "__cls__": "calamari_ocr.ocr.model.layers.conv2d:Conv2DLayerParams"}, {"name": "maxpool2d_1", "pool_size": {"x": 2, "y": 2, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": null, "padding": "same", "__cls__": "calamari_ocr.ocr.model.layers.pool2d:MaxPool2DLayerParams"}, {"name": "lstm_0", "hidden_nodes": 200, "merge_mode": "concat", "__cls__": "calamari_ocr.ocr.model.layers.bilstm:BiLSTMLayerParams"}, {"name": "dropout_0", "rate": 0.5, "__cls__": "calamari_ocr.ocr.model.layers.dropout:DropoutLayerParams"}], "classes": 88, "ctc_merge_repeated": true, "ensemble": 0, "temperature": 1, "masking_mode": false, "__cls__": "calamari_ocr.ocr.model.params:ModelParams"}, "model_kwargs": {}, "graph_kwargs": {}}, "inbound_nodes": [{"predict": {"img": ["lambda", 0, 0, {}], "img_len": ["lambda", 0, 1, {}]}}], "shared_object_id": 3}2 + root.layer_with_weights-0._graph"_tf_keras_layer*{"name": "CalamariGraph", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "CalamariGraph", "config": {"name": "CalamariGraph", "trainable": true, "dtype": "float32", "params": {"layers": [{"name": "conv2d_0", "filters": 40, "kernel_size": {"x": 3, "y": 3, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": {"x": 1, "y": 1, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "padding": "same", "activation": "relu", "__cls__": "calamari_ocr.ocr.model.layers.conv2d:Conv2DLayerParams"}, {"name": "maxpool2d_0", "pool_size": {"x": 2, "y": 2, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": null, "padding": "same", "__cls__": "calamari_ocr.ocr.model.layers.pool2d:MaxPool2DLayerParams"}, {"name": "conv2d_1", "filters": 60, "kernel_size": {"x": 3, "y": 3, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": {"x": 1, "y": 1, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "padding": "same", "activation": "relu", "__cls__": "calamari_ocr.ocr.model.layers.conv2d:Conv2DLayerParams"}, {"name": "maxpool2d_1", "pool_size": {"x": 2, "y": 2, "__cls__": "calamari_ocr.ocr.model.layers.layer:IntVec2D"}, "strides": null, "padding": "same", "__cls__": "calamari_ocr.ocr.model.layers.pool2d:MaxPool2DLayerParams"}, {"name": "lstm_0", "hidden_nodes": 200, "merge_mode": "concat", "__cls__": "calamari_ocr.ocr.model.layers.bilstm:BiLSTMLayerParams"}, {"name": "dropout_0", "rate": 0.5, "__cls__": "calamari_ocr.ocr.model.layers.dropout:DropoutLayerParams"}], "classes": 88, "ctc_merge_repeated": true, "ensemble": 0, "temperature": 1, "masking_mode": false, "__cls__": "calamari_ocr.ocr.model.params:ModelParams"}}, "shared_object_id": 7}2 +-(root.layer_with_weights-0._graph.reshape"_tf_keras_layer*{"name": "to_input_dims_layer", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "ToInputDimsLayer", "config": {"layer was saved without config": true}}2 +.'root.layer_with_weights-0._graph.logits"_tf_keras_layer*{"name": "logits", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Dense", "config": {"name": "logits", "trainable": true, "dtype": "float32", "units": 88, "activation": "linear", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 8}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 9}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 10, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 2, "axes": {"-1": 400}}, "shared_object_id": 11}, "build_input_shape": {"class_name": "TensorShape", "items": [null, null, 400]}}2 +/(root.layer_with_weights-0._graph.softmax"_tf_keras_layer*{"name": "softmax", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Softmax", "config": {"name": "softmax", "trainable": true, "dtype": "float32", "axis": -1}, "shared_object_id": 12}2 +92root.layer_with_weights-0._graph.layer_instances.0"_tf_keras_layer*{"name": "conv2d_0", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Conv2DLayer", "config": {"name": "conv2d_0", "trainable": true, "dtype": "float32"}, "shared_object_id": 13}2 +:2root.layer_with_weights-0._graph.layer_instances.1"_tf_keras_layer*{"name": "maxpool2d_0", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "MaxPool2DLayer", "config": {"name": "maxpool2d_0", "trainable": true, "dtype": "float32"}, "shared_object_id": 14}2 +;2root.layer_with_weights-0._graph.layer_instances.2"_tf_keras_layer*{"name": "conv2d_1", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Conv2DLayer", "config": {"name": "conv2d_1", "trainable": true, "dtype": "float32"}, "shared_object_id": 15}2 +<2root.layer_with_weights-0._graph.layer_instances.3"_tf_keras_layer*{"name": "maxpool2d_1", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "MaxPool2DLayer", "config": {"name": "maxpool2d_1", "trainable": true, "dtype": "float32"}, "shared_object_id": 16}2 +=2root.layer_with_weights-0._graph.layer_instances.4"_tf_keras_layer*{"name": "lstm_0", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "BiLSTMLayer", "config": {"name": "lstm_0", "trainable": true, "dtype": "float32"}, "shared_object_id": 17}2 +>2root.layer_with_weights-0._graph.layer_instances.5"_tf_keras_layer*{"name": "dropout_0", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "DropoutLayer", "config": {"name": "dropout_0", "trainable": true, "dtype": "float32"}, "shared_object_id": 18}2 + P7root.layer_with_weights-0._graph.layer_instances.0.conv"_tf_keras_layer* {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 40, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 19}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 20}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 21, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 1}}, "shared_object_id": 22}, "build_input_shape": {"class_name": "TensorShape", "items": [null, null, 48, 1]}}2 +U7root.layer_with_weights-0._graph.layer_instances.1.conv"_tf_keras_layer*{"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "MaxPooling2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "pool_size": {"class_name": "__tuple__", "items": [2, 2]}, "padding": "same", "strides": {"class_name": "__tuple__", "items": [2, 2]}, "data_format": "channels_last"}, "shared_object_id": 23, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {}}, "shared_object_id": 24}}2 + Z7root.layer_with_weights-0._graph.layer_instances.2.conv"_tf_keras_layer* {"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Conv2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "filters": 60, "kernel_size": {"class_name": "__tuple__", "items": [3, 3]}, "strides": {"class_name": "__tuple__", "items": [1, 1]}, "padding": "same", "data_format": "channels_last", "dilation_rate": {"class_name": "__tuple__", "items": [1, 1]}, "groups": 1, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 25}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 26}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}, "shared_object_id": 27, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": null, "max_ndim": null, "min_ndim": 4, "axes": {"-1": 40}}, "shared_object_id": 28}, "build_input_shape": {"class_name": "TensorShape", "items": [null, null, 24, 40]}}2 +_7root.layer_with_weights-0._graph.layer_instances.3.conv"_tf_keras_layer*{"name": "conv", "trainable": true, "expects_training_arg": false, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "MaxPooling2D", "config": {"name": "conv", "trainable": true, "dtype": "float32", "pool_size": {"class_name": "__tuple__", "items": [2, 2]}, "padding": "same", "strides": {"class_name": "__tuple__", "items": [2, 2]}, "data_format": "channels_last"}, "shared_object_id": 29, "input_spec": {"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 4, "max_ndim": null, "min_ndim": null, "axes": {}}, "shared_object_id": 30}}2 + d7root.layer_with_weights-0._graph.layer_instances.4.lstm"_tf_keras_layer* {"name": "bidirectional", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Bidirectional", "config": {"name": "bidirectional", "trainable": true, "dtype": "float32", "layer": {"class_name": "LSTM", "config": {"name": "lstm", "trainable": true, "dtype": "float32", "return_sequences": true, "return_state": false, "go_backwards": false, "stateful": false, "unroll": false, "time_major": false, "units": 200, "activation": "tanh", "recurrent_activation": "sigmoid", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 31}, "recurrent_initializer": {"class_name": "Orthogonal", "config": {"gain": 1.0, "seed": null}, "shared_object_id": 32}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 33}, "unit_forget_bias": true, "kernel_regularizer": null, "recurrent_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "recurrent_constraint": null, "bias_constraint": null, "dropout": 0.0, "recurrent_dropout": 0.0, "implementation": 2}, "shared_object_id": 35}, "merge_mode": "concat"}, "shared_object_id": 36, "input_spec": [{"class_name": "InputSpec", "config": {"dtype": null, "shape": null, "ndim": 3, "max_ndim": null, "min_ndim": null, "axes": {}}, "shared_object_id": 37}], "build_input_shape": {"class_name": "TensorShape", "items": [null, null, 720]}}2 +i:root.layer_with_weights-0._graph.layer_instances.5.dropout"_tf_keras_layer*{"name": "dropout", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "Dropout", "config": {"name": "dropout", "trainable": true, "dtype": "float32", "rate": 0.5, "noise_shape": null, "seed": null}, "shared_object_id": 38}2 + Eroot.layer_with_weights-0._graph.layer_instances.4.lstm.forward_layer"_tf_keras_rnn_layer* +{"name": "forward_lstm", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "LSTM", "config": {"name": "forward_lstm", "trainable": true, "dtype": "float32", "return_sequences": true, "return_state": false, "go_backwards": false, "stateful": false, "unroll": false, "time_major": false, "zero_output_for_mask": true, "units": 200, "activation": "tanh", "recurrent_activation": "sigmoid", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 39}, "recurrent_initializer": {"class_name": "Orthogonal", "config": {"gain": 1.0, "seed": null}, "shared_object_id": 40}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 41}, "unit_forget_bias": true, "kernel_regularizer": null, "recurrent_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "recurrent_constraint": null, "bias_constraint": null, "dropout": 0.0, "recurrent_dropout": 0.0, "implementation": 2}, "shared_object_id": 43, "input_spec": [{"class_name": "InputSpec", "config": {"dtype": null, "shape": {"class_name": "__tuple__", "items": [null, null, 720]}, "ndim": 3, "max_ndim": null, "min_ndim": null, "axes": {}}, "shared_object_id": 44}]}2 + Froot.layer_with_weights-0._graph.layer_instances.4.lstm.backward_layer"_tf_keras_rnn_layer* +{"name": "backward_lstm", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "LSTM", "config": {"name": "backward_lstm", "trainable": true, "dtype": "float32", "return_sequences": true, "return_state": false, "go_backwards": true, "stateful": false, "unroll": false, "time_major": false, "zero_output_for_mask": true, "units": 200, "activation": "tanh", "recurrent_activation": "sigmoid", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 45}, "recurrent_initializer": {"class_name": "Orthogonal", "config": {"gain": 1.0, "seed": null}, "shared_object_id": 46}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 47}, "unit_forget_bias": true, "kernel_regularizer": null, "recurrent_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "recurrent_constraint": null, "bias_constraint": null, "dropout": 0.0, "recurrent_dropout": 0.0, "implementation": 2}, "shared_object_id": 49, "input_spec": [{"class_name": "InputSpec", "config": {"dtype": null, "shape": {"class_name": "__tuple__", "items": [null, null, 720]}, "ndim": 3, "max_ndim": null, "min_ndim": null, "axes": {}}, "shared_object_id": 50}]}2 +Jroot.layer_with_weights-0._graph.layer_instances.4.lstm.forward_layer.cell"_tf_keras_layer*{"name": "lstm_cell_1", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "LSTMCell", "config": {"name": "lstm_cell_1", "trainable": true, "dtype": "float32", "units": 200, "activation": "tanh", "recurrent_activation": "sigmoid", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 39}, "recurrent_initializer": {"class_name": "Orthogonal", "config": {"gain": 1.0, "seed": null}, "shared_object_id": 40}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 41}, "unit_forget_bias": true, "kernel_regularizer": null, "recurrent_regularizer": null, "bias_regularizer": null, "kernel_constraint": null, "recurrent_constraint": null, "bias_constraint": null, "dropout": 0.0, "recurrent_dropout": 0.0, "implementation": 2}, "shared_object_id": 42}2 +Kroot.layer_with_weights-0._graph.layer_instances.4.lstm.backward_layer.cell"_tf_keras_layer*{"name": "lstm_cell_2", "trainable": true, "expects_training_arg": true, "dtype": "float32", "batch_input_shape": null, "stateful": false, "must_restore_from_config": false, "class_name": "LSTMCell", "config": {"name": "lstm_cell_2", "trainable": true, "dtype": "float32", "units": 200, "activation": "tanh", "recurrent_activation": "sigmoid", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}, "shared_object_id": 45}, "recurrent_initializer": {"class_name": "Orthogonal", "config": {"gain": 1.0, "seed": null}, "shared_object_id": 46}, "bias_initializer": {"class_name": "Zeros", "config": {}, "shared_object_id": 47}, "unit_forget_bias": true, "kernel_regularizer": null, "recurrent_regularizer": null, "bias_regularizer": null, "kernel_constraint": null, "recurrent_constraint": null, "bias_constraint": null, "dropout": 0.0, "recurrent_dropout": 0.0, "implementation": 2}, "shared_object_id": 48}2 \ No newline at end of file diff --git a/calamari_ocr/test/models/version6/0.ckpt/saved_model.pb b/calamari_ocr/test/models/version6/0.ckpt/saved_model.pb new file mode 100644 index 00000000..c6e9c589 Binary files /dev/null and b/calamari_ocr/test/models/version6/0.ckpt/saved_model.pb differ diff --git a/calamari_ocr/test/models/version6/0.ckpt/variables/variables.data-00000-of-00001 b/calamari_ocr/test/models/version6/0.ckpt/variables/variables.data-00000-of-00001 new file mode 100644 index 00000000..f461b3f1 Binary files /dev/null and b/calamari_ocr/test/models/version6/0.ckpt/variables/variables.data-00000-of-00001 differ diff --git a/calamari_ocr/test/models/version6/0.ckpt/variables/variables.index b/calamari_ocr/test/models/version6/0.ckpt/variables/variables.index new file mode 100644 index 00000000..3f3ce5c1 Binary files /dev/null and b/calamari_ocr/test/models/version6/0.ckpt/variables/variables.index differ diff --git a/calamari_ocr/test/test_model_migration.py b/calamari_ocr/test/test_model_migration.py index 1424d085..71c74143 100644 --- a/calamari_ocr/test/test_model_migration.py +++ b/calamari_ocr/test/test_model_migration.py @@ -26,6 +26,16 @@ def predict_and_eval(self, model_path): sample.outputs.avg_char_probability, 0.95 ) # The model was trained and should yield good results + def test_upgrade_from_6(self): + with tempfile.TemporaryDirectory() as d: + shutil.copytree( + os.path.join(models_dir, "version6", "0.ckpt"), + os.path.join(d, "0.ckpt"), + ) + shutil.copyfile(os.path.join(models_dir, "version6", "0.ckpt.json"), os.path.join(d, "0.ckpt.json")) + ckpt = SavedCalamariModel(os.path.join(d, "0.ckpt.json")) + self.predict_and_eval(ckpt.ckpt_path) + def test_upgrade_from_5(self): with tempfile.TemporaryDirectory() as d: for filename in {"0.ckpt.h5", "0.ckpt.json"}: