File size: 7,470 Bytes
315f3ab
 
 
 
d852ed4
 
 
315f3ab
 
d852ed4
d209343
d852ed4
315f3ab
d852ed4
 
 
315f3ab
 
d852ed4
 
315f3ab
 
 
d209343
315f3ab
d852ed4
315f3ab
 
 
d209343
315f3ab
 
 
 
 
 
 
d209343
315f3ab
 
d209343
d852ed4
315f3ab
d209343
315f3ab
 
 
d209343
315f3ab
 
 
d209343
315f3ab
 
d209343
315f3ab
 
 
d209343
 
 
 
 
 
ac34952
 
d209343
ac34952
d209343
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ac34952
 
d209343
 
 
 
 
 
 
 
 
 
 
 
 
 
ac34952
d209343
 
 
 
 
 
 
 
 
 
 
 
ac34952
d209343
 
 
 
 
 
 
 
 
 
 
 
 
 
ac34952
 
 
d209343
 
 
ac34952
 
d209343
 
 
ac34952
 
d209343
 
 
 
 
 
315f3ab
 
 
 
 
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
import os
import gradio as gr
import torch
from PIL import Image
# Importation directe de la classe spécifique pour Qwen2.5-VL
from transformers import AutoProcessor, Qwen2_5_VLForConditionalGeneration
import spaces  # Import crucial pour utiliser les GPU gratuits de Hugging Face

# --- CONFIGURATION ---
# REMPLACEZ 'votre_vrai_username' par votre véritable identifiant Hugging Face
MODEL_ID = "hamoud-7/schema-bac-qwen-model"
HF_TOKEN = os.environ.get("HF_TOKEN")

print("⏳ Chargement du processeur et du modèle de vision Qwen2.5-VL...")
processor = AutoProcessor.from_pretrained(MODEL_ID, token=HF_TOKEN)
model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
    MODEL_ID,
    torch_dtype=torch.float16,
    device_map="auto",
    token=HF_TOKEN
)
print("✅ Application prête !")


# --- FONCTION D'INFÉRENCE PROPULSÉE PAR ZEROGPU ---
@spaces.GPU  # Active le GPU gratuitement à la demande pour l'utilisateur
def run_inference(image_pil, question_text):
    if image_pil is None or not question_text.strip():
        return "Erreur : Veuillez fournir une image ET une question."

    # Préparation du prompt au format Qwen-VL
    messages = [
        {"role": "user", "content": [
            {"type": "image", "image": image_pil},
            {"type": "text", "text": question_text}
        ]}
    ]

    # Application du template de chat standard
    text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)

    # Traitement des entrées pour le modèle (gère automatiquement les images et le texte)
    inputs = processor(text=[text], images=[image_pil], padding=True, return_tensors="pt").to("cuda")

    # Génération de la réponse
    with torch.no_grad():
        generated_ids = model.generate(**inputs, max_new_tokens=128, do_sample=False)

    # Découpage des tokens générés pour ne garder que la réponse de l'assistant
    generated_ids_trimmed = [out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)]
    response_text = processor.batch_decode(generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]

    return response_text.strip()


# --- INTERFACE DESIGN GRADIO ---
logo_url = "https://imgur.com/GDKnbVp.png"

custom_css = """
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');

.gradio-container {
    font-family: 'Poppins', sans-serif !important;
    background: linear-gradient(135deg, #f5f7fb 0%, #eef1f8 100%) !important;
    width: 100% !important;
    max-width: 100% !important; /* Modifié pour occuper tout l'espace disponible */
    margin: 0 auto !important;
    padding: 0 10px !important;
}

/* --- En-tête --- */
#header-block {
    background: linear-gradient(120deg, #4338CA 0%, #6366F1 55%, #4F46E5 100%);
    border-radius: 22px;
    padding: 34px 20px !important;
    margin-bottom: 26px;
    box-shadow: 0 12px 30px rgba(79, 70, 229, 0.28);
}
#header-block h1 {
    color: #ffffff !important;
    font-weight: 700 !important;
    letter-spacing: 0.3px;
}
#header-block p {
    color: #E0E7FF !important;
}

/* --- Cartes --- */
.card {
    background: #ffffff !important;
    border-radius: 18px !important;
    padding: 22px !important;
    box-shadow: 0 4px 22px rgba(15, 23, 42, 0.07) !important;
    border: 1px solid #EEF2FF !important;
}

.card h3, .card .prose h3 {
    margin-top: 0 !important;
    color: #312E81 !important;
    font-weight: 600 !important;
}

/* --- Bouton principal --- */
#submit-btn {
    background: linear-gradient(120deg, #4F46E5, #6366F1) !important;
    border: none !important;
    color: #ffffff !important;
    font-weight: 600 !important;
    border-radius: 12px !important;
    box-shadow: 0 6px 16px rgba(79, 70, 229, 0.35) !important;
    transition: transform 0.15s ease, box-shadow 0.15s ease !important;
}
#submit-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 24px rgba(79, 70, 229, 0.45) !important;
}

/* --- Zone de réponse --- */
#output-box textarea {
    background: #F8FAFC !important;
    border-radius: 12px !important;
    font-size: 1rem !important;
    border: 1px solid #E2E8F0 !important;
}

/* --- Exemples --- */
#examples-card .gr-samples-table, #examples-card table {
    border-radius: 12px !important;
    overflow: hidden !important;
}

footer {visibility: hidden}
"""

with gr.Blocks(
    theme=gr.themes.Soft(primary_hue="indigo", secondary_hue="blue"),
    title="SCHÉMA BAC",
    css=custom_css,
    fill_width=True # Ajouté pour forcer l'interface à s'étaler horizontalement
) as demo:

    with gr.Column(elem_id="header-block"):
        gr.HTML(f'''
        <div style="text-align: center;">
           <img src="{logo_url}" alt="SCHÉMA BAC Logo" style="height: 70px; display: block; margin: 0 auto 12px auto;">
           <h1 style="margin:0; font-size: 2.1rem;">SCHÉMA BAC</h1>
           <p style="font-size: 1.05rem; margin-top: 8px; max-width: 640px; margin-left:auto; margin-right:auto;">
               L'intelligence artificielle au service de la compréhension des schémas scientifiques.
           </p>
        </div>
        ''')

    with gr.Row(equal_height=True):
        with gr.Column(scale=1, min_width=320, elem_classes="card"): # min_width adapté pour maintenir la structure côte à côte
            gr.Markdown("### 1️⃣ Téléchargez votre schéma")
            image_input = gr.Image(type="pil", label="", height=280)

            gr.Markdown("### 2️⃣ Posez votre question")
            question_input = gr.Textbox(
                label="",
                placeholder="Ex : Quelle est la fonction de la structure numéro 3 ?",
                lines=3
            )

            submit_btn = gr.Button("🔍 Analyser le schéma", variant="primary", elem_id="submit-btn")

        with gr.Column(scale=1, min_width=320, elem_classes="card"): # min_width adapté pour maintenir la structure côte à côte
            gr.Markdown("### 🤖 Analyse de l'assistant")
            output_text = gr.Textbox(
                label="",
                interactive=False,
                lines=14,
                elem_id="output-box",
                placeholder="La réponse de l'IA apparaîtra ici..."
            )

    with gr.Group(elem_classes="card", elem_id="examples-card"):
        gr.Markdown("### 📚 Exemples à essayer — cliquez pour tester")
        gr.Examples(
            examples=[
                [
                    # 1. On remplace "examples" par "images"
                    # 2. Changez "votre_image_1.jpg" par le nom exact de votre premier fichier
                    os.path.join("images", "image.jpg"), 
                    "Quel est le rôle de la structure numéro 3 ?"
                ],
                [
                    # Faites de même pour la deuxième image
                    os.path.join("images", "image (3).jpg"),
                    "Que représente la structure numéro 3 sur cette coupe de rein ?"
                ],
                [
                    # Et pour la troisième image
                    os.path.join("images", "image (1).jpg"),
                    "Quelle est la fonction de la structure numéro 2 ?"
                ],
            ],
            inputs=[image_input, question_input],
            label=""
        )

    submit_btn.click(fn=run_inference, inputs=[image_input, question_input], outputs=output_text)

if __name__ == "__main__":
    demo.launch()