Ejemplos de ImageMagick -- Manejo de vídeo
- Ejemplos de ImageMagick: prefacio e índice
- De vídeo a GIF, resumen de optimización
- Desentrelazar un fotograma de vídeo
ImageMagick no está especialmente indicado para el manejo de vídeo digital, pero se usa habitualmente con este fin, sobre todo en el entorno Linux. Aquí exploro técnicas y ejemplos específicos del manejo de secuencias de vídeo reales (y trazadas por rayos).
De vídeo a GIF, resumen de optimización
Un desarrollador de software que usa IM para crear GIF de películas, Benoit Rouleau, en una conversación conmigo, me dio un vídeo AVI de un avión que pasa volando por encima, para ayudarnos a explorar mutuamente las técnicas de conversión de vídeo de IM. Sin embargo, aunque el propio AVI es bastante pequeño, el vídeo sin comprimir tiene un tamaño enorme de
bytes, y contiene
colores a lo largo de
fotogramas. No obstante, IM no tiene ningún problema real para convertir este vídeo en una animación GIF. Pero ten en cuenta que probablemente obtendrás algunos errores de 'AVI chunk' no soportado, que pueden ignorarse usando un ajuste de control "[-quiet](https://imagemagick.org/command-line-options/#quiet)" ajuste de control. |
magick -quiet -delay 1 plane.avi plane.gif
![[IM Output]](../static/img/video/plane.gif)
Esto usó los métodos predeterminados de cuantización de color y tramado de ImageMagick para producir una conversión muy razonable del vídeo. Apenas existen problemas de color, porque el vídeo usa de partida muy pocos colores. No siempre es así, sobre todo porque GIF tiene un límite de 256 colores por fotograma. Sin embargo, el archivo de animación tiene un tamaño de
bytes, que aunque solo es 1/5 del tamaño, gracias a la reducción de color y a la compresión de los datos de píxeles de GIF, sigue siendo bastante grande. Además, si estudias más a fondo la animación resultante, descubrirás que de los
fotogramas de la imagen,
fotogramas tenían añadida su propia tabla de colores local separada. Es decir, todos y cada uno de los fotogramas de la animación GIF requirieron su propia tabla de índices de color. O sea, aunque cada fotograma tiene menos de 256 colores (debido a las limitaciones del formato GIF), la animación completa usa un total de
colores. Por desgracia, el formato GIF no comprime las tablas de colores, así que todas esas tablas de colores adicionales podrían estar usando hasta: 256 colores * 3 bytes por color * 106 fotogramas; es decir, 81.408 bytes de espacio de archivo. No es mucho para un vídeo de 1 Gbyte, pero sigue siendo una cantidad apreciable de espacio, sobre todo a medida que optimizamos más el vídeo. A esto se añade que la animación no se optimizará muy bien por fotogramas en GIF. No solo porque el fondo se está moviendo (debido a que la cámara panea hacia arriba), sino también porque IM usó un tramado de corrección de errores (tramado por curva de Hilbert), que produce un patrón de colores pseudoaleatorio que difiere de un fotograma a otro. Un ejemplo posterior hará este 'ruido de tramado' mucho más visible.
Tabla de colores global común
Aquí genero una única tabla de colores global para todos los fotogramas del vídeo. |
magick -quiet -delay 1 plane.avi +remap plane_cgc.gif
Esto da lugar, como es natural, a
tablas de colores locales, y a un tamaño de archivo de
bytes. ![[IM Output]](../static/img/video/plane_cgc.gif)
Como puedes ver, la animación resultante no tiene tablas de colores locales adicionales. En su lugar, IM generó una única tabla de colores global de los
'mejores' colores basándose en todos los fotogramas de la animación. Por desgracia, esto también hizo que los datos de píxeles no se comprimieran tan bien como antes, ya que se requirió un tramado más fuerte. El resultado es una animación con un aspecto ligeramente peor, que tiene aproximadamente el mismo tamaño que la anterior. Para este vídeo concreto, de colores limitados, incluso podría reducir aún más el número de colores empleados, digamos a solo 64 colores, sin demasiados problemas, produciendo un tamaño de archivo de animación todavía más pequeño. Esto, sin embargo, depende mucho de la secuencia de vídeo usada, y puede que no quede muy bien. Tu propio vídeo puede dar un resultado mejor o peor, especialmente al tratar con un vídeo que usa muchos más colores y posiblemente varias escenas.
Tabla de colores global universal
La mejor manera de generar una animación GIF más 'pequeña' es simplemente proporcionar un rango general universal de colores en lugar de generar la 'mejor' tabla de colores global para una animación. Usa una que funcione bien sin importar qué colores estén presentes en el vídeo original. Otra razón para hacer esto es que puedes alargar tu vídeo sin efectos perjudiciales serios en la selección de color, ni tener que recurrir a tablas de colores locales para cada fotograma. Cada fotograma se trama al mismo mapa de colores, de forma completamente independiente de qué otros fotogramas haya en la animación. Aquí uso un mapa de colores '332', que suele considerarse un mapa de colores estándar muy bueno cuando no se necesita transparencia. A menudo he visto que este mapa de colores (o un mapa de colores de 219 colores 'seguro para la web') se usa con frecuencia en varios formatos de vídeo. |
magick -quiet -delay 1 plane.avi -remap colormap_332.png plane_ugc.gif
![[IM Output]](../static/img/video/plane_ugc.gif)
Esta animación tiene
tablas de colores locales, y como resultado la animación es más pequeña, o sea
bytes de tamaño. El problema, sin embargo, es que a menudo verás un 'ruido' evidente y molesto en zonas de color constante. Este ruido también estaba presente en TODAS las animaciones de vídeo anteriores. Solo ahora resulta visible debido al uso de un mapeo de color más universal y, por tanto, más ampliamente distribuido. El ruido lo causa en realidad el tramado del conjunto de colores reducido al regenerar la imagen. Sin embargo, esto produce un patrón de colores pseudoaleatorio que cambia de un fotograma a otro, lo que da lugar a la aparición de ruido de fondo en la imagen. Consulta Problemas con los tramados de error para más detalles sobre por qué ocurre esto. Podríamos simplemente desactivar el tramado de color para eliminar el 'ruido de tramado'... |
magick -quiet -delay 1 plane.avi \
+dither -remap colormap_332.png plane_ugc_nd.gif
Lo cual tiene
tablas de colores locales, y es de
bytes de tamaño. ![[IM Output]](../static/img/video/plane_ugc_nd.gif)
La animación resultante es muy pequeña, 1/60 del tamaño de la animación original, en general porque las grandes extensiones de color sólido producen una compresión de píxeles extremadamente buena. Pero aunque corrige el ruido de tramado y consigue un tamaño de archivo muy pequeño, a cambio obtienes bandas de color, lo que suele considerarse una compensación muy mala.
Vídeo con tramado ordenado
La verdadera solución es usar una técnica de tramado de color distinta, que no produzca un patrón diferente de un fotograma al siguiente. Por ejemplo, aquí usé un tramado ordenado con niveles de color posterizados para tramar el mismo mapa de colores universal '332'. |
magick -quiet -delay 1 plane.avi \
-ordered-dither o8x8,8,8,4 +remap plane_od.gif
Lo cual tiene
tablas de colores locales, y es de
bytes de tamaño. ![[IM Output]](../static/img/video/plane_od.gif)
Lo anterior también usó el operador "[+remap](https://imagemagick.org/command-line-options/#remap)" para asegurar que todas las imágenes usen exactamente el mismo mapa de colores global (que el tramado ordenado ya había reducido a un máximo de 256 colores). Como el número de colores ya es óptimo, el operador "[+remap](https://imagemagick.org/command-line-options/#remap)" no realiza ningún tramado ni reducción de color. El patrón de tramado resultante no es aleatorio, y no cambia mucho de un fotograma al siguiente. Así, el 'ruido de tramado' se ha eliminado de la animación, dando lugar a un patrón de color fijo de fotograma a fotograma. El patrón es además muy repetitivo, lo que permite una compresión mucho mejor. Y por último, como el mapa de colores es fijo, debería funcionar razonablemente bien sea cual sea el vídeo usado.
Vídeo con tramado ordenado de mayor calidad
Sin embargo, este vídeo concreto solo usa un rango pequeño de colores, en su mayoría diversos tonos de azul, así que en realidad no usa muchos de los colores que proporciona un mapa de colores uniforme general. De hecho, ¡en la última animación de vídeo solo se usaron
colores! Esto es extremadamente bajo y, como tal, también bastante visible. Pero también significa que esta animación en concreto puede beneficiarse de usar un gran número de 'niveles de color' en la operación de tramado ordenado, a fin de mejorar la calidad general. Pero primero necesitamos determinar cuántos niveles de color puede manejar la animación antes de alcanzar el límite de 256 colores que imponen tanto el formato de archivo GIF como el remapeo del mapa de colores global. Lo complicado, no obstante, es que debes determinarlos ANTES de guardar la animación en el limitado formato GIF. Y este es el comando que uso...
magick -quiet plane.avi -ordered-dither o8x8,23 -append -format %k info:
Básicamente, aumenté y disminuí el número de niveles de color a usar, hasta tener una cifra que quedara justo dentro del límite requerido de 256 colores. Después puedo aplicar la elección de 'nivel de color' descubierta a la animación del avión. |
magick -quiet -delay 1 plane.avi \
-ordered-dither o8x8,23 +remap plane_od2.gif
Lo cual tiene
tablas de colores locales, es de
bytes de tamaño, y
colores. ![[IM Output]](../static/img/video/plane_od2.gif)
Como puedes ver, se generó un vídeo con tramado ordenado de muy alta calidad, que está a la par con la versión de mapa de colores global de 'mejor mapa de colores' que generamos antes, pero además 1/3 más pequeño en tamaño, mientras que el 'ruido de tramado' ahora es mucho más difícil de ver. Por supuesto, como la calidad es mucho mayor, requiere un tamaño de archivo más grande, ya que no se comprime tan bien como la versión de baja calidad. Por otro lado, ahora tienes realmente un buen control sobre la compensación entre calidad y tamaño de archivo en forma del número de 'niveles de color' usados. Recuerda solo que esta técnica es un caso especial, para una animación que no usa demasiados colores. Y alargar el vídeo añadiendo más fotogramas también añadirá más colores y, por tanto, requerirá una reducción en el control de calidad de 'nivel de color'. Este es más o menos el mejor método de optimización de color que he visto hasta ahora para animaciones GIF generales. Elimina el 'ruido de tramado', proporciona cierto control de calidad y conserva la capacidad de usar otros métodos de optimización de animaciones GIF, como la optimización de fotogramas.
Optimización de compresión (transparencia)
Como este vídeo usa una cámara que panea, el fondo del vídeo cambia de un fotograma a otro. Esto significa que la animación GIF no se optimizará por fotogramas muy bien. Sin embargo, todavía podemos usar una sencilla optimización de transparencia para reducir aún más el tamaño final de la animación GIF. |
magick plane_od2.gif -layers OptimizeTransparency +remap plane_opt.gif
El resultado es de
bytes de tamaño, y
colores. ![[IM Output]](../static/img/video/plane_opt.gif)
Es decir, se añadió un color extra a la imagen, un índice de color transparente, y cualquier píxel que no cambia el color mostrado en ese momento se hizo transparente. Esto a su vez genera grandes segmentos de áreas transparentes en la animación original, así como repeticiones de secuencias de píxeles similares, lo que genera una compresión LZW mejorada en la imagen GIF final. No está mal: la animación es ahora la mitad que la de la conversión directa a GIF, y conserva aún una calidad razonablemente alta. Si quieres añadir algo a lo anterior, o debatir técnicas para mejorarlas aún más, contacta conmigo o con el foro de IM. Estaré más que encantado de conocer tus opiniones, técnicas y comentarios, o de examinar algún problema concreto de vídeo/animación que puedas tener. Uno de esos debates es Finding the "right levels" for quantization with anim GIF.
Optimización LZW con compresión Giflossy
Una nueva herramienta, GifLossy, que es una bifurcación del programa original Gifsicle, modifica los colores de cada fotograma para permitir que LZW comprima la imagen mucho más. Por ejemplo, aquí la apliqué a la animación GIF original, pidiéndole que redujera los colores a una única tabla de 256 colores. |
gifsicle -O3 --lossy=80 --colors 256 plane.gif -o plane_giflossy.gif
Lo cual tiene un tamaño absolutamente asombroso de
bytes. No es ni de lejos de tan alta calidad como lo que logramos usando el tramado ordenado, pero es menos de la mitad del tamaño. ![[IM Output]](../static/img/video/plane_giflossy.gif)
Animado por ese resultado anterior, decidí usar GifLossy sobre el mejor resultado de tramado ordenado que obtuvimos, para ver si podía hacerlo aún más pequeño. |
gifsicle -O3 --lossy=80 plane_od2.gif -o plane_od2_giflossy.gif
Y sí obtuvimos un tamaño todavía más pequeño de
bytes. Por desgracia, básicamente perdimos el resultado de tramado ordenado de alta calidad que tanto nos costó lograr antes. Lo cual es decepcionante.
Desentrelazado de un fotograma de vídeo
No todas las imágenes provienen de cámaras digitales. Es muy común extraer imágenes de una señal de vídeo digital procedente de una cámara de vídeo no CCD. Estas imágenes están entrelazadas para mostrarse directamente en un televisor, lo que da como resultado que cada segunda línea sea un fotograma distinto de la imagen (entrelazado). Para dos fotogramas en los que las cosas no se mueven, el entrelazado normalmente no es muy perceptible. Quizá produce solo un ligero desenfoque de los bordes de la imagen. Pero cuando interviene un objeto que se mueve rápido, la imagen entrelazada resultante es muy desconcertante, ya que se han fundido dos fotogramas. Wolfgang Hugemann Auto@Hugemann.de (Alemania) tenía este problema y me envió una instantánea de una prueba de choque, tomada por el propio Wolfgang. Pero para la demostración usaré una imagen más pequeña recortada de esta. Las técnicas, no obstante, funcionarán con la imagen a tamaño completo. |
magick video_frame.png -crop 100x100+200+470 +repage interlaced.png
![[IM Output]](../static/img/video/interlaced.png)
| Wolfgang Hugemann usó un formato TIFF para el fotograma de vídeo original; yo lo convertí a PNG para usarlo en IM Examples. NO caigas en la tentación de usar JPEG para estas imágenes hasta que hayas terminado de procesarlas, ya que destruirá la calidad de bajo nivel necesaria para este proceso.
---|---
Como puedes ver, el entrelazado muestra dos fotogramas separados, ya que proviene de una secuencia de vídeo digital PAL entrelazada (aproximadamente 50 medios fotogramas por segundo). Sí, el coche se movía muy rápido y la cámara usa un obturador de alta velocidad, produciendo una imagen de vídeo de muy alta calidad. La imagen resultante son dos medios fotogramas entretejidos, en los que el retrovisor lateral del coche se desplaza una distancia considerable durante el intervalo de 1/50 de segundo que media entre los medios fotogramas. Aquí simplemente reemplazamos uno de los medios fotogramas entrelazados (cada segunda línea) por blanco. Este es el método de desentrelazado estándar, conocido como filtro 'BoB'. Lo aportó Wolfgang para IM Examples. |
magick interlaced.png -fx "floor(j/2)==j/2 ? u : 1" deinterlace_1.png
![[IM Output]](../static/img/video/deinterlace_1.png)
Ahora bien, el operador FX es lento, así que una alternativa es crear una 'imagen de rayas'. Una imagen así puede generarse a partir de la imagen integrada especial "pattern:Horizontal2". Esa imagen puede entonces superponerse a la original, usando un método de composición '[Screen](compose.html#screen)' para superponer líneas blancas, o usar '[Multiply](compose.html#multiply)' para superponer líneas negras. Por ejemplo... |
magick -size 100x100 pattern:Horizontal2 \
interlaced.png -compose Multiply -composite deinterlace_2.png
![[IM Output]](../static/img/video/deinterlace_2.png)
Negar el patrón puede usarse para seleccionar la otra mitad de la imagen entrelazada. O, si cambias el 'Multiply' por 'Screen', puedes extraer fotogramas con un fondo blanco. Como alternativa, intenté rellenar las líneas de fotograma que faltan simplemente duplicando la línea anterior. |
magick interlaced.png -fx "u.p{i,j-j%2}" deinterlace_3.png
![[IM Output]](../static/img/video/deinterlace_3.png)
También puedes usar una técnica de pixelización para encoger y expandir una imagen y así duplicar cada segunda línea. |
magick interlaced.png -sample 100%x50% \
-sample 100%x200% deinterlace_4.png
![[IM Output]](../static/img/video/deinterlace_4.png)
Y con una ligera variación puedes combinar las líneas de ambos lados para suavizar verticalmente la imagen de medio fotograma como parte de la expansión del redimensionado. |
magick interlaced.png -sample 100%x50% \
-resize 100%x200% deinterlace_5.png
![[IM Output]](../static/img/video/deinterlace_5.png)
El resultado es una extracción particularmente buena de un fotograma de la imagen de vídeo entrelazada. Si quieres extraer el otro medio fotograma de la imagen, puedes ajustar el 'sampling:offset' (a partir de IM v6.8.4-7) |
magick interlaced.png -define sample:offset=75 \
-sample 100%x50% -resize 100%x200% deinterlace_6.png
![[IM Output]](../static/img/video/deinterlace_6.png)
Antes de esta versión de IM, habrías necesitado "[-roll](https://imagemagick.org/command-line-options/#roll)" la imagen un píxel para conseguir el mismo resultado.
![[IM Text]](../static/img/video/plane_od2_find.txt.gif)