Aller au contenu principal

Module 2 — Composants pour texte, image, audio et vidéo

Le module précédent a montré gr.Interface sur un cas simple. La difficulté suivante n'est plus l'architecture mais le détail des composants : que reçoit votre fonction quand l'utilisateur téléverse une image ? Un chemin de fichier ? Un tableau NumPy ? Une image PIL ? La réponse dépend d'un seul paramètre, et la moitié des messages d'erreur des débutants vient de son mauvais réglage. Ce module fait le tour des composants les plus utilisés et fixe le format exact que la fonction reçoit dans chaque cas.

Le texte : gr.Textbox sous toutes ses formes

Le composant texte est le plus utilisé, et il a plusieurs visages. Sans paramètre, il présente une ligne de saisie. Avec lines=5, il devient une zone multiligne. Avec placeholder="entrez votre requête ici", il affiche un texte grisé qui disparaît à la première frappe. Avec type="password", il masque les caractères, ce qui est utile pour saisir une clé d'API dans une démonstration privée.

La fonction reçoit toujours une chaîne de caractères, éventuellement vide si l'utilisateur n'a rien saisi. Ce cas de la chaîne vide est le premier piège : le composant n'appelle pas la fonction avec None, il l'appelle avec "", et si votre code fait if entree: cela fonctionne, mais if entree is None: échoue silencieusement. Rendre la fonction robuste à ce cas dès la première ligne évite des heures de débogage.

L'image : type change tout

gr.Image accepte trois valeurs pour son paramètre type. Avec type="numpy" (la valeur par défaut), votre fonction reçoit un numpy.ndarray de forme (hauteur, largeur, 3), en RGB, avec des entiers de 0 à 255. Avec type="pil", elle reçoit un objet PIL.Image déjà chargé et prêt à être passé à un pipeline torchvision ou Hugging Face. Avec type="filepath", elle reçoit une chaîne : le chemin absolu d'un fichier temporaire créé par Gradio, qui reste vivant le temps de l'appel.

Le choix se fait selon ce que consomme le modèle. Un pipeline transformers de type image-classification accepte les trois formats et les convertit lui-même. Un modèle torchvision préfère le PIL. Un outil qui lit l'image avec cv2.imread a besoin d'un chemin, donc type="filepath". Une bibliothèque qui accepte un tableau NumPy s'accommode du défaut. Se tromper coûte un AttributeError immédiat, ce qui rend le diagnostic rapide.

L'audio : le couple fréquence et tableau

gr.Audio a lui aussi un paramètre type, avec les valeurs "numpy" (défaut) et "filepath". En NumPy, la fonction reçoit un tuple (fréquence_hertz, tableau)fréquence_hertz est un entier (souvent 44100 ou 48000) et tableau un tableau NumPy des échantillons audio, éventuellement stéréo si l'entrée l'était. Ce format est exactement celui attendu par scipy.io.wavfile.write, ce qui rend l'écriture d'un fichier temporaire triviale.

Un paramètre supplémentaire décide de la source : sources=["upload", "microphone"] (défaut) permet à l'utilisateur de téléverser un fichier ou d'enregistrer depuis son navigateur. Restreindre à sources=["microphone"] force l'enregistrement en direct, ce qui est utile pour une démonstration de transcription temps réel. Restreindre à sources=["upload"] désactive l'accès au micro, ce qui rassure les utilisateurs soucieux de leur vie privée.

Un outil de transcription audio

Le fil rouge s'enrichit d'une deuxième démonstration : un outil qui reçoit un enregistrement audio et retourne sa transcription. On utilise un modèle Whisper de Hugging Face, en version tiny pour garder le temps de calcul acceptable sur CPU.

import gradio as gr
from transformers import pipeline

transcripteur = pipeline(
task="automatic-speech-recognition",
model="openai/whisper-tiny",
)

def transcrire(audio):
if audio is None:
return "Aucun audio fourni."
frequence, tableau = audio
if tableau.ndim > 1:
tableau = tableau.mean(axis=1)
tableau = tableau.astype("float32") / 32768.0
resultat = transcripteur({"raw": tableau, "sampling_rate": frequence})
return resultat["text"].strip()

demo = gr.Interface(
fn=transcrire,
inputs=gr.Audio(sources=["upload", "microphone"], type="numpy"),
outputs=gr.Textbox(label="Transcription", lines=6),
title="Transcription audio (Whisper tiny)",
description="Enregistrez ou téléversez un fichier audio en français ou en anglais. Le modèle retourne le texte transcrit.",
)
demo.launch()

Trois pièges classiques apparaissent dans le prétraitement. La conversion stéréo vers mono (tableau.mean(axis=1)) est nécessaire car Whisper attend un signal mono. La normalisation (/ 32768.0) convertit les entiers 16 bits en flottants dans [-1, 1], format standard des modèles audio. Le passage explicite de sampling_rate évite que le pipeline devine la fréquence, ce qui produit parfois des transcriptions bruitées.

La vidéo : chemin de fichier obligatoire

gr.Video n'a pas de raccourci en tableau NumPy — les vidéos sont trop lourdes pour être passées en mémoire brute. La fonction reçoit toujours un chemin de fichier (chaîne), et il vous appartient d'utiliser cv2.VideoCapture, moviepy.editor.VideoFileClip ou imageio pour lire les images. Le format retourné en sortie doit également être un chemin, ce qui impose d'écrire un fichier temporaire (tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)) et de retourner son nom.

Ce fonctionnement par fichier explique pourquoi les démonstrations vidéo sont plus lentes qu'attendu : chaque appel copie la vidéo sur le disque, l'analyse, réécrit un fichier, le sert. Pour une démonstration temps réel sur un flux vidéo, mieux vaut passer par gr.Image(sources=["webcam"], streaming=True) qui livre l'image courante en continu, plutôt que par gr.Video.

Les composants de sortie qui sauvent des lignes de code

Trois composants de sortie sont particulièrement utiles et méritent d'être connus. gr.Label attend un dictionnaire {étiquette: probabilité} et affiche une barre pour chacune, avec un tri automatique. gr.Gallery attend une liste d'images (PIL, NumPy ou chemins) et affiche une grille cliquable. gr.Dataframe attend un pandas.DataFrame ou une liste de listes, et l'affiche comme un tableau navigable, avec tri et copie possibles.

Utiliser ces composants au lieu de sérialiser à la main la sortie dans un gr.Textbox rend la démonstration à la fois plus lisible et plus interactive. gr.Label en particulier remplace avantageusement le classique f"{etiquette} ({probabilite:.2%})\n" répété trois fois.

Les événements de changement, plus discrets qu'un bouton

Par défaut, gr.Interface attend un clic sur « Envoyer » pour appeler la fonction. Sur les composants où l'entrée est légère à recalculer, on peut passer live=True à l'interface pour appeler la fonction à chaque changement d'un composant. Cela rend une démonstration de curseurs particulièrement fluide — glisser un gr.Slider met à jour la sortie en temps réel — mais devient catastrophique sur une image (une image glissée déclenche une requête par pixel). Le mode live est un excellent choix pour les composants légers, un très mauvais pour les composants lourds.

Le format reçu par la fonction est un contrat

Un modèle qui accepte PIL.Image et une entrée gr.Image(type="numpy") produisent un TypeError à la première image envoyée. Vérifier ce contrat en lisant la documentation du composant, plutôt qu'en essayant, économise du temps. Les paramètres type ne sont pas décoratifs : ils décident du type Python exact que votre fonction va recevoir.

En résumé

  • gr.Textbox livre toujours une chaîne, éventuellement vide ; tester is None échoue, tester la troncature avec if entree.strip() est plus sûr.
  • gr.Image livre un tableau NumPy (défaut), un objet PIL ou un chemin de fichier selon type ; ce choix décide la ligne d'interface avec le modèle.
  • gr.Audio(type="numpy") livre (fréquence, tableau) ; convertir en mono, normaliser en flottant et passer explicitement sampling_rate évite les transcriptions bruitées.
  • gr.Video livre un chemin, la lecture se fait avec OpenCV ou moviepy ; pour un flux temps réel, préférer gr.Image(sources=["webcam"], streaming=True).

Le module suivant passe de la simple Interface aux Blocks : plusieurs zones, une mise en page en lignes et colonnes, et des événements qui enchaînent transcription puis résumé.