Rascunhar texto alternativo de imagens: AWS Rekognition e Node.js
Use a operação DetectLabels do AWS Rekognition para sugerir objetos e cenas que um editor
possa considerar ao escrever texto alternativo. Este tutorial imprime sugestões de rótulos
específicas para cada imagem com um sinalizador reviewRequired, depois adiciona cache
para arquivos locais e um pequeno executor sequencial de processamento em lote.
Por que automatizar o texto alternativo?
Os rótulos podem ajudar você a começar uma descrição, mas não conseguem determinar a finalidade de
uma imagem na página. Siga a árvore de decisão de texto alternativo do W3C
para decidir se deve descrever a imagem, explicar sua função como link ou botão ou usar uma
alternativa vazia para uma imagem decorativa. Mantenha as sugestões fora do atributo
alt até que alguém revise a imagem em seu contexto.
Integração entre AWS Rekognition e Node.js
DetectLabels
retorna rótulos e pontuações de confiança, em vez de uma legenda contextual. Os rótulos são
retornados em inglês e podem incluir vários nomes para um mesmo objeto, como o objeto específico e
sua categoria superior. Este exemplo mantém até cinco nomes com um limiar de confiança escolhido.
Para ler palavras dentro de uma imagem, use a operação separada DetectText do
Rekognition.
Configurar o ambiente
Você precisa de Bash, Node.js 24 LTS, npm e um perfil AWS autenticado
ou uma função IAM com permissão para chamar rekognition:DetectLabels. Os exemplos foram
testados com Node.js 24.21.0 e 26.8.1 e com @aws-sdk/client-rekognition 3.1148.0 do AWS SDK v3.
Essas versões foram testadas, mas não representam um requisito mínimo intrínseco da API; use uma
versão do Node.js que ainda receba manutenção. Enviar uma imagem à AWS pode gerar uma cobrança.
Use o método de autenticação existente na sua organização e a
cadeia padrão de provedores de credenciais do SDK.
Para um perfil local, autentique-se primeiro e defina AWS_PROFILE nos comandos
abaixo. Para uma função IAM, omita essa variável. Credenciais temporárias em variáveis de ambiente
precisam de AWS_SESSION_TOKEN, além da chave de acesso e do segredo; mantenha seus
valores fora dos arquivos de código-fonte e da saída do terminal.
Execute o bloco a seguir no diretório onde deseja criar um projeto image-labels.
O subshell preserva seu diretório atual, e mkdir se recusa a sobrescrever um
projeto existente. O destino explícito do npm também mantém a instalação no novo projeto quando o
diretório pai usa outro gerenciador de pacotes:
(
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
)
Salve os arquivos abaixo em image-labels. Se a instalação falhar depois de criar o
diretório, corrija o problema informado e execute novamente o comando npm install
de dentro desse diretório. Mantenha os arquivos existentes; executar novamente todo o bloco de
criação faz com que ele recuse esse diretório, intencionalmente.
Requisitos das imagens
Use imagens JPEG ou PNG. A AWS documenta um máximo de 5 MB para bytes enviados diretamente e 15 MB
para um objeto S3; DetectLabels exige que cada dimensão esteja entre 80 e 10.000
pixels. Consulte as cotas do Rekognition para ver os limites atuais.
O código envia seus bytes originais sem redimensionar, decodificar ou alterar perfis de cor.
Uma solicitação bem-sucedida não verifica se a imagem está intacta nem se os rótulos são precisos.
Implementar sugestões de rótulos
Salve o código a seguir como image-labels/labels.mjs. O SDK aceita o buffer do arquivo
diretamente; não o codifique manualmente em base64. Deixar credentials sem
valor definido preserva a autenticação por perfil e função. Definir maxAttempts: 1
desativa as novas tentativas automáticas, para que uma análise com falha continue sendo uma
invocação com falha.
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 }
Salve o ponto 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()
}
Escolha sua região e substitua MY_PROFILE e ./example.jpg.
Os caminhos são relativos ao diretório em que você executa o Node.js; coloque entre aspas os nomes
de arquivos que contêm espaços:
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review.mjs './example.jpg'
O JSON contém o source selecionado, o limiar,
reviewRequired: true e labelSuggestions. Compare esses nomes com sua imagem
real. Uma lista vazia representa uma análise bem-sucedida sem rótulos utilizáveis, portanto ainda
precisa de revisão. Falhas relacionadas a arquivos, credenciais e provedores imprimem um
diagnóstico em stderr, não geram um registro de sucesso e encerram o processo com um código
diferente de zero. Nenhum campo contém texto alternativo pronto.
Processar imagens do S3
Para uma imagem existente no S3, a região do bucket deve corresponder à região do cliente
Rekognition, e a identidade que faz a chamada precisa de s3:GetObject para o
objeto. Consulte o guia de imagens do S3 e as
orientações sobre IAM da AWS.
Este exemplo lê o objeto atual na chave e não seleciona uma versão histórica.
Acrescente o código a seguir a image-labels/labels.mjs:
async function suggestImageLabelsFromS3(bucket, key, minConfidence = 75) {
return detectImageLabels({ S3Object: { Bucket: bucket, Name: key } }, minConfidence)
}
export { suggestImageLabelsFromS3 }
Salve 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()
}
Substitua o bucket e a chave pelos da sua própria imagem existente. Passe a chave literalmente, incluindo espaços ou caracteres de porcentagem; não aplique codificação de URL. Este fluxo de acesso ao S3 não usa cache, portanto analisa o objeto atual a cada invocação:
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review-s3.mjs my-bucket 'images/example.jpg'
Tratar casos especiais e erros
Uma solicitação com falha é diferente de uma lista vazia de rótulos. As funções auxiliares rejeitam as falhas; os pontos de entrada as informam sem expor respostas brutas da AWS ou credenciais. Para investigar problemas, verifique primeiro o caminho local, depois a autenticação e a região. Para o S3, verifique também a chave exata e as permissões de leitura do objeto. A documentação de erros de DetectLabels da AWS abrange formatos não compatíveis, parâmetros inválidos, objetos S3 inacessíveis e falhas de serviço ou de cota. Corrija a causa antes de executar o comando novamente. Cada nova tentativa é outra possível análise.
Ampliar a funcionalidade
Implementar cache
Para imagens locais, armazene em cache os nomes retornados com sucesso, usando o conteúdo das
imagens e as configurações da solicitação como chave. Esta seção opcional pressupõe um servidor
Redis já em execução e sua URL de conexão. Ela foi testada com Redis 7.4.11 e o cliente atual
redis, 6.3.0. Instale o cliente no projeto existente:
test -f image-labels/package.json &&
npm install --prefix "$PWD/image-labels" --workspaces=false --ignore-scripts --save-exact redis@6.3.0
Salve image-labels/cache.mjs. A identidade inclui a região, o hash do conteúdo, o limiar de
confiança e as configurações fixas de rótulos. Leia e analise o mesmo buffer para que um arquivo
alterado não possa reutilizar rótulos de uma versão anterior. Armazene os dados somente após uma
resposta bem-sucedida do provedor; registros de cache malformados ou incompatíveis causam falhas
visíveis, em vez de serem tratados como rótulos:
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 }
Salve 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()
}
Forneça sua URL do Redis e execute o mesmo comando duas vezes. A segunda execução deve ler o resultado armazenado sem outra análise do Rekognition. O TTL de 24 horas limita a reutilização, mas não fixa a versão do modelo da AWS. Incremente o namespace do cache ao alterar a projeção ou as configurações fixas. Este exemplo falha se o Redis estiver indisponível; ele não muda silenciosamente para uma solicitação cobrada sem 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'
Processamento em lote
Para uma lista pequena, processe um arquivo por vez e mantenha o resultado de cada arquivo.
Uma falha em um item não impede a análise dos arquivos seguintes, mas faz o comando como um todo
encerrar com um código diferente de zero. Este executor não usa cache e envia uma análise por
imagem legível. Salve 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()
}
Inspecione o source de cada registro antes de usar suas sugestões.
O registro com falha não contém dados de rótulos:
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review-batch.mjs './image1.jpg' './image2.jpg' './image3.jpg'
Ajuste do limiar de confiança
Passe um limiar entre 0 e 100 como último argumento de um comando para uma única imagem. Por exemplo, solicite uma confiança mínima de 90%:
AWS_REGION=us-east-1 AWS_PROFILE=MY_PROFILE node image-labels/review.mjs './example.jpg' 90
Um limiar mais alto pode remover tanto rótulos úteis quanto incertos; mesmo uma previsão com alta confiança pode estar errada. A função auxiliar de cache inclui esse valor na identidade, portanto uma solicitação com 90% não pode reutilizar um resultado solicitado com 75%.
Considerações finais
Use as sugestões como ponto de partida para um editor que possa ver a imagem e o conteúdo ao redor dela. A confiança descreve a previsão do modelo, não a qualidade da acessibilidade. Se sua aplicação também precisar de outras operações de análise de imagens, explore nosso 🤖 serviço de inteligência artificial.
