Miniaturas significativas para um vídeo usando o FFmpeg

80

O FFmpeg pode capturar imagens de vídeos que podem ser usados ​​como miniaturas para representar o vídeo. As formas mais comuns de fazer isso são capturadas no FFmpeg Wiki .

Mas não quero escolher quadros aleatórios em alguns intervalos. Encontrei algumas opções usando filtros no FFmpeg para capturar alterações de cena:

O filtro thumbnailtenta encontrar os quadros mais representativos no vídeo:

ffmpeg -i input.mp4 -vf  "thumbnail,scale=640:360" -frames:v 1 thumb.png

e o comando a seguir seleciona apenas os quadros que têm mais de 40% das alterações em comparação com as anteriores (e provavelmente são as mudanças de cena) e gera uma sequência de 5 PNGs.

ffmpeg -i input.mp4 -vf  "select=gt(scene\,0.4),scale=640:360" -frames:v 5 thumb%03d.png

Crédito de informação para os comandos acima para Fabio Sonnati . O segundo parecia melhor, pois eu podia obter n imagens e escolher o melhor. Eu tentei e gerou a mesma imagem 5 vezes.

Um pouco mais de investigação me levou a:

ffmpeg -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr  out%02d.png

-vsync vfrgarante que você obtenha imagens diferentes. Isso ainda escolhe sempre o primeiro quadro do vídeo; na maioria dos casos, o primeiro quadro é de créditos / logotipo e não tem significado, então adicionei um -ss3 para descartar os primeiros 3 segundos do vídeo.

Meu comando final é assim:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr out%02d.jpg

Foi o melhor que pude fazer. Percebi que, como escolho apenas 5 vídeos, todos são principalmente do início do vídeo e podem perder cenas importantes que ocorrem mais tarde no vídeo.

Gostaria de escolher seu cérebro para outras opções melhores.

d33pika
fonte
Exemplos de comandos agradáveis. FWIW, não tive problemas com as imagens JPEG geradas pelo FFmpeg no OS X (10.8, FFmpeg 1.1 e abaixo). Seu penúltimo comando funciona bem para mim - o mesmo acontece com o último - e nenhum desses resulta em arquivos JPG em branco. Eu compilei com libopenjpeg.. não tenho certeza se isso faz alguma diferença.
slhck
Obrigado slhck. Editou a pergunta com detalhes de configuração / versão do ffmpeg. Não atualizei para 1.1 nesta máquina. Vou fazer isso e ver se isso altera algum resultado.
D33pika
11
Então você está no Ubuntu? Você pode experimentar a versão mais recente do Git Master a partir de uma compilação estática ou compilando-se e executando novamente? Ou o mais recente estável. Acabei de verificar, ele usa o mjpegcodificador para mim também, e também verifiquei jpegoptime exiv2, os quais funcionam bem para mim com todos os resultados JPG dos seus comandos de exemplo.
slhck
11
Eu atualizei e funciona agora! Eu acho que a versão anterior tinha alguns erros.
d33pika
Você pode seguir em frente e publicar a solução - nova versão, de preferência com o link para o changelog, mostrando o bug que você encontrou e posteriormente corrigido com a nova versão?
Lizz

Respostas:

29

Que tal procurar, idealmente, o primeiro quadro de mudança de> 40% em cada um dos cinco intervalos de tempo, em que o intervalo de tempo é o 1º, 2º, 3º, 4º e 5º 20% do vídeo.

Você também pode dividi-lo em seis intervalos de tempo e desconsiderar o primeiro para evitar créditos.

Na prática, isso significaria definir os fps para um número baixo ao aplicar sua verificação de mudança de cena e seu argumento para lançar o primeiro trecho do vídeo.

...algo como:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.4)" -frames:v 5 -vsync vfr -vf fps=fps=1/600 out%02d.jpg
SOU
fonte
11
Imagens totalmente em preto e branco são captadas. Como evito isso?
D33pika
11
Existem filtros para detectar quadros e seqüências em preto deles ( ffmpeg.org/ffmpeg-filters.html#blackframe ou ffmpeg.org/ffmpeg-filters.html#blackdetect ). Você não consegue trabalhar com nenhum deles em uma linha única em crescimento, mas definitivamente deve poder remover os quadros pretos (etapa separada) e extrair miniaturas do vídeo resultante.
AM
5
Quanto aos quadros brancos, agora está ficando complicado, mas ainda parece que existe uma maneira: 1. retirar os quadros pretos 2. negar (o branco fica preto) 3. retirar os quadros brancos 4. negar novamente 5. extrair miniaturas (Se você conseguir retirar os quadros em preto ou branco, especialmente em uma linha, poderá publicá-lo aqui? Posso adicioná-lo à resposta para futuros leitores. ... ou, na verdade, você pode criar uma nova pergunta e responder Eu definitivamente iria votar novamente.)
AM
3
Para evitar imagens chatas, como preto e branco comum: gere mais miniaturas do que você precisa, compacte-as com jpeg e escolha as imagens com tamanho de arquivo maior. Isso funciona surpreendentemente bem por si só para obter miniaturas decentes.
Sam Watkins
2
Este comando não funcionou, recebi um erro Unable to find a suitable output format for 'fps=fps=1/600'. A solução é adicionar -vfantes que o argumento (ver stackoverflow.com/questions/28519403/ffmpeg-command-issue )
John Wiseman
7

Definir significado é difícil, mas se você quiser criar N miniaturas de maneira eficiente, abrangendo todo o arquivo de vídeo, é isso que eu uso para gerar miniaturas na produção com o conteúdo carregado pelo usuário.

Pseudo-código

for X in 1..N
  T = integer( (X - 0.5) * D / N )  
  run `ffmpeg -ss <T> -i <movie>
              -vf select="eq(pict_type\,I)" -vframes 1 image<X>.jpg`

Onde:

  • D - duração do vídeo lida ffmpeg -i <movie>sozinha ou ffprobecom bom gravador de saída JSON
  • N - número total de miniaturas que você deseja
  • X - número da miniatura, de 1 a N
  • T - ponto no tempo da miniatura

Simplesmente o quadro acima anota o quadro-chave central de cada partição do filme. Por exemplo, se o filme tiver 300s e você quiser 3 miniaturas, será necessário um quadro-chave após 50s, 150s e 250s. Para 5 miniaturas, seriam 30s, 90s, 150s, 210s, 270s. Você pode ajustar N, dependendo da duração do filme D, que, por exemplo, um filme de 5 minutos terá 3 miniaturas, mas mais de 1 hora terá 20 miniaturas.

atuação

Cada chamada do ffmpegcomando acima leva uma fração de segundo (!) Para ~ 1GB H.264. Isso ocorre porque ele salta instantaneamente para a <time>posição (lembre-se -ssantes -i) e pega o primeiro quadro-chave, que é praticamente o JPEG completo. Não há tempo perdido para renderizar o filme para corresponder à posição exata do tempo.

Pós-processamento

Você pode misturar acima com scaleou qualquer outro método de redimensionamento. Você também pode remover quadros de cores sólidas ou tentar misturá-los com outros filtros, como thumbnail.

gertas
fonte
2
uau mudar -ss Npara antes -ié uma dica incrível. obrigado!
Apinstein 18/03/16
2

Certa vez, fiz algo semelhante, mas exportei todos os quadros do vídeo (em 1 qps) e os comparei com um utilitário perl que achei que calcula a diferença entre as imagens. Comparei cada quadro às miniaturas anteriores e, se fosse diferente de todas as miniaturas, adicionei-o à coleção de miniaturas. A vantagem aqui é que, se o seu vídeo passar da cena A para B e retornar para A, o ffmpeg exportará 2 quadros de A.

Eran Ben-Natan
fonte
Quais fatores você usou para comparar 2 imagens?
d33pika
Infelizmente, não me lembro, foi há muito tempo. Você precisa decidir primeiro qual método de comparação usar e, em seguida, faça alguns testes para encontrar o fator correto. Isso não deve demorar muito.
Eran Ben-Natan
Só estou pensando em voz alta aqui, mas essa opção não é pior? Ao comparar duas imagens exportadas do vídeo, você já perdeu algumas informações. Quero dizer, é mais fácil calcular a similaridade das informações do codec do que das duas imagens, não é? Recentemente, estive pesquisando alguns algoritmos / bibliotecas para comparar imagens e elas não funcionam tão bem quanto se pode exigir.
Samuel
Bem, com o ffmpeg você pode extrair quadros sem perda de qualidade usando o sinalizador same_quality. Se você usar informações de codec, precisará verificar se não obtém apenas os Iframes. De qualquer forma, esse utilitário perl funcionou bem para mim e é muito configurável. Veja aqui: search.cpan.org/~avif/Image-Compare-0.3/Compare.pm
Eran Ben-Natan
2

Tente isto

 ffmpeg -i input.mp4 -vf fps= no_of_thumbs_req/total_video_time out%d.png

Usando este comando, sou capaz de gerar o número necessário de miniaturas que são representativas de todo o vídeo.

LostPuppy
fonte
Não seria a fórmula correta para fpsser (no_of_frames_req * fps_of_vid) / total_video_frames?
Flolilo 31/08/19
Estou procurando uma solução oposta: selecione mais quadros de períodos em que a câmera é mais estável e não se move? (onde a diferença entre os quadros sucessivos é menor, não maior). Existe uma maneira de fazer isso?
Tina J
1

Aqui está o que eu faço para gerar uma miniatura periódica para os fluxos m3u8 ao vivo usarem como pôster. Eu descobri que executar uma tarefa ffmpeg contínua apenas para gerar miniaturas consome toda a minha CPU, então, em vez disso, executo um cronjob a cada 60 segundos que gera todas as miniaturas para meus fluxos.

#!/usr/bin/env bash

## this is slow but gives better thumbnails (takes about 1+ minutes for 20 files)
#-vf thumbnail,scale=640:360 -frames:v 1 poster.png

## this is faster but thumbnails are random (takes about 8 seconds for 20 files)
#-vf scale=640:360 -frames:v 1 poster.png
cd ~/path/to/streams

find . -type f \
  -name "*.m3u8" \
  -execdir sh -c 'ffmpeg -y -i "$0" -vf scale=640:360 -frames:v 1 "poster.png"' \
  {} \;

Se você precisar fazer mais de 60 segundos (limitação do cronjob), poderá fazer um whileloop no seu script e ele será executado para sempre. Basta adicionar a sleep 30para alterar a frequência para 30 segundos. Porém, não recomendo fazer isso com um grande número de vídeos, pois a execução anterior pode não ser concluída antes do início da próxima.

Idealmente, com o cronjob, eu apenas o executo a cada 5 minutos.

chovy
fonte