Miniaturas significativas para un video usando FFmpeg

80

FFmpeg puede capturar imágenes de videos que pueden usarse como miniaturas para representar el video. Las formas más comunes de hacerlo están capturadas en el FFmpeg Wiki .

Pero, no quiero elegir marcos aleatorios en algunos intervalos. Encontré algunas opciones usando filtros en FFmpeg para capturar cambios de escena:

El filtro thumbnailintenta encontrar los cuadros más representativos en el video:

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

y el siguiente comando selecciona solo los cuadros que tienen más del 40% de los cambios en comparación con los anteriores (y probablemente sean cambios de escena) y genera una secuencia de 5 PNG.

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

Información de crédito para los comandos anteriores a Fabio Sonnati . El segundo parecía mejor, ya que podía obtener n imágenes y elegir el mejor. Lo probé y generó la misma imagen 5 veces.

Un poco más de investigación me llevó a:

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

-vsync vfrasegura que obtengas imágenes diferentes. Esto siempre selecciona el primer fotograma del video, en la mayoría de los casos el primer fotograma es créditos / logotipo y no tiene sentido, así que agregué un -ss3 para descartar los primeros 3 segundos del video.

Mi comando final se ve así:

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

Esto fue lo mejor que pude hacer. Me di cuenta de que, dado que elijo solo 5 videos, todos son en su mayoría desde el comienzo del video y pueden perderse escenas importantes que ocurren más adelante en el video

Me gustaría elegir tu cerebro para cualquier otra opción mejor.

d33pika
fuente
Buenos ejemplos de comandos. FWIW, no tuve ningún problema con las imágenes JPEG generadas por FFmpeg en OS X (10.8, FFmpeg 1.1 y versiones posteriores). Su penúltimo comando funciona bien para mí, igual que el último, y ninguno de estos resultados da como resultado archivos JPG en blanco. Compilé con libopenjpeg... no estoy seguro si eso hace la diferencia.
slhck
Gracias slhck Editó la pregunta con los detalles de configuración / versión de ffmpeg. No he actualizado a 1.1 en esta máquina. Haré eso y veré si cambia algún resultado.
d33pika
1
¿Entonces estás en Ubuntu? ¿Puedes probar la última versión de Git Master desde una compilación estática o compilarte y volver a ejecutar? O el último establo. Acabo de comprobar, también utiliza el mjpegcodificador para mí, y también compruebo jpegoptimy exiv2, ambos funcionan bien para mí con todos los resultados JPG de sus comandos de ejemplo.
slhck
1
¡Actualicé y funciona ahora! Supongo que la versión anterior tenía algunos errores.
d33pika
¿Puede continuar y publicar la solución: nueva versión, preferiblemente con un enlace al registro de cambios que muestre el error que encontró y que posteriormente se solucionó con la nueva versión?
Lizz

Respuestas:

29

¿Qué tal si busca, idealmente, el primer marco de cambio> 40% dentro de cada uno de 5 intervalos de tiempo, donde los intervalos de tiempo son el 1 °, 2 °, 3 °, 4 ° y 5 ° 20% del video.

También puede dividirlo en 6 períodos de tiempo y pasar por alto el primero para evitar créditos.

En la práctica, esto significaría establecer los fps en un número bajo mientras aplica la verificación de cambio de escena y su argumento para tirar el primer fragmento del video.

...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
A.M
fuente
1
Se recogen imágenes completamente en blanco o negro. ¿Cómo las evito?
d33pika
1
Hay filtros para detectar cuadros negros y secuencias de ellos ( ffmpeg.org/ffmpeg-filters.html#blackframe o ffmpeg.org/ffmpeg-filters.html#blackdetect ). Usted no podrá trabajar con ninguno de ellos en su línea única en crecimiento, pero definitivamente debería ser capaz de quitar los cuadros negros (paso separado) y extraer miniaturas del video resultante.
AM
55
En cuanto a los cuadros blancos, bueno, ahora se está volviendo complicado, pero todavía parece que hay una manera: 1. quitar los cuadros negros 2. negar (el blanco se convierte en negro) 3. quitar los cuadros blancos 4. negar nuevamente 5. extraer miniaturas (Si logra eliminar los marcos en blanco o negro, especialmente en una línea, ¿puede publicarlo aquí? Puedo agregarlo a la respuesta para futuros lectores ... o podría crear una nueva pregunta y respuesta definitivamente lo votaría)
AM
3
Para evitar imágenes aburridas como blanco y negro: genere más miniaturas de las que necesita, comprímalas con jpeg y elija las imágenes con un tamaño de archivo más grande. Esto funciona sorprendentemente bien por sí solo para obtener miniaturas decentes.
Sam Watkins, el
2
Este comando no funcionó, recibí el error Unable to find a suitable output format for 'fps=fps=1/600'. La solución es agregar -vfantes de ese argumento (ver stackoverflow.com/questions/28519403/ffmpeg-command-issue )
John Wiseman
7

Definir el significado es difícil, pero si desea que las miniaturas de N abarquen todo el archivo de video de manera eficiente, esto es lo que uso para generar miniaturas en producción con contenido cargado por el usuario.

Pseudocó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`

Dónde:

  • D: duración del video que se lee ffmpeg -i <movie>solo o ffprobeque tiene un buen escritor de salida JSON por cierto
  • N: número total de miniaturas que desea
  • X: número de miniatura, de 1 a N
  • T - punto de tiempo para tumbnail

Simplemente lo anterior escribe el fotograma clave central de cada partición de la película. Por ejemplo, si la película tiene 300 segundos de duración y desea 3 miniaturas, se necesita un fotograma clave después de 50, 150 y 250 segundos. Para 5 miniaturas sería 30s, 90s, 150s, 210s, 270s. Puede ajustar N dependiendo de la duración de la película D, por ejemplo, una película de 5 minutos tendrá 3 miniaturas pero más de 1 hora tendrá 20 miniaturas.

Actuación

Cada invocación del ffmpegcomando anterior toma una fracción de segundo (!) Para ~ 1GB H.264. Esto se debe a que salta instantáneamente a la <time>posición (mente -ssantes -i) y toma el primer fotograma clave que es prácticamente completo JPEG. No se pierde tiempo en hacer que la película coincida con la posición exacta del tiempo.

Postprocesamiento

Puede mezclar arriba con scaleo cualquier otro método de cambio de tamaño. También puede eliminar marcos de color sólido o intentar mezclarlo con otros filtros como thumbnail.

gertas
fuente
2
wow mudarse -ss Na antes -ies un consejo increíble. ¡gracias!
apinstein
2

Una vez hice algo similar, pero exporté todos los cuadros del video (en 1 fps) y los comparé con una utilidad perl que encontré que calcula la diferencia entre las imágenes. Comparé cada cuadro con las miniaturas anteriores, y si era diferente de todas las miniaturas, lo agregué a la colección de miniaturas. La ventaja aquí es que si su video se mueve de la escena A a B y regresa a A, ffmpeg exportará 2 cuadros de A.

Eran Ben-Natan
fuente
¿Qué factores usaste para comparar 2 imágenes?
d33pika
Lamentablemente no recuerdo, fue hace bastante tiempo. Primero debe decidir qué método de comparación usar, y luego hacer algunas pruebas para encontrar el factor correcto. Esto no debería llevar mucho tiempo.
Eran Ben-Natan
Solo estoy pensando en voz alta aquí, pero ¿no es esta peor opción? Cuando compara 2 imágenes exportadas del video, ya ha perdido algo de información. Quiero decir, es más fácil calcular la similitud a partir de la información del códec que a partir de 2 imágenes, ¿no? Recientemente, he estado buscando algunos algoritmos / bibliotecas para comparar imágenes y no funcionan tan bien como uno podría requerir.
Samuel
Bueno, con ffmpeg puede extraer cuadros sin pérdida de calidad utilizando el indicador same_quality. Si usa información de códec, debe verificar que no solo obtenga los Iframes. De todos modos, esa utilidad perl funcionó bien para mí y es muy configurable. Mire aquí: search.cpan.org/~avif/Image-Compare-0.3/Compare.pm
Eran Ben-Natan
2

Prueba esto

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

Con este comando, puedo generar la cantidad requerida de miniaturas que son representativas de todo el video.

Cachorro perdido
fuente
¿No sería la fórmula correcta para fpsser (no_of_frames_req * fps_of_vid) / total_video_frames?
flolilo
Estoy buscando una solución opuesta: ¿seleccionar más cuadros de períodos donde la cámara es más estable y no se mueve? (donde la diferencia entre cuadros sucesivos es menor, no mayor). ¿Hay una manera de hacerlo?
Tina J
1

Esto es lo que hago para generar una miniatura periódica para transmisiones m3u8 en vivo para usar como póster. Descubrí que ejecutar una tarea ffmpeg continua solo para generar miniaturas consume toda mi CPU, por lo que ejecuto un cronjob cada 60 segundos que genera todas las miniaturas para mis transmisiones.

#!/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"' \
  {} \;

Si necesita hacerlo con más frecuencia de 60 segundos (limitación de cronjob), puede hacer un whilebucle en su script y se ejecutará para siempre. Simplemente agregue a sleep 30para cambiar la frecuencia a 30 segundos. Sin embargo, no recomiendo hacer esto con una gran cantidad de videos, ya que la ejecución anterior puede no completarse antes de que comience la siguiente.

Idealmente con cronjob solo lo ejecuto cada 5 minutos.

chovy
fuente