Aller au contenu principal

Encoder et Envoyer des Images en Base64

Pourquoi utiliser le Base64 ?

Dans la leçon précédente, vous avez appris à envoyer des images par URL. Mais que faire si vos images sont locales, générées dynamiquement, ou si vous ne souhaitez pas les héberger sur un serveur public ? C’est là qu’intervient l’encodage Base64.

Le Base64 transforme les données binaires d’une image en une chaîne de texte que vous pouvez inclure directement dans votre requête API, sans URL externe.

Quand utiliser URL vs Base64

CritèreURLBase64
Images déjà hébergéesIdéalInutile
Images localesNon adaptéIdéal
Images générées à la voléeNon adaptéIdéal
Latence réseauDépend du serveurAucune (intégré)
Taille de la requêteLégèrePlus lourde (~33%)
Sécurité/vie privéeURL publiqueDonnées dans la requête

Règle simple : si l’image est accessible via une URL stable, utilisez l’URL. Sinon, utilisez le Base64.

Encoder une image en Base64

Python fournit le module base64 dans la bibliothèque standard :

import base64

def encoder_image(chemin_fichier):
    """Encode un fichier image en chaîne Base64."""
    with open(chemin_fichier, "rb") as f:
        return base64.standard_b64encode(f.read()).decode("utf-8")

# Encoder une image locale
image_b64 = encoder_image("captures/screenshot.png")
print(f"Longueur de la chaîne Base64 : {len(image_b64)} caractères")

L’encodage Base64 augmente la taille d’environ 33 % par rapport au fichier original. Une image de 1 Mo produira une chaîne d’environ 1,33 Mo.

Format Data URI

Pour envoyer l’image encodée à l’API Mistral, vous devez la formater en data URI :

data:image/{format};base64,{donnees_base64}

Les formats supportés sont :

  • data:image/jpeg;base64,...
  • data:image/png;base64,...
  • data:image/gif;base64,...
  • data:image/webp;base64,...

Exemple complet

Voici comment envoyer une image locale à l’API Vision :

import base64
import os
from mistralai import Mistral

def encoder_image(chemin):
    """Encode une image locale en Base64."""
    with open(chemin, "rb") as f:
        return base64.standard_b64encode(f.read()).decode("utf-8")

# Déterminer le type MIME
def obtenir_mime(chemin):
    """Retourne le type MIME basé sur l'extension."""
    extensions = {
        ".jpg": "image/jpeg",
        ".jpeg": "image/jpeg",
        ".png": "image/png",
        ".gif": "image/gif",
        ".webp": "image/webp"
    }
    ext = os.path.splitext(chemin)[1].lower()
    return extensions.get(ext, "image/jpeg")

# Préparer l'image
chemin_image = "documents/facture-2024.png"
image_b64 = encoder_image(chemin_image)
mime_type = obtenir_mime(chemin_image)
data_uri = f"data:{mime_type};base64,{image_b64}"

# Appeler l'API
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

response = client.chat.complete(
    model="mistral-small-latest",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Extrayez toutes les informations de cette facture."},
                {"type": "image_url", "image_url": data_uri}
            ]
        }
    ]
)

print(response.choices[0].message.content)

Encoder des images depuis d’autres sources

Depuis une capture d’écran (Pillow)

import base64
from io import BytesIO
from PIL import Image

def capture_vers_base64(image_pil):
    """Convertit une image Pillow en data URI Base64."""
    buffer = BytesIO()
    image_pil.save(buffer, format="PNG")
    b64 = base64.standard_b64encode(buffer.getvalue()).decode("utf-8")
    return f"data:image/png;base64,{b64}"

# Exemple avec une image Pillow
img = Image.open("capture.png")
data_uri = capture_vers_base64(img)

Depuis une réponse HTTP (sans enregistrer)

import base64
import requests

def url_vers_base64(url):
    """Télécharge une image et la convertit en data URI."""
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    content_type = response.headers.get("Content-Type", "image/jpeg")
    b64 = base64.standard_b64encode(response.content).decode("utf-8")
    return f"data:{content_type};base64,{b64}"

data_uri = url_vers_base64("https://exemple.com/photo.jpg")

Optimiser la taille avant encodage

Les images volumineuses ralentissent l’envoi et ne sont pas mieux analysées. Redimensionnez systématiquement :

from PIL import Image
from io import BytesIO
import base64

def preparer_et_encoder(chemin, taille_max=1024, qualite=85):
    """Redimensionne, compresse et encode une image."""
    img = Image.open(chemin)

    # Redimensionner si nécessaire
    if max(img.size) > taille_max:
        img.thumbnail((taille_max, taille_max))

    # Convertir en JPEG pour réduire la taille
    buffer = BytesIO()
    img.convert("RGB").save(buffer, format="JPEG", quality=qualite)

    b64 = base64.standard_b64encode(buffer.getvalue()).decode("utf-8")
    taille_ko = len(buffer.getvalue()) / 1024
    print(f"Image encodée : {taille_ko:.0f} Ko")

    return f"data:image/jpeg;base64,{b64}"

data_uri = preparer_et_encoder("photo_haute_resolution.png")

Limites à connaître

  • Taille de la requête : l’image encodée s’ajoute au corps de la requête HTTP. Les images de plus de 10 Mo en Base64 peuvent causer des timeouts
  • Mémoire : l’encodage charge l’image entière en mémoire. Pour du traitement en batch, traitez les images une par une
  • Pas de cache : contrairement aux URL, les images Base64 sont envoyées à chaque requête

Points clés à retenir

  • Le Base64 est idéal pour les images locales ou générées dynamiquement
  • Le format data URI combine le type MIME et les données : data:image/png;base64,...
  • Redimensionnez et compressez vos images avant l’encodage pour optimiser la latence
  • Le Base64 augmente la taille d’environ 33 % — gardez vos images sous 10 Mo
  • Utilisez l’URL quand l’image est déjà hébergée, le Base64 dans tous les autres cas