Intelligence Artificielle angularforall.com

- IA générative images : DALL-E, Midjourney, Stable Diffusion

Dall-E Midjourney Stable-Diffusion Ia-Generative Generation-Image-Ia Openai Prompt-Image Art-Ia Comparaison-Ia Api-Openai Creativite-Ia Design-Ia Image-Ai Text-To-Image
IA générative images : DALL-E, Midjourney, Stable Diffusion

Comparez DALL-E 3, Midjourney et Stable Diffusion pour générer des images par IA, et apprenez à intégrer l'API DALL-E 3 dans vos projets web.

Comparatif des modèles: DALL-E, Flux, Stable Diffusion

Le marché de la génération d'images IA a beaucoup évolué depuis 2023. En 2026, Flux (Black Forest Labs) s'est imposé comme le meilleur rapport qualité/prix en open-source, surpassant Stable Diffusion SDXL sur la plupart des benchmarks.

Modèle API Prix indicatif Points forts Cas d'usage
DALL-E 3 OpenAI REST 0.04–0.12$/image Compréhension du texte, intégration facile Apps web, prototypage rapide
Flux.1 [dev] Replicate / HF 0.025–0.05$/image Qualité photoréaliste, détails fins Production, e-commerce
Flux.1 [schnell] Replicate 0.003$/image Ultra-rapide (4 étapes), distillé Preview temps réel, volume
SDXL (local) AUTOMATIC1111 Gratuit (GPU local) Personnalisable, LoRA, ControlNet Confidentialité, usage en masse
Imagen 3 Vertex AI 0.02$/image Texte dans les images, rendu cohérent Entreprise, intégration Google
Recommandation 2026 : DALL-E 3 pour les MVP et intégrations rapides, Flux.1 [dev] pour la qualité de production, Flux.1 [schnell] pour la génération en volume ou les previews interactifs.

API DALL-E 3: génération, inpainting, variations

L'API OpenAI expose trois endpoints pour les images: /images/generations (texte vers image), /images/edits (inpainting/modification) et /images/variations (variantes d'une image existante).

Génération de base

import OpenAI from 'openai';

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

// Génération avec tous les paramètres disponibles
async function generateImage(prompt: string) {
  const response = await openai.images.generate({
    model: 'dall-e-3',
    prompt,
    n: 1,                    // DALL-E 3 supporte uniquement n=1
    size: '1024x1024',       // '1024x1024' | '1792x1024' | '1024x1792'
    quality: 'standard',     // 'standard' ou 'hd' (2x plus détaillé, 2x plus cher)
    style: 'vivid',          // 'vivid' (dramatique) ou 'natural' (photoréaliste)
    response_format: 'url',  // 'url' (expire après 1h) ou 'b64_json'
  });

  // DALL-E 3 retourne toujours le prompt révisé (souvent enrichi)
  console.log('Prompt révisé:', response.data[0].revised_prompt);
  return response.data[0].url!;
}

Inpainting: modifier une zone d'image

// Inpainting: remplacer une zone masquée par du contenu généré
// Le masque doit être une image PNG RGBA où les zones transparentes = zones à remplacer

import { createReadStream } from 'fs';
import { toFile } from 'openai/uploads';

async function inpaintImage(imagePath: string, maskPath: string, prompt: string) {
  const response = await openai.images.edit({
    model: 'dall-e-2',    // l'inpainting est disponible sur DALL-E 2
    image: await toFile(createReadStream(imagePath), 'image.png', { type: 'image/png' }),
    mask: await toFile(createReadStream(maskPath), 'mask.png', { type: 'image/png' }),
    prompt,
    size: '1024x1024',
    n: 1,
  });

  return response.data[0].url!;
}

// Usage: remplacer l'arrière-plan d'un portrait
const newImageUrl = await inpaintImage(
  './portrait.png',
  './background-mask.png',  // zone transparente = arrière-plan
  'modern office with floor-to-ceiling windows, city view, afternoon light'
);

Tarification DALL-E 3

RésolutionStandardHD
1024×10240.040$/img0.080$/img
1024×17920.080$/img0.120$/img
1792×10240.080$/img0.120$/img

Flux sur Replicate et Hugging Face

Flux.1 (Black Forest Labs) est disponible via plusieurs plateformes. Replicate offre l'API la plus simple à intégrer, avec facturation à la prédiction.

Flux.1 via Replicate

// npm install replicate
import Replicate from 'replicate';

const replicate = new Replicate({ auth: process.env.REPLICATE_API_TOKEN });

async function generateWithFlux(prompt: string) {
  // flux-schnell: 4 étapes, ultra-rapide (~2s)
  const output = await replicate.run(
    'black-forest-labs/flux-schnell',
    {
      input: {
        prompt,
        num_outputs: 1,
        aspect_ratio: '1:1',       // '1:1' | '16:9' | '9:16' | '4:3' | '3:4'
        output_format: 'webp',     // 'webp' | 'jpg' | 'png'
        output_quality: 90,
      },
    }
  );

  // output est un array d'URLs ReadableStream ou string[]
  return Array.isArray(output) ? output[0] : output;
}

// flux-dev: qualité maximale (~10-15s)
async function generateHighQuality(prompt: string) {
  const output = await replicate.run(
    'black-forest-labs/flux-dev',
    {
      input: {
        prompt,
        num_inference_steps: 28,  // 20-50 steps
        guidance_scale: 3.5,      // 1-10, plus haut = plus fidèle au prompt
        output_format: 'webp',
        output_quality: 90,
      },
    }
  );
  return Array.isArray(output) ? output[0] : output;
}

Flux via Hugging Face Inference API

// Alternative: Hugging Face Inference API (gratuit avec limites)
const HF_TOKEN = process.env.HF_TOKEN;

async function generateWithHuggingFace(prompt: string): Promise<string> {
  const response = await fetch(
    'https://api-inference.huggingface.co/models/black-forest-labs/FLUX.1-schnell',
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${HF_TOKEN}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ inputs: prompt }),
    }
  );

  if (!response.ok) throw new Error(`HF API error: ${response.status}`);

  // La réponse est un Blob (image binaire)
  const blob = await response.blob();

  // Convertir en URL objet pour affichage immédiat
  return URL.createObjectURL(blob);

  // Ou en base64 pour stockage
  // const buffer = await blob.arrayBuffer();
  // return `data:image/jpeg;base64,${Buffer.from(buffer).toString('base64')}`;
}

Stable Diffusion local avec AUTOMATIC1111

AUTOMATIC1111 (Stable Diffusion Web UI) est le frontend le plus utilisé pour exécuter des modèles de diffusion en local. Son API REST permet de l'intégrer dans n'importe quelle application.

# Lancer AUTOMATIC1111 avec API REST activée
./webui.sh --api --nowebui --listen

# Vérifier que l'API est disponible
curl http://localhost:7860/sdapi/v1/sd-models

# Générer une image (txt2img)
curl -X POST http://localhost:7860/sdapi/v1/txt2img \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "a futuristic developer workspace, neon lights, ultrawide monitors",
    "negative_prompt": "blurry, low quality, distorted, watermark, text",
    "steps": 25,
    "width": 1024,
    "height": 1024,
    "cfg_scale": 7,
    "sampler_name": "DPM++ 2M Karras",
    "restore_faces": false
  }'

Intégration Node.js avec AUTOMATIC1111

interface Txt2ImgRequest {
  prompt: string;
  negative_prompt?: string;
  steps?: number;
  cfg_scale?: number;
  width?: number;
  height?: number;
  sampler_name?: string;
}

interface Txt2ImgResponse {
  images: string[];          // tableau d'images en base64
  parameters: object;
  info: string;              // JSON stringify des métadonnées
}

async function generateLocal(params: Txt2ImgRequest): Promise<string> {
  const response = await fetch('http://localhost:7860/sdapi/v1/txt2img', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      negative_prompt: 'blurry, low quality, distorted, ugly, watermark',
      steps: 20,
      width: 512,
      height: 512,
      cfg_scale: 7,
      ...params,             // les paramètres de l'appelant écrasent les défauts
    }),
  });

  const data: Txt2ImgResponse = await response.json();

  // data.images[0] est une image PNG en base64
  return `data:image/png;base64,${data.images[0]}`;
}

// Image-to-image (img2img): transformer une image existante
async function transformImage(
  sourceImageBase64: string,
  prompt: string,
  strength = 0.75  // 0=identique, 1=libre (plus proche du prompt)
) {
  const response = await fetch('http://localhost:7860/sdapi/v1/img2img', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      init_images: [sourceImageBase64],
      prompt,
      denoising_strength: strength,
      steps: 20,
    }),
  });

  const data = await response.json();
  return `data:image/png;base64,${data.images[0]}`;
}

Rédiger de bons prompts d'image

Le prompt est le facteur numéro un de qualité d'une image générée. La structure ci-dessous fonctionne pour DALL-E, Flux et Stable Diffusion.

Structure d'un prompt efficace

// Template de prompt structuré (fonctionne avec tous les modèles)
interface ImagePromptTemplate {
  subject: string;      // "a senior developer"
  action: string;       // "reviewing code on three monitors"
  environment: string;  // "in a minimalist office with plants"
  lighting: string;     // "soft morning light from large windows"
  style: string;        // "photorealistic, professional photography"
  quality: string;      // "8k, sharp focus, detailed"
  negative?: string;    // "blurry, low quality, distorted"
}

function buildImagePrompt(t: ImagePromptTemplate): string {
  return [t.subject, t.action, t.environment, t.lighting, t.style, t.quality]
    .filter(Boolean)
    .join(', ');
}

// Exemples par style
const prompts = {
  // Photoréaliste (DALL-E/Flux)
  realistic: buildImagePrompt({
    subject: 'portrait of a web developer, 30s, focused expression',
    action: 'typing on a mechanical keyboard',
    environment: 'modern home office, dual monitors showing TypeScript code',
    lighting: 'soft natural light, blue hour',
    style: 'professional photography, Canon EOS R5',
    quality: 'highly detailed, sharp focus, realistic skin texture',
    negative: 'cartoon, anime, painting, blurry, distorted',
  }),

  // Style graphique (Flux/SD)
  techIllustration: buildImagePrompt({
    subject: 'Angular framework logo',
    action: 'surrounded by floating code snippets and UI components',
    environment: 'dark blue gradient background, cyber aesthetic',
    lighting: 'neon blue and red glow',
    style: 'digital art, vector illustration style, flat design',
    quality: 'clean lines, 4k, professional design',
  }),
};

Techniques avancées de prompting d'image

  • Poids de tokens (Stable Diffusion): (terme:1.3) pour augmenter l'importance, (terme:0.7) pour diminuer.
  • Negative prompt détaillé: aussi important que le prompt positif. Listez tout ce que vous ne voulez pas.
  • Ratio d'aspect: définissez toujours la taille cible. Les modèles sont entraînés sur des ratios spécifiques.
  • Style référence: mentionner un photographe, un artiste ou un style cinématographique affine considérablement le rendu.
  • Seed fixé: pour reproduire une image ou faire des variations contrôlées, fixez le seed et modifiez le prompt progressivement.

Service Angular pour la génération d'images

Voici un service Angular complet qui abstrait les appels à plusieurs APIs de génération d'images avec gestion des erreurs, des états et du cache.

// image-generation.service.ts
import { Injectable, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';

export interface GenerationOptions {
  prompt: string;
  model?: 'dalle3' | 'flux-schnell' | 'flux-dev';
  size?: '1024x1024' | '1024x1792' | '1792x1024';
  quality?: 'standard' | 'hd';
  style?: 'vivid' | 'natural';
}

export interface GeneratedImage {
  url: string;
  prompt: string;
  revisedPrompt?: string;
  model: string;
  createdAt: Date;
}

@Injectable({ providedIn: 'root' })
export class ImageGenerationService {
  #http = inject(HttpClient);

  // État de la génération en cours
  readonly isGenerating = signal(false);
  readonly lastError = signal<string | null>(null);

  // Cache en mémoire: évite de re-générer les mêmes prompts
  readonly #cache = new Map<string, GeneratedImage>();

  async generate(options: GenerationOptions): Promise<GeneratedImage> {
    const cacheKey = JSON.stringify(options);
    if (this.#cache.has(cacheKey)) {
      return this.#cache.get(cacheKey)!;
    }

    this.isGenerating.set(true);
    this.lastError.set(null);

    try {
      // Appel vers votre backend proxy (JAMAIS depuis le front directement)
      const result = await firstValueFrom(
        this.#http.post<GeneratedImage>('/api/generate-image', options)
      );

      this.#cache.set(cacheKey, result);
      return result;
    } catch (error: any) {
      this.lastError.set(error.message || 'Erreur lors de la génération');
      throw error;
    } finally {
      this.isGenerating.set(false);
    }
  }

  clearCache() {
    this.#cache.clear();
  }
}
// Composant utilisant le service
@Component({
  selector: 'app-image-generator',
  standalone: true,
  template: `
    <form (submit)="generate($event)">
      <textarea [(ngModel)]="prompt" name="prompt" rows="3"
                placeholder="Décrivez votre image..."></textarea>
      <button type="submit" [disabled]="imageService.isGenerating()">
        @if (imageService.isGenerating()) { Génération... }
        @else { Générer }
      </button>
    </form>

    @if (imageService.lastError()) {
      <div class="alert alert-danger">{{ imageService.lastError() }}</div>
    }

    @if (generatedImage()) {
      <figure>
        <img [src]="generatedImage()!.url"
             [alt]="generatedImage()!.prompt"
             loading="lazy">
        <figcaption>{{ generatedImage()!.revisedPrompt }}</figcaption>
      </figure>
    }
  `,
})
export class ImageGeneratorComponent {
  imageService = inject(ImageGenerationService);
  prompt = '';
  generatedImage = signal<GeneratedImage | null>(null);

  async generate(event: Event) {
    event.preventDefault();
    if (!this.prompt.trim()) return;

    const result = await this.imageService.generate({ prompt: this.prompt });
    this.generatedImage.set(result);
  }
}

Pipeline production: modération, CDN, cache

En production, il ne faut jamais exposer les clés API côté client ni afficher les URLs temporaires OpenAI directement. Voici le workflow complet.

// Backend Node.js/Express: proxy sécurisé pour la génération d'images
import express from 'express';
import OpenAI from 'openai';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { v4 as uuid } from 'uuid';
import sharp from 'sharp'; // pour la conversion WebP

const router = express.Router();
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const s3 = new S3Client({ region: process.env.AWS_REGION });

router.post('/generate-image', async (req, res) => {
  const { prompt, size = '1024x1024', quality = 'standard' } = req.body;

  // 1. Validation du prompt (longueur, contenu)
  if (!prompt || prompt.length > 1000) {
    return res.status(400).json({ error: 'Prompt invalide' });
  }

  // 2. Génération via l'API OpenAI (clé jamais exposée côté client)
  const response = await openai.images.generate({
    model: 'dall-e-3',
    prompt,
    size,
    quality,
    response_format: 'b64_json', // base64 pour traitement côté serveur
  });

  const imageBase64 = response.data[0].b64_json!;
  const revisedPrompt = response.data[0].revised_prompt;

  // 3. Conversion en WebP (réduit la taille de ~30-50%)
  const webpBuffer = await sharp(Buffer.from(imageBase64, 'base64'))
    .webp({ quality: 85 })
    .toBuffer();

  // 4. Upload vers S3/CDN avec un nom unique
  const filename = `generated/${uuid()}.webp`;
  await s3.send(new PutObjectCommand({
    Bucket: process.env.AWS_S3_BUCKET,
    Key: filename,
    Body: webpBuffer,
    ContentType: 'image/webp',
    CacheControl: 'public, max-age=31536000', // 1 an de cache navigateur
  }));

  // 5. Retourner l'URL CDN permanente (pas l'URL temporaire OpenAI)
  const cdnUrl = `${process.env.CDN_BASE_URL}/${filename}`;

  res.json({
    url: cdnUrl,
    prompt,
    revisedPrompt,
    model: 'dall-e-3',
    createdAt: new Date(),
  });
});
  • Clé API côté serveur uniquement: jamais dans le front-end ou le client-side JavaScript.
  • URLs temporaires OpenAI: expirent après 1h — toujours télécharger et re-stocker sur votre CDN.
  • Modération du contenu: utiliser l'API OpenAI Moderation avant la génération pour filtrer les prompts problématiques.
  • Cache par hash du prompt: éviter de re-générer le même prompt (économie de coûts significative).
  • Mention légale obligatoire: indiquer que les images sont générées par IA, conformément aux CGU OpenAI et à la réglementation européenne.
  • Rate limiting: limiter les générations par utilisateur (ex: 10/heure) pour éviter les abus et les coûts incontrôlés.

Partager