Redacta texto alternativo de imagen con AWS Rekognition y Node.js
Usa la operación DetectLabels de AWS Rekognition para sugerir objetos y escenas
que un editor puede considerar al redactar texto alternativo. Este tutorial muestra sugerencias
de etiquetas específicas de cada imagen con un indicador reviewRequired, y luego
incorpora almacenamiento en caché para archivos locales y un pequeño ejecutor secuencial por lotes.
¿Por qué automatizar el texto alternativo?
Las etiquetas pueden ayudarte a empezar una descripción, pero no pueden determinar el propósito
de una imagen en una página. Sigue el
árbol de decisión de W3C para texto alternativo para decidir
si debes describir la imagen, explicar su función como enlace o botón, o usar una alternativa
vacía para una imagen decorativa. No incluyas las sugerencias en el atributo
alt hasta que alguien haya revisado la imagen en su contexto.
Integración de AWS Rekognition y Node.js
DetectLabels
devuelve etiquetas y puntuaciones de confianza, en lugar de una descripción contextual. Las
etiquetas se devuelven en inglés y pueden incluir varios nombres para un objeto, como el objeto
específico y su categoría superior. Este ejemplo conserva hasta cinco nombres con un umbral de
confianza elegido. Para leer palabras dentro de una imagen, usa la operación independiente
DetectText de Rekognition.
Configura tu entorno
Necesitas Bash, Node.js 24 LTS, npm y un perfil de AWS autenticado
o un rol de IAM con permiso para llamar a rekognition:DetectLabels. Los ejemplos se probaron
con Node.js 24.21.0 y 26.8.1, y con @aws-sdk/client-rekognition 3.1148.0 de AWS SDK v3.
Estas son versiones probadas, no un mínimo intrínseco de la API; usa una versión de Node.js que
reciba mantenimiento. Enviar una imagen a AWS puede generar un cargo.
Usa el método de autenticación existente de tu organización y la
cadena predeterminada de proveedores de credenciales del SDK.
Para un perfil local, inicia sesión primero y establece AWS_PROFILE en los
comandos siguientes. Para un rol de IAM, omite esa variable. Las credenciales temporales del
entorno necesitan AWS_SESSION_TOKEN, además de la clave de acceso y la clave secreta;
no incluyas sus valores en archivos de código fuente ni en la salida de la terminal.
Ejecuta esto desde el directorio donde quieras crear un proyecto image-labels.
El subshell conserva tu directorio actual y mkdir se niega a sobrescribir
un proyecto existente. El destino explícito de npm también mantiene la instalación en el proyecto
nuevo cuando su directorio superior usa otro gestor de paquetes:
(
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
)
Guarda los archivos siguientes en image-labels. Si la instalación falla después
de crear el directorio, corrige el problema indicado y vuelve a ejecutar el comando
npm install desde ese directorio. Conserva los archivos existentes; volver a
ejecutar todo el bloque de creación rechaza ese directorio de forma intencional.
Requisitos de las imágenes
Usa imágenes JPEG o PNG. AWS documenta un máximo de 5 MB para bytes enviados directamente y
15 MB para un objeto de S3; DetectLabels requiere que cada dimensión esté entre
80 y 10.000 píxeles. Consulta las
cuotas de Rekognition para conocer los límites actuales.
El código envía los bytes originales sin cambiar el tamaño, decodificar ni modificar los perfiles
de color. Una solicitud exitosa no verifica que la imagen esté intacta ni que las etiquetas sean
precisas.
Implementa las sugerencias de etiquetas
Guarda esto como image-labels/labels.mjs. El SDK acepta directamente el búfer del archivo;
no lo conviertas manualmente a base64. Dejar credentials sin establecer conserva
la autenticación mediante perfiles y roles. Establecer maxAttempts: 1 desactiva
los reintentos automáticos, de modo que un análisis fallido siga siendo una invocación fallida.
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 }
Guarda el punto de entrada como 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()
}
Elige tu región y reemplaza MY_PROFILE y ./example.jpg.
Las rutas son relativas al directorio desde el que ejecutas Node.js; escribe entre comillas los
nombres de archivo que contengan espacios:
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review.mjs './example.jpg'
El JSON contiene el valor seleccionado de source, el umbral,
reviewRequired: true y labelSuggestions. Compara esos nombres con tu
imagen real. Una lista vacía indica un análisis exitoso sin etiquetas utilizables, por lo que
igualmente requiere revisión. Los fallos de archivo, credenciales y proveedor muestran un
diagnóstico en stderr, no generan ningún registro de éxito y terminan con un código distinto de
cero. Ningún campo contiene texto alternativo terminado.
Procesa imágenes de S3
Para una imagen existente en S3, la región del bucket debe coincidir con la región del cliente
de Rekognition, y la identidad que realiza la llamada necesita s3:GetObject
para el objeto. Consulta la
guía de imágenes de S3 y las
orientaciones sobre IAM de AWS.
Este ejemplo lee el objeto actual correspondiente a la clave y no selecciona una versión histórica.
Añade esto a image-labels/labels.mjs:
async function suggestImageLabelsFromS3(bucket, key, minConfidence = 75) {
return detectImageLabels({ S3Object: { Bucket: bucket, Name: key } }, minConfidence)
}
export { suggestImageLabelsFromS3 }
Guarda 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()
}
Reemplaza el bucket y la clave por los de tu propia imagen existente. Pasa la clave literalmente, incluidos los espacios o caracteres de porcentaje; no la codifiques para URL. Esta ruta de S3 no usa caché, por lo que analiza el objeto actual en cada invocación:
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review-s3.mjs my-bucket 'images/example.jpg'
Maneja casos límite y errores
Una solicitud fallida es distinta de una lista de etiquetas vacía. Las funciones auxiliares rechazan los fallos; los puntos de entrada los notifican sin exponer respuestas sin procesar de AWS ni credenciales. Para diagnosticar problemas, comprueba primero la ruta local y luego la autenticación y la región. Para S3, comprueba también la clave exacta y los permisos de lectura del objeto. Los errores de DetectLabels de AWS abarcan formatos no compatibles, parámetros no válidos, objetos de S3 inaccesibles y fallos del servicio o de las cuotas. Corrige la causa antes de volver a ejecutar el comando. Cada reintento puede dar lugar a otro análisis.
Amplía la funcionalidad
Implementa almacenamiento en caché
Para imágenes locales, almacena en caché los nombres obtenidos con éxito según el contenido y
los ajustes de la solicitud. Esta sección opcional presupone que ya tienes un servidor Redis en
funcionamiento y su URL de conexión. Se probó con Redis 7.4.11 y la versión actual del cliente
redis, 6.3.0. Instala el cliente en el proyecto existente:
test -f image-labels/package.json &&
npm install --prefix "$PWD/image-labels" --workspaces=false --ignore-scripts --save-exact redis@6.3.0
Guarda image-labels/cache.mjs. La identidad incluye la región, el hash del contenido,
el umbral de confianza y los ajustes fijos de las etiquetas. Lee y analiza el mismo búfer para
que un archivo modificado no pueda reutilizar etiquetas de una versión anterior. Almacena el
resultado solo después de una respuesta exitosa del proveedor; los registros de caché con
formato incorrecto o que no coincidan provocan un fallo visible en lugar de tratarse como etiquetas:
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 }
Guarda 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()
}
Proporciona tu URL de Redis y ejecuta el mismo comando dos veces. La segunda ejecución debería leer el resultado almacenado sin realizar otro análisis de Rekognition. El TTL de 24 horas limita la reutilización, pero no fija la versión del modelo de AWS. Incrementa el espacio de nombres de la caché al cambiar la proyección o los ajustes fijos. Este ejemplo falla si Redis no está disponible; no pasa silenciosamente a una solicitud facturable sin caché.
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'
Procesamiento por lotes
Para una lista pequeña, procesa un archivo a la vez y conserva el resultado de cada uno.
El fallo de un archivo no impide que se analicen los siguientes, pero hace que el comando completo
termine con un código distinto de cero. Este ejecutor no usa caché y envía un análisis por cada
imagen que se pueda leer. Guarda 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()
}
Examina source de cada registro antes de usar sus sugerencias. El registro
fallido no contiene datos de etiquetas:
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review-batch.mjs './image1.jpg' './image2.jpg' './image3.jpg'
Ajuste del umbral de confianza
Pasa un umbral entre 0 y 100 como último argumento de un comando para una sola imagen. Por ejemplo, solicita una confianza mínima del 90 %:
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review.mjs './example.jpg' 90
Un umbral más alto puede eliminar tanto etiquetas útiles como inciertas; incluso una predicción con alta confianza puede ser incorrecta. La función auxiliar de caché incluye este valor en su identidad, por lo que una solicitud al 90 % no puede reutilizar un resultado solicitado al 75 %.
Reflexiones finales
Usa las sugerencias como punto de partida para un editor que pueda ver la imagen y el contenido que la rodea. La confianza describe la predicción del modelo, no la calidad de la accesibilidad. Si tu aplicación también necesita otras operaciones de análisis de imágenes, explora nuestro 🤖 servicio de inteligencia artificial.
