Aller au contenu
Projet de développement SGBD

Reconnaissance d'images

Reconnaissance d'images

Vous rêvez d’avoir un chatbot suffisamment malin pour décrire les photos de vos (malheureux) selfies ou deviner ce qui se trouve dans l’assiette ? Bonne nouvelle : certains modèles de langage savent gérer des images en plus du texte. Voici comment les utiliser sans transformer votre application en gouffre financier… et sans envoyer des toiles de 8 000 px de large.

1. Présentation Générale

Les LLM multimodaux peuvent interpréter un contenu visuel et vous renvoyer une description, un résumé ou tout autre commentaire pertinent. Pour en profiter, encore faut-il leur envoyer les images correctement : il ne suffit pas de coller un .jpg brut dans votre JSON.

  • Certains modèles ne sont pas (encore) capables d’interpréter les images. Vérifiez donc la compatibilité.
  • La mise en forme du message user change : on envoie un tableau de “blocs” (text, image_url), et non plus une simple chaîne de caractères.

2. Vérifier la Compatibilité

Avant de bombarder un LLM avec des photos de votre chat (le vrai, pas le chatbot), assurez-vous qu’il sache quoi en faire. Dans la méthode getModels() (de votre ChatService), nous déterminons la capacité du modèle via :

'supports_image' => 'text+image->text' === $model['architecture']['modality']

Si supports_image est true, bingo : ce modèle sait traiter des images. Sinon, vous obtiendrez plus ou moins le même résultat qu’un poisson rouge devant un livre de math.

3. Format de Requête Multimodale

Pour envoyer une image au LLM, vous devez structurer le contenu de votre message sous forme de tableau. Exemple :

"messages": [
  {
    "role": "user",
    "content": [
      {
        "type": "text",
        "text": "What is this image about?"
      },
      {
        "type": "image_url",
        "image_url": {
          "url": "https://example.com/magnifique_photo.jpg"
        }
      }
    ]
  }
]
  • type: "text" : la partie texte classique.
  • type: "image_url" : un bloc contenant l’URL d’une image (ou son équivalent base64, voir section suivante).

Lors de l’envoi, l’API traitera ces blocs pour comprendre que vous lui fournissez à la fois du texte et une image.

4. Image par URL vs Base64

4.1 URL Publique

La solution la plus simple consiste à héberger votre image quelque part (CDN, bucket S3, etc.), puis à placer cette URL dans la requête. Le modèle ira la chercher directement.

  • Avantage : Aucun besoin de manipuler l’image côté backend, hormis vérifier qu’elle soit accessible.
  • Inconvénient : Vous devez impérativement que l’image soit en ligne, et idéalement de taille raisonnable (ou la facture risque de grimper selon la politique du modèle).

4.2 Encodage Base64

Si vous avez une image locale ou que vous ne pouvez pas l’héberger, vous pouvez l’envoyer sous forme de chaîne base64, par exemple :

"messages": [
  {
    "role": "user",
    "content": [
      {
        "type": "text",
        "text": "What's in this image?"
      },
      {
        "type": "image_url",
        "image_url": {
          "url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA..."
        }
      }
    ]
  }
]

Attention : la chaîne base64 doit inclure data:image/jpeg;base64, ou autre content-type, histoire que le modèle sache de quel format il s’agit.

  • Avantage : Aucune dépendance à un serveur de fichiers distant.
  • Inconvénient : La requête HTTP peut doubler (ou plus) de taille si vous balancez une photo en 4K. Vive la surcharge…

5. Optimiser l’Image : Pourquoi et Comment

Plus l’image est lourde, plus vous risquez de payer, et plus la requête mettra du temps à arriver au LLM (et inversement). Autrement dit, si vous uploadez un panorama en 8K, préparez-vous à sortir le champagne (et le porte-monnaie).

5.1 Exemple d’une Classe d’Optimisation

Pour redimensionner, convertir et compresser l’image en JPEG de façon plus civilisée :

class ImageService
{
    private const MAX_WIDTH = 800;
    private const JPEG_QUALITY = 85;

    public function optimizeImage(string $sourcePath): string
    {
        [$width, $height, $type] = getimagesize($sourcePath);
        $ratio = $width / $height;
        $newWidth = min($width, self::MAX_WIDTH);
        $newHeight = (int)($newWidth / $ratio);

        $newImage = imagecreatetruecolor($newWidth, $newHeight);

        $sourceImage = match ($type) {
            IMAGETYPE_JPEG => imagecreatefromjpeg($sourcePath),
            IMAGETYPE_PNG => imagecreatefrompng($sourcePath),
            IMAGETYPE_WEBP => imagecreatefromwebp($sourcePath),
            default => throw new \Exception('Format d\'image non supporté'),
        };

        imagecopyresampled($newImage, $sourceImage, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);

        ob_start();
        imagejpeg($newImage, null, self::JPEG_QUALITY);
        $imageData = ob_get_clean();

        imagedestroy($sourceImage);
        imagedestroy($newImage);

        return $imageData;
    }
}

La fonction optimizeImage() :

  1. Redimensionne l’image si elle dépasse MAX_WIDTH (ex. 800 px).
  2. Convertit en JPEG de qualité 85 % (assez correct pour du descriptif).
  3. Retourne les données binaires dans une chaîne.

5.2 Enchaîner sur le Base64

Si vous devez envoyer l’image en base64, il vous suffit de faire :

$optimizedData = (new ImageService())->optimizeImage($cheminFichier);
$base64Data = base64_encode($optimizedData); // Ajoute le beau code encodé
$url = "data:image/jpeg;base64," . $base64Data; // Ou bien votre code "automatique"

Ensuite, vous placez $url dans la propriété image_url de votre request, comme vu plus haut.

6. Mise en Œuvre Rapide

  1. Vérifiez si le modèle supporte les images (supports_image == true).

  2. Récupérez votre image (upload de l’utilisateur, dossier local, etc.).

  3. Optimisez (optionnel mais conseillé) pour éviter les fiascos sur la facture.

  4. Créez le messages :

    $messages = [
      [
        'role' => 'user',
        'content' => [
          [
            'type' => 'text',
            'text' => 'Can you describe what this is?'
          ],
          [
            'type' => 'image_url',
            'image_url' => [
              'url' => $url // Soit un lien http(s), soit data:image/jpeg;base64,...
            ]
          ]
        ]
      ]
    ];
    
  5. Envoyez le tout au LLM via votre client OpenRouter ou OpenAI.

  6. Observez la réponse : le modèle devrait vous décrire l’image ou répondre à vos questions, si tout s’est bien passé.

7. Bonnes Pratiques

  • Bouclez sur supports_image : on répète l’importance de vérifier que le modèle est vraiment multimodal, sous peine de voir votre requête se faire bâcher.
  • Dimension et format : compressez suffisamment, sinon l’API risque d’exploser le temps d’analyse (et peut-être vos factures).
  • Encodage : assurez-vous de fournir le bon content-type dans la data URL si vous faites du base64.
  • Filtrage : L’API fera de la modération côté serveur. Vous n’avez pas à mettre un système anti-chaton ou anti-contenu gore vous-même (sauf besoin particulier).

Voilà, vous avez désormais tous les ingrédients pour rendre votre chatbot (vaguement) visionnaire. Alors, à vos images et n’oubliez pas : “light is right” – plus l’image est légère, moins vous vous ferez taper sur les doigts (ou sur le portefeuille).

Documentation