From e4009f22ce465a36379bc34836a149e38ec966e6 Mon Sep 17 00:00:00 2001 From: Folzo Date: Sat, 11 Apr 2026 01:04:48 +0200 Subject: [PATCH] docs: add effi-ocr-legacy for OCR comparison effi-ocr runs on port 8301 alongside Kreuzberg on 8300 for quality comparison --- effi-ocr-legacy/Dockerfile | 20 +++ effi-ocr-legacy/README.md | 232 +++++++++++++++++++++++++++++ effi-ocr-legacy/TOOL-OPENWEBUI.md | 181 ++++++++++++++++++++++ effi-ocr-legacy/TROUBLESHOOTING.md | 80 ++++++++++ effi-ocr-legacy/docker-compose.yml | 22 +++ effi-ocr-legacy/main.py | 208 ++++++++++++++++++++++++++ effi-ocr-legacy/requirements.txt | 5 + 7 files changed, 748 insertions(+) create mode 100644 effi-ocr-legacy/Dockerfile create mode 100644 effi-ocr-legacy/README.md create mode 100644 effi-ocr-legacy/TOOL-OPENWEBUI.md create mode 100644 effi-ocr-legacy/TROUBLESHOOTING.md create mode 100644 effi-ocr-legacy/docker-compose.yml create mode 100644 effi-ocr-legacy/main.py create mode 100644 effi-ocr-legacy/requirements.txt diff --git a/effi-ocr-legacy/Dockerfile b/effi-ocr-legacy/Dockerfile new file mode 100644 index 0000000..6085dc3 --- /dev/null +++ b/effi-ocr-legacy/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.11-slim +# Installer Tesseract et les langues +RUN apt-get update && apt-get install -y \ + tesseract-ocr \ + tesseract-ocr-fra \ + tesseract-ocr-eng \ + libtesseract-dev \ + && rm -rf /var/lib/apt/lists/* +# Créer le répertoire de travail +WORKDIR /app +# Copier les dépendances +COPY requirements.txt . +# Installer les dépendances Python +RUN pip install --no-cache-dir -r requirements.txt +# Copier le code +COPY main.py . +# Exposer le port +EXPOSE 8300 +# Lancer l'application +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8300"] diff --git a/effi-ocr-legacy/README.md b/effi-ocr-legacy/README.md new file mode 100644 index 0000000..aafc8a4 --- /dev/null +++ b/effi-ocr-legacy/README.md @@ -0,0 +1,232 @@ +# 📝 Effi-OCR + +Service OCR (Optical Character Recognition) pour Effi. Extrait le texte des images et photos de documents. + +--- + +## 🎯 Fonctionnalités + +- Extraction de texte à partir d'images (JPEG, PNG, GIF, BMP, TIFF, WebP) +- Support multilingue (Français, Anglais, Allemand, Espagnol, Italien) +- Prétraitement automatique pour améliorer la qualité +- API REST simple et rapide +- Intégration OpenWebUI via Tool + +--- + +## 🚀 Démarrage rapide + +```bash +# Construire et démarrer +cd ~/Documents/OpenCode/Projets/Effi/effi-ocr +sudo docker compose up -d --build + +# Vérifier le service +curl http://localhost:8300/health +``` + +--- + +## 📡 API Endpoints + +### Health Check +```bash +GET /health +``` + +### OCR - Upload fichier +```bash +POST /ocr +Content-Type: multipart/form-data + +Parameters: + - file: Image à analyser + - lang: Code langue (fra, eng, fra+eng, etc.) + - preprocess: true/false (prétraitement auto) +``` + +Exemple: +```bash +curl -X POST -F "file=@photo.jpg" -F "lang=fra" http://localhost:8300/ocr +``` + +### OCR - Base64 +```bash +POST /ocr/base64 +Content-Type: application/json + +{ + "image_base64": "...", + "lang": "fra", + "preprocess": true +} +``` + +### Langues disponibles +```bash +GET /languages +``` + +--- + +## 🔧 Configuration + +### Variables d'environnement + +| Variable | Défaut | Description | +|----------|--------|-------------| +| `OCR_URL` | `http://192.168.1.11:8300` | URL du service OCR | + +### Langues supportées + +- `fra` - Français +- `eng` - Anglais +- `fra+eng` - Bilingue FR+EN +- `deu` - Allemand +- `spa` - Espagnol +- `ita` - Italien + +--- + +## 🐳 Docker + +### Construction +```bash +sudo docker compose build +``` + +### Démarrage +```bash +sudo docker compose up -d +``` + +### Logs +```bash +sudo docker logs effi-ocr -f +``` + +### Arrêt +```bash +sudo docker compose down +``` + +--- + +## 🔗 Intégration OpenWebUI + +Voir [TOOL-OPENWEBUI.md](TOOL-OPENWEBUI.md) pour la configuration complète du Tool. + +**Résumé rapide:** +1. Admin → Tools → Create Tool +2. Copier le code Python fourni +3. Configurer la variable `OCR_URL` +4. Activer le Tool dans les paramètres de conversation + +--- + +## 🧪 Tests + +### Test de santé +```bash +curl http://localhost:8300/health +``` + +### Test OCR simple +```bash +curl -X POST \ + -F "file=@/chemin/vers/image.jpg" \ + -F "lang=fra" \ + http://localhost:8300/ocr +``` + +### Test avec prétraitement désactivé +```bash +curl -X POST \ + -F "file=@image.png" \ + -F "lang=eng" \ + -F "preprocess=false" \ + http://localhost:8300/ocr +``` + +--- + +## 📊 Performance + +- **Temps de traitement** : ~1-3 secondes par image (dépend de la taille) +- **Formats supportés** : JPEG, PNG, GIF, BMP, TIFF, WebP +- **Taille max recommandée** : 2048x2048 pixels +- **Prétraitement** : Conversion niveaux de gris + contraste (optionnel) + +--- + +## 🐛 Dépannage + +### Service ne répond pas +```bash +# Vérifier si le conteneur tourne +sudo docker ps | grep effi-ocr + +# Redémarrer +sudo docker restart effi-ocr + +# Voir les logs +sudo docker logs effi-ocr +``` + +### Tesseract non trouvé +L'erreur indique que Tesseract n'est pas installé dans le conteneur. Reconstruire: +```bash +sudo docker compose down +sudo docker compose up -d --build +``` + +### OCR de mauvaise qualité +- Utiliser `lang=fra+eng` pour les documents mixtes +- S'assurer que l'image est bien éclairée +- Vérifier la résolution (300 DPI recommandé pour documents) +- Activer le prétraitement (par défaut: true) + +--- + +## 📁 Structure + +``` +effi-ocr/ +├── main.py # API FastAPI +├── Dockerfile # Configuration Docker +├── docker-compose.yml # Orchestration +├── requirements.txt # Dépendances Python +├── README.md # Ce fichier +└── TOOL-OPENWEBUI.md # Guide Tool OpenWebUI +``` + +--- + +## 📝 Dépendances + +- Python 3.11 +- FastAPI +- Pillow (traitement d'images) +- pytesseract (wrapper Tesseract) +- Tesseract OCR 5.x + +--- + +## 🎨 Cas d'usage + +- **Numérisation de livres** : Extraire le texte de photos de pages +- **Factures** : Récupérer les informations de factures scannées +- **Documents** : OCR sur PDF et images de documents +- **Notes manuscrites** : Reconnaissance de texte manuscrit (limitée) + +--- + +## 🔒 Sécurité + +- Le service écoute uniquement sur localhost (127.0.0.1:8300) +- Aucune donnée n'est stockée persistante +- Les images sont traitées en mémoire et supprimées après + +--- + +Créé le 6 Avril 2026 diff --git a/effi-ocr-legacy/TOOL-OPENWEBUI.md b/effi-ocr-legacy/TOOL-OPENWEBUI.md new file mode 100644 index 0000000..e5decc7 --- /dev/null +++ b/effi-ocr-legacy/TOOL-OPENWEBUI.md @@ -0,0 +1,181 @@ +# Tool OCR pour OpenWebUI + +## Configuration + +Dans OpenWebUI (http://192.168.1.27:3000) : + +1. **Aller dans** : Admin → Tools → Create Tool +2. **Nom** : OCR Extracteur de Texte +3. **ID** : ocr_extractor +4. **Description** : Extrait le texte des images pour les livres et documents + +## Code du Tool + +```python +from typing import Optional +import requests +import base64 +import os + +class Tools: + def __init__(self): + """Initialize OCR Tool""" + self.ocr_url = os.getenv("OCR_URL", "http://192.168.1.11:8300") + + def extract_text_from_image(self, image_path: str, language: str = "fra") -> str: + """ + Extract text from an image file using OCR + + Args: + image_path: Path to the image file (jpg, png, etc.) + language: Language code (fra, eng, fra+eng, etc.) + + Returns: + Extracted text from the image + """ + try: + # Read image file + with open(image_path, 'rb') as f: + files = {'file': f} + data = {'lang': language} + + response = requests.post( + f"{self.ocr_url}/ocr", + files=files, + data=data, + timeout=30 + ) + + if response.status_code == 200: + result = response.json() + if result.get('success'): + text = result.get('text', '') + char_count = result.get('characters_count', 0) + word_count = result.get('words_count', 0) + return f"Texte extrait ({char_count} caractères, {word_count} mots):\n\n{text}" + else: + return f"Erreur OCR: {result.get('error', 'Unknown error')}" + else: + return f"Erreur HTTP {response.status_code}: {response.text}" + + except FileNotFoundError: + return f"Erreur: Fichier non trouvé: {image_path}" + except Exception as e: + return f"Erreur lors de l'OCR: {str(e)}" + + def extract_text_from_base64(self, image_base64: str, language: str = "fra") -> str: + """ + Extract text from a base64 encoded image + + Args: + image_base64: Base64 encoded image string + language: Language code (fra, eng, fra+eng, etc.) + + Returns: + Extracted text from the image + """ + try: + response = requests.post( + f"{self.ocr_url}/ocr/base64", + json={ + 'image_base64': image_base64, + 'lang': language, + 'preprocess': True + }, + timeout=30 + ) + + if response.status_code == 200: + result = response.json() + if result.get('success'): + text = result.get('text', '') + char_count = result.get('characters_count', 0) + word_count = result.get('words_count', 0) + return f"Texte extrait ({char_count} caractères, {word_count} mots):\n\n{text}" + else: + return f"Erreur OCR: extraction échouée" + else: + return f"Erreur HTTP {response.status_code}: {response.text}" + + except Exception as e: + return f"Erreur lors de l'OCR: {str(e)}" + + def get_available_languages(self) -> str: + """ + Get list of available OCR languages + + Returns: + List of available language codes + """ + try: + response = requests.get(f"{self.ocr_url}/languages", timeout=10) + + if response.status_code == 200: + result = response.json() + languages = result.get('available_languages', []) + return f"Langues disponibles: {', '.join(languages)}" + else: + return f"Erreur: {response.status_code}" + + except Exception as e: + return f"Erreur: {str(e)}" +``` + +## Variables d'environnement + +Dans les Settings d'OpenWebUI (Admin → Settings → Environment Variables) : + +**Si effi-ocr est sur le MEME serveur (recommandé):** +``` +OCR_URL=http://host.docker.internal:8300 +``` + +**Si effi-ocr est sur un autre serveur (Fedora 192.168.1.11):** +``` +OCR_URL=http://192.168.1.11:8300 +``` + +## Utilisation + +Une fois le Tool créé, les agents pourront l'utiliser avec ces fonctions : + +- `extract_text_from_image("/chemin/vers/image.jpg", "fra")` - Extraire le texte d'un fichier image +- `extract_text_from_base64("base64encoded...", "fra")` - Extraire d'une image encodée en base64 +- `get_available_languages()` - Lister les langues disponibles + +## Exemple de prompt + +``` +"Effi, lis-moi ce texte" [upload d'image] +→ L'agent utilise automatiquement le Tool OCR +``` + +## Langues disponibles + +- `fra` - Français +- `eng` - Anglais +- `fra+eng` - Français + Anglais (bilingue) +- `deu` - Allemand +- `spa` - Espagnol +- `ita` - Italien + +## Dépannage + +**Erreur de connexion** : +```bash +# Vérifier que le service OCR tourne +curl http://192.168.1.11:8300/health + +# Redémarrer si nécessaire +cd ~/Documents/OpenCode/Projets/Effi/effi-ocr +sudo docker compose restart +``` + +**Texte mal extrait** : +- Essayer `fra+eng` pour les documents bilingues +- Vérifier la qualité de l'image (résolution, contraste) +- L'OCR fonctionne mieux avec des images bien éclairées + +--- + +*Tool créé le 6 Avril 2026* diff --git a/effi-ocr-legacy/TROUBLESHOOTING.md b/effi-ocr-legacy/TROUBLESHOOTING.md new file mode 100644 index 0000000..a0b34dc --- /dev/null +++ b/effi-ocr-legacy/TROUBLESHOOTING.md @@ -0,0 +1,80 @@ +# Dépannage effi-OCR + +## Problèmes Connus + +### 1. "Fichier non trouvé" - Erreur Common + +**Symptôme :** +``` +Erreur: Fichier non trouvé pour l'ID: page_image.jpg (cherché dans /app/backend/data/uploads/) +``` + +**Cause :** +effi-ocr est sur une autre machine que OpenWebUI. Les chemins de fichiers ne sont pas partagés. + +**Solution :** +Installer effi-ocr sur le **MÊME serveur** qu'OpenWebUI (effi-server 192.168.1.27) + +--- + +### 2. effi-ocr loop / se bloque + +**Symptôme :** +Le modèle répète "call the tool call the tool..." en boucle. + +**Causes possibles :** +- Tool mal configuré dans OpenWebUI +- URL OCR_URL incorrecte +- Le modèle a un problème avec le tool calling + +**Solutions :** +1. Vérifier que `OCR_URL` pointe vers le bon serveur +2. Recréer le tool OCR dans OpenWebUI +3. Désactiver Sub Agent/Planner Agent temporairement + +--- + +### 3. OCR retourne du texte vide + +**Causes possibles :** +- Image de mauvaise qualité +- Langue incorrecte (utiliser `fra+eng`) +- Prétraitement désactivé + +**Solutions :** +1. Tester avec une image plus claire +2. Utiliser `lang=fra+eng` pour documents mixtes +3. Activer le prétraitement + +--- + +## Checklist Diagnostic + +- [ ] effi-ocr tourne sur le port 8300 +- [ ] OCR_URL est correct dans OpenWebUI +- [ ] effi-ocr est sur le MÊME serveur qu'OpenWebUI +- [ ] Le tool OCR est correctement créé dans Admin → Tools +- [ ] Le fichier uploadé existe dans /app/backend/data/uploads/ + +--- + +## Commandes de Test + +### Vérifier effi-ocr (sur le serveur où il tourne) +```bash +curl http://localhost:8300/health +``` + +### Tester OCR avec une image +```bash +curl -X POST -F "file=@test.jpg" -F "lang=fra" http://localhost:8300/ocr +``` + +### Vérifier les fichiers uploadés (OpenWebUI) +```bash +ls -la /app/backend/data/uploads/ +``` + +--- + +*Dernière mise à jour: Avril 2026* diff --git a/effi-ocr-legacy/docker-compose.yml b/effi-ocr-legacy/docker-compose.yml new file mode 100644 index 0000000..432cb98 --- /dev/null +++ b/effi-ocr-legacy/docker-compose.yml @@ -0,0 +1,22 @@ +services: + effi-ocr: + build: . + container_name: effi-ocr + ports: + - "8300:8300" + restart: unless-stopped + environment: + - PYTHONUNBUFFERED=1 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8300/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s + networks: + - effi-network + +networks: + effi-network: + external: true + name: effi-network diff --git a/effi-ocr-legacy/main.py b/effi-ocr-legacy/main.py new file mode 100644 index 0000000..aefba3d --- /dev/null +++ b/effi-ocr-legacy/main.py @@ -0,0 +1,208 @@ +""" +Effi-OCR : Service OCR pour Effi +API FastAPI qui expose Tesseract via une interface REST +""" + +from fastapi import FastAPI, File, UploadFile, HTTPException +from fastapi.responses import JSONResponse +from PIL import Image +import pytesseract +import io +import logging +from typing import Optional + +# Configuration logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +app = FastAPI( + title="Effi-OCR", + description="Service OCR pour extraction de texte d'images", + version="1.0.0" +) + + +@app.get("/health") +async def health_check(): + """Endpoint de santé pour monitoring""" + try: + # Vérifier que Tesseract est accessible + import subprocess + result = subprocess.run(['tesseract', '--version'], capture_output=True, text=True) + version_line = result.stdout.split('\n')[0] if result.stdout else "unknown" + tesseract_version = str(version_line) # Forcer en string + except Exception as e: + tesseract_version = f"error: {str(e)}" + + return { + "status": "healthy", + "service": "effi-ocr", + "version": "1.0.0", + "tesseract_version": tesseract_version + } + + +@app.post("/ocr") +async def ocr_image( + file: UploadFile = File(...), + lang: str = "fra", + preprocess: bool = True +): + """ + Extraire le texte d'une image + + Args: + file: Image à analyser (jpg, png, etc.) + lang: Code langue (fra, eng, fra+eng, etc.) + preprocess: Appliquer un prétraitement pour améliorer la qualité + + Returns: + JSON avec le texte extrait et les métadonnées + """ + logger.info(f"OCR demandé - lang: {lang}, fichier: {file.filename}") + + # Vérifier le type de fichier + allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/tiff', 'image/webp'] + if file.content_type not in allowed_types: + raise HTTPException( + status_code=400, + detail=f"Type de fichier non supporté: {file.content_type}. Types acceptés: {', '.join(allowed_types)}" + ) + + try: + # Lire l'image + contents = await file.read() + image = Image.open(io.BytesIO(contents)) + + # Convertir en RGB si nécessaire (pour les images avec transparence) + if image.mode in ('RGBA', 'LA', 'P'): + image = image.convert('RGB') + + # Prétraitement optionnel pour améliorer la qualité OCR + if preprocess: + # Convertir en niveaux de gris pour meilleure détection texte + image = image.convert('L') + # Augmenter le contraste + from PIL import ImageEnhance + enhancer = ImageEnhance.Contrast(image) + image = enhancer.enhance(2.0) + + # OCR avec Tesseract + text = pytesseract.image_to_string(image, lang=lang) + + # Nettoyer le texte + text = text.strip() + + # Informations sur l'image + width, height = image.size + + logger.info(f"OCR terminé - {len(text)} caractères extraits") + + return JSONResponse(content={ + "success": True, + "text": text, + "language": lang, + "filename": file.filename, + "image_size": {"width": width, "height": height}, + "characters_count": len(text), + "words_count": len(text.split()) if text else 0 + }) + + except Exception as e: + logger.error(f"Erreur OCR: {str(e)}") + raise HTTPException(status_code=500, detail=f"Erreur lors de l'OCR: {str(e)}") + + +@app.post("/ocr/base64") +async def ocr_base64( + image_base64: str, + lang: str = "fra", + preprocess: bool = True +): + """ + Extraire le texte d'une image en base64 + + Args: + image_base64: Image encodée en base64 + lang: Code langue (fra, eng, fra+eng, etc.) + preprocess: Appliquer un prétraitement + + Returns: + JSON avec le texte extrait + """ + import base64 + + logger.info(f"OCR base64 demandé - lang: {lang}") + + try: + # Décoder le base64 + image_data = base64.b64decode(image_base64) + image = Image.open(io.BytesIO(image_data)) + + # Convertir en RGB si nécessaire + if image.mode in ('RGBA', 'LA', 'P'): + image = image.convert('RGB') + + # Prétraitement + if preprocess: + image = image.convert('L') + from PIL import ImageEnhance + enhancer = ImageEnhance.Contrast(image) + image = enhancer.enhance(2.0) + + # OCR + text = pytesseract.image_to_string(image, lang=lang) + text = text.strip() + + width, height = image.size + + logger.info(f"OCR base64 terminé - {len(text)} caractères") + + return { + "success": True, + "text": text, + "language": lang, + "image_size": {"width": width, "height": height}, + "characters_count": len(text), + "words_count": len(text.split()) if text else 0 + } + + except Exception as e: + logger.error(f"Erreur OCR base64: {str(e)}") + raise HTTPException(status_code=500, detail=f"Erreur lors de l'OCR: {str(e)}") + + +@app.get("/languages") +async def list_languages(): + """Lister les langues disponibles pour l'OCR""" + try: + # Commande tesseract pour lister les langues + import subprocess + result = subprocess.run( + ['tesseract', '--list-langs'], + capture_output=True, + text=True + ) + + # Parser la sortie + lines = result.stdout.strip().split('\n') + # La première ligne est "List of available languages (xxx):" + languages = lines[1:] if len(lines) > 1 else [] + + return { + "available_languages": languages, + "count": len(languages) + } + except Exception as e: + logger.error(f"Erreur listage langues: {str(e)}") + # Fallback avec les langues communes + return { + "available_languages": ["fra", "eng", "fra+eng", "deu", "spa", "ita"], + "count": 6, + "note": "Liste par défaut, erreur lors de la récupération" + } + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8300) diff --git a/effi-ocr-legacy/requirements.txt b/effi-ocr-legacy/requirements.txt new file mode 100644 index 0000000..9a579d7 --- /dev/null +++ b/effi-ocr-legacy/requirements.txt @@ -0,0 +1,5 @@ +fastapi==0.109.0 +uvicorn[standard]==0.27.0 +python-multipart==0.0.6 +pillow==10.2.0 +pytesseract==0.3.10