docs: add effi-ocr-legacy for OCR comparison
effi-ocr runs on port 8301 alongside Kreuzberg on 8300 for quality comparison
This commit is contained in:
@@ -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"]
|
||||||
@@ -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
|
||||||
@@ -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*
|
||||||
@@ -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*
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user