Eliminación de fondo transparente para GIF / animaciones
Entradas a recopilar
- Archivo fuente: GIF, secuencia PNG o video. Los videos se extraen cuadro por cuadro con ffmpeg (el script ya lo tiene integrado).
- Color de fondo de visualización: ¿Sobre qué fondo está viendo el usuario esta imagen? Un fondo oscuro o una página blanca pueden revelar residuos completamente diferentes. Si no se sabe, por defecto realizar la verificación completa de colores de fondo (el script ya lo tiene integrado), no omitir.
- ¿El sujeto tiene partes de color claro cercanas al color de fondo? (guantes blancos, botas blancas, bordes blancos). Esto es un límite de capacidad, ver más abajo "Realizar evaluación de solubilidad primero".
Realizar evaluación de solubilidad primero (saltarse esto puede resultar en horas de trabajo en vano)
La eliminación de fondo es un problema de recuperación de información, no de ajuste de algoritmos. Cuando las partes claras del sujeto tienen un color cercano al fondo, el posprocesamiento no puede separarlas: la cromaticidad, suavidad, mediana temporal y ajuste de superficie de fondo dañarán al sujeto.
No confíes en la estimación visual del color; ejecuta:
.venv-bg\Scripts\python scripts\cutout.py --input <archivo_fuente> --work work --stage solvability
Esto separa el fondo del sujeto usando "cámara estática + sujeto en movimiento" y da tres valores:
- Color mediano del fondo / tono más brillante del sujeto / diferencia entre ambos
- Porcentaje de píxeles del sujeto dentro de ±25/255 del color de fondo: este número es el más interpretable
En pruebas con este material robótico: fondo (212,207,210), sujeto más brillante (250,251,250), diferencia 40.7, pero el 32.6% de los píxeles del sujeto cae dentro de 25 del color de fondo → imprime AMBIGUOUS.
Si ves AMBIGUOUS, debes informar primero al usuario:
Este material tiene partes claras muy cercanas al color de fondo; la simple eliminación de fondo no puede lograr una limpieza a nivel de píxel, a lo sumo se obtendrá un residuo muy fino al ampliar. Si tienes el archivo fuente original, vuelve a renderizar con un fondo de croma verde/magenta saturado y podré lograr una limpieza total; si solo tienes el archivo final, solo puedo aproximarme a la limpieza.
Luego preguntar al usuario si desea continuar, no ejecutar en silencio durante 20 minutos.
Nota: Esta verificación es una señal de advertencia, no una sentencia. La decisión final se basa en la cuantificación de residuos después de la ejecución completa: "separables" también pueden tener residuos debido a bordes imprecisos del modelo, y "no separables" pueden quedar muy limpios.
Procedimiento
-
Crear el espacio de trabajo e instalar el entorno
python -m venv .venv-bg && .venv-bg\Scripts\python -m pip install rembg onnxruntime pillow scipy(scipy proviene de la cadena de dependencias de rembg, se usa para filtrado guiado — ver más abajo) -
Ejecutar el pipeline
.venv-bg\Scripts\python scripts\cutout.py --input <archivo_fuente> --outdir out --scale 2--inputacepta archivos de video, archivos GIF/PNG, o un directorio de secuencia PNG ya extraída.--scale 2no es opcional: los espacios entre extremidades y torso son de solo 5–15 px, y el modelo de segmentación no los ve en 720p. Si algún cuadro persiste, usa--stage mattepara reejecutar solo esos cuadros,--scale 3.En CPU puro, 70 cuadros a 720p con 2× toma aproximadamente 20 minutos (aprox. 17.6 s/cuadro, onnxruntime sin GPU). Informa al usuario de esta magnitud para que no piense que se ha colgado.
-
Verificación (ver sección siguiente), si no pasa, reejecutar la fase específica, no todo el pipeline.
-
Entrega: Entregar los tres formatos GIF + WebP + APNG, y explicar los casos de uso de cada uno.
Reglas estrictas (violarlas ha tenido un costo alto)
- Para procesamiento topológico, solo usar apertura, nunca cierre.
binary_closingrellenará costuras de fondo de ≤2 px, soldando espacios estrechos entre brazos y torso, y ningún procesamiento posterior podrá recuperarlos. Según pruebas, esta regla ha causado más retrabajos que todos los demás problemas juntos. - Umbral de relleno de agujeros pequeño (del orden de 60 px, no cientos). Los espacios reales entre extremidades tienen miles de píxeles; los agujeros de alfiler solo decenas.
- Radio de filtrado guiado ≤2, y por defecto no usar filtro de mediana. Un filtro de mediana 3×3 y un filtro guiado de radio 4 eliminarán costuras de unos pocos píxeles de ancho.
- No aplicar compresión no lineal al canal alfa. Anteriormente se usó un mapeo como
clip((clip((a-0.04)/0.92)-0.06)/0.88)con la intención de ajustar bordes, pero en realidad eleva el alfa en valores alrededor de 0.7, reforzando los residuos. - GIF no usar tramado ordenado (Bayer). En cuadros estáticos los bordes son más suaves, pero ese patrón de puntos está fijo en el sistema de coordenadas de la pantalla; al moverse el contorno, produce un parpadeo "hirviente" — peor en animación. Para GIF usar umbral duro después de limpieza; el alfa suave se deja para WebP / APNG.
- Modelo elegir BiRefNet. isnet / u2net clasifican reflejos especulares del suelo como parte del sujeto; al cambiar a
birefnet-general-lite, el alfa en la suela de las botas llega a cero. - No usar "limpieza de color de fondo" para obtener más claridad. Usar criterios de superficie de fondo / cromaticidad para eliminar residuos grandes también eliminará partes claras del sujeto. La solución correcta para residuos es aumentar la resolución del matte, no cambiar umbrales.
Contrato de salida
out/<nombre>.gif— Índice transparente 255,transparency=255,disposal=2, borde duro de 1 bit. La limpieza topológica (eliminación de puntos dispersos) solo afecta al GIF.out/<nombre>.webp— Conserva alfa continuo, sin limpieza topológica, por lo que mantendrá una capa de bordes suaves y cálidos. Usar preferiblemente sobre fondos complejos; esta diferencia es intencional, no es un error.out/<nombre>.png— APNG, mejor compatibilidad con navegadores.- Mantener el tamaño del lienzo, número de cuadros, duración por cuadro y configuración de bucle del archivo fuente; listar estos valores medidos en la respuesta (PIL no puede leer la duración por cuadro de WebP animado; en ese caso decir "basado en la medición del GIF", no inventar).
- Entregar archivos con la etiqueta
<media>y explicar los casos de uso respectivos de GIF y WebP.
Manejo de fallos
- GIF se muestra con fondo gris en el navegador: No modificar el archivo primero. Verificar el bit ALPHA de VP8X y el color de fondo de ANIM (
verify.pylo imprime); si ambos son normales, es el visor que añade un fondo a las áreas transparentes. Siverify.pyreportaANMF ... blend=0, entonces es un problema real —cutout.pyexporta automáticamente conblend=1; si este archivo tiene blend=0, significa que no fue generado por este script o pasó por una herramienta externa. - Parte del sujeto eliminada: Casi siempre es un criterio de fondo demasiado amplio. Retroceder a la capa
final/y reexportar, no seguir ajustando parámetros en la lógica de eliminación. - Ciertos cuadros con espacios no abiertos:
--stage matte --scale 3solo reejecuta esos cuadros, costo por cuadro de aproximadamente 20 segundos. - Descarga lenta del modelo rembg: rembg 2.x coloca los modelos en
~/.rembg/models/<modelo>/<modelo>.onnx(no la versión anterior~/.u2net), birefnet-general-lite aproximadamente 214 MB. La primera ejecución requiere esperar la descarga.
Verificación (este paso no se puede omitir)
Debe hacerse cuadro por cuadro sobre el color de fondo real donde el usuario verá la imagen, de lo contrario no cuenta como verificación:
.venv-bg\Scripts\python scripts\verify.py out\<nombre>.gif --backdrop grey --ref work\final
Esto imprime: número de puntos dispersos, proporción de opacidad por cuadro (para detectar parpadeo), línea de opacidad más baja, error de cuantización de color del GIF relativo al matte fuente, indicadores del contenedor WebP, y genera un gráfico de comparación de cuatro colores de fondo + tabla de contacto de todos los cuadros + ampliación de área especificada.
Criterios de evaluación:
| Indicador | Aprobado |
|---|---|
| Número de puntos dispersos | 0 |
| Rango de opacidad por cuadro (máx-mín) | < 0.05 |
| Línea de opacidad más baja | Consistente entre cuadros (los residuos de reflejo la harán subir/bajar o llegar al borde inferior) |
| Error promedio de cuantización de color | < 3/255 (en miniatura no se notará pérdida de tono, solo con este número) |
| Comparación de cuatro colores de fondo | Cada fondo se transparece, sin un gris claro uniforme |
| Tabla de contacto | Sin imágenes residuales, sin ruido, espacios entre extremidades transparentes |
Examinar solo un cuadro o solo sobre fondo negro hará que se pasen por alto problemas: los residuos cálidos son visibles solo sobre fondos grises oscuros.
Notas para plataforma Windows (win32)
- El intérprete usar
pyopython,python3no existe en Windows; no usar elpythondel PATH (puede apuntar a un hermes venv sin pip); preferir el del venv:.venv-bg\Scripts\python.exe - Cuando la ruta contenga caracteres chinos, usar las herramientas
Write/Readpara escribir archivos de script, noSet-Contentpara construir contenido. ImageFilter.GaussianBlurde PIL no soporta mode"F"ni float RGB; para desenfoque en imágenes flotantes usarscipy.ndimage.gaussian_filter(rembg ya tiene scipy).Image.fromarray(float32_array, "RGB")dará error directamente; convertir a.astype(np.float32)primero y luego procesar por canal.- No usar
Remove-Itempara limpieza;cutout.py --cleanya encapsula la limpieza de directorios.
Ejemplos
Entrada: "Haz transparente el fondo de este GIF"
→ cutout.py --input accion.gif --outdir out --scale 2, luego verify.py,
→ Entregar los tres formatos GIF/WebP/APNG, listando 720×720 / 70 cuadros / 100 ms por cuadro / total 7000 ms.
Entrada: "Limpia más, el borde tiene ruido, hay partes no recortadas entre el brazo y el cuerpo"
→ No ajustar umbrales. Primero ejecutar verify.py --zoom <coordenadas del área> para confirmar la ubicación del defecto,
→ Si hay muchos puntos dispersos, añadir --median 3; si los espacios no se abren, usar --stage matte --scale 3 para reejecutar los cuadros correspondientes,
→ Luego verificar nuevamente. En la respuesta, explicar claramente qué tipo de defecto y qué corrección se usó, no solo decir "optimizado".