¿Cómo codificar sin pérdida una secuencia de imágenes jpg en un video en ffmpeg?

21

Tengo un gran conjunto de archivos jpgs que quiero convertir a un video sin pérdidas (o, al menos, muy cerca de sin pérdidas, siempre y cuando el tiempo de codificación no sea mucho mayor que lo contrario).

Ingenuamente, pensaría que debería haber algún códec que pueda almacenar cada fotograma jpg individual tal como está (sin recompresión), y tal vez lograr una buena compresión al reemplazar algunos de los fotogramas solo por la información del delta del fotograma anterior. En mi caso, hay muchas secuencias de cuadros que son idénticas entre sí, o que tienen una pequeña diferencia entre ellas.

¿Hay algún códec y configuraciones adecuadas para ffmpeg que puedan lograr esto?

GJ
fuente
Relacionado: avp.stackexchange.com/questions/4642/…
Friend Of George
También relacionado: avp.stackexchange.com/questions/7300/…
Friend Of George
1
Secuencia de JPEG ha sido un códec durante mucho tiempo. Las cámaras digitales que no usan h.264 graban invariablemente MJPEG, y creo que las tarjetas de captura de video solían usarlo.
Peter Cordes

Respuestas:

24

Solo mux las imágenes

Simplemente puede modificar las imágenes JPG para hacer un video:

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

Tenga en cuenta que si omite , se aplicará -framerateun valor predeterminado de -framerate 25a la entrada.

Optimización sin pérdidas

Puede usar jpegtranpara realizar una optimización sin pérdidas en cada marco que puede proporcionar ahorros significativos en el tamaño del archivo:

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

Ahora mux con ffmpegcomo se muestra arriba.

Comprobando que en realidad no tiene pérdidas

El framehash muxer se puede utilizar para comparar el hash único de cada cuadro para garantizar que el resultado sea realmente sin pérdidas:

$ 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
...

En los ejemplos anteriores, cada cuadro asociado para la entrada y la salida comparten el mismo hash asegurando que los cuadros sean idénticos y que la salida no tenga pérdidas.

Ver también

lema
fuente
¿podría aclarar qué framemd5se supone que deben lograr los dos comandos más allá de simplemente enumerar los hashes? ¿Cómo obtendría una compresión adicional cuando se identifican cuadros idénticos?
GJ.
1
Los hash se incluyeron solo para mostrarle que los marcos son los mismos que las imágenes individuales, por lo tanto, cumple con su requisito de almacenar "cada uno de los marcos jpg individuales tal como están (sin recompresión)".
llogan
Publiqué una respuesta propia con una idea no probada para eliminar los cuadros duplicados, para terminar con un VFR MJPEG.mkv. VFR es la única forma en que se me ocurre aprovechar la redundancia temporal con MJPEG. : P
Peter Cordes
SSIM puede ser una forma más rápida de comparar fidelidad.
Gyan
11

Esto generará un video H.264 sin pérdidas donde los cuadros usarán información de otros cuadros

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

Explicación de opciones:

  • -f image2 - le dice a ffmpeg que seleccione un grupo de imágenes
  • -r 30 - le dice a ffmpeg que codifique a 30 cuadros (o imágenes) por segundo (cambie esto a la velocidad de cuadros deseada)
  • -i %09d.jpg- le dice a ffmpeg que use las imágenes 000000000.jpg a 999999999.jpg como entrada. Cambiar el 9en %09d.jpgque el número de ceros los nombres de su secuencia de imágenes tiene. Si sus nombres de archivo son, por ejemplo, img0001.jpg, esto se expresaría como img% 04d.jpg
  • -vcodec libx264 - le dice a ffmpeg que envíe a un archivo compatible con H.264
  • -profile:v high444 - le dice a libx264 que use el perfil 4: 4: 4 predictivo sin pérdidas, lo que permite la codificación sin pérdidas
  • -refs 16 - le dice a libx264 que tenga 16 imágenes almacenadas en un búfer, para que otras imágenes en el video puedan hacer referencia a ellas
  • -crf 0 - le dice a libx264 que realice una codificación sin pérdidas
  • -preset ultrafast - le dice a libx264 que priorice la velocidad de codificación sobre el tamaño del archivo de salida
  • a.mp4- le dice a ffmpeg que guarde la salida en un archivo MP4 llamado a.mp4. Cambie esto al nombre de archivo y al formato que desea usar
Adam Chance
fuente
3
Algunas notas: -f image2es superfluo aquí. El archivo de imagen demuxer debe usar en -frameratelugar de -r. libx264 elegirá automáticamente el apropiado -profilepara sin pérdidas, y -presetse ocupará de ello -refs.
llogan
-refs 5como máximo, a menos que sepa que su contenido tiene imágenes idénticas separadas por varias otras, eso podría causar que x264 pierda la referencia antes de que llegue al duplicado. Más alto que ultrafasthace poco diferente en el modo sin pérdida, aparte de la ganancia de ~ 10% de CABAC sobre CAVLC (por un alto costo de CPU a las tasas de bits requeridas para sin pérdida). En serio, en algunos 720x480p60 de acción en vivo (salida de desentrelazado), superfastera de 28 GB, slowerera de 27 GB. Si el tiempo de codificación no importa, pero el tiempo de decodificación sí, asegúrese de evitar CABAC. Quizás incluso -tune fastdecode. El recuento moderado de referencia no debería doler.
Peter Cordes
Y si tiene CPU para grabar, incluso puede intentar -preset placeboalgunas fracciones adicionales de porcentaje.
DrYak
También para completar h265 también tiene su propio modo sin pérdidas. -vcodec libx265 -x265-params lossless=1Es la opción equivalente. (Pero en mi experiencia (= grabación de presentaciones de diapositivas de Powerpoint), no es necesariamente mejor, y es mucho más lento que h264) Manténgase sintonizado para AV1 de AOMedia / NETVC1 de IETF / Daala de Xiph / lo que sea que vaya a cambiar de nombre para entonces ... el modo sin pérdidas
DrYak
5

Puede crear una avianimación como una serie de pngimágenes (no png tiene pérdidas, por lo que la jpeg => pngconversión no debería degradar sus imágenes):

si tus imágenes tienen un nombre img_0001.jpg

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

donde "25" es la velocidad de fotogramas que desea en el video resultante. -start_numberno es necesario si es 1, pero es útil si su primer número de video no es 1.

Si desea codificar mjpegcon la línea de comandos de mayor calidad es:

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

Y lo bueno de esto es que puedes convertir el video a una serie de imágenes:

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

etc ...

Olivier S
fuente
Esto realmente no satisface las necesidades de los solicitantes. Está buscando una manera de que el marco se pueda actualizar sin pérdidas solo cuando la imagen cambia. Esto significa que la misma imagen se puede usar más de una vez. Además, el JPEG por naturaleza no es sin pérdidas, ya que creo que utiliza la compresión JPEG incluso con la máxima calidad.
AJ Henderson
En realidad, supongo que estaba dispuesto a tener algo de compresión, aunque no estoy seguro de cómo va a funcionar en secuencias largas del mismo cuadro. Todavía creo que se necesita un formato de presentación de velocidad de cuadro variable, aunque no estoy seguro de si ffmpeg es compatible con alguno.
AJ Henderson
CorePNG es capaz de crear marcos P también. Por lo general, jpeg no es una compresión sin pérdidas, y dudo que mjpeg pueda crear fotogramas P. Estoy de acuerdo en que no respondo la pregunta como se hace, pero doy una solución para tener un video sin pérdidas con ffmpeg.
Olivier S
4

Para ampliar la respuesta de LordNeckbeard, sí, solo mux los datos JPEG en una transmisión de video MJPEG. Esa será la representación más pequeña de la secuencia exacta de imágenes de salida, a pesar de que MJPEG es un códec terriblemente ineficiente para los estándares actuales. (sin redundancia temporal, y ni siquiera una predicción intra.

Puede hacer un video MJPEG de velocidad de fotogramas variable para aprovechar las imágenes duplicadas en su entrada.

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

Hrm, esto no va a funcionar, ya que mpdecimate no funcionará en datos comprimidos, y no podemos dejar que ffmpeg decodifique y luego vuelva a jpeg los datos de imagen sin pérdida y costo de CPU.

¿Tal vez si reemplaza los archivos fuente duplicados jpg con archivos vacíos con ese número de secuencia, o algo así?

Como esta pregunta ni siquiera es reciente, no me tomaré el tiempo para descubrir cómo hacerlo a menos que alguien responda y pregunte cómo. Pero como MJPEG puede entrar en un contenedor mkv, estoy seguro de que es posible tener un archivo que no duplique los datos jpeg para cuadros repetidos, sino que simplemente no tiene un cuadro de salida para decodificar hasta que la secuencia de duplicados sea encima.

Oh, aquí hay una idea:

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

Luego, elimine (o mueva a un lado) todos los archivos jpegs de los marcos que mpdecimate quiere eliminar (¿probablemente tiene algunas opciones de registro? los archivos JPEG caídos?). muje eso a un MJPEG.mkv, luego haga algo con mkvmerge para reemplazar las marcas de tiempo del marco en eso con las marcas de tiempo de mpdecimate.timestamps.

Si estuviera codificando, en lugar de simplemente enviar datos jpeg a MJPEG, esto sería MUCHO más fácil, ya que simplemente usaría mi primer comando con mpdecimate y cualquier códec que no sea copy, y simplemente Funcionaría (tm).

No he intentado nada de esto, ya que esta era una vieja pregunta. También la razón por la que no he rellenado los vacíos de cómo filtrar realmente su directorio de archivos JPEG en función de la salida mpdecimate, o cómo usar realmente la secuencia de marca de tiempo.

Peter Cordes
fuente