#! /usr/bin/env python3 # -*- coding: utf-8 -*- """Streamlit interface for OCR/HTR with Kraken""" import os import datetime import random import streamlit as st from lib.constants import CONFIG_METADATA from lib.display_utils import (display_baselines, display_baselines_with_text, prepare_segments, open_image) from lib.kraken_utils import (load_model_seg, load_model_rec, segment_image, recognize_text) # === PAGE CONFIGURATION === st.set_page_config(layout="wide") # === I/O UTILS === def get_real_path(path: str) -> str: """Get absolute path of a file.""" return os.path.join(os.path.dirname(__file__), path) def load_random_example_image(folder_path: str): """Load a random image from a folder.""" images = [os.path.join(folder_path, img) for img in os.listdir(folder_path) if img.endswith(('jpg', 'jpeg'))] return random.choice(images) if images else None def write_temporary_model(file_path, custom_model_loaded): """Write a temporary model to disk.""" with open(get_real_path(file_path), "wb") as file: file.write(custom_model_loaded.getbuffer()) def load_model_seg_cache(model_path): return load_model_seg(model_path) def load_model_rec_cache(model_path): return load_model_rec(model_path) MODEL_SEG_BLLA = load_model_seg_cache(get_real_path("data/default/blla.mlmodel")) def load_models(model_rec_in, model_seg_in=None): """Generic bridge to load models. """ if model_rec_in is not None: try: model_rec_out = load_model_rec_cache(model_rec_in) except Exception as e: st.error(f" ❌ Modèle de reconnaissance non chargé. Erreur : {e}") return None, None else: st.error(" ❌ Modèle de reconnaissance non trouvé.") return None, None if model_seg_in is not None: try: model_seg_out = load_model_seg_cache(model_seg_in) except Exception as e: st.error(f" ❌ Modèle de segmentation non chargé. Erreur : {e}") return None, None else: model_seg_out = MODEL_SEG_BLLA return model_rec_out, model_seg_out # === MODELS EXAMPLES === endp_model_rec, endp_model_seg = load_models(model_rec_in=get_real_path("data/endp/models/e-NDP_V7.mlmodel"), model_seg_in=get_real_path("data/endp/models/e-NDP-seg_V3.mlmodel")) lectaurep_model_rec = load_model_rec(get_real_path("data/lectaurep/models/lectaurep_base.mlmodel")) catmus_model_rec = load_model_rec(get_real_path("data/catmus/models/catmus-tiny.mlmodel")) # === MODELS EXAMPLES CONFIGURATION === DEFAULT_CONFIG = { 'endp': { 'model_rec': endp_model_rec, 'model_seg': endp_model_seg, 'example_images': get_real_path("data/endp/images") }, 'lectaurep': { 'model_rec': lectaurep_model_rec, 'model_seg': None, 'example_images': get_real_path("data/lectaurep/images") }, 'catmus':{ 'model_rec': catmus_model_rec, 'model_seg': None, 'example_images': get_real_path("data/catmus/images") } } # === USER INTERFACE === st.title("📜🦑 Reconnaissance de Texte (OCR/HTR) avec Kraken") st.markdown("[![https://img.shields.io/badge/Kraken_version-5.2.9-orange](https://img.shields.io/badge/Kraken_version-5.2.9-orange)](https://github.com/mittagessen/kraken)") st.markdown( """ *⚠️ Cette application est à visée pédagogique ou à des fins de tests uniquement. L'auteur se dégage de toutes responsabilités quant à son usage pour la production.* """ ) st.markdown( """ ##### 🔗 Ressources : - 📂 Données de tests ou d'entraînement dans l'organisation [HTR United](https://htr-united.github.io/index.html) - 📦 Modèles (mlmodel) à tester sur le groupe [OCR/HTR Zenodo](https://zenodo.org/communities/ocr_models/records?q=&l=list&p=1&s=10&sort=newest) - 🛠 Évaluer vos prédictions avec l'application [KaMI (Kraken as Model Inspector)](https://huggingface.co/spaces/lterriel/kami-app) """, unsafe_allow_html=True ) # Configuration choices st.sidebar.header("📁 Configuration HTR") st.sidebar.markdown('---') button_placeholder = st.sidebar.empty() success_loaded_models_msg_container = st.sidebar.empty() download_predictions_placeholder = st.sidebar.empty() st.sidebar.markdown('---') config_choice = st.sidebar.radio( "Choisissez une configuration :", options=["Custom", "endp (exemple)", "lectaurep (exemple)", "catmus (exemple)"] ) config_choice_placeholder = st.sidebar.empty() info_title_desc = st.sidebar.empty() place_metadata = st.sidebar.empty() map_config_choice = { "Custom": "Custom", "endp (exemple)": "endp", "lectaurep (exemple)": "lectaurep", "catmus (exemple)": "catmus" } config_choice = map_config_choice[config_choice] flag_rec_model = False flag_seg_model = False if config_choice != "Custom": config = DEFAULT_CONFIG[config_choice] config_choice_placeholder.success(f"Configuration sélectionnée : {CONFIG_METADATA[config_choice]['title']}") place_metadata.markdown(CONFIG_METADATA[config_choice]['description'], unsafe_allow_html=True) flag_rec_model = True else: st.sidebar.warning("Configuration personnalisée") custom_model_seg = st.sidebar.file_uploader( "Modèle de segmentation (optionnel)", type=["mlmodel"] ) custom_model_rec = st.sidebar.file_uploader( "Modèle de reconnaissance", type=["mlmodel"] ) if custom_model_rec: write_temporary_model('tmp/model_rec_temp.mlmodel', custom_model_rec) flag_rec_model = True if custom_model_seg: write_temporary_model('tmp/model_seg_temp.mlmodel', custom_model_seg) flag_seg_model = True # Image choice flag_image = False image_source = st.radio("Source de l'image :", options=["Exemple", "Personnalisée"]) info_example_image = st.empty() info_example_image_description = st.empty() upload_image_placeholder = st.empty() col1, col2, col3 = st.columns([1, 1, 1]) image = None with col1: st.markdown("## 🖼 Image Originale") st.markdown("---") if image_source == "Exemple": if config_choice != "Custom": example_image_path = load_random_example_image(config["example_images"]) if example_image_path: image = open_image(example_image_path) flag_image = True info_example_image.info(f"Image d'exemple chargée : {os.path.basename(example_image_path)}") info_title_desc.markdown( "