Como codificar sem perdas uma sequência de imagens jpg em um vídeo no ffmpeg?


21

Eu tenho um grande conjunto de jpgs que desejo converter em um vídeo sem perdas (ou, pelo menos, muito próximo a sem perdas, desde que o tempo de codificação não seja muito maior do que o contrário).

Ingenuamente, eu pensaria que deveria haver algum codec que possa armazenar cada quadro jpg como está (sem recompressão) e talvez obter uma boa compactação substituindo alguns quadros apenas pelas informações sobre o delta do quadro anterior. No meu caso, existem muitas sequências de quadros idênticas entre si, ou que têm uma pequena diferença entre elas.

Existe algum codec e configurações adequadas para o ffmpeg que podem conseguir isso?




1
A sequência de jpegs é um codec há muito tempo. Câmeras digitais que não usam h.264 invariavelmente gravam MJPEG e placas de captura de vídeo costumavam usá-lo, eu acho.
Peter Cordes

Respostas:


24

Apenas mux as imagens

Você pode simplesmente compactar as imagens JPG para criar um vídeo:

ffmpeg -framerate 30 -i input%03d.jpg -codec copy output.mkv

Observe que, se você omitir -framerate, um padrão de -framerate 25será aplicado à entrada.

Otimização sem perdas

Você pode usar jpegtranpara executar a otimização sem perdas em cada quadro, o que pode proporcionar uma economia significativa no tamanho do arquivo:

mkdir outputdir
for f in *.jpg; do jpegtran -optimize -copy none -perfect -v "$f" > "outputdir/$f"; done

Agora mux com ffmpegcomo mostrado acima.

Verificando se está realmente sem perdas

O framehash muxer pode ser usado para comparar o hash exclusivo de cada quadro para garantir que o resultado seja realmente sem perdas:

$ ffmpeg -i input%03d.jpg -f framehash -
stream_index, packet_dts, packet_pts, packet_duration, packet_size, hash
0,          0,          0,        1,   460800, 29bcc2db3726c7dfec1826c5740f603f
0,          1,          1,        1,   460800, b5fdc23d93cbd043dc2b9290dc8378f0
0,          2,          2,        1,   460800, ee0709942f24b458fd2380d134dcb59d
...

$ ffmpeg -i output.mkv -map 0:v -f framehash -
stream_index, packet_dts, packet_pts, packet_duration, packet_size, hash
0,          0,          0,        1,   460800, 29bcc2db3726c7dfec1826c5740f603f
0,          1,          1,        1,   460800, b5fdc23d93cbd043dc2b9290dc8378f0
0,          2,          2,        1,   460800, ee0709942f24b458fd2380d134dcb59d
...

Nos exemplos acima, cada quadro associado à entrada e saída compartilha o mesmo hash, garantindo que os quadros sejam idênticos e que a saída seja sem perdas.

Veja também


você poderia esclarecer o que os dois framemd5comandos devem alcançar além de apenas listar os hashes? como obter compressão adicional quando quadros idênticos são identificados?
GJ.

1
Os hashes foram incluídos apenas para mostrar que os quadros são os mesmos que as imagens individuais, atendendo assim ao seu requisito de armazenar "cada quadro jpg individual como está (sem recompressão)".
Llogan

Publiquei uma resposta minha com uma idéia não testada para eliminar os quadros duplicados, para acabar com um VFR MJPEG.mkv. VFR é a única maneira em que posso pensar em tirar proveito da redundância temporal com o MJPEG. : P
Peter Cordes

O SSIM pode ser uma maneira mais rápida de comparar a fidelidade.
Gyan

11

Isso produzirá um vídeo H.264 sem perdas, onde os quadros usarão informações de outros quadros

ffmpeg -f image2 -r 30 -i %09d.jpg -vcodec libx264 -profile:v high444 -refs 16 -crf 0 -preset ultrafast a.mp4

Explicação das opções:

  • -f image2 - diz ao ffmpeg para selecionar um grupo de imagens
  • -r 30 - diz ao ffmpeg para codificar a 30 quadros (ou imagens) por segundo (altere para a taxa de quadros desejada)
  • -i %09d.jpg- diz ao ffmpeg para usar as imagens 000000000.jpg a 999999999.jpg como entrada. Altere 9in %09d.jpgpara quantos zeros têm os nomes da sequência de imagens. Se os nomes dos seus arquivos forem, por exemplo, img0001.jpg, isso será expresso como img% 04d.jpg
  • -vcodec libx264 - instrui o ffmpeg a enviar para um arquivo compatível com H.264
  • -profile:v high444 - diz à libx264 para usar o perfil sem perdas preditivas 4: 4: 4 alto, permitindo codificação sem perdas
  • -refs 16 - diz à libx264 para ter 16 imagens armazenadas em um buffer, para que possam ser referenciadas por outras imagens no vídeo
  • -crf 0 - diz à libx264 para executar uma codificação sem perdas
  • -preset ultrafast - diz à libx264 para priorizar a velocidade de codificação sobre o tamanho do arquivo de saída
  • a.mp4- diz ao ffmpeg para salvar a saída em um arquivo MP4 chamado a.mp4. Mude para o nome e formato do arquivo que você deseja usar

3
Algumas notas: -f image2é supérfluo aqui. O desmuxador do arquivo de imagem deve usar em -frameratevez de -r. A libx264 escolherá automaticamente o apropriado -profilepara sem perdas e o que -presetserá tratado -refs.
Llogan

-refs 5no máximo, a menos que você saiba que seu conteúdo tem imagens idênticas separadas por várias outras, isso pode fazer com que x264 perca a referência antes de chegar à duplicata. Maior que o ultrafastque pouco diferencia no modo sem perdas, exceto o ganho de ~ 10% do CABAC sobre o CAVLC (para um alto custo de CPU nas taxas de bits exigidas para as perdas). Sério, em alguns 720x480p60 de ação ao vivo (saída de desentrelaçamento), superfastera de 28 GB, slowerera de 27 GB . Se o tempo de codificação não importa, mas o tempo de decodificação, não se esqueça de evitar o CABAC. Talvez até -tune fastdecode. A contagem moderada de árbitros não deve doer.
22816 Peter Cordes

E se você tiver CPU para gravar, pode tentar -preset placeboalgumas frações extras de porcentagem.
DrYak

Também para completar, o h265 também possui seu próprio modo sem perdas. -vcodec libx265 -x265-params lossless=1é a opção equivalente. (Mas, na minha experiência (= gravação de apresentações de slides do Powerpoint), não é necessariamente melhor, e é muito mais lento que o h264. ... do modo sem perdas
DrYak 22/06

5

Você pode criar uma avianimação como uma série de pngimagens ( png é lossless assim que a jpeg => pngconversão não deve degradar suas fotos):

se suas imagens tiverem um nome img_0001.jpg

ffmpeg -r 25 -start_number 1 -f image2 -i "img_%04d.jpg" -vcodec png video.avi

onde "25" é a taxa de quadros que você deseja no vídeo resultante. -start_numbernão é necessário se for 1, mas é útil se o seu primeiro número de vídeo não for 1.

Se você deseja codificar mjpegcom a linha de comando da mais alta qualidade, é:

ffmpeg -r 25 -start_number 1 -f image2 -i "img_%04d.jpg" -vcodec mjpeg -qscale 1 video.avi

E o mais importante é que você pode converter o vídeo em uma série de imagens:

ffmpeg -i video.avi "img_series_%04d.png"
ffmpeg -i video.avi "img_series_%04d.jpg"

etc ...


Isso realmente não atende às necessidades dos solicitantes. Ele está procurando uma maneira de atualizar o quadro sem perdas apenas quando a imagem muda. Isso significa que a mesma imagem pode ser usada mais de uma vez. Além disso, o jpeg por natureza não é sem perdas, pois acredito que ele usa compactação jpeg mesmo na qualidade máxima.
AJ Henderson

Na verdade, acho que ele estava disposto a ter alguma compressão, embora não tenha certeza de como será em longas sequências do mesmo quadro. Ainda acho que é necessário um formato de apresentação de taxa de quadros variável, embora não tenha certeza se o ffmpeg suporta algum.
AJ Henderson

O CorePNG também pode criar quadros P. Normalmente, o jpeg não é uma compactação sem perdas e duvido que o mjpeg possa criar quadros P. Concordo que não respondo à pergunta como ela é solicitada, mas dou uma solução para ter um vídeo sem perdas com ffmpeg.
Olivier S

4

Para expandir a resposta de LordNeckbeard, sim, basta compactar os dados JPEG em um fluxo de vídeo MJPEG. Essa será a menor representação da sequência exata de imagens de saída, mesmo que o MJPEG seja um codec terrivelmente ineficiente para os padrões atuais. (sem redundância temporal e nem mesmo previsão interna.

Você pode criar um vídeo MJPEG com taxa de quadros variável para aproveitar as imagens duplicadas em sua entrada.

ffmpeg -framerate 30 -i input%03d.jpg -vf mpdecimate -codec copy output.mkv  # doesn't work.

Hm, isso não vai funcionar, pois o mpdecimate não funciona com dados compactados, e não podemos permitir que o ffmpeg decodifique e depois repita o jpeg nos dados da imagem sem perda e custo da CPU.

Talvez se você substituísse arquivos de origem jpg duplicados por arquivos vazios com esse número de sequência, ou algo assim?

Como essa pergunta nem é recente, não vou demorar para descobrir como fazê-lo, a menos que alguém responda para perguntar como. Mas como o MJPEG pode entrar em um contêiner mkv, tenho certeza de que é possível ter um arquivo que não duplique os dados jpeg para quadros repetidos, mas apenas não tenha um quadro de saída para decodificar até que a sequência de duplicatas seja sobre.

Oh, aqui está uma ideia:

ffmpeg -framerate blah -input blah -vf mpdecimate -f mkvtimestamp_v2 mpdecimate.timestamps

Em seguida, remova (ou mova para o lado) todos os jpegs dos quadros que o mpdecimate deseja soltar (provavelmente possui algumas opções de registro? Ou -vf showinfo, analise-o e mova ou vincule somente os quadros que aparecem em sua saída, deixando para trás os JPEGs descartados?). mux isso para um MJPEG.mkv e, em seguida, faça algo com mkvmerge para substituir os carimbos de data / hora do quadro naqueles pelos carimbos de data e hora de mpdecimate.timestamps.

Se você estivesse usando o xcoding, em vez de apenas misturar dados jpeg no MJPEG, isso seria MUITO mais fácil, já que você usaria meu primeiro comando com mpdecimate e qualquer outro codec que copynão fosse, e funcionaria apenas (tm).

Eu não tentei nada disso, pois essa era uma pergunta antiga. Além disso, o motivo pelo qual não preenchi as lacunas de como realmente filtrar seu diretório de jpegs com base na saída mpdecimate, ou como realmente usar o fluxo de carimbo de data / hora.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.