Aller au contenu

Image Alt Textify

AI generated translation.

Le texte alternatif est essentiel pour rendre les images accessibles à tous, y compris aux personnes malvoyantes. Il fournit une description textuelle de l’image, permettant aux lecteurs d’écran de transmettre le contenu aux utilisateurs qui ne peuvent pas voir l’image. Cependant, écrire du texte alternatif pour les images peut prendre du temps, surtout lorsqu’il s’agit d’un grand nombre d’images. C’est là que l’IA peut aider. En utilisant un modèle de langage comme GPT-4 d’OpenAI, vous pouvez générer automatiquement du texte alternatif pour les images, ce qui permet de gagner du temps et de l’effort.

Dans cet exemple, nous allons construire un outil qui génère du texte alternatif pour les images dans les fichiers Markdown.

Ce script est composé de code TypeScript, conçu pour s’exécuter avec le GenAIScript CLI. Décomposons-le :

script({
title: "Image Alt Textify",
description: "Generate alt text for images in markdown files",
accept: "none",
parameters: {
docs: {
type: "string",
description: "path to search for markdown files",
default: "**.{md,mdx}",
},
force: {
type: "boolean",
description: "regenerate all descriptions",
default: false,
},
assets: {
type: "string",
description: "image assets path",
// change the default path to your assets folder
default: "./assets/images",
},
},
})

Ici, nous déclarons le script avec un titre et une description, en précisant qu’il utilise le modèle GPT-4 d’OpenAI. Nous définissons également des paramètres pour les chemins de fichiers, l’option de régénérer toutes les descriptions, ainsi que le chemin des ressources.

Ensuite, nous extrayons les variables d’environnement :

const { docs, force, assets } = env.vars

Ensuite, nous définissons une expression régulière pour trouver les images dans Markdown :

const rx = force
? // match ![alt?](path) with alt text or not
/!\[[^\]]*\]\(([^\)]+\.(png|jpg))\)/g
: // match ![](path) without alt text
/!\[\s*\]\(([^\)]+\.(png|jpg))\)/g
const { files } = await workspace.grep(rx, {
path: docs,
glob: "*.{md,mdx}",
readText: true,
})

Le script utilise workspace.grep pour trouver toutes les occurrences du motif regex dans les documents spécifiés.

Pour chaque URL d’image trouvée, nous générons un texte alternatif en utilisant un prompt inline et defImages.

for (const file of files) {
const { filename, content } = file
// map documentation relative path to assets path
const url = resolveUrl(filename)
// execute a LLM query to generate alt text
const { text } = await runPrompt(
(_) => {
_.defImages(resolvedUrl)
_.$`
You are an expert in assistive technology.
You will analyze the image
and generate a description alt text for the image.
- Do not include alt text in the description.
- Keep it short but descriptive.
- Do not generate the [ character.`
},
{
// safety system message to prevent generating harmful text
system: ["system.safety_harmful_content"],
// use multi-model model
model: "openai:gpt-4o",
...
}
)
imgs[url] = text
}

Enfin, nous mettons à jour le contenu Markdown avec le texte alternatif généré :

const newContent = content.replace(
rx,
(m, url) => `![${imgs[url] ?? ""}](${url})`
)
if (newContent !== content) await workspace.writeText(filename, newContent)

Nous remplaçons le substitut dans le contenu original par le texte alternatif et enregistrons le fichier mis à jour.

Pour exécuter ce script, vous aurez besoin du GenAIScript CLI. Si vous ne l’avez pas encore installé, consultez le guide d’installation.

Une fois que vous avez le CLI, vous pouvez exécuter le script avec la commande suivante :

Fenêtre de terminal
npx genaiscript run iat

Automatisation avec GitHub Actions et GitHub Models

Section intitulée « Automatisation avec GitHub Actions et GitHub Models »

L’action GitHub suivante automatise le processus de génération du texte alternatif pour les images dans les fichiers Markdown. Elle s’exécute à chaque push sur la branche dev et utilise le CLI genaiscript pour exécuter le script. Elle utilise le modèle OpenAI gpt-4.1 via GitHub Models pour l’inférence.

.github/workflows/genai-iat.yml
name: genai iat
on:
workflow_dispatch:
push:
branches:
- dev
paths:
- "**.md*"
concurrency:
group: iat-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
pull-requests: write
models: read
contents: write
jobs:
generate-alt-text:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: genaiscript
run: npx --yes genaiscript run iat -m github:openai/gpt-4.1 --out-trace $GITHUB_STEP_SUMMARY
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "[genai] image alt text"
commit_user_name: "genaiscript"
iat.genai.mts
/*
* Markdown image alt text updater
*/
script({
title: "Image Alt Textify",
description: "Generate alt text for images in markdown files",
accept: "none",
unlisted: true,
parameters: {
docs: {
type: "string",
description: "path to search for markdown files",
default: "docs",
},
force: {
type: "boolean",
description: "regenerate all descriptions",
default: false,
},
assets: {
type: "string",
description: "image assets path",
default: "./slides/public",
},
dryRun: {
type: "boolean",
description: "show matches, do not compute alt text",
default: false,
},
},
});
/** ------------------------------------------------
* Configuration
*/
const { docs, force, assets, dryRun } = env.vars;
/** ------------------------------------------------
* Helper functions (update as needed)
*/
/**
* Return the resolved url for the image
*/
const resolveUrl = (filename: string, url: string) => {
// github assets ok
if (/^https:\/\/github.com\/user-attachments\/assets\//i.test(url)) return url;
if (url?.startsWith("https://raw.githubusercontent.com/")) return url;
// ignore external urls
if (/^http?s:\/\//i.test(url)) return undefined;
// map / to assets
else if (/^\//.test(url)) return path.join(assets, url.slice(1));
// resolve local paths
else return path.join(path.dirname(filename), url);
};
/** ------------------------------------------------
* Collect files
*/
// search for ![](...) in markdown files and generate alt text for images
const rx = force // upgrade all urls
? // match ![alt](url) with any alt
/!\[[^\]]*\]\(([^\)]+)\)/g
: // match ![alt](url) where alt is empty
/!\[\s*\]\(([^\)]+)\)/g;
console.log(`Searching for ${rx} in ${docs}`);
const { files } = await workspace.grep(rx, {
path: docs,
glob: "**/*.md*",
readText: true,
});
/** ------------------------------------------------
* Generate alt text for images
* and update markdown files
*/
// a cache of generated alt text for images
const imgs: Record<string, string> = {};
// process each file
for (const file of files) {
const { filename, content } = file;
console.log(filename);
const matches = Array.from(content.matchAll(rx));
console.log(`.. found ${matches.length} matches`);
// pre-compute matches
for (const match of matches) {
const url = match[1];
if (imgs[url]) continue; // already processed
console.log(`.. processing ${url}`);
const resolvedUrl = resolveUrl(filename, url);
if (!resolvedUrl) {
console.log(`... unknown image url`);
continue; // can't process url
}
console.log(`└─ ${resolvedUrl}`);
if (dryRun) continue;
// execute a LLM query to generate alt text
const { text, error } = await runPrompt(
(_) => {
_.defImages(resolvedUrl);
_.$`
You are an expert in assistive technology.
You will analyze the image
and generate a description alt text for the image.
- Do not include alt text in the description.
- Keep it short but descriptive.
- Do not generate the [ character.`;
},
{
// safety system message to prevent generating harmful text
system: [
"system.assistant",
"system.safety_jailbreak",
"system.safety_harmful_content",
"system.safety_validate_harmful_content",
],
maxTokens: 4000,
temperature: 0.5,
cache: "alt-text",
label: `alt-textify ${resolvedUrl}`,
},
);
if (error) throw error;
else if (!text) console.log(`.. no description generated`);
else imgs[url] = text.replace(/\[/g, ""); // remove [ from alt text
}
// apply replacements
const newContent = content.replace(rx, (m, url) => `![${imgs[url] ?? ""}](${url})`);
// save updated content
if (newContent !== content) {
console.log(`.. updating ${filename}`);
await workspace.writeText(filename, newContent);
}
}

Les mesures suivantes sont prises pour garantir la sécurité du contenu généré.

  • Ce script inclut des invites système pour empêcher les injections de prompt et la génération de contenu nuisible.
  • La description générée est sauvegardée dans un fichier à un chemin spécifique, ce qui permet une revue manuelle avant de valider les modifications.

Des mesures supplémentaires pour renforcer la sécurité consisteraient à utiliser un modèle avec un filtre de sécurité ou à valider le message avec un service de sécurité du contenu.

Consultez la Note de transparence pour plus d’informations sur la sécurité du contenu.