feat(generate-song): add ACE-Step download script, remove legacy effi-ocr
- Add download-ace-step-full.sh script for easier model downloads - Remove effi-ocr (migrated to effi-kreuzberg for OCR functionality) - Generate Song v0.5.4 now fully functional with JK-AceStep-Nodes
This commit is contained in:
Executable
+72
@@ -0,0 +1,72 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Script de téléchargement des modèles ACE-Step 1.5 COMPLETS
|
||||||
|
# À lancer sur Folzo Corp
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BASE_DIR="/home/folzo/Documents/OpenCode/Projets/Effi/comfyui/models"
|
||||||
|
|
||||||
|
echo "🎵 ACE-Step 1.5 - Téléchargement COMPLET des modèles"
|
||||||
|
echo "===================================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 1. VAE
|
||||||
|
echo "📦 1/4 - Téléchargement du VAE..."
|
||||||
|
mkdir -p "$BASE_DIR/vae"
|
||||||
|
cd "$BASE_DIR/vae"
|
||||||
|
if [ ! -f "ace_1.5_vae.safetensors" ]; then
|
||||||
|
wget -q --progress=bar:force "https://huggingface.co/ACE-Step/Ace-Step1.5/resolve/main/vae/diffusion_pytorch_model.safetensors" -O "ace_1.5_vae.safetensors"
|
||||||
|
echo "✅ VAE téléchargé"
|
||||||
|
else
|
||||||
|
echo "✅ VAE déjà présent"
|
||||||
|
fi
|
||||||
|
ls -lh "ace_1.5_vae.safetensors"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 2. Diffusion Model
|
||||||
|
echo "📦 2/4 - Téléchargement du modèle de diffusion..."
|
||||||
|
mkdir -p "$BASE_DIR/diffusion_models"
|
||||||
|
cd "$BASE_DIR/diffusion_models"
|
||||||
|
if [ ! -f "acestep_v1.5_turbo.safetensors" ]; then
|
||||||
|
wget -q --progress=bar:force "https://huggingface.co/ACE-Step/Ace-Step1.5/resolve/main/acestep-v15-turbo/model.safetensors" -O "acestep_v1.5_turbo.safetensors"
|
||||||
|
echo "✅ Diffusion model téléchargé"
|
||||||
|
else
|
||||||
|
echo "✅ Diffusion model déjà présent"
|
||||||
|
fi
|
||||||
|
ls -lh "acestep_v1.5_turbo.safetensors"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 3. Text Encoder 0.6B
|
||||||
|
echo "📦 3/4 - Téléchargement du text encoder 0.6B..."
|
||||||
|
mkdir -p "$BASE_DIR/text_encoders"
|
||||||
|
cd "$BASE_DIR/text_encoders"
|
||||||
|
if [ ! -f "qwen_0.6b_ace15.safetensors" ]; then
|
||||||
|
wget -q --progress=bar:force "https://huggingface.co/ACE-Step/Ace-Step1.5/resolve/main/acestep-5Hz-lm-0.6B/model.safetensors" -O "qwen_0.6b_ace15.safetensors"
|
||||||
|
echo "✅ Text encoder 0.6B téléchargé"
|
||||||
|
else
|
||||||
|
echo "✅ Text encoder 0.6B déjà présent"
|
||||||
|
fi
|
||||||
|
ls -lh "qwen_0.6b_ace15.safetensors"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 4. Text Encoder 4B
|
||||||
|
echo "📦 4/4 - Téléchargement du text encoder 4B..."
|
||||||
|
cd "$BASE_DIR/text_encoders"
|
||||||
|
if [ ! -f "qwen_4b_ace15.safetensors" ]; then
|
||||||
|
wget -q --progress=bar:force "https://huggingface.co/ACE-Step/Ace-Step1.5/resolve/main/acestep-5Hz-lm-4B/model.safetensors" -O "qwen_4b_ace15.safetensors"
|
||||||
|
echo "✅ Text encoder 4B téléchargé"
|
||||||
|
else
|
||||||
|
echo "✅ Text encoder 4B déjà présent"
|
||||||
|
fi
|
||||||
|
ls -lh "qwen_4b_ace15.safetensors"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "===================================================="
|
||||||
|
echo "🎉 Téléchargement terminé !"
|
||||||
|
echo ""
|
||||||
|
echo "Prochaine étape : redémarrer ComfyUI"
|
||||||
|
echo "docker restart comfyui"
|
||||||
|
echo "===================================================="
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
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"]
|
|
||||||
@@ -1,232 +0,0 @@
|
|||||||
# 📝 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
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
# 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*
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
# 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*
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
"""
|
|
||||||
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)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
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