Intelligence Artificielle angularforall.com

- GPT-4o Vision : analyser images et documents en JS

Gpt-4O Vision-Ia Multimodal Openai Ocr Extraction-Donnees Structured-Outputs Javascript Node-Js Ia-Generative Computer-Vision Zod Image-Ai Llm
GPT-4o Vision : analyser images et documents en JS

Analysez images et documents avec GPT-4o Vision en JavaScript : envoi base64, OCR, extraction structuree avec Structured Outputs, comparaison multi-images et maitrise des couts.

La vision multimodale en bref

Avec GPT-4o Vision, le modele ne se contente plus de lire du texte : il voit. Vous lui envoyez une image accompagnee d'une question, et il decrit, analyse, lit le texte present, interprete un graphique ou extrait des donnees structurees.

Cette capacite ouvre des cas d'usage concrets : lecture de factures, analyse de captures d'ecran, moderation visuelle, accessibilite (decrire une image pour un malvoyant), extraction de tableaux. Le tout via la meme API chat completions que vous connaissez deja.

L'avantage decisif sur l'OCR classique : GPT-4o ne fait pas que lire les caracteres, il comprend le contexte. Il distingue un total HT d'un total TTC sur une facture, associe une etiquette a sa valeur, et structure l'information selon vos besoins.

Prerequis et cle API

Avant de commencer :
  • Compte OpenAI et cle dans OPENAI_API_KEY
  • Node.js 18+ et le SDK openai
  • Des images au format PNG, JPEG, WebP ou GIF non anime
npm install openai
echo "OPENAI_API_KEY=sk-..." >> .env

Premiere analyse d'image

Le message utilisateur devient un tableau melant texte et image. Voici l'analyse d'une image accessible via URL.

// vision-url.js — analyser une image depuis une URL
import 'dotenv/config';
import OpenAI from 'openai';

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

const response = await client.chat.completions.create({
  model: 'gpt-4o',
  messages: [{
    role: 'user',
    content: [
      // Partie texte : la question posee sur l'image
      { type: 'text', text: 'Decris cette image et liste les objets visibles.' },
      // Partie image : via une URL publique
      {
        type: 'image_url',
        image_url: { url: 'https://example.com/photo.jpg' },
      },
    ],
  }],
});

console.log(response.choices[0].message.content);
Un message peut contenir plusieurs blocs texte et image dans l'ordre que vous voulez. Le modele les lit comme un tout coherent : vous pouvez intercaler des instructions entre deux images.

Image locale en base64

Pour une image stockee localement (ou recue d'un upload), encodez-la en base64 et passez-la comme data URL — pas besoin de l'heberger publiquement.

// vision-base64.js — analyser une image locale
import { readFile } from 'node:fs/promises';
import OpenAI from 'openai';

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

// Lire et encoder l'image en base64
const buffer = await readFile('./facture.png');
const base64 = buffer.toString('base64');
const dataUrl = `data:image/png;base64,${base64}`;

const response = await client.chat.completions.create({
  model: 'gpt-4o',
  messages: [{
    role: 'user',
    content: [
      { type: 'text', text: 'Quel est le montant total TTC de cette facture ?' },
      { type: 'image_url', image_url: { url: dataUrl } },
    ],
  }],
});

console.log(response.choices[0].message.content);
Astuce upload web : cote serveur, recevez le fichier via multipart, lisez le buffer, encodez en base64 et envoyez. Evitez de stocker l'image si vous n'en avez pas besoin apres l'analyse — cela simplifie la conformite RGPD.

OCR et extraction de texte

GPT-4o lit le texte present sur une image, y compris l'ecriture manuscrite, les panneaux, les captures d'ecran. Un prompt precis ameliore la fidelite.

// ocr.js — extraire le texte brut d'une image
const response = await client.chat.completions.create({
  model: 'gpt-4o',
  messages: [{
    role: 'user',
    content: [
      {
        type: 'text',
        text: 'Transcris fidelement tout le texte present sur cette image, '
            + 'en conservant la mise en forme (sauts de ligne, listes). '
            + 'Ne commente pas, renvoie uniquement le texte.',
      },
      { type: 'image_url', image_url: { url: dataUrl } },
    ],
  }],
  temperature: 0,   // 0 pour une transcription fidele et deterministe
});

console.log(response.choices[0].message.content);
Mettez temperature: 0 pour les taches d'extraction : vous voulez une transcription fidele, pas une reformulation creative. Precisez aussi de ne pas commenter pour eviter le texte parasite.

Extraction structuree avec Structured Outputs

Le vrai pouvoir apparait en combinant la vision avec les Structured Outputs : extraire des donnees d'une image directement dans un JSON valide et type.

// vision-structured.js — extraire une facture en JSON garanti
import OpenAI from 'openai';
import { z } from 'zod';
import { zodResponseFormat } from 'openai/helpers/zod';

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

// Schema cible de l'extraction
const Facture = z.object({
  numero: z.string(),
  date: z.string(),
  fournisseur: z.string(),
  lignes: z.array(z.object({
    description: z.string(),
    quantite: z.number(),
    prixUnitaire: z.number(),
  })),
  totalHT: z.number(),
  totalTTC: z.number(),
});

const response = await client.chat.completions.parse({
  model: 'gpt-4o',
  messages: [{
    role: 'user',
    content: [
      { type: 'text', text: 'Extrais les donnees de cette facture.' },
      { type: 'image_url', image_url: { url: dataUrl } },
    ],
  }],
  // Force une sortie JSON conforme au schema
  response_format: zodResponseFormat(Facture, 'facture'),
});

// Objet type, valide, directement exploitable
const facture = response.choices[0].message.parsed;
console.log(`Facture ${facture.numero} — ${facture.totalTTC} EUR TTC`);
facture.lignes.forEach((l) => console.log(`  ${l.description}: ${l.quantite} x ${l.prixUnitaire}`));
Pattern le plus puissant : vision + Structured Outputs transforme n'importe quel document visuel (facture, formulaire, carte de visite, ticket) en donnees structurees exploitables, sans regex ni parsing fragile. La validation Zod garantit la conformite.

Comparer plusieurs images

On peut envoyer plusieurs images dans un meme message pour les comparer, detecter des differences ou suivre une evolution.

// vision-compare.js — comparer deux images
const response = await client.chat.completions.create({
  model: 'gpt-4o',
  messages: [{
    role: 'user',
    content: [
      { type: 'text', text: 'Compare ces deux maquettes UI et liste les differences visuelles.' },
      { type: 'image_url', image_url: { url: dataUrlAvant } },
      { type: 'image_url', image_url: { url: dataUrlApres } },
    ],
  }],
});

console.log(response.choices[0].message.content);
Chaque image ajoutee consomme des tokens supplementaires. Pour comparer beaucoup d'images, envisagez de les analyser individuellement puis de comparer les descriptions textuelles, moins couteuses.

Le parametre detail et les couts

Le cout de la vision depend de la resolution et du parametre detail. Trois valeurs : low (rapide, peu de tokens), high (analyse fine, plus cher), auto (le modele decide).

// Regler detail selon le besoin reel
const content = [
  { type: 'text', text: 'Cette image contient-elle un chat ?' },
  {
    type: 'image_url',
    image_url: {
      url: dataUrl,
      detail: 'low',     // suffisant pour une question simple : economique
    },
  },
];

// Pour lire un petit texte dans une image complexe :
// detail: 'high' decoupe l'image en tuiles haute resolution (plus cher)
Maitriser les couts vision :
  • Utiliser detail: low pour les questions simples
  • Redimensionner les images avant envoi (inutile d'envoyer du 4K)
  • Reserver detail: high aux petits textes ou details fins
  • Combiner vision + Structured Outputs pour eviter les allers-retours
  • Analyser les images individuellement plutot qu'en lot quand possible

Chaine d'upload depuis le navigateur

Dans une vraie application, l'image vient d'un <input type="file">. Deux bonnes pratiques cote front : redimensionner l'image avant l'envoi (inutile d'uploader du 4K pour une analyse) et n'envoyer que le strict necessaire au serveur, qui reste le seul a detenir la cle API.

// front.js — redimensionner l'image cote client avant upload (canvas)
async function resizeImage(file, maxDim = 1024) {
  const bitmap = await createImageBitmap(file);
  const scale = Math.min(1, maxDim / Math.max(bitmap.width, bitmap.height));
  const canvas = document.createElement('canvas');
  canvas.width = bitmap.width * scale;
  canvas.height = bitmap.height * scale;
  canvas.getContext('2d').drawImage(bitmap, 0, 0, canvas.width, canvas.height);

  // Exporter en JPEG compresse (qualite 0.8) pour reduire le poids
  return new Promise((resolve) =>
    canvas.toBlob(resolve, 'image/jpeg', 0.8)
  );
}

// Envoyer l'image redimensionnee a notre serveur (jamais directement a OpenAI)
async function analyser(file, question) {
  const blob = await resizeImage(file);
  const form = new FormData();
  form.append('image', blob);
  form.append('question', question);
  const res = await fetch('/api/vision', { method: 'POST', body: form });
  return res.json();
}
// server.js — recevoir l'upload et le transmettre a GPT-4o Vision
import express from 'express';
import multer from 'multer';
import OpenAI from 'openai';

const upload = multer({ limits: { fileSize: 5 * 1024 * 1024 } });  // 5 Mo max
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const app = express();

app.post('/api/vision', upload.single('image'), async (req, res) => {
  if (!req.file) return res.status(400).json({ error: 'Image manquante' });

  // Convertir le buffer recu en data URL
  const dataUrl = `data:${req.file.mimetype};base64,${req.file.buffer.toString('base64')}`;

  const r = await client.chat.completions.create({
    model: 'gpt-4o',
    messages: [{
      role: 'user',
      content: [
        { type: 'text', text: String(req.body.question || 'Decris cette image.') },
        { type: 'image_url', image_url: { url: dataUrl, detail: 'low' } },
      ],
    }],
  });
  res.json({ resultat: r.choices[0].message.content });
});

app.listen(3000);
Pourquoi resizer cote client : on reduit le poids du transfert, on accelere l'analyse et on diminue le cout en tokens — le tout sans perdre en qualite pour la plupart des taches. Plafonnez toujours la taille cote serveur (multer limits) pour eviter les abus.

GPT-4o Vision vs OCR et services dedies

La vision LLM ne remplace pas tout. Selon le volume, le besoin de structuration et le budget, un OCR classique ou un service dedie peut etre plus adapte. Le tableau ci-dessous resume les compromis.

CritereGPT-4o VisionOCR classique (Tesseract)Service dedie (Document AI)
Comprehension du contexteExcellenteNulle (texte brut)Bonne (sur formats connus)
Extraction structureeNative (Structured Outputs)Manuelle (regex)Native (templates)
Cout au volumeEleveQuasi nul (self-hosted)Moyen a eleve
VitesseMoyenneTres rapideRapide
Documents non standardTres flexibleFragileRigide
Ideal pourDocuments varies, faible volumeNumerisation massivePipeline metier stable
Strategie hybride courante : un OCR rapide et gratuit pour le gros du texte, et GPT-4o Vision en renfort uniquement sur les documents complexes ou ambigus que l'OCR n'arrive pas a structurer. On paie l'IA seulement quand elle apporte une vraie valeur.

Robustesse et validation en production

La vision hallucine plus volontiers que le texte sur les details ambigus. En production, on n'expose jamais brute la sortie du modele : on la valide et on detecte les cas peu fiables pour les router vers une verification humaine.

// validate.js — controle de coherence sur une extraction de facture
function validerFacture(f) {
  const erreurs = [];

  // Coherence arithmetique : somme des lignes ~ total HT
  const sommeLignes = f.lignes.reduce((s, l) => s + l.quantite * l.prixUnitaire, 0);
  if (Math.abs(sommeLignes - f.totalHT) > 0.02) {
    erreurs.push('Total HT incoherent avec les lignes');
  }
  // TTC doit etre superieur ou egal au HT
  if (f.totalTTC < f.totalHT) erreurs.push('TTC inferieur au HT');
  // Champs obligatoires non vides
  if (!f.numero || !f.date) erreurs.push('Numero ou date manquant');

  return { valide: erreurs.length === 0, erreurs };
}

const { valide, erreurs } = validerFacture(facture);
if (!valide) {
  // Router vers une revue humaine plutot que d'ingerer des donnees fausses
  await mettreEnFileRevue(facture, erreurs);
}
Regle de production : toute donnee chiffree extraite par vision doit passer un controle de coherence (sommes, bornes, formats). Un document qui echoue part en revue humaine. C'est ce filet qui rend l'extraction par IA exploitable sur des donnees comptables ou juridiques.

Cas d'usage et limites

GPT-4o Vision excelle pour :
  • Extraction de donnees de documents (factures, formulaires, tickets)
  • Description d'images pour l'accessibilite
  • Analyse de captures d'ecran et de maquettes UI
  • Interpretation de graphiques et tableaux
  • Moderation de contenu visuel
  • Classification visuelle avec contexte
Limites a connaitre : le modele peut halluciner sur des details ambigus, se tromper sur des comptages precis ou des positions spatiales fines, et n'est pas fiable pour des mesures exactes. Pour des donnees critiques, ajoutez une etape de validation humaine ou un controle de coherence.

Erreurs frequentes a l'integration

Erreur de debutant la plus couteuse : envoyer l'image en pleine resolution avec detail: high par defaut. Une photo de smartphone en 12 megapixels est decoupee en de nombreuses tuiles, chacune facturee — alors qu'un redimensionnement a 1024 px et detail: low suffit pour la plupart des questions. Redimensionnez systematiquement cote client, comme dans la chaine d'upload vue plus haut, avant tout appel.

Deuxieme piege : faire confiance aveuglement aux chiffres extraits. La vision excelle pour comprendre une mise en page, mais elle peut se tromper sur un total, inverser deux colonnes d'un tableau ou mal lire un chiffre manuscrit. Pour toute donnee comptable ou juridique, ajoutez un controle de coherence (les sommes correspondent-elles ?) et un parcours de revue humaine sur les cas suspects.

Cote application, deux oublis reviennent souvent : ne pas valider le type MIME et le poids du fichier recu (un PDF ou un fichier de plusieurs Mo envoye comme image fait echouer ou exploser le cout), et laisser fuiter des donnees personnelles presentes sur les documents analyses. Validez le format en amont, plafonnez la taille, et ne conservez l'image que le temps strictement necessaire a l'analyse pour rester conforme au RGPD.

Cas particulier frequent : les documents PDF multi-pages. GPT-4o Vision attend des images, pas des PDF. Il faut donc convertir chaque page en image (via pdf-to-img ou pdfjs cote Node) avant l'envoi, puis decider d'une strategie : analyser page par page (precis mais couteux en appels) ou assembler quelques pages cles en une seule image (economique mais au risque de perdre du detail). Pour un document de dizaines de pages, un pre-filtrage — ne soumettre a la vision que les pages reellement pertinentes — evite de payer l'analyse de pages vides ou hors-sujet.

Conclusion

GPT-4o Vision apporte la comprehension visuelle directement dans l'API que vous utilisez deja. Envoyer une image revient a ajouter un bloc image_url au message — via URL ou base64. La combinaison avec les Structured Outputs est particulierement puissante : elle transforme n'importe quel document visuel en JSON type et valide, sans parsing fragile.

Pilotez les couts avec le parametre detail et en redimensionnant vos images. Gardez en tete les limites du modele sur les comptages et mesures precises, et ajoutez une validation pour les usages critiques. De l'extraction de factures a l'accessibilite, la vision multimodale ouvre une nouvelle classe d'applications.

A retenir :
  • Ajouter un bloc image_url (URL ou base64) au message
  • temperature: 0 pour l'OCR et l'extraction fidele
  • Vision + Structured Outputs = documents vers JSON type
  • Regler detail (low/high) selon le besoin pour le cout
  • Valider les donnees critiques (comptages, mesures)

Partager