Remoção de Fundo Transparente para GIF / Animação
Informações a Coletar
- Arquivo fonte: GIF, sequência PNG ou vídeo. Vídeos passam por extração de quadros via ffmpeg (já incluso no script).
- Cor de fundo onde o usuário visualiza: O usuário vê a imagem sobre fundo escuro ou claro? Cores de fundo diferentes revelam resíduos diferentes. Se não souber, execute a verificação completa com todas as cores de fundo (já inclusa). Não pule esta etapa.
- O objeto possui partes claras próximas à cor de fundo (luvas, botas, bordas brancas)? Esta é a limitação técnica – veja "Teste de Solvabilidade" abaixo.
Teste de Solvabilidade (Pular esta etapa pode custar dezenas de minutos)
Remoção de fundo é um problema de recuperação de informação, não de ajuste de parâmetros. Quando partes claras do objeto são cromaticamente próximas ao fundo, o pós-processamento não consegue separá-las – critérios de croma, suavidade, mediana temporal e ajuste de superfície de fundo sempre prejudicam o objeto.
Não confie na estimativa visual da cor. Execute:
.venv-bg\Scripts\python scripts\cutout.py --input <arquivo_fonte> --work work --stage solvability
Ele separa o fundo (câmera parada + objeto em movimento) do objeto e gera três números:
- Cor mediana do fundo / Matiz mais claro do objeto / Diferença entre eles
- Percentual de pixels do objeto que caem dentro de ±25/255 da cor de fundo – este é o mais explicativo.
Testado com material de robô: fundo (212,207,210), objeto mais claro (250,251,250), diferença 40,7, mas 32,6% dos pixels do objeto estão dentro de 25 da cor de fundo → exibe AMBIGUOUS.
Ao ver AMBIGUOUS, informe o usuário imediatamente:
As partes claras deste material estão muito próximas da cor de fundo. A remoção pura não consegue atingir limpeza em nível de pixel – no máximo, resíduos muito finos visíveis apenas com ampliação. Se tiver o projeto fonte, renderize novamente com fundo verde/rosa puro de alta saturação para obter resultado sem resíduos. Com apenas o arquivo final, consigo chegar próximo da limpeza.
Pergunte se deseja continuar. Não rode 20 minutos sem aviso.
Nota: Este teste é um sinal de alerta, não uma sentença. O resultado final depende da quantificação de resíduos após o processamento – "separável" pode gerar resíduos devido a bordas imprecisas do modelo; "não separável" pode, na prática, ficar muito limpo.
Procedimento
-
Criar ambiente de trabalho e instalar dependências
python -m venv .venv-bg && .venv-bg\Scripts\python -m pip install rembg onnxruntime pillow scipy(scipy vem da cadeia de dependências do rembg; é usado para filtragem guiada – veja abaixo) -
Executar pipeline
.venv-bg\Scripts\python scripts\cutout.py --input <arquivo_fonte> --outdir out --scale 2--inputaceita arquivos de vídeo, GIF/PNG ou um diretório de quadros PNG já extraídos.--scale 2não é opcional: as fendas entre membros têm apenas 5–15 px e o modelo de segmentação em 720p não as enxerga. Se alguns quadros persistirem, use--stage matteapenas para aqueles quadros com--scale 3.Em CPU puro, para 70 quadros em 720p com escala 2×, leva cerca de 20 minutos (~17,6 s/quadro, onnxruntime sem GPU). Informe o usuário com antecedência para não parecer travado.
-
Verificação (veja seção abaixo): se falhar, localize o estágio e execute novamente apenas ele – não reprocesse tudo.
-
Entrega: forneça os três formatos GIF + WebP + APNG, explicando o cenário de uso de cada um.
Regras Rígidas (Violações custaram caro)
- Use apenas abertura (opening) para topologia, nunca fechamento (closing).
binary_closingpreencheria fendas de ≤2 px, soldando braços ao tronco de forma irreversível. Esta regra gerou mais retrabalho que todos os outros problemas juntos. - Limiar de preenchimento de buracos pequeno (ordem de 60 px, não centenas). Fendas reais têm milhares de pixels; buracos de agulha, dezenas.
- Raio da filtragem guiada ≤ 2 e sem mediana por padrão. Mediana 3×3 e filtragem guiada com raio 4 apagam fendas de alguns pixels.
- Não comprimir alpha de forma não linear. Já houve mapeamentos como
clip((clip((a-0.04)/0.92)-0.06)/0.88)que, na prática, elevam alpha em torno de 0,7, tornando os resíduos mais sólidos. - GIF sem dither ordenado (Bayer). Em quadro estático, a borda parece mais suave, mas o ponto de tela fixo na coordenada da tela causa "fervura" ao se mover – pior no animado. Use threshold duro limpo para GIF; alpha suave fica para WebP / APNG.
- Modelo: BiRefNet. isnet / u2net podem interpretar reflexos especulares do chão como parte do objeto;
birefnet-general-litezera alpha em solas de botas. - Não "limpar" resíduos apagando cor de fundo. Limpar grandes áreas com superfície de fundo / critério cromático apagará também partes claras do objeto. A solução correta é aumentar a resolução do matte, não alterar limiares.
Contrato de Saída
out/<nome>.gif– transparência indexada 255,transparency=255,disposal=2, borda dura 1-bit. Limpeza topológica (remoção de pontos) aplicada apenas ao GIF.out/<nome>.webp– alpha contínuo preservado, sem limpeza topológica, portanto pode reter uma fina borda suave de cor quente. Use prioritariamente sobre fundos complexos – esta diferença é intencional, não é bug.out/<nome>.png– APNG, melhor compatibilidade com navegadores.- Dimensões, número de quadros, duração por quadro e configuração de loop do arquivo original são mantidos; liste esses valores medidos na resposta (para WebP animado, PIL não lê duração por quadro – neste caso, informe "com base no GIF medido", não invente).
- Entregue os arquivos com tag
<media>e explique os cenários de uso de GIF vs. WebP.
Tratamento de Falhas
- GIF exibido com fundo cinza no navegador: não altere o arquivo imediatamente. Verifique o bit ALPHA do VP8X e a cor de fundo do ANIM (
verify.pyexibe). Se ambos estiverem normais, é o visualizador que está colocando fundo na área transparente. Severify.pyindicarANMF ... blend=0, é um problema real –cutout.pyexporta comblend=1; seblend=0, o arquivo não foi gerado por este script ou passou por ferramenta externa. - Objeto cortado: quase sempre devido a critério de fundo muito amplo. Volte ao nível
final/e reexporte; não ajuste parâmetros da lógica de remoção. - Fendas não abertas em alguns quadros:
--stage matte --scale 3apenas nesses quadros – cada quadro leva cerca de 20 segundos. - Download lento do modelo rembg: rembg 2.x armazena modelos em
~/.rembg/models/<model>/<model>.onnx(não no antigo~/.u2net).birefnet-general-litetem ~214 MB. A primeira execução requer download.
Verificação (Não Pular)
Assista quadro a quadro sobre a cor de fundo real do usuário, caso contrário a verificação é inútil:
.venv-bg\Scripts\python scripts\verify.py out\<nome>.gif --backdrop grey --ref work\final
O script gera: contagem de pontos soltos, proporção de opacidade por quadro (para detectar cintilação), linha menos opaca, erro de quantização de cor do GIF em relação ao matte de referência, flags do contêiner WebP, além de uma imagem comparativa em quatro cores de fundo + tabela de contato de todos os quadros + ampliação de região especificada.
Critérios de aprovação:
| Indicador | Aprovado se |
|---|---|
| Pontos soltos | 0 |
| Variação de opacidade entre quadros | < 0,05 |
| Linha menos opaca | Consistente entre quadros (resíduo de reflexo a faz variar ou tocar o fundo) |
| Erro médio de quantização de cor | < 3/255 (só este número detecta perda de matiz em miniatura) |
| Comparação em 4 cores de fundo | Cada cor de fundo aparece, sem um cinza claro uniforme |
| Tabela de contato | Sem resíduos, sem ruído, fendas entre membros transparentes |
Inspecionar apenas um quadro ou apenas sobre fundo preto deixará passar problemas – resíduos de cores quentes só aparecem sobre fundo cinza escuro.
Notas para Windows (win32)
- Use
pyoupythonno terminal –python3não existe no Windows. Não use opythondo PATH (pode apontar para um hermes venv sem pip); prefira o.venv-bg\Scripts\python.exedo ambiente virtual. - Para caminhos com caracteres chineses, use as ferramentas
Write/Readpara escrever arquivos de script; não useSet-Contentpara montar conteúdo. - O
ImageFilter.GaussianBlurdo PIL não suporta o modo"F"e float RGB; para desfocar imagens float, usescipy.ndimage.gaussian_filter(o scipy já está instalado pelo rembg). Image.fromarray(float32_array, "RGB")falha diretamente – converta primeiro para.astype(np.float32)e processe por canal.- Não use
Remove-Itempara limpeza;cutout.py --cleanjá encapsula a limpeza de diretórios.
Exemplos
Entrada: "Remova o fundo deste GIF deixando transparente"
→ cutout.py --input acao.gif --outdir out --scale 2, então verify.py,
→ Entrega GIF/WebP/APNG, listando 720×720 / 70 quadros / 100ms por quadro / total 7000ms.
Entrada: "Limpe melhor, ainda tem ruído nas bordas e fendas entre braço e corpo não abertas"
→ Não ajuste limiares. Primeiro execute verify.py --zoom <coordenadas> para confirmar a localização do defeito,
→ Se muitos pontos soltos, adicione --median 3; se fendas não abertas, --stage matte --scale 3 apenas para os quadros afetados,
→ Verifique novamente. Na resposta, especifique qual tipo de defeito e qual correção foi aplicada – não diga apenas "otimizado".