Exemplos do ImageMagick -- Tratamento de Vídeo
- Prefácio e Índice dos Exemplos do ImageMagick
- De Vídeo para GIF, Resumo de Otimização
- Desentrelaçar um Quadro de Vídeo
O ImageMagick não é particularmente indicado para o tratamento de vídeo digital, mas é comumente usado para esse fim, especialmente no ambiente Linux. Aqui exploro técnicas e exemplos específicos do tratamento de sequências de vídeo reais (e geradas por raytracing).
De Vídeo para GIF, Resumo de Otimização
Um desenvolvedor de software que usa o IM para criar GIFs animados, Benoit Rouleau, em uma conversa comigo, me deu um vídeo AVI de um avião passando voando, para nos ajudar a explorar mutuamente as técnicas de conversão de vídeo do IM. Porém, embora o próprio AVI seja bem pequeno, o vídeo descomprimido tem o tamanho enorme de
bytes, e envolve
cores, ao longo de
quadros. No entanto, o IM não tem nenhuma dificuldade real em converter esse vídeo em uma animação GIF. Mas fique avisado de que você provavelmente verá alguns erros de 'AVI chunk' não suportado, que podem ser ignorados usando uma configuração de controle "[-quiet](https://imagemagick.org/command-line-options/#quiet)". |
magick -quiet -delay 1 plane.avi plane.gif
![[IM Output]](../static/img/video/plane.gif)
Isso usou os métodos padrão de Quantização de Cor e Pontilhamento do ImageMagick para produzir uma conversão bastante razoável do vídeo. Há pouquíssimos problemas de cor, porque o vídeo usa pouquíssimas cores desde o início. Nem sempre é assim, especialmente porque o GIF tem um limite de 256 cores por quadro. Mesmo assim, o arquivo da animação tem
bytes de tamanho, o que, embora seja apenas 1/5 do tamanho original devido à redução de cores e à compressão dos dados de pixel do GIF, ainda é bastante grande. Além disso, se você estudar a animação resultante com mais atenção, descobrirá que, dos
quadros da imagem,
quadros tiveram sua própria tabela de cores local separada adicionada. Ou seja, cada quadro da animação GIF exigiu sua própria tabela de índice de cores. Isto é, embora cada quadro tenha menos de 256 cores (devido às limitações do formato GIF), a animação inteira usa um total de
cores. Infelizmente, o formato GIF não comprime as tabelas de cores, de modo que todas essas tabelas de cores extras podem chegar a usar: 256 cores * 3 bytes por cor * 106 quadros; ou seja, 81.408 bytes de espaço de arquivo. Não é muito para um vídeo de 1 Gbyte, mas ainda assim é uma quantidade apreciável de espaço, especialmente à medida que otimizamos ainda mais o vídeo. Soma-se a isso o fato de que a animação não vai se prestar muito bem à otimização de quadros do GIF. Não apenas porque o fundo está em movimento (devido à panorâmica da câmera para cima), mas também porque o IM usou um Pontilhamento por Correção de Erro (Pontilhamento por Curva de Hilbert), que produz um padrão pseudoaleatório de cores diferente de um quadro para o outro. Um exemplo posterior tornará esse 'ruído de pontilhamento' muito mais visível.
Tabela de Cores Global Comum
Aqui eu Gero uma Única Tabela de Cores Global para todos os quadros do vídeo. |
magick -quiet -delay 1 plane.avi +remap plane_cgc.gif
Isso naturalmente resulta em
tabelas de cores locais, e em um tamanho de arquivo de
bytes. ![[IM Output]](../static/img/video/plane_cgc.gif)
Como você pode ver, a animação resultante não tem tabelas de cores locais extras. Em vez disso, o IM gerou uma única tabela de cores global com as
'melhores' cores com base em todos os quadros da animação. Infelizmente, isso também fez com que os dados de pixel não comprimissem tão bem quanto antes, pois foi necessário um pontilhamento mais forte. O resultado é uma animação com aparência ligeiramente pior, com tamanho aproximadamente igual ao anterior. Para este vídeo específico, de cores limitadas, eu poderia até reduzir ainda mais o número de cores usadas, digamos para apenas 64 cores, sem problemas demais, produzindo um arquivo de animação ainda menor. Isso, porém, depende muito da sequência de vídeo usada, e pode não ficar muito bom. Seu próprio vídeo pode ter um resultado melhor ou pior, especialmente ao lidar com um vídeo que usa muito mais cores e, possivelmente, várias cenas.
Tabela de Cores Global Universal
A melhor maneira de gerar uma animação GIF 'menor' é simplesmente fornecer uma faixa universal e geral de cores, em vez de gerar a 'melhor' tabela de cores global para a animação. Use uma que funcione bem independentemente de quais cores estejam presentes no vídeo original. Outra razão para fazer isso é que você pode tornar o vídeo mais longo sem efeitos prejudiciais sérios sobre a seleção de cores, nem ter de recorrer a tabelas de cores locais para cada quadro. Cada quadro é pontilhado para o mesmo mapa de cores, de forma completamente independente dos demais quadros presentes na animação. Aqui eu uso um mapa de cores '332', que costuma ser considerado um mapa de cores padrão muito bom quando não é necessária transparência. Tenho visto com frequência esse mapa de cores (ou um mapa de cores 'web-safe' de 219 cores) sendo usado em vários 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 animação tem
tabelas de cores locais e, como resultado, a animação é menor, ou seja,
bytes de tamanho. O problema, porém, é que você frequentemente verá um 'ruído' óbvio e incômodo em áreas de cor constante. Esse ruído também estava presente em TODAS as animações de vídeo anteriores. Só agora ele fica visível, devido ao uso de um mapeamento de cores mais universal e, portanto, mais espalhado. O ruído é na verdade causado pelo pontilhamento do conjunto reduzido de cores ao regenerar a imagem. No entanto, isso produz um padrão pseudoaleatório de cores que muda de um quadro para o outro, resultando no aparecimento de ruído de fundo na imagem. Veja Problemas com Pontilhamentos por Erro para mais detalhes sobre por que isso acontece. Poderíamos simplesmente desligar o pontilhamento de cores para remover o 'ruído de pontilhamento'... |
magick -quiet -delay 1 plane.avi \
+dither -remap colormap_332.png plane_ugc_nd.gif
Isso tem
tabelas de cores locais, e tem
bytes de tamanho. ![[IM Output]](../static/img/video/plane_ugc_nd.gif)
A animação resultante tem um tamanho muito pequeno, 1/60 do tamanho da animação original, geralmente por causa das grandes extensões de cor sólida, que produzem uma compressão de pixel extremamente boa. Mas, embora corrija o ruído de pontilhamento e gere um arquivo de tamanho muito pequeno, você fica com banding de cor (faixas de cor), o que costuma ser considerado uma troca muito ruim.
Vídeo com Pontilhamento Ordenado
A verdadeira solução é usar uma técnica diferente de pontilhamento de cores, que não produza um padrão diferente de um quadro para o seguinte. Por exemplo, aqui eu usei um Pontilhamento Ordenado com Níveis de Cor Posterizados para pontilhar o mesmo mapa de cores '332' universal. |
magick -quiet -delay 1 plane.avi \
-ordered-dither o8x8,8,8,4 +remap plane_od.gif
Isso tem
tabelas de cores locais, e tem
bytes de tamanho. ![[IM Output]](../static/img/video/plane_od.gif)
O exemplo acima também usou o operador "[+remap](https://imagemagick.org/command-line-options/#remap)", para garantir que todas as imagens usem exatamente o mesmo mapa de cores global (que o pontilhamento ordenado já reduziu a no máximo 256 cores). Como o número de cores já é ótimo, o operador "[+remap](https://imagemagick.org/command-line-options/#remap)" não faz nenhum pontilhamento nem redução de cores. O padrão de pontilhamento resultante não é aleatório, e não muda muito de um quadro para o seguinte. Assim, o 'ruído de pontilhamento' foi removido da animação, resultando em um padrão de cores fixo de quadro para quadro. O padrão também é muito repetitivo, permitindo uma compressão bem melhor. E, por fim, como o mapa de cores é fixo, ele deve funcionar razoavelmente bem independentemente do vídeo usado.
Vídeo com Pontilhamento Ordenado de Maior Qualidade
Este vídeo específico, porém, usa apenas uma pequena faixa de cores, principalmente vários tons de azul, de modo que ele não usa, de fato, muitas das cores fornecidas por um mapa de cores uniforme geral. De fato, apenas
cores foram usadas na última animação de vídeo! Isso é extremamente baixo e, por isso, também bastante perceptível. Mas também significa que essa animação em particular pode se beneficiar do uso de um número grande de 'níveis de cor' na operação de pontilhamento ordenado, de modo a melhorar a qualidade geral. Primeiro, no entanto, precisamos determinar quantos níveis de cor a animação consegue suportar antes de atingir o limite de 256 cores imposto tanto pelo formato de arquivo GIF quanto pelo remapeamento do mapa de cores global. A parte complicada, porém, é que você precisa determinar isso ANTES de salvar a animação no formato GIF, que é limitado. E aqui está o comando que eu uso...
magick -quiet plane.avi -ordered-dither o8x8,23 -append -format %k info:
Basicamente, aumentei e diminuí o número de níveis de cor a usar, até obter um valor que ficasse logo dentro do limite exigido de 256 cores. Em seguida, posso aplicar a escolha de 'nível de cor' descoberta à animação do avião. |
magick -quiet -delay 1 plane.avi \
-ordered-dither o8x8,23 +remap plane_od2.gif
Isso tem
tabelas de cores locais, tem
bytes de tamanho, e
cores. ![[IM Output]](../static/img/video/plane_od2.gif)
Como você pode ver, foi gerado um vídeo com pontilhamento ordenado de altíssima qualidade, que está à altura da versão com mapa de cores global do 'melhor mapa de cores' que geramos anteriormente, mas também 1/3 menor em tamanho, enquanto o 'ruído de pontilhamento' está agora muito mais difícil de ver. É claro que, como a qualidade é muito mais alta, isso exige um arquivo de tamanho maior, pois não comprime tão bem quanto a versão de baixa qualidade. Por outro lado, agora você realmente tem um bom controle sobre a troca entre qualidade e tamanho de arquivo, na forma do número de 'níveis de cor' usados. Apenas lembre-se de que essa técnica é um caso especial, para uma animação que não usa cores demais. E tornar o vídeo mais longo, adicionando mais quadros, também adicionará mais cores e, portanto, exigirá uma redução no controle de qualidade dos 'níveis de cor'. Este é mais ou menos o melhor método de otimização de cores que já vi para animações GIF em geral. Ele remove o 'ruído de pontilhamento', oferece algum controle de qualidade e mantém a capacidade de usar outros métodos de otimização de animação GIF, como a Otimização de Quadros.
Otimização de Compressão (Transparência)
Como este vídeo usa uma câmera com panorâmica, o fundo do vídeo muda de um quadro para o outro. Isso significa que a animação GIF não fará uma boa Otimização de Quadros. No entanto, ainda podemos usar uma simples Otimização de Transparência para reduzir ainda mais o tamanho final da animação GIF. |
magick plane_od2.gif -layers OptimizeTransparency +remap plane_opt.gif
O resultado tem
bytes de tamanho, e
cores. ![[IM Output]](../static/img/video/plane_opt.gif)
Ou seja, uma cor extra, um índice de cor transparente, foi adicionada à imagem, e todo pixel que não altera a cor atualmente exibida foi tornado transparente. Isso, por sua vez, gera grandes segmentos de áreas transparentes na animação original, bem como repetições de sequências de pixels semelhantes, o que gera uma compressão LZW melhorada na imagem GIF final. Nada mal: a animação agora tem metade do tamanho da conversão direta para GIF, e ainda mantém uma qualidade razoavelmente alta. Se você quiser acrescentar algo ao exposto acima, discutir as técnicas para aprimorá-las ainda mais, entre em contato comigo ou com o fórum do IM. Ficarei muito feliz em conhecer suas opiniões, técnicas e discussões, ou em examinar um problema específico de vídeo/animação que você possa ter. Uma dessas discussões é Encontrar os "níveis certos" para a quantização com GIF animado.
Otimização LZW por Compressão Giflossy
Uma nova ferramenta, o GifLossy, que é um fork do programa original Gifsicle, modifica as cores de cada quadro de modo a permitir que o LZW comprima a imagem muito mais. Por exemplo, aqui eu o apliquei à animação GIF original, pedindo que ele reduzisse as cores a uma única tabela de 256 cores. |
gifsicle -O3 --lossy=80 --colors 256 plane.gif -o plane_giflossy.gif
Isso tem um tamanho absolutamente incrível de
bytes. Não é nem de longe tão alta qualidade quanto o que conseguimos usando o pontilhamento ordenado, mas tem menos de 1/2 do tamanho. ![[IM Output]](../static/img/video/plane_giflossy.gif)
Encorajado por esse resultado, decidi usar o GifLossy no melhor resultado de pontilhamento ordenado que obtivemos, para ver se ele conseguiria torná-lo ainda menor. |
gifsicle -O3 --lossy=80 plane_od2.gif -o plane_od2_giflossy.gif
E de fato obtivemos um tamanho ainda menor, de
bytes. Infelizmente, perdemos basicamente o resultado de pontilhamento ordenado de alta qualidade que tanto trabalho nos deu antes. O que é decepcionante.
Desentrelaçar um Quadro de Vídeo
Nem todas as imagens vêm de câmeras digitais. É muito comum extrair imagens de um sinal de vídeo digital de uma câmera de vídeo que não seja CCD. Essas imagens são entrelaçadas para exibição direta em uma TV, resultando em que cada linha alternada seja um quadro diferente da imagem (entrelaçamento). Para dois quadros em que as coisas não estão se movendo, o entrelaçamento geralmente não é muito perceptível. Talvez produza apenas uma leve borda desfocada na imagem. Mas quando há um objeto se movendo rapidamente, a imagem entrelaçada resultante é muito desconcertante, pois dois quadros foram mesclados. Wolfgang Hugemann Auto@Hugemann.de (Alemanha) teve esse problema e me enviou um instantâneo de um crash test que o próprio Wolfgang tirou. Mas, para fins de demonstração, vou usar uma imagem menor recortada desta. As técnicas, porém, funcionarão na imagem em tamanho real. |
magick video_frame.png -crop 100x100+200+470 +repage interlaced.png
![[IM Output]](../static/img/video/interlaced.png)
| Wolfgang Hugemann usou o formato TIFF para o quadro de vídeo original; eu o converti para PNG para uso nos IM Examples. NÃO se sinta tentado a usar JPEG para essas imagens, até terminar o processamento, pois isso destruirá a qualidade de baixo nível necessária para esse processo.
---|---
Como você pode ver, o entrelaçamento mostra dois quadros separados, pois vem de uma sequência de vídeo digital PAL entrelaçada (aproximadamente 50 meios-quadros por segundo). Sim, o carro estava se movendo muito rápido e a câmera está usando um obturador de alta velocidade, produzindo uma imagem de vídeo de altíssima qualidade. A imagem resultante são dois meios-quadros entrelaçados, com o espelho retrovisor lateral do carro tendo se deslocado uma distância considerável durante o intervalo de 1/50 de segundo entre os meios-quadros. Aqui simplesmente substituímos um dos meios-quadros entrelaçados (cada linha alternada) por branco. Este é o método padrão de desentrelaçamento, conhecido como filtro 'BoB'. Ele foi contribuído por Wolfgang para os IM Examples. |
magick interlaced.png -fx "floor(j/2)==j/2 ? u : 1" deinterlace_1.png
![[IM Output]](../static/img/video/deinterlace_1.png)
Agora, o operador FX é lento, então uma alternativa é criar uma 'imagem listrada'. Tal imagem pode ser gerada a partir da imagem interna especial "pattern:Horizontal2". Essa imagem pode então ser sobreposta à original, usando um método de composição '[Screen](compose.html#screen)' para sobrepor linhas brancas, ou usar '[Multiply](compose.html#multiply)' para sobrepor linhas pretas. Por exemplo... |
magick -size 100x100 pattern:Horizontal2 \
interlaced.png -compose Multiply -composite deinterlace_2.png
![[IM Output]](../static/img/video/deinterlace_2.png)
Negar o padrão pode ser usado para selecionar a outra metade da imagem entrelaçada. Ou, se você mudar o 'Multiply' para 'Screen', poderá extrair quadros com fundo branco. Como alternativa, tentei preencher as linhas de quadro que faltam apenas duplicando a linha anterior. |
magick interlaced.png -fx "u.p{i,j-j%2}" deinterlace_3.png
![[IM Output]](../static/img/video/deinterlace_3.png)
Você também pode usar uma Técnica de Pixelização para encolher e expandir uma imagem, de modo a duplicar cada linha alternada. |
magick interlaced.png -sample 100%x50% \
-sample 100%x200% deinterlace_4.png
![[IM Output]](../static/img/video/deinterlace_4.png)
E, com uma leve variação, você pode combinar as linhas de ambos os lados para suavizar verticalmente a imagem de meio-quadro como parte da expansão de redimensionamento. |
magick interlaced.png -sample 100%x50% \
-resize 100%x200% deinterlace_5.png
![[IM Output]](../static/img/video/deinterlace_5.png)
O resultado é uma extração particularmente boa de um quadro da imagem de vídeo entrelaçada. Se você quiser extrair o outro meio-quadro da imagem, pode ajustar o 'sampling:offset' (a partir do 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 desta versão do IM, você precisaria fazer um "[-roll](https://imagemagick.org/command-line-options/#roll)" na imagem por um pixel, para obter o mesmo resultado.
![[IM Text]](../static/img/video/plane_od2_find.txt.gif)