Spaces:
Runtime error
Runtime error
File size: 9,189 Bytes
e9bbd1d 5315d86 e9bbd1d 5315d86 e9bbd1d 5ffe7d7 5315d86 4c314e3 5ffe7d7 4c314e3 5315d86 e9bbd1d 5ffe7d7 e9bbd1d 5315d86 e9bbd1d 2e1e6c9 5b046b1 b1538d0 5b046b1 db78f91 5315d86 e9bbd1d 5ffe7d7 e9bbd1d 5ffe7d7 e9bbd1d 5ffe7d7 e9bbd1d 5ffe7d7 e9bbd1d 5ffe7d7 e9bbd1d 5ffe7d7 e9bbd1d 5ffe7d7 e9bbd1d 5ffe7d7 4c314e3 5ffe7d7 4c314e3 5ffe7d7 4c314e3 5ffe7d7 4c314e3 5ffe7d7 4c314e3 5ffe7d7 4c314e3 5ffe7d7 4c314e3 5ffe7d7 4c314e3 5ffe7d7 4c314e3 5ffe7d7 4c314e3 5ffe7d7 4c314e3 e9bbd1d 5ffe7d7 4c314e3 5ffe7d7 4c314e3 5315d86 4c314e3 5ffe7d7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | import gradio as gr
from PIL import Image
import os
import json
import time
import torch
from transformers import MllamaForConditionalGeneration, AutoProcessor
import spaces
ckpt = "unsloth/Llama-3.2-11B-Vision-Instruct"
device = "cuda" if torch.cuda.is_available() else "cpu"
# Chargement du modèle et processeur
model = MllamaForConditionalGeneration.from_pretrained(
ckpt,
torch_dtype=torch.bfloat16 if device=="cuda" else torch.float32
).to(device)
processor = AutoProcessor.from_pretrained(ckpt)
SAVE_DIR = "corrections"
os.makedirs(SAVE_DIR, exist_ok=True)
@spaces.GPU
def ocr_on_image(image):
prompt = (
"Output ONLY the raw text exactly as it appears in the image. Do not add anything.\n\n"
"The image may contain both handwritten and printed text in French and/or English, including punctuation and underscores.\n\n"
"Your task: Transcribe all visible text exactly, preserving:\n"
"- All characters, accents, punctuation, spacing, and line breaks.\n"
"- The original reading order and layout, including tables and forms if present.\n\n"
"Rules:\n"
"- Do NOT add any explanations, summaries, comments, or extra text.\n"
"- Do NOT duplicate any content.\n"
"- Do NOT indicate blank space.\n"
"- Do NOT separate handwritten and printed text.\n"
"- Do NOT confuse '.' (a period) with '|' (a border).\n\n"
"Only extract the text that is actually visible in the image, and nothing else."
)
prompt2 =( "Extract all visible text from the image, including both handwritten and printed content."
"Do not translate the text — preserve the original language exactly as it appears."
"Return only the extracted text, with no explanation, no formatting, and no additions." )
prompt3 = (
"Output ONLY the raw text as it appears in the image, nothing else."
"You have an image containing both handwritten and printed text in French and/or English, and alsos punctuation and underscores.\n"
"Your task: transcribe EXACTLY all visible text, preserving all characters, accents, punctuation, spacing, and line breaks.\n"
"Include tables and forms clearly if present.\n"
"Do NOT add any explanations, comments, summaries, or extra text.\n"
"Check the output first to not duplicate results."
"Preserve the original reading order, including line breaks and the natural layout of tables or forms. Output the text exactly as it appears visually, maintaining the structure."
"Don't indicate blank space."
"Don't separate handwritten and printex text."
"DO NOT confuse between '.' a point and '|' a boder"
"Extract only the raw text with and do not add any comment"
"Extract the content ligne by ligne"
)
messages = [{"role": "user", "content": [{"type": "text", "text": "Extract handwritten text from the image and output only the extracted text without any additional description or commentary in output"}, {"type": "image"}]}]
texts = processor.apply_chat_template(messages, add_generation_prompt=True)
inputs = processor(text=texts, images=[image], return_tensors="pt").to(device)
outputs = model.generate(**inputs, max_new_tokens=250)
result = processor.decode(outputs[0], skip_special_tokens=True)
# Nettoyage simple
if "assistant" in result.lower():
result = result[result.lower().find("assistant") + len("assistant"):].strip()
result = result.replace("user", "").replace(prompt, "").strip()
return result
def batch_ocr(images):
if not images:
return [], "Aucune image uploadée."
results = []
status_text = f"Traitement de {len(images)} image(s)...\n"
for i, img_file in enumerate(images):
try:
pil_img = Image.open(img_file.name).convert("RGB")
text = ocr_on_image(pil_img)
results.append({
"image": pil_img,
"filepath": img_file.name,
"ocr_text": text,
"corrected_text": text
})
status_text += f"Image {i+1}: ✓ Texte extrait\n"
except Exception as e:
status_text += f"Image {i+1}: ❌ Erreur: {str(e)}\n"
return results, status_text
def save_all_corrections(data_list, *corrections):
if not data_list:
return "Aucune donnée à sauvegarder."
# Mettre à jour les corrections avec les textes modifiés
for i, correction in enumerate(corrections):
if i < len(data_list) and correction.strip():
data_list[i]["corrected_text"] = correction
timestamp = int(time.time())
saved_files = []
for i, data in enumerate(data_list):
img_path = f"{SAVE_DIR}/image_{timestamp}_{i}.png"
json_path = f"{SAVE_DIR}/correction_{timestamp}_{i}.jsonl"
data["image"].save(img_path)
entry = {
"image_path": img_path,
"ocr_text": data["ocr_text"],
"corrected_text": data["corrected_text"]
}
with open(json_path, "a", encoding="utf-8") as f:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
saved_files.append(json_path)
return f"✅ {len(saved_files)} correction(s) sauvegardée(s) dans le dossier '{SAVE_DIR}'."
# Interface Gradio simplifiée
with gr.Blocks(title="OCR avec Llama Vision", theme=gr.themes.Soft()) as demo:
gr.Markdown("# 🔍 OCR Multi-Images avec Correction Manuelle")
gr.Markdown("Uploadez vos images et extrayez le texte automatiquement, puis corrigez si nécessaire.")
with gr.Row():
uploaded = gr.Files(
file_types=[".png", ".jpg", ".jpeg", ".tif"],
label="📁 Uploader plusieurs images",
file_count="multiple"
)
btn_ocr = gr.Button("🚀 Extraire le texte OCR", variant="primary", size="lg")
status = gr.Textbox(label="📊 Status", lines=3, visible=False)
# Conteneurs pour les résultats (fixes, pas dynamiques)
results_data = gr.State([])
with gr.Column(visible=False) as results_section:
gr.Markdown("## 📝 Résultats OCR - Vous pouvez modifier le texte ci-dessous")
# Interface fixe pour jusqu'à 5 images (ajustez selon vos besoins)
image_components = []
text_components = []
for i in range(5): # Maximum 5 images
with gr.Row(visible=False) as row:
with gr.Column(scale=1):
img_comp = gr.Image(label=f"Image {i+1}", height=300)
image_components.append((row, img_comp))
with gr.Column(scale=2):
txt_comp = gr.Textbox(
label=f"Texte extrait - Image {i+1}",
lines=10,
placeholder="Le texte extrait apparaîtra ici..."
)
text_components.append(txt_comp)
btn_save = gr.Button("💾 Sauvegarder toutes les corrections", variant="secondary", size="lg")
save_status = gr.Textbox(label="💾 Status de sauvegarde", visible=False)
def process_images(images):
if not images:
return (
gr.update(visible=True, value="❌ Aucune image uploadée."),
gr.update(visible=False),
gr.update(visible=False),
[],
*[gr.update(visible=False) for _ in range(5)],
*[gr.update(value="") for _ in range(5)]
)
results, status_text = batch_ocr(images)
# Mise à jour des composants d'image et de texte
image_updates = []
text_updates = []
for i in range(5):
if i < len(results):
# Montrer l'image et le texte
image_updates.append(gr.update(visible=True))
image_updates.append(gr.update(value=results[i]["image"]))
text_updates.append(gr.update(value=results[i]["ocr_text"]))
else:
# Cacher les composants non utilisés
image_updates.append(gr.update(visible=False))
image_updates.append(gr.update(value=None))
text_updates.append(gr.update(value=""))
return (
gr.update(visible=True, value=status_text),
gr.update(visible=True),
gr.update(visible=True),
results,
*image_updates,
*text_updates
)
# Préparer les outputs pour le clic
image_outputs = []
for row, img in image_components:
image_outputs.extend([row, img])
btn_ocr.click(
process_images,
inputs=[uploaded],
outputs=[
status,
results_section,
save_status,
results_data,
*image_outputs,
*text_components
]
)
btn_save.click(
save_all_corrections,
inputs=[results_data] + text_components,
outputs=save_status
)
if __name__ == "__main__":
demo.launch(debug=True) |