Redimensione uma pasta de imagens com Pillow mantendo os originais
Redimensione várias imagens para uma nova pasta sem distorcê-las nem substituir os arquivos de origem. Um pequeno conjunto de imagens sintéticas facilita o cálculo manual das dimensões esperadas de saída.
Conteúdo verificado 2026.09.20Inclui arquivos de exemplo
Ver o sumário
A tradução foi feita com IA. Confira o código, as unidades e os valores junto com o original. A revisão por falantes nativos de cada idioma ainda não foi concluída. English
Para quem éEste guia é voltado a pessoas que precisam de cópias menores de muitas imagens, preservando as proporções de cada imagem e mantendo os originais intactos.
Preparação
Python 3.12 e um comando de terminal que inicie essa versão.
Pillow instalado com python -m pip install Pillow.
Uma pasta de trabalho em que os scripts possam criar novas pastas dentro de outputs.
Espaço em disco suficiente para as imagens sintéticas originais e as cópias redimensionadas.
01Defina primeiro a regra de redimensionamento
O objetivo é ajustar cada imagem dentro de uma caixa de 300 by 300 pixel preservando sua proporção. Imagens que já são menores do que essa caixa não são ampliadas. As imagens de origem permanecem inalteradas, e cada cópia redimensionada é gravada em uma pasta de saída separada.
Para uma imagem com largura W e altura H, calcule scale como o menor valor entre 300/W, 300/H e 1. Depois, calcule a nova largura e altura a partir desse único scale factor. Usar um único fator para ambas as dimensões evita distorção.
Os arquivos a seguir são sintéticos e foram criados especificamente para este artigo. O script de preparação cria quatro imagens PNG simples com dimensões conhecidas. O conteúdo visual não é importante; as dimensões foram escolhidas para que todos os resultados esperados do redimensionamento possam ser verificados manualmente.
Os quatro originais contêm 1,240,000 pixels no total: 480,000 + 480,000 + 20,000 + 360,000. A contagem de pixels é útil para compreender o exemplo, mas não equivale ao tamanho do arquivo em bytes porque a compactação PNG depende do conteúdo da imagem.
03Calcule manualmente as quatro dimensões esperadas
Para landscape.png, a dimensão limitante é a largura: 300/800 = 0.375. Multiplicar ambas as dimensões por 0.375 resulta em 300 by 225. Para portrait.png, a altura é o limite: 300/800 = 0.375, produzindo 225 by 300.
small.png já cabe porque ambas as dimensões são iguais ou inferiores a 300. O scale é limitado a 1, portanto permanece 200 by 100. Para wide.png, a largura limita o resultado: 300/1200 = 0.25, resultando em 300 by 75.
Arquivo
Scale
Saída esperada
Pixels de saída
landscape.png
0.375
300 × 225
67,500
portrait.png
0.375
225 × 300
67,500
small.png
1
200 × 100
20,000
wide.png
0.25
300 × 75
22,500
Portanto, as cópias redimensionadas contêm 177,500 pixels no total. Os originais continuam presentes separadamente, então esse número não representa economia de espaço em disco.
04Redimensione a pasta para um novo destino
Salve o script a seguir como batch_image_resize.py. Ele processa arquivos PNG, JPEG e WebP diretamente dentro de SOURCE. Não pesquisa subpastas recursivamente. O destino é criado com mkdir sem exist_ok, portanto um diretório de saída já existente faz o script parar em vez de misturar novos resultados com uma execução anterior.
python
from pathlib import Path
from PIL import Image, ImageOps
SOURCE = Path("outputs") / "image_resize_demo"
OUTPUT_DIR = Path("outputs") / "image_resize_result"
MAX_WIDTH = 300
MAX_HEIGHT = 300
SUPPORTED = {".png", ".jpg", ".jpeg", ".webp"}
def target_size(width: int, height: int) -> tuple[int, int]:
scale = min(MAX_WIDTH / width, MAX_HEIGHT / height, 1)
new_width = max(1, int(width * scale))
new_height = max(1, int(height * scale))
return new_width, new_height
def main() -> None:
if not SOURCE.is_dir():
raise FileNotFoundError(f"Source folder not found: {SOURCE}")
if MAX_WIDTH < 1 or MAX_HEIGHT < 1:
raise ValueError("Maximum dimensions must be positive integers.")
source_resolved = SOURCE.resolve()
output_resolved = OUTPUT_DIR.resolve()
if source_resolved == output_resolved:
raise ValueError("SOURCE and OUTPUT_DIR must be different.")
OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir() # Refuse to reuse an existing destination.
candidates = sorted(
path for path in SOURCE.iterdir()
if path.is_file() and path.suffix.lower() in SUPPORTED
)
resized_count = 0
unchanged_size_count = 0
for source_path in candidates:
output_path = OUTPUT_DIR / source_path.name
if output_path.exists():
raise FileExistsError(f"Output already exists: {output_path}")
with Image.open(source_path) as opened:
image = ImageOps.exif_transpose(opened)
image.load()
old_size = image.size
new_size = target_size(*old_size)
if new_size == old_size:
result = image.copy()
unchanged_size_count += 1
else:
result = image.resize(new_size, Image.Resampling.LANCZOS)
resized_count += 1
save_options = {}
if source_path.suffix.lower() in {".jpg", ".jpeg"}:
save_options["quality"] = 90
result.save(output_path, **save_options)
result.close()
print(f"{source_path.name}: {old_size[0]}x{old_size[1]} -> {new_size[0]}x{new_size[1]}")
print(f"Processed {len(candidates)} images.")
print(f"Resized: {resized_count}; already within limit: {unchanged_size_count}.")
print(f"Output folder: {OUTPUT_DIR.as_posix()}")
if __name__ == "__main__":
main()
ImageOps.exif_transpose aplica a orientação EXIF da imagem antes do cálculo de redimensionamento quando esses dados de orientação existem. Os arquivos PNG sintéticos não têm essa necessidade, portanto suas dimensões exibidas continuam sendo as listadas acima.
05Compare a saída esperada
Como os nomes dos arquivos são processados em ordem, as linhas esperadas do console são landscape.png, portrait.png, small.png e wide.png. Três imagens ficam menores e uma permanece com suas dimensões originais.
Esta saída esperada foi derivada manualmente, e não capturada de uma execução. As verificações importantes são as dimensões, o número de arquivos e o fato de que os originais permanecem em outputs/image_resize_demo.
Verificação
Valor esperado
Arquivos na pasta de origem
4
Arquivos na pasta de saída
4
Arquivos redimensionados
3
Arquivos que já estavam dentro do limite
1
Maior largura de saída
300
Maior altura de saída
300
06Verifique você mesmo as imagens geradas
Não dependa apenas dos nomes dos arquivos ou do texto do console. Abra os arquivos resultantes e verifique suas dimensões. O verificador opcional abaixo lê as duas pastas e informa as dimensões. Ele não realiza gravações.
python
from pathlib import Path
from PIL import Image
SOURCE = Path("outputs") / "image_resize_demo"
OUTPUT_DIR = Path("outputs") / "image_resize_result"
for source_path in sorted(SOURCE.glob("*.png")):
output_path = OUTPUT_DIR / source_path.name
with Image.open(source_path) as original, Image.open(output_path) as resized:
print(
f"{source_path.name}: "
f"original={original.size}, output={resized.size}"
)
Confirme que toda largura de saída seja no máximo 300 e toda altura de saída seja no máximo 300.
Confirme que landscape.png permaneça 4:3, portrait.png permaneça 3:4 e wide.png permaneça 4:1.
Confirme que small.png permaneça 200 by 100 em vez de ser ampliada para 300 by 150.
Compare horários de modificação ou hashes se precisar de evidência adicional de que os próprios arquivos de origem não foram regravados.
Execute o script de redimensionamento uma segunda vez sem alterar OUTPUT_DIR. Ele deve parar com FileExistsError antes de processar outro lote.
07Reconheça erros comuns
Sintoma
O que verificar
ModuleNotFoundError: No module named PIL
Instale Pillow no mesmo ambiente Python usado para executar o script com python -m pip install Pillow.
FileNotFoundError
Verifique SOURCE e o diretório de trabalho atual do terminal. Neste exemplo, execute primeiro o script de preparação sintético.
FileExistsError
A pasta de saída já existe. Revise-a e selecione um novo destino em vez de permitir uma sobrescrita automática.
Image cannot be identified
A extensão pode parecer suportada enquanto o conteúdo está danificado ou não é realmente uma imagem. Trate o lote com falha como incompleto.
Unexpected rotation
Os metadados de orientação e a orientação dos pixels podem diferir. ImageOps.exif_transpose é usado antes do redimensionamento, mas inspecione visualmente imagens importantes.
Unexpected output file size in bytes
As dimensões em pixels, por si só, não determinam o tamanho do arquivo compactado. Formato, conteúdo da imagem, metadados e qualidade JPEG também influenciam.
Se o processamento falhar depois que alguns arquivos já tiverem sido gravados, o script não exclui esses resultados parciais. Mantenha o destino para investigação ou use um novo diretório de saída depois de corrigir o problema.
08Entenda os limites antes de usar imagens reais
Este exemplo trata apenas de arquivos diretamente dentro de uma pasta e suporta extensões PNG, JPEG e WebP. Ele não reproduz uma árvore de diretórios aninhada, não redimensiona individualmente frames animados, não preserva todas as formas de metadados nem garante que as informações de gerenciamento de cores permaneçam inalteradas.
A reamostragem altera os dados de pixels. Mesmo quando uma imagem mantém as mesmas dimensões, abri-la e salvá-la pode alterar os bytes codificados ou os metadados. A saída JPEG é com perdas e este script salva cópias JPEG com quality 90; isso não preserva o bitstream JPEG original nem as configurações originais de compactação.
O cálculo com int arredonda dimensões fracionárias para baixo. Para tamanhos de origem arbitrários, a proporção de pixels resultante pode, portanto, diferir ligeiramente da proporção matemática porque as dimensões das imagens precisam ser números inteiros. As dimensões sintéticas deste guia se dividem exatamente, portanto os quatro resultados esperados não exigem arredondamento ambíguo.
Foram verificadas manualmente as quatro dimensões sintéticas de origem: 800x600, 600x800, 200x100 e 1200x300.
Foram calculados manualmente os scale factors 0.375, 0.375, 1 e 0.25.
Foram derivadas manualmente as dimensões esperadas de saída 300x225, 225x300, 200x100 e 300x75.
Foi calculado manualmente o total de pixels de origem como 1,240,000 e o total de pixels de saída como 177,500.
O código foi inspecionado para confirmar que os arquivos de origem são abertos para leitura, os resultados vão para uma pasta separada e uma pasta de destino já existente faz a execução parar.
A saída esperada do console e as contagens foram derivadas manualmente.
Limites da verificação
O código não foi executado pelo autor desta resposta; o comportamento do Pillow e as gravações no sistema de arquivos não foram testados aqui.
JPEG, WebP, orientação EXIF, arquivos corrompidos, perfis de cores, animação e preservação de metadados foram discutidos, mas não testados.
Nenhum tamanho de arquivo compactado em bytes foi previsto porque depende do conteúdo da imagem e da codificação.
As URLs da documentação oficial foram fornecidas com base em locais de documentação conhecidos, mas não foram verificadas ao vivo.
Percorra subpastas e registre caminhos, extensões, tamanhos e datas de modificação dos arquivos em uma tabela. Comece com 4 pequenos arquivos de exemplo, mantendo os originais e os resultados existentes intactos.
Combine em ordem arquivos CSV com a mesma estrutura de colunas e adicione uma coluna source_file. Use conjuntos de dados pequenos para verificar nomes de itens com vírgulas, colunas ausentes e saídas já existentes.