Ébaucher un texte alternatif avec AWS Rekognition et Node.js
Utilisez l’opération DetectLabels d’AWS Rekognition pour suggérer des objets
et des scènes qu’un rédacteur peut prendre en compte pour rédiger un texte alternatif. Ce guide
affiche des suggestions d’étiquettes propres à chaque image avec un indicateur
reviewRequired, puis ajoute un cache pour les fichiers locaux et un petit
programme de traitement séquentiel par lots.
Pourquoi automatiser le texte alternatif ?
Les étiquettes peuvent vous aider à commencer une description, mais elles ne peuvent pas déterminer
le rôle d’une image sur une page. Suivez l’arbre de décision du W3C pour le texte alternatif
pour décider s’il faut décrire l’image, expliquer sa fonction de lien ou de bouton, ou utiliser une
alternative vide pour une image décorative. Ne placez pas les suggestions dans l’attribut
alt avant qu’une personne ait examiné l’image dans son contexte.
Intégration d’AWS Rekognition à Node.js
DetectLabels
renvoie des étiquettes et des scores de confiance plutôt qu’une légende contextualisée. Les
étiquettes sont renvoyées en anglais et peuvent inclure plusieurs noms pour un même objet, comme
un objet précis et sa catégorie parente. Cet exemple conserve jusqu’à cinq noms selon un seuil de
confiance choisi. Pour lire les mots présents dans une image, utilisez l’opération distincte
DetectText de Rekognition.
Configurer votre environnement
Vous avez besoin de Bash, de Node.js 24 LTS, de npm et d’un
profil AWS authentifié ou d’un rôle IAM autorisé à appeler rekognition:DetectLabels.
Les exemples ont été testés avec Node.js 24.21.0 et 26.8.1, ainsi qu’avec
@aws-sdk/client-rekognition 3.1148.0 du SDK AWS v3. Ce sont les versions testées,
pas un minimum intrinsèquement requis par l’API ; utilisez une version de Node.js encore maintenue.
L’envoi d’une image à AWS peut entraîner des frais.
Utilisez la méthode d’authentification existante de votre organisation et la
chaîne de fournisseurs d’informations d’accès par défaut
du SDK. Pour un profil local, connectez-vous d’abord et définissez
AWS_PROFILE dans les commandes ci-dessous. Pour un rôle IAM, omettez cette
variable. Les informations d’accès temporaires définies dans l’environnement nécessitent
AWS_SESSION_TOKEN en plus de la clé d’accès et du secret ; ne placez pas leurs
valeurs dans les fichiers sources ni dans la sortie du terminal.
Exécutez ceci depuis le répertoire où vous souhaitez créer un projet
image-labels. Le sous-shell préserve votre répertoire courant, et
mkdir refuse d’écraser un projet existant. La cible npm explicite
maintient également l’installation dans le nouveau projet lorsque son répertoire parent utilise
un autre gestionnaire de paquets :
(
command -v node >/dev/null || { printf 'Install Node.js first.\n' >&2; exit 1; }
command -v npm >/dev/null || { printf 'Install npm first.\n' >&2; exit 1; }
[ -z "${NODE_OPTIONS:-}" ] || { printf 'Use a shell without NODE_OPTIONS.\n' >&2; exit 1; }
mkdir image-labels && cd image-labels &&
printf '{"private":true,"type":"module"}\n' > package.json &&
npm install --prefix "$PWD" --workspaces=false --ignore-scripts --save-exact \
@aws-sdk/client-rekognition@3.1148.0
)
Enregistrez les fichiers ci-dessous dans image-labels. Si l’installation
échoue après la création du répertoire, corrigez le problème signalé et relancez la commande
npm install depuis ce répertoire. Conservez les fichiers existants ;
relancer l’ensemble du bloc de création refuse délibérément d’utiliser ce répertoire.
Exigences relatives aux images
Utilisez des images JPEG ou PNG. AWS indique un maximum de 5 MB pour les octets envoyés directement
et de 15 MB pour un objet S3 ; DetectLabels exige que chaque dimension soit
comprise entre 80 et 10 000 pixels. Consultez les
quotas de Rekognition pour connaître les limites actuelles.
Le code envoie vos octets d’origine sans redimensionnement, décodage ni modification des profils
colorimétriques. Une requête réussie ne vérifie ni l’intégrité de l’image ni l’exactitude des
étiquettes.
Implémenter les suggestions d’étiquettes
Enregistrez ceci sous image-labels/labels.mjs. Le SDK accepte directement le tampon
contenant le fichier ; ne l’encodez pas manuellement en base64. Ne pas définir
credentials préserve l’authentification par profil et par rôle. Définir
maxAttempts: 1 désactive les nouvelles tentatives automatiques, afin qu’une
analyse échouée reste un appel en échec.
import { DetectLabelsCommand, RekognitionClient } from '@aws-sdk/client-rekognition'
import fs from 'node:fs'
const client = new RekognitionClient({ region: process.env.AWS_REGION, maxAttempts: 1 })
async function detectImageLabels(image, minConfidence = 75) {
if (!Number.isFinite(minConfidence) || minConfidence < 0 || minConfidence > 100) {
throw new Error('Confidence must be a number between 0 and 100.')
}
const response = await client.send(new DetectLabelsCommand({
Image: image,
Features: ['GENERAL_LABELS'],
MaxLabels: 5,
MinConfidence: minConfidence,
}))
return (response.Labels ?? [])
.map((label) => label.Name)
.filter((name) => typeof name === 'string' && name.trim().length > 0)
}
async function suggestImageLabels(imagePath, minConfidence = 75) {
const imageBytes = fs.readFileSync(imagePath)
return detectImageLabels({ Bytes: imageBytes }, minConfidence)
}
function reviewRecord(source, labelSuggestions, minConfidence = 75) {
return { source, minConfidence, reviewRequired: true, labelSuggestions }
}
export { client, detectImageLabels, reviewRecord, suggestImageLabels }
Enregistrez le point d’entrée sous image-labels/review.mjs :
import { client, reviewRecord, suggestImageLabels } from './labels.mjs'
try {
const [imagePath, confidence = '75', ...extra] = process.argv.slice(2)
if (!imagePath || extra.length > 0 || !process.env.AWS_REGION) {
throw new Error('Supply AWS_REGION and an image path, with optional confidence.')
}
const minConfidence = Number(confidence)
const labels = await suggestImageLabels(imagePath, minConfidence)
console.log(JSON.stringify(reviewRecord(imagePath, labels, minConfidence)))
} catch {
console.error('Unable to retrieve image label suggestions. Check the path, region, credentials, and image.')
process.exitCode = 1
} finally {
client.destroy()
}
Choisissez votre région et remplacez MY_PROFILE et
./example.jpg. Les chemins sont relatifs au répertoire depuis lequel vous
exécutez Node.js ; placez les noms de fichiers contenant des espaces entre guillemets :
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review.mjs './example.jpg'
Le JSON contient le source sélectionné, le seuil,
reviewRequired: true et labelSuggestions. Comparez ces noms à votre
image réelle. Une liste vide correspond à une analyse réussie sans étiquettes exploitables ;
elle nécessite donc toujours une révision. Les échecs liés aux fichiers, aux informations d’accès
et au fournisseur affichent un diagnostic sur stderr, ne produisent aucun enregistrement de
réussite et se terminent avec un code de sortie non nul. Aucun champ ne contient de texte
alternatif finalisé.
Traiter des images depuis S3
Pour une image S3 existante, la région du bucket doit correspondre à celle du client Rekognition,
et l’identité qui effectue l’appel doit disposer de s3:GetObject pour
l’objet. Consultez le guide des images S3 et les
recommandations IAM d’AWS.
Cet exemple lit l’objet actuel à la clé indiquée et ne sélectionne pas de version antérieure.
Ajoutez ceci à image-labels/labels.mjs :
async function suggestImageLabelsFromS3(bucket, key, minConfidence = 75) {
return detectImageLabels({ S3Object: { Bucket: bucket, Name: key } }, minConfidence)
}
export { suggestImageLabelsFromS3 }
Enregistrez image-labels/review-s3.mjs :
import { client, reviewRecord, suggestImageLabelsFromS3 } from './labels.mjs'
try {
const [bucket, key, confidence = '75', ...extra] = process.argv.slice(2)
if (!bucket || !key || extra.length > 0 || !process.env.AWS_REGION) {
throw new Error('Supply AWS_REGION, a bucket, and an object key, with optional confidence.')
}
const minConfidence = Number(confidence)
const labels = await suggestImageLabelsFromS3(bucket, key, minConfidence)
console.log(JSON.stringify(reviewRecord({ bucket, key }, labels, minConfidence)))
} catch {
console.error('Unable to retrieve S3 image label suggestions. Check the key, region, credentials, and permissions.')
process.exitCode = 1
} finally {
client.destroy()
}
Remplacez le bucket et la clé par ceux de votre propre image existante. Transmettez la clé littéralement, y compris les espaces ou les caractères de pourcentage ; ne l’encodez pas pour une URL. Ce mode d’accès S3 n’utilise pas de cache ; il analyse donc l’objet actuel à chaque appel :
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review-s3.mjs my-bucket 'images/example.jpg'
Gérer les cas particuliers et les erreurs
Une requête échouée est différente d’une liste d’étiquettes vide. Les fonctions auxiliaires rejettent les échecs ; les points d’entrée les signalent sans exposer les réponses brutes d’AWS ni les informations d’accès. Pour résoudre un problème, vérifiez d’abord le chemin local, puis l’authentification et la région. Pour S3, vérifiez également la clé exacte et les autorisations de lecture de l’objet. Les erreurs de DetectLabels documentées par AWS couvrent les formats non pris en charge, les paramètres invalides, les objets S3 inaccessibles et les échecs liés au service ou aux quotas. Corrigez la cause avant de relancer la commande. Chaque nouvelle tentative peut entraîner une nouvelle analyse.
Étendre les fonctionnalités
Mettre en place un cache
Pour les images locales, mettez en cache les noms obtenus après une analyse réussie en fonction
du contenu des images et des paramètres de la requête. Cette section facultative suppose que vous
disposez d’un serveur Redis déjà en cours d’exécution et de son URL de connexion. Elle a été testée
avec Redis 7.4.11 et la version actuelle du client redis, 6.3.0.
Installez le client dans le projet existant :
test -f image-labels/package.json &&
npm install --prefix "$PWD/image-labels" --workspaces=false --ignore-scripts --save-exact redis@6.3.0
Enregistrez image-labels/cache.mjs. L’identité du cache inclut la région, le hachage
du contenu, le seuil de confiance et les paramètres fixes des étiquettes. Lisez et analysez le
même tampon afin qu’un fichier modifié ne puisse pas réutiliser les étiquettes d’une version
antérieure. Ne stockez les résultats qu’après une réponse réussie du fournisseur ; les
entrées de cache mal formées ou non concordantes provoquent un échec visible au lieu d’être
traitées comme des étiquettes :
import { createHash } from 'node:crypto'
import fs from 'node:fs'
import { createClient } from 'redis'
import { detectImageLabels } from './labels.mjs'
const redisClient = createClient({
url: process.env.REDIS_URL,
socket: { connectTimeout: 5000, reconnectStrategy: false },
})
redisClient.on('error', () => console.error('Redis connection error.'))
async function getCachedOrSuggestImageLabels(imagePath, minConfidence = 75) {
if (!Number.isFinite(minConfidence) || minConfidence < 0 || minConfidence > 100) {
throw new Error('Confidence must be a number between 0 and 100.')
}
const imageBytes = fs.readFileSync(imagePath)
const imageHash = createHash('sha256').update(imageBytes).digest('hex')
const identity = JSON.stringify({
region: process.env.AWS_REGION, imageHash, minConfidence,
features: ['GENERAL_LABELS'], maxLabels: 5,
})
const cacheKey = `image_labels:v2:${createHash('sha256').update(identity).digest('hex')}`
const cached = await redisClient.get(cacheKey)
if (cached !== null) {
const record = JSON.parse(cached)
if (record.identity !== identity || !Array.isArray(record.labels) ||
!record.labels.every((name) => typeof name === 'string' && name.trim().length > 0)) {
throw new Error('Invalid image-label cache record.')
}
return record.labels
}
const labels = await detectImageLabels({ Bytes: imageBytes }, minConfidence)
await redisClient.set(cacheKey, JSON.stringify({ identity, labels }), { EX: 60 * 60 * 24 })
return labels
}
export { getCachedOrSuggestImageLabels, redisClient }
Enregistrez image-labels/review-cached.mjs :
import { client, reviewRecord } from './labels.mjs'
let redisClient
try {
const [imagePath, confidence = '75', ...extra] = process.argv.slice(2)
if (!imagePath || extra.length > 0 || !process.env.AWS_REGION || !process.env.REDIS_URL) {
throw new Error('Supply AWS_REGION, REDIS_URL, and an image path, with optional confidence.')
}
const cache = await import('./cache.mjs')
redisClient = cache.redisClient
await redisClient.connect()
const minConfidence = Number(confidence)
const labels = await cache.getCachedOrSuggestImageLabels(imagePath, minConfidence)
console.log(JSON.stringify(reviewRecord(imagePath, labels, minConfidence)))
} catch {
console.error('Unable to retrieve cached image label suggestions. Check Redis and the image prerequisites.')
process.exitCode = 1
} finally {
if (redisClient?.isOpen) await redisClient.close()
client.destroy()
}
Fournissez votre URL Redis et exécutez la même commande deux fois. La seconde exécution devrait lire le résultat stocké sans nouvelle analyse Rekognition. Le TTL de 24 heures limite la réutilisation, mais ne fige pas la version du modèle d’AWS. Incrémentez l’espace de noms du cache lorsque vous modifiez la projection ou les paramètres fixes. Cet exemple échoue si Redis est indisponible ; il ne bascule pas silencieusement vers une requête facturée sans cache.
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE REDIS_URL=redis://127.0.0.1:6379 node image-labels/review-cached.mjs './example.jpg'
Traiter par lots
Pour une petite liste, traitez un fichier à la fois et conservez le résultat de chaque fichier.
L’échec d’un élément n’empêche pas l’analyse des fichiers suivants, mais entraîne un code de
sortie non nul pour l’ensemble de la commande. Ce programme n’utilise pas de cache et envoie
une analyse par image lisible. Enregistrez image-labels/review-batch.mjs :
import { client, reviewRecord, suggestImageLabels } from './labels.mjs'
async function batchProcessImages(imagePaths) {
const results = []
for (const imagePath of imagePaths) {
try {
const labels = await suggestImageLabels(imagePath)
results.push(reviewRecord(imagePath, labels))
} catch {
results.push({ source: imagePath, reviewRequired: true, status: 'failed' })
}
}
return results
}
try {
const imagePaths = process.argv.slice(2)
if (imagePaths.length === 0 || !process.env.AWS_REGION) {
throw new Error('Supply AWS_REGION and at least one image path.')
}
const results = await batchProcessImages(imagePaths)
console.log(JSON.stringify(results))
if (results.some((result) => result.status === 'failed')) process.exitCode = 1
} catch {
console.error('Unable to run the image-label batch. Check the region and arguments.')
process.exitCode = 1
} finally {
client.destroy()
}
Examinez le champ source de chaque enregistrement avant d’utiliser
ses suggestions. L’enregistrement en échec ne contient aucune donnée d’étiquette :
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review-batch.mjs './image1.jpg' './image2.jpg' './image3.jpg'
Ajuster le seuil de confiance
Transmettez un seuil compris entre 0 et 100 comme dernier argument d’une commande portant sur une seule image. Par exemple, demandez un niveau de confiance minimal de 90 % :
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review.mjs './example.jpg' 90
Un seuil plus élevé peut supprimer des étiquettes utiles aussi bien que des étiquettes incertaines ; même une prédiction avec un niveau de confiance élevé peut être erronée. La fonction auxiliaire de cache inclut cette valeur dans son identité ; une requête à 90 % ne peut donc pas réutiliser un résultat demandé à 75 %.
Dernières réflexions
Utilisez les suggestions comme pistes pour un rédacteur qui peut voir l’image et le contenu qui l’entoure. La confiance décrit la prédiction du modèle, pas la qualité de l’accessibilité. Si votre application a également besoin d’autres opérations d’analyse d’images, découvrez notre 🤖 service d’intelligence artificielle.
