Como funciona a compressão de imagens
Imagens costumam ser a maior parte do peso de uma página web. Uma foto tirada pelo celular facilmente passa de 4MB — e servir isso direto pro usuário é um desperdício enorme de banda, memória e tempo de carregamento.
Compressão de imagem é o processo de reduzir esse tamanho sem (ou com o mínimo de) perda de qualidade perceptível. E existem várias técnicas para isso, que geralmente se combinam.
Lossy vs Lossless
Antes de tudo, vale entender a diferença fundamental:
- Lossless (sem perda): o arquivo fica menor, mas a imagem pode ser reconstruída pixel a pixel com 100% de fidelidade. Útil para logos, ícones, screenshots.
- Lossy (com perda): descarta informações que o olho humano dificilmente percebe. Resulta em arquivos muito menores, mas a perda é irreversível. Útil para fotos e imagens com gradientes.
Na prática, a maioria das imagens na web se beneficia de compressão lossy — o ganho de tamanho é significativo e a perda de qualidade é imperceptível para o usuário.
Técnica 1: Redimensionamento
A técnica mais simples e muitas vezes mais impactante. Uma imagem de 4000×3000px em uma tela que exibe no máximo 800px de largura está carregando ~25x mais pixels do que o necessário.
# Python / Pillow
nova_largura = img.width // 3
nova_altura = img.height // 3
img.resize((nova_largura, nova_altura), Image.Resampling.LANCZOS)
O filtro LANCZOS (também chamado de Sinc) é o mais recomendado para redução de tamanho — ele é mais lento, mas preserva melhor a nitidez e evita artefatos.
Técnica 2: Formato
O formato da imagem tem um impacto enorme no tamanho final — às vezes maior do que o nível de qualidade.
| Formato | Tipo | Quando usar |
|---|---|---|
| JPEG | Lossy | Fotos, imagens com gradientes |
| PNG | Lossless | Imagens com transparência, logos |
| WebP | Lossy + Lossless | Substituto moderno para JPEG e PNG |
| AVIF | Lossy + Lossless | Melhor compressão disponível, menor suporte |
| GIF | Lossless (256 cores) | Animações simples |
WebP é o ponto ideal hoje: suportado por todos os browsers modernos, oferece ~25-35% menos tamanho que JPEG na mesma qualidade, e suporta transparência (ao contrário do JPEG).
AVIF é ainda melhor em compressão, mas o custo de encoding é alto — o tempo para gerar o arquivo é significativamente maior.
# Converter para WebP com Pillow
img.save('saida.webp', 'WEBP', quality=90, method=6)
Técnica 3: Qualidade (quality)
O parâmetro de qualidade controla o quanto de informação é descartada na compressão lossy. Geralmente vai de 0 a 100.
| Faixa | Resultado |
|---|---|
| 80–90 | Ponto ideal — boa qualidade, tamanho bem menor |
| 60–70 | Aceitável para thumbnails e previews |
| abaixo de 50 | Artefatos visíveis, casos muito específicos |
Não existe um valor universal — depende do conteúdo da imagem e do contexto de uso.
Técnica 4: Optimize flag
Independente do nível de qualidade, existe uma passagem extra de otimização lossless que muitas libs suportam. Ela reanalisa o arquivo gerado e tenta reduzir ainda mais sem perder informação.
img.save('saida.webp', 'WEBP', quality=90, optimize=True)
É mais lento, mas o resultado costuma ser alguns KBs menor sem nenhuma perda adicional de qualidade. Vale a pena ativar sempre que possível.
Técnica 5: Método de compressão
Algumas libs expõem o “método” ou “nível” de compressão — basicamente, quanto esforço computacional o encoder vai gastar tentando comprimir o arquivo.
# method vai de 0 (rápido, menos compressão) a 6 (lento, mais compressão)
img.save('saida.webp', 'WEBP', quality=90, method=6)
Método mais alto = arquivo menor, mas tempo de encoding maior. Em processamento offline ou em batch, vale usar o nível mais alto. Para compressão em tempo real no browser, níveis mais baixos são preferíveis.
Técnica 6: Modo de cor (RGBA → RGB)
Imagens podem ter diferentes modos de cor:
- RGB — 3 canais: vermelho, verde, azul
- RGBA — 4 canais: RGB + canal alpha (transparência)
JPEG não suporta transparência. Se você for salvar como JPEG sem converter primeiro, a lib vai retornar erro ou resultado inesperado. Remover o canal alpha quando ele não é necessário também reduz levemente o tamanho do arquivo.
if img.mode != 'RGB':
img = img.convert('RGB')
Técnica 7: Grayscale
Se a imagem não precisa de cor, converter para escala de cinza elimina 2 dos 3 canais, reduzindo o tamanho de forma significativa.
img_cinza = img.convert('L') # 'L' = luminance (grayscale)
Útil para fotos em preto e branco, documentos escaneados, e casos onde a cor não agrega informação.
Técnica 8: Compressão iterativa
Em vez de aplicar um nível de qualidade fixo, é possível testar múltiplos níveis e escolher o menor arquivo que ainda atinge um threshold de qualidade aceitável.
para quality em [90, 80, 70, 60, 50]:
gerar imagem com esse quality
se tamanho <= alvo:
usar esse resultado
parar
Esse é o modo “otimização máxima” do Tiny Image — ele testa vários níveis automaticamente e devolve o menor arquivo possível dentro do critério definido.
Combinando as técnicas
Na prática, a melhor compressão vem da combinação de várias dessas técnicas:
- Redimensionar para o tamanho máximo que vai ser exibido
- Converter para WebP (ou AVIF se o tempo de encoding não for problema)
- Ajustar qualidade para o contexto (80–90 para imagens exibidas, menos para thumbnails)
- Ativar optimize para a passagem lossless extra
- Usar method alto se o processamento for offline ou em batch
- Converter o modo de cor se necessário (RGBA → RGB para JPEG)
- Grayscale quando a cor for irrelevante
Uma imagem de 4MB pode chegar a menos de 100KB com esse conjunto de técnicas — sem que o usuário perceba qualquer diferença visual.