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 |
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ésolution | Standard | HD |
|---|---|---|
| 1024×1024 | 0.040$/img | 0.080$/img |
| 1024×1792 | 0.080$/img | 0.120$/img |
| 1792×1024 | 0.080$/img | 0.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.