


Il y a quelques mois, j'ai commencé à collaborer sur un projet consacré au contenu généré par l'IA pour un client axé sur le secteur technologique. Mon rôle était principalement axé sur la mise en place de SSG en utilisant WordPress comme Headless CMS pour un front-end Nuxt.
Le client avait l'habitude d'écrire des articles plusieurs fois par semaine sur différentes tendances ou situations affectant le secteur, dans l'espoir d'augmenter le trafic vers le site et sa production d'articles, il a décidé d'utiliser l'IA pour générer des articles pour lui.
Après un certain temps, avec les bonnes invites, le client a obtenu des informations qui correspondaient presque exactement à un article écrit par un humain, il est très difficile de repérer qu'elles ont été créées par une machine.
Quelque temps après avoir commencé à travailler sur différentes fonctionnalités, on me demandait continuellement une chose spécifique.
Ey, pouvez-vous mettre à jour l'image sélectionnée pour cet article ?
Après 2 semaines de mise à jour quotidienne des messages, j'ai eu un petit moment d'eurêka.
Pourquoi ne pas automatiser la génération d'images sélectionnées pour ces articles à l'aide de l'Intelligence artificielle ?
Nous avons déjà automatisé la rédaction des articles, pourquoi ne pas automatiser les images présentées ?
Pendant mon temps libre, j'expérimentais des LLM génératifs sur mon ordinateur, j'avais donc une bonne idée de plus ou moins comment aborder cette quête secondaire. J'ai envoyé un message au client détaillant quel est le problème, ce que je veux faire et quels seraient les avantages et sans avoir à convaincre, j'ai eu le feu vert pour travailler sur cette fonctionnalité et j'ai tout de suite opté pour mon premier pas.
1. Concevoir à quoi ressemblera la solution.
Étant donné que j'ai eu une certaine exposition à l'exécution de modèles localement, j'ai tout de suite su qu'il n'était pas possible d'héberger soi-même ces modèles. Cela mis de côté, j'ai commencé à jouer avec des API qui généraient des images basées sur des invites textuelles.
Les images présentées se composaient de 2 parties : le graphique principal composé et un slogan accrocheur.
Le graphique composé serait constitué de quelques éléments liés à l'article, disposés de manière agréable avec ensuite des couleurs et des textures avec des modes de fusion appliqués pour obtenir des effets fantaisistes suivant le branding.
Les slogans étaient des phrases courtes de 8 à 12 mots avec une simple ombre portée en dessous.
Sur la base de mes tests, j'ai réalisé que poursuivre la voie de l'IA pour la génération d'images n'était pas pratique. La qualité de l’image n’était pas à la hauteur des attentes et le processus prenait trop de temps pour justifier son utilisation. Considérant que cela fonctionnerait comme une fonction AWS Lambda, où le temps d'exécution aurait un impact direct sur les coûts.
Cela étant écarté, j'ai opté pour le plan B : mélanger des images et des éléments de conception à l'aide de l'API Canvas de JavaScript.
En y regardant de plus près, nous avions principalement 5 styles de messages simples, et environ 4 types de textures, dont 3 utilisant le même alignement de texte, le même style et la même position. Après avoir fait quelques calculs, j'ai pensé :
Hmm, si je prends ces 3 images, saisis 8 textures et joue avec les modes de fusion, je peux me débrouiller avec 24 variations
Étant donné que ces 3 types de messages avaient le même style de texte, il s'agissait pratiquement d'un seul modèle.
Une fois cela réglé, je suis passé au générateur de slogan. Je voulais créer un slogan basé sur le contenu et le titre de l'article. J'ai décidé d'utiliser l'API de ChatGPT étant donné que l'entreprise payait déjà pour cela, et après quelques expérimentations et ajustements des invites, j'ai eu un très bon MVP pour mon générateur de slogan.
Une fois les 2 parties les plus difficiles de la tâche comprises, j'ai passé du temps dans Figma à assembler le schéma de l'architecture finale de mon service.
2.Codage de mon lambda
Le plan était de créer une fonction Lambda capable d'analyser le contenu de la publication, de générer un slogan et d'assembler une image sélectionnée, le tout parfaitement intégré à WordPress.
Je fournirai du code mais juste assez pour communiquer l'idée générale à ke.
Analyser le contenu
La fonction Lambda commence par extraire les paramètres nécessaires de la charge utile de l'événement entrant :
const { title : request_title, content, backend, app_password} = JSON.parse(event.body);
- titre et contenu : Ceux-ci fournissent le contexte de l’article.
- backend : L'URL du backend WordPress pour les téléchargements d'images.
- app_password : Le jeton d'authentification que je vais utiliser pour télécharger en tant qu'utilisateur à l'aide de l'API Wordpress Rest.
Générer le slogan
La première tâche principale de la fonction consiste à générer un slogan à l'aide de la fonction analyseContent, qui utilise l'API d'OpenAI pour créer un slogan digne d'un clic basé sur le titre et le contenu de l'article.
Notre fonction prend le titre et le contenu de la publication mais renvoie un slogan, un sentiment de publication pour savoir si la publication est une opinion positive, négative ou neutre et un symbole d'entreprise facultatif des sociétés de l'indice S&P.
const { slogan, sentiment, entreprise } = wait analyseContent({ titre : request_title, content });
Cette étape est cruciale, car le slogan influence directement l’esthétique de l’image.
Création de l'image sélectionnée
Ensuite, la fonction generateImage entre en jeu :
let buffer; buffer = await generateImage({ title: tagline, company_logo: company_logo, sentiment: sentiment, });
Cette fonction gère :
- Concevoir la composition.
- Superposition de textures, de couleurs et d'éléments de marque.
- Appliquer des effets et créer le titre.
Voici un aperçu étape par étape de son fonctionnement :
La fonction generateImage commence par créer un canevas vierge, définir ses dimensions et le préparer à gérer tous les éléments de conception.
let buffer; buffer = await generateImage({ title: tagline, company_logo: company_logo, sentiment: sentiment, });
À partir de là, une image d'arrière-plan aléatoire est chargée à partir d'une collection prédéfinie d'actifs. Ces images ont été organisées pour correspondre à la marque axée sur la technologie tout en permettant suffisamment de variété entre les publications. L'image d'arrière-plan est sélectionnée au hasard en fonction de son sentiment.
Pour garantir que chaque image d'arrière-plan soit superbe, j'ai calculé ses dimensions de manière dynamique en fonction du rapport hauteur/largeur. Cela évite les distorsions tout en gardant l'équilibre visuel intact.
Ajout du slogan
Le slogan est court mais basé sur certaines règles, cette phrase percutante est divisée en morceaux gérables et est stylisée de manière dynamique pour garantir qu'elle est toujours lisible, quelle que soit la longueur ou la taille du canevas en fonction du nombre de mots pour la ligne, de la longueur des mots, etc. .
const COLOURS = { BLUE: "#33b8e1", BLACK: "#000000", } const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const images_path = path.join(__dirname, 'images/'); const files_length = fs.readdirSync(images_path).length; const images_folder = process.env.ENVIRONMENT === "local" ? "./images/" : "/var/task/images/"; registerFont("/var/task/fonts/open-sans.bold.ttf", { family: "OpenSansBold" }); registerFont("/var/task/fonts/open-sans.regular.ttf", { family: "OpenSans" }); console.log("1. Created canvas"); const canvas = createCanvas(1118, 806); let image = await loadImage(`${images_folder}/${Math.floor(Math.random() * (files_length - 1 + 1)) + 1}.jpg`); let textBlockHeight = 0; console.log("2. Image loaded"); const canvasWidth = canvas.width; const canvasHeight = canvas.height; const aspectRatio = image.width / image.height; console.log("3. Defined ASPECT RATIO",) let drawWidth, drawHeight; if (image.width > image.height) { // Landscape orientation: fit by width drawWidth = canvasWidth; drawHeight = canvasWidth / aspectRatio; } else { // Portrait orientation: fit by height drawHeight = canvasHeight; drawWidth = canvasHeight * aspectRatio; } // Center the image const x = (canvasWidth - drawWidth) / 2; const y = (canvasHeight - drawHeight) / 2; const ctx = canvas.getContext("2d"); console.log("4. Centered Image") ctx.drawImage(image, x, y, drawWidth, drawHeight);
Enfin, le canevas est converti en tampon PNG.
console.log("4.1 Text splitting"); if (splitText.length === 1) { const isItWiderThanHalf = ctx.measureText(splitText[0]).width > ((canvasWidth / 2) + 160); const wordCount = splitText[0].split(" ").length; if (isItWiderThanHalf && wordCount > 4) { const refactored_line = splitText[0].split(" ").reduce((acc, curr, i) => { if (i % 3 === 0) { acc.push([curr]); } else { acc[acc.length - 1].push(curr); } return acc; }, []).map((item) => item.join(" ")); refactored_line[1] = "[s]" + refactored_line[1] + "[s]"; splitText = refactored_line } } let tagline = splitText.filter(item => item !== '' && item !== '[br]' && item !== '[s]' && item !== '[/s]' && item !== '[s]'); let headlineSentences = []; let lineCounter = { total: 0, reduced_line_counter: 0, reduced_lines_indexes: [] } console.log("4.2 Tagline Preparation", tagline); for (let i = 0; i item !== '' && item !== '[s]' && item !== '[/s]'); const lineWidth = ctx.measureText(finalLine[0]).width const halfOfWidth = canvasWidth / 2; if (lineWidth > halfOfWidth && finalLine[0]) { let splitted_text = finalLine[0].split(" ").reduce((acc, curr, i) => { const modulus = finalLine[0].split(" ").length >= 5 ? 3 : 2; if (i % modulus === 0) { acc.push([curr]); } else { acc[acc.length - 1].push(curr); } return acc; }, []); let splitted_text_arr = [] splitted_text.forEach((item, _) => { let lineText = item.join(" "); item = lineText splitted_text_arr.push(item) }) headlineSentences[i] = splitted_text_arr[0] + '/s/' if (splitted_text_arr[1]) { headlineSentences.splice(i + 1, 0, splitted_text_arr[1] + '/s/') } } else { headlineSentences.push("/s/" + finalLine[0] + "/s/") } } else { headlineSentences.push(line) } } console.log("5. Drawing text on canvas", headlineSentences); const headlineSentencesLength = headlineSentences.length; let textHeightAccumulator = 0; for (let i = 0; i item !== '/s/'); const nextLine = headlineSentences[i + 1]; if (nextLine && /^\s*$/.test(nextLine)) { headlineSentences.splice(i + 1, 1); } let line = headlineSentences[i]; if (!line) continue; let lineText = line.trim(); let textY; ctx.font = " 72px OpenSans"; const cleanedUpLine = lineText.includes('/s/') ? lineText.replace(/\s+/g, ' ') : lineText; const lineWidth = ctx.measureText(cleanedUpLine).width const halfOfWidth = canvasWidth / 2; lineCounter.total += 1 const isLineTooLong = lineWidth > (halfOfWidth + 50); if (isLineTooLong) { if (lineText.includes(':')) { const split_line_arr = lineText.split(":") if (split_line_arr.length > 1) { lineText = split_line_arr[0] + ":"; if (split_line_arr[1]) { headlineSentences.splice(i + 1, 0, split_line_arr[1]) } } } ctx.font = "52px OpenSans"; lineCounter.reduced_line_counter += 1 if (i === 0 && headlineSentencesLength === 2) { is2LinesAndPreviewsWasReduced = true } lineCounter.reduced_lines_indexes.push(i) } else { if (i === 0 && headlineSentencesLength === 2) { is2LinesAndPreviewsWasReduced = false } } if (lineText.includes("/s/")) { lineText = lineText.replace(/\/s\//g, ""); if (headlineSentencesLength > (i + 1) && i (canvasWidth / 2.35)) { ctx.font = "84px OpenSansBold"; assignedSize = 80 } else { ctx.font = "84px OpenSansBold"; assignedSize = 84 } } else { if (i === headlineSentencesLength - 1 && lineWidth (canvasWidth / 2) + 120) { if (assignedSize === 84) { ctx.font = "72px OpenSansBold"; } else if (assignedSize === 80) { ctx.font = "64px OpenSansBold"; textHeightAccumulator += 8 } else { ctx.font = "52px OpenSansBold"; } } } else { const textWidth = ctx.measureText(lineText).width if (textWidth > (canvasWidth / 2)) { ctx.font = "44px OpenSans"; textHeightAccumulator += 12 } else if (i === headlineSentencesLength - 1) { textHeightAccumulator += 12 } } ctx.fillStyle = "white"; ctx.textAlign = "center"; const textHeight = ctx.measureText(lineText).emHeightAscent; textHeightAccumulator += textHeight; if (headlineSentencesLength == 3) { textY = (canvasHeight / 3) } else if (headlineSentencesLength == 4) { textY = (canvasHeight / 3.5) } else { textY = 300 } textY += textHeightAccumulator; const words = lineText.split(' '); console.log("words", words, lineText, headlineSentences) const capitalizedWords = words.map(word => { if (word.length > 0) return word[0].toUpperCase() + word.slice(1) return word }); const capitalizedLineText = capitalizedWords.join(' '); ctx.fillText(capitalizedLineText, canvasWidth / 2, textY); }
Enfin!!! Téléchargement de l'image sur WordPress
Après avoir généré avec succès le tampon d'image, la fonction uploadImageToWordpress est appelée.
Cette fonction gère le gros du travail consistant à envoyer l'image à WordPress à l'aide de son API REST en codant l'image pour WordPress.
La fonction prépare d'abord le slogan à utiliser comme nom de fichier en nettoyant les espaces et les caractères spéciaux :
const buffer = canvas.toBuffer("image/png"); return buffer;
Le buffer d'image est ensuite converti en objet Blob pour le rendre compatible avec l'API WordPress :
const file = new Blob([buffer], { type : "image/png" });
Préparation de la requête API À l'aide de l'image et du slogan encodés, la fonction crée un objet FormData et j'ajoute des métadonnées facultatives, telles que alt_text pour l'accessibilité et une légende pour le contexte.
const createSlug = (string) => { return string.toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, ''); }; const image_name = createSlug(tagline);
Pour l'authentification, le nom d'utilisateur et le mot de passe de l'application sont encodés en Base64 et inclus dans les en-têtes de requête :
formData.append("file", file, image_name + ".png"); formData.append("alt_text", `${tagline} image`); formData.append("caption", "Uploaded via API");
Envoi de l'image Une requête POST est faite au point de terminaison média WordPress avec les données et en-têtes préparés et après avoir attendu la réponse, je valide le succès ou les erreurs.
const credentials = `${username}:${app_password}`; const base64Encoded = Buffer.from(credentials).toString("base64");
En cas de succès, je renvoie la même réponse médiatique dans le lambda.
Voici à quoi ressemble ma lambda au final.
const response = await fetch(`${wordpress_url}wp-json/wp/v2/media`, { method: "POST", headers: { Authorization: "Basic " + base64Encoded, contentType: "multipart/form-data", }, body: formData, }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Error uploading image: ${response.statusText}, Details: ${errorText}`); }
Ceci est un exemple d'image produit par mon script. Il n'est pas utilisé en production, juste créé avec des actifs génériques pour cet exemple.
Conséquences
Un certain temps a passé et tout le monde est heureux que nous n'ayons plus d'articles sans images de mauvaise qualité ou vides, que les images correspondent étroitement à celles que le designer crée, le designer est heureux de pouvoir se concentrer uniquement sur concevoir pour d'autres efforts de marketing à travers l'entreprise.
Mais ensuite un nouveau problème est apparu : parfois, le client n'aimait pas l'image générée et il me demandait de lancer mon script pour en générer une nouvelle pour un article spécifique.
Cela m'a amené à ma prochaine quête secondaire : un Plugin Wordpress pour générer manuellement une image en vedette à l'aide de l'intelligence artificielle pour une publication spécifique
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Différents moteurs JavaScript ont des effets différents lors de l'analyse et de l'exécution du code JavaScript, car les principes d'implémentation et les stratégies d'optimisation de chaque moteur diffèrent. 1. Analyse lexicale: convertir le code source en unité lexicale. 2. Analyse de la grammaire: générer un arbre de syntaxe abstrait. 3. Optimisation et compilation: générer du code machine via le compilateur JIT. 4. Exécuter: Exécutez le code machine. Le moteur V8 optimise grâce à une compilation instantanée et à une classe cachée, SpiderMonkey utilise un système d'inférence de type, résultant en différentes performances de performances sur le même code.

Les applications de JavaScript dans le monde réel incluent la programmation côté serveur, le développement des applications mobiles et le contrôle de l'Internet des objets: 1. La programmation côté serveur est réalisée via Node.js, adaptée au traitement de demande élevé simultané. 2. Le développement d'applications mobiles est effectué par le reactnatif et prend en charge le déploiement multiplateforme. 3. Utilisé pour le contrôle des périphériques IoT via la bibliothèque Johnny-Five, adapté à l'interaction matérielle.

J'ai construit une application SAAS multi-locataire fonctionnelle (une application EdTech) avec votre outil technologique quotidien et vous pouvez faire de même. Premièrement, qu'est-ce qu'une application SaaS multi-locataire? Les applications saas multi-locataires vous permettent de servir plusieurs clients à partir d'un chant

Cet article démontre l'intégration frontale avec un backend sécurisé par permis, construisant une application fonctionnelle EdTech SaaS en utilisant Next.js. Le frontend récupère les autorisations des utilisateurs pour contrôler la visibilité de l'interface utilisateur et garantit que les demandes d'API adhèrent à la base de rôles

JavaScript est le langage central du développement Web moderne et est largement utilisé pour sa diversité et sa flexibilité. 1) Développement frontal: construire des pages Web dynamiques et des applications à une seule page via les opérations DOM et les cadres modernes (tels que React, Vue.js, Angular). 2) Développement côté serveur: Node.js utilise un modèle d'E / S non bloquant pour gérer une concurrence élevée et des applications en temps réel. 3) Développement des applications mobiles et de bureau: le développement de la plate-forme multiplateuse est réalisé par réact noral et électron pour améliorer l'efficacité du développement.

Les dernières tendances de JavaScript incluent la montée en puissance de TypeScript, la popularité des frameworks et bibliothèques modernes et l'application de WebAssembly. Les prospects futurs couvrent des systèmes de type plus puissants, le développement du JavaScript côté serveur, l'expansion de l'intelligence artificielle et de l'apprentissage automatique, et le potentiel de l'informatique IoT et Edge.

JavaScript est la pierre angulaire du développement Web moderne, et ses principales fonctions incluent la programmation axée sur les événements, la génération de contenu dynamique et la programmation asynchrone. 1) La programmation axée sur les événements permet aux pages Web de changer dynamiquement en fonction des opérations utilisateur. 2) La génération de contenu dynamique permet d'ajuster le contenu de la page en fonction des conditions. 3) La programmation asynchrone garantit que l'interface utilisateur n'est pas bloquée. JavaScript est largement utilisé dans l'interaction Web, les applications à une page et le développement côté serveur, améliorant considérablement la flexibilité de l'expérience utilisateur et du développement multiplateforme.

Python est plus adapté à la science des données et à l'apprentissage automatique, tandis que JavaScript est plus adapté au développement frontal et complet. 1. Python est connu pour sa syntaxe concise et son écosystème de bibliothèque riche, et convient à l'analyse des données et au développement Web. 2. JavaScript est le cœur du développement frontal. Node.js prend en charge la programmation côté serveur et convient au développement complet.


Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

MantisBT
Mantis est un outil Web de suivi des défauts facile à déployer, conçu pour faciliter le suivi des défauts des produits. Cela nécessite PHP, MySQL et un serveur Web. Découvrez nos services de démonstration et d'hébergement.

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

MinGW - GNU minimaliste pour Windows
Ce projet est en cours de migration vers osdn.net/projects/mingw, vous pouvez continuer à nous suivre là-bas. MinGW : un port Windows natif de GNU Compiler Collection (GCC), des bibliothèques d'importation et des fichiers d'en-tête librement distribuables pour la création d'applications Windows natives ; inclut des extensions du runtime MSVC pour prendre en charge la fonctionnalité C99. Tous les logiciels MinGW peuvent fonctionner sur les plates-formes Windows 64 bits.

PhpStorm version Mac
Le dernier (2018.2.1) outil de développement intégré PHP professionnel

SublimeText3 version chinoise
Version chinoise, très simple à utiliser