Exemples

Exemples pratiques et extraits de code pour divers cas d'usage

Exemples Pratiques

Découvrez comment utiliser Qwen Image Edit dans des scénarios réels avec des exemples de code complets et des cas d'usage concrets.

🎯 Édition de Texte de Base

Modification de Texte Simple

import { QwenImageEdit } from 'qwen-image-edit';

const editor = new QwenImageEdit({
  apiKey: process.env.QWEN_API_KEY
});

// Changer un titre sur une affiche
const resultat = await editor.editText({
  image: './affiche-evenement.jpg',
  prompt: 'Remplacer "Concert 2023" par "Concert 2024"',
  options: {
    preserveStyle: true,
    language: 'fr'
  }
});

console.log('Affiche mise à jour :', resultat.imageUrl);

Traduction de Contenu

// Traduire automatiquement le texte dans une image
const traduction = await editor.editText({
  image: './menu-anglais.jpg',
  prompt: 'Traduire tout le texte anglais en français en conservant la mise en forme',
  options: {
    sourceLanguage: 'en',
    targetLanguage: 'fr',
    preserveLayout: true
  }
});

console.log('Menu traduit :', traduction.imageUrl);

Correction d'Erreurs

// Corriger une faute de frappe
const correction = await editor.editText({
  image: './document-avec-erreur.jpg',
  prompt: 'Corriger "recieve" en "receive" dans le texte',
  options: {
    preserveStyle: true,
    exactMatch: true
  }
});

console.log('Document corrigé :', correction.imageUrl);

🎨 Ajout d'Éléments

Logos et Filigranes

// Ajouter un logo d'entreprise
const avecLogo = await editor.editElement({
  image: './photo-produit.jpg',
  prompt: 'Ajouter le logo de l\'entreprise dans le coin inférieur droit avec 70% d\'opacité',
  options: {
    logoUrl: './logo-entreprise.png',
    position: 'bottom-right',
    opacity: 0.7,
    size: 'small'
  }
});

// Ajouter un filigrane de copyright
const avecFiligrane = await editor.editElement({
  image: './photo-originale.jpg',
  prompt: 'Ajouter "© 2024 MonEntreprise" en filigrane diagonal transparent au centre',
  options: {
    watermarkStyle: 'diagonal',
    opacity: 0.3,
    color: 'white'
  }
});

Badges et Étiquettes

// Ajouter un badge de promotion
const avecBadge = await editor.editElement({
  image: './produit-ecommerce.jpg',
  prompt: 'Ajouter un badge circulaire rouge "PROMO -30%" dans le coin supérieur gauche',
  options: {
    badgeStyle: 'circle',
    color: '#FF0000',
    textColor: 'white',
    position: 'top-left'
  }
});

// Ajouter une étiquette "Nouveau"
const avecEtiquette = await editor.editElement({
  image: './nouveau-produit.jpg',
  prompt: 'Ajouter une étiquette "NOUVEAU" dorée en forme de ruban en haut',
  options: {
    ribbonStyle: 'gold',
    position: 'top-center',
    animation: false
  }
});

🎭 Transfert de Style Avancé

Styles Artistiques

// Appliquer un style aquarelle
const styleAquarelle = await editor.transferStyle({
  image: './portrait.jpg',
  stylePrompt: 'Transformer en peinture aquarelle avec des couleurs douces et des bords flous',
  options: {
    intensity: 0.8,
    preserveFaces: true,
    colorPalette: 'pastel'
  }
});

// Style vintage
const styleVintage = await editor.transferStyle({
  image: './photo-moderne.jpg',
  stylePrompt: 'Appliquer un effet vintage années 70 avec tons sépia et grain de film',
  options: {
    era: '1970s',
    grainIntensity: 0.6,
    colorTone: 'sepia'
  }
});

Styles Photographiques

// Style cinématographique
const styleCinema = await editor.transferStyle({
  image: './scene-urbaine.jpg',
  stylePrompt: 'Appliquer un look cinématographique avec des tons bleus et oranges',
  options: {
    colorGrading: 'cinematic',
    contrast: 'high',
    saturation: 0.8
  }
});

// Style minimaliste
const styleMinimal = await editor.transferStyle({
  image: './photo-chargee.jpg',
  stylePrompt: 'Simplifier en style minimaliste avec des couleurs neutres et des lignes épurées',
  options: {
    simplification: 'high',
    colorPalette: 'monochrome',
    contrast: 'low'
  }
});

📦 Traitement par Lots

Traitement de Catalogue Produits

import fs from 'fs';
import path from 'path';
import pLimit from 'p-limit';

// Limiter la concurrence
const limit = pLimit(3);

async function traiterCatalogueProduits() {
  const dossierProduits = './images-produits';
  const fichiers = fs.readdirSync(dossierProduits)
    .filter(f => f.match(/\.(jpg|jpeg|png)$/i));
  
  console.log(`Traitement de ${fichiers.length} images produits...`);
  
  const resultats = await Promise.allSettled(
    fichiers.map(fichier => 
      limit(async () => {
        const cheminImage = path.join(dossierProduits, fichier);
        
        // Ajouter logo et badge promo
        const resultat = await editor.editElement({
          image: cheminImage,
          prompt: 'Ajouter logo entreprise en bas à droite et badge "SOLDES" rouge en haut à gauche',
          options: {
            outputPath: `./produits-traites/${fichier}`,
            quality: 'high'
          }
        });
        
        return {
          fichier,
          succes: true,
          url: resultat.imageUrl
        };
      })
    )
  );
  
  // Analyser les résultats
  const succes = resultats.filter(r => r.status === 'fulfilled');
  const echecs = resultats.filter(r => r.status === 'rejected');
  
  console.log(`✅ ${succes.length} images traitées avec succès`);
  console.log(`❌ ${echecs.length} échecs`);
  
  return { succes, echecs };
}

// Exécuter le traitement
traiterCatalogueProduits().then(resultats => {
  console.log('Traitement terminé :', resultats);
});

Localisation Multilingue

async function localiserContenu() {
  const langues = [
    { code: 'fr', nom: 'Français' },
    { code: 'es', nom: 'Español' },
    { code: 'de', nom: 'Deutsch' },
    { code: 'it', nom: 'Italiano' }
  ];
  
  const imageOriginale = './publicite-anglaise.jpg';
  
  for (const langue of langues) {
    try {
      console.log(`Localisation en ${langue.nom}...`);
      
      const resultat = await editor.editText({
        image: imageOriginale,
        prompt: `Traduire tout le texte anglais en ${langue.nom} en conservant le style et la mise en page`,
        options: {
          targetLanguage: langue.code,
          preserveLayout: true,
          outputPath: `./localisations/pub-${langue.code}.jpg`
        }
      });
      
      console.log(`✅ Version ${langue.nom} créée : ${resultat.imageUrl}`);
      
    } catch (error) {
      console.error(`❌ Erreur pour ${langue.nom}:`, error.message);
    }
  }
}

localiserContenu();

🛒 Cas d'Usage E-commerce

Mise à Jour des Prix

class GestionnairePrixProduits {
  constructor(editor) {
    this.editor = editor;
  }
  
  async mettreAJourPrix(produits) {
    const resultats = [];
    
    for (const produit of produits) {
      try {
        console.log(`Mise à jour du prix pour ${produit.nom}...`);
        
        const resultat = await this.editor.editText({
          image: produit.imagePath,
          prompt: `Changer le prix "${produit.ancienPrix}" en "${produit.nouveauPrix}" en conservant le style`,
          options: {
            preserveStyle: true,
            exactMatch: true
          }
        });
        
        resultats.push({
          produit: produit.nom,
          succes: true,
          nouvelleImage: resultat.imageUrl,
          creditsUtilises: resultat.credits
        });
        
      } catch (error) {
        resultats.push({
          produit: produit.nom,
          succes: false,
          erreur: error.message
        });
      }
    }
    
    return resultats;
  }
  
  async ajouterPromotions(produits, typePromo) {
    const templates = {
      soldes: 'Ajouter un badge "SOLDES -{{pourcentage}}%" rouge dans le coin supérieur droit',
      nouveaute: 'Ajouter une étiquette "NOUVEAU" dorée en forme de ruban en haut',
      bestseller: 'Ajouter un badge "BESTSELLER" vert dans le coin supérieur gauche'
    };
    
    const template = templates[typePromo];
    if (!template) {
      throw new Error(`Type de promotion non supporté: ${typePromo}`);
    }
    
    const resultats = [];
    
    for (const produit of produits) {
      const prompt = template.replace('{{pourcentage}}', produit.pourcentageReduction || '');
      
      const resultat = await this.editor.editElement({
        image: produit.imagePath,
        prompt,
        options: {
          outputPath: `./produits-promo/${produit.id}-${typePromo}.jpg`
        }
      });
      
      resultats.push({
        produitId: produit.id,
        typePromo,
        nouvelleImage: resultat.imageUrl
      });
    }
    
    return resultats;
  }
}

// Utilisation
const gestionnaire = new GestionnairePrixProduits(editor);

const produits = [
  {
    id: 'prod1',
    nom: 'T-shirt Bleu',
    imagePath: './produits/tshirt-bleu.jpg',
    ancienPrix: '29.99€',
    nouveauPrix: '24.99€',
    pourcentageReduction: 20
  },
  {
    id: 'prod2',
    nom: 'Jeans Noir',
    imagePath: './produits/jeans-noir.jpg',
    ancienPrix: '79.99€',
    nouveauPrix: '59.99€',
    pourcentageReduction: 25
  }
];

// Mettre à jour les prix
const resultats = await gestionnaire.mettreAJourPrix(produits);
console.log('Résultats mise à jour prix :', resultats);

// Ajouter des promotions
const promos = await gestionnaire.ajouterPromotions(produits, 'soldes');
console.log('Promotions ajoutées :', promos);

Variations Saisonnières

async function creerVariationsSaisonnieres() {
  const saisons = {
    printemps: {
      couleurs: ['vert', 'rose', 'jaune'],
      elements: 'fleurs et feuilles vertes',
      ambiance: 'fraîche et lumineuse'
    },
    ete: {
      couleurs: ['bleu', 'orange', 'jaune vif'],
      elements: 'soleil et plage',
      ambiance: 'chaude et énergique'
    },
    automne: {
      couleurs: ['orange', 'rouge', 'brun'],
      elements: 'feuilles d\'automne',
      ambiance: 'chaleureuse et cosy'
    },
    hiver: {
      couleurs: ['bleu', 'blanc', 'argent'],
      elements: 'flocons de neige',
      ambiance: 'froide et festive'
    }
  };
  
  const imageBase = './produit-base.jpg';
  
  for (const [saison, config] of Object.entries(saisons)) {
    const prompt = `Adapter l'image pour la saison ${saison} : ajouter des ${config.elements}, utiliser des tons ${config.couleurs.join(', ')}, créer une ambiance ${config.ambiance}`;
    
    const resultat = await editor.transferStyle({
      image: imageBase,
      stylePrompt: prompt,
      options: {
        seasonalTheme: saison,
        colorPalette: config.couleurs,
        outputPath: `./variations-saisonnieres/${saison}.jpg`
      }
    });
    
    console.log(`✅ Variation ${saison} créée : ${resultat.imageUrl}`);
  }
}

creerVariationsSaisonnieres();

📱 Cas d'Usage Réseaux Sociaux

Formats Multiples

class GenerateurReseauxSociaux {
  constructor(editor) {
    this.editor = editor;
    this.formats = {
      instagram_post: { width: 1080, height: 1080, ratio: '1:1' },
      instagram_story: { width: 1080, height: 1920, ratio: '9:16' },
      facebook_post: { width: 1200, height: 630, ratio: '1.91:1' },
      twitter_post: { width: 1200, height: 675, ratio: '16:9' },
      linkedin_post: { width: 1200, height: 627, ratio: '1.91:1' }
    };
  }
  
  async creerVariationsFormats(imageOriginale, contenu) {
    const resultats = {};
    
    for (const [plateforme, format] of Object.entries(this.formats)) {
      console.log(`Création du format ${plateforme}...`);
      
      const prompt = `Adapter l'image au format ${format.ratio} pour ${plateforme.replace('_', ' ')}: ${contenu.prompt}`;
      
      const resultat = await this.editor.editElement({
        image: imageOriginale,
        prompt,
        options: {
          width: format.width,
          height: format.height,
          cropMode: 'smart',
          outputPath: `./reseaux-sociaux/${plateforme}.jpg`
        }
      });
      
      resultats[plateforme] = {
        url: resultat.imageUrl,
        format: format,
        credits: resultat.credits
      };
    }
    
    return resultats;
  }
  
  async ajouterTexteReseauxSociaux(image, textes) {
    const resultats = [];
    
    for (const texte of textes) {
      const prompt = `Ajouter le texte "${texte.contenu}" en ${texte.style} ${texte.position}`;
      
      const resultat = await this.editor.editText({
        image,
        prompt,
        options: {
          fontSize: texte.taille || 'medium',
          fontWeight: texte.poids || 'normal',
          color: texte.couleur || 'white',
          position: texte.position
        }
      });
      
      resultats.push({
        texte: texte.contenu,
        image: resultat.imageUrl
      });
    }
    
    return resultats;
  }
}

// Utilisation
const generateur = new GenerateurReseauxSociaux(editor);

const contenu = {
  prompt: 'Ajouter le titre "Nouvelle Collection Été 2024" en grand et "Découvrez nos nouveautés" en sous-titre'
};

const variations = await generateur.creerVariationsFormats(
  './campagne-ete.jpg',
  contenu
);

console.log('Variations créées :', variations);

Campagnes A/B Testing

async function creerVariationsAB() {
  const imageBase = './pub-base.jpg';
  
  const variations = [
    {
      nom: 'Version A - CTA Rouge',
      prompt: 'Ajouter un bouton "ACHETER MAINTENANT" rouge en bas avec texte blanc'
    },
    {
      nom: 'Version B - CTA Vert',
      prompt: 'Ajouter un bouton "DÉCOUVRIR" vert en bas avec texte blanc'
    },
    {
      nom: 'Version C - CTA Bleu',
      prompt: 'Ajouter un bouton "EN SAVOIR PLUS" bleu en bas avec texte blanc'
    }
  ];
  
  const resultats = [];
  
  for (const [index, variation] of variations.entries()) {
    console.log(`Création de ${variation.nom}...`);
    
    const resultat = await editor.editElement({
      image: imageBase,
      prompt: variation.prompt,
      options: {
        outputPath: `./ab-testing/version-${String.fromCharCode(65 + index)}.jpg`
      }
    });
    
    resultats.push({
      version: variation.nom,
      image: resultat.imageUrl,
      prompt: variation.prompt
    });
  }
  
  return resultats;
}

const variationsAB = await creerVariationsAB();
console.log('Variations A/B créées :', variationsAB);

🎓 Cas d'Usage Éducatif

Matériel Pédagogique

class GenerateurMaterielPedagogique {
  constructor(editor) {
    this.editor = editor;
  }
  
  async creerDiagrammeAnnote(image, annotations) {
    let imageActuelle = image;
    
    for (const [index, annotation] of annotations.entries()) {
      const prompt = `Ajouter l'annotation "${annotation.texte}" avec une flèche pointant vers ${annotation.cible}`;
      
      const resultat = await this.editor.editElement({
        image: imageActuelle,
        prompt,
        options: {
          annotationStyle: 'educational',
          arrowColor: annotation.couleur || 'red',
          textSize: 'medium'
        }
      });
      
      imageActuelle = resultat.imageUrl;
    }
    
    return imageActuelle;
  }
  
  async traduireMateriel(image, langueSource, langueCible) {
    const resultat = await this.editor.editText({
      image,
      prompt: `Traduire tout le texte de ${langueSource} vers ${langueCible} en conservant la mise en page éducative`,
      options: {
        sourceLanguage: langueSource,
        targetLanguage: langueCible,
        preserveLayout: true,
        educationalContext: true
      }
    });
    
    return resultat;
  }
  
  async creerQuizVisuel(image, questions) {
    let imageActuelle = image;
    
    for (const question of questions) {
      const prompt = `Ajouter la question "${question.texte}" avec les options A) ${question.optionA} B) ${question.optionB} C) ${question.optionC}`;
      
      const resultat = await this.editor.editText({
        image: imageActuelle,
        prompt,
        options: {
          quizFormat: true,
          fontSize: 'large',
          backgroundColor: 'white',
          opacity: 0.9
        }
      });
      
      imageActuelle = resultat.imageUrl;
    }
    
    return imageActuelle;
  }
}

// Utilisation
const generateur = new GenerateurMaterielPedagogique(editor);

// Créer un diagramme annoté
const annotations = [
  { texte: 'Noyau', cible: 'le centre de la cellule', couleur: 'blue' },
  { texte: 'Membrane', cible: 'le contour de la cellule', couleur: 'green' },
  { texte: 'Cytoplasme', cible: 'l\'intérieur de la cellule', couleur: 'orange' }
];

const diagramme = await generateur.creerDiagrammeAnnote(
  './cellule-base.jpg',
  annotations
);

console.log('Diagramme annoté créé :', diagramme);

🔧 Optimisation des Performances

Cache et Réutilisation

import NodeCache from 'node-cache';
import crypto from 'crypto';

class EditorAvecCache {
  constructor(editor) {
    this.editor = editor;
    this.cache = new NodeCache({ stdTTL: 3600 }); // Cache 1 heure
  }
  
  genererCleCache(image, prompt, options = {}) {
    const donnees = JSON.stringify({ image, prompt, options });
    return crypto.createHash('md5').update(donnees).digest('hex');
  }
  
  async editTextAvecCache(options) {
    const cleCache = this.genererCleCache(
      options.image,
      options.prompt,
      options.options
    );
    
    // Vérifier le cache
    const resultatCache = this.cache.get(cleCache);
    if (resultatCache) {
      console.log('✅ Résultat trouvé dans le cache');
      return resultatCache;
    }
    
    // Exécuter l'édition
    console.log('🔄 Exécution de l\'édition...');
    const resultat = await this.editor.editText(options);
    
    // Mettre en cache
    this.cache.set(cleCache, resultat);
    
    return resultat;
  }
  
  obtenirStatistiquesCache() {
    return {
      taille: this.cache.keys().length,
      statistiques: this.cache.getStats()
    };
  }
}

const editorAvecCache = new EditorAvecCache(editor);

// Utilisation avec cache
const resultat1 = await editorAvecCache.editTextAvecCache({
  image: './test.jpg',
  prompt: 'Ajouter logo'
});

// Cette requête utilisera le cache
const resultat2 = await editorAvecCache.editTextAvecCache({
  image: './test.jpg',
  prompt: 'Ajouter logo'
});

console.log('Stats cache :', editorAvecCache.obtenirStatistiquesCache());

Gestion d'Erreurs Robuste

import pRetry from 'p-retry';

class EditorRobuste {
  constructor(editor) {
    this.editor = editor;
    this.statistiques = {
      tentatives: 0,
      succes: 0,
      echecs: 0
    };
  }
  
  async editTextAvecRetry(options, configRetry = {}) {
    const config = {
      retries: 3,
      factor: 2,
      minTimeout: 1000,
      maxTimeout: 10000,
      ...configRetry
    };
    
    return pRetry(async (tentative) => {
      this.statistiques.tentatives++;
      
      try {
        console.log(`Tentative ${tentative}/${config.retries + 1}`);
        
        const resultat = await this.editor.editText(options);
        
        this.statistiques.succes++;
        return resultat;
        
      } catch (error) {
        console.log(`❌ Tentative ${tentative} échouée:`, error.message);
        
        // Ne pas retry pour certaines erreurs
        if (error.code === 'INVALID_API_KEY' || error.code === 'QUOTA_EXCEEDED') {
          throw new pRetry.AbortError(error);
        }
        
        throw error;
      }
    }, config).catch(error => {
      this.statistiques.echecs++;
      throw error;
    });
  }
  
  obtenirStatistiques() {
    return {
      ...this.statistiques,
      tauxSucces: this.statistiques.tentatives > 0 ? 
        (this.statistiques.succes / this.statistiques.tentatives * 100).toFixed(2) + '%' : '0%'
    };
  }
}

const editorRobuste = new EditorRobuste(editor);

// Utilisation avec retry automatique
try {
  const resultat = await editorRobuste.editTextAvecRetry({
    image: './image-problematique.jpg',
    prompt: 'Modifier le texte'
  }, {
    retries: 5,
    minTimeout: 2000
  });
  
  console.log('✅ Succès après retry :', resultat.imageUrl);
  
} catch (error) {
  console.error('❌ Échec définitif :', error.message);
}

console.log('Statistiques :', editorRobuste.obtenirStatistiques());

Explorez plus loin : Ces exemples montrent la polyvalence de Qwen Image Edit. Adaptez-les à vos besoins spécifiques et n'hésitez pas à combiner différentes techniques pour des résultats optimaux.