Estoy trabajando en un experimento de seguimiento de video y me quedé atascado con videos que están bastante mal comprimidos con el códec MPEG4 DivX 5x/6x. Soy bastante nuevo en formatos de imagen, códecs y compresión, pero creo que me di cuenta de que estaré estancado en esta calidad a menos que viole la segunda ley de la termodinámica.
Ahora, para rastrear mis insectos (sí, eso es lo que estoy haciendo), sólo me interesan los fotogramas I (la velocidad de fotogramas es bastante alta), y no me interesan los canales de color U y V, ya que sólo tienen un valor para cada bloque y por lo tanto no me da la resolución que quiero. Es el canal Y el que tiene toda la información que me interesa. Escribí mi rastreador yo mismo y no puede analizar videos, por lo que necesita una carpeta con imágenes fijas.
Ahora mi pregunta es: ¿cómo puedo extraer todos los fotogramas I a imágenes en escala de grises (solo canal Y) SIN ninguna pérdida adicional de calidad? Estoy trabajando en ubuntu 14.04 y preferiría usar ffmpeg o imageJ, ya que ya están presentes en mi canalización. Donde estoy ahora es:
Creo que descubrí que cada segundo cuadro es un cuadro I, pero no estoy seguro de eso. Solía:
ffprobe -show_frames movie.avi | grep -A2 "video" | grep "key_frame"
output:
key_frame=1
key_frame=0
key_frame=1
key_frame=0
key_frame=1
key_frame=0
key_frame=1
key_frame=0
key_frame=1
key_frame=0
--
this goes on for exactly the number of frames, as this bit of code tells me:
ffprobe -show_frames movie.avi | grep -A2 "video" | grep -c "key")
13369
Ahora, pensé que había descubierto cómo extraer cada cuadro I:
ffmpeg -i movie.avi -vf '[in]select=eq(pict_type\,I)[out]' /picture%d.jpg
Pero parece devolverme todos los fotogramas.
ls *jpg | wc -l
133370
¿Qué estoy haciendo mal? Esta es la salida que me da ffmpeg:
ffmpeg version N-77455-g4707497 Copyright (c) 2000-2015 the FFmpeg developers
built with gcc 4.8 (Ubuntu 4.8.4-2ubuntu1~14.04)
configuration: --extra-libs=-ldl --prefix=/opt/ffmpeg --mandir=/usr/share/man --enable-avresample --disable-debug --enable-nonfree --enable-gpl --enable-version3 --enable-libopencore-amrnb --enable-libopencore-amrwb --disable-decoder=amrnb --disable-decoder=amrwb --enable-libpulse --enable-libdcadec --enable-libfreetype --enable-libx264 --enable-libx265 --enable-libfdk-aac --enable-libvorbis --enable-libmp3lame --enable-libopus --enable-libvpx --enable-libspeex --enable-libass --enable-avisynth --enable-libsoxr --enable-libxvid --enable-libvo-aacenc --enable-libvidstab
libavutil 55. 11.100 / 55. 11.100
libavcodec 57. 20.100 / 57. 20.100
libavformat 57. 20.100 / 57. 20.100
libavdevice 57. 0.100 / 57. 0.100
libavfilter 6. 21.101 / 6. 21.101
libavresample 3. 0. 0 / 3. 0. 0
libswscale 4. 0.100 / 4. 0.100
libswresample 2. 0.101 / 2. 0.101
libpostproc 54. 0.100 / 54. 0.100
Guessed Channel Layout for Input Stream #0.1 : stereo
Input #0, avi, from 'movie.avi':
Duration: 00:08:54.76, start: 0.000000, bitrate: 3006 kb/s
Stream #0:0: Video: mpeg4 (Simple Profile) (DX50 / 0x30355844), yuv420p, 720x576 [SAR 16:15 DAR 4:3], 1462 kb/s, 25 fps, 25 tbr, 25 tbn, 25 tbc
Stream #0:1: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 48000 Hz, 2 channels, s16, 1536 kb/s
[swscaler @ 0x3c2e920] deprecated pixel format used, make sure you did set range correctly
Output #0, image2, to './picture%d.jpg':
Metadata:
encoder : Lavf57.20.100
Stream #0:0: Video: mjpeg, yuvj420p(pc), 720x576 [SAR 16:15 DAR 4:3], q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc
Metadata:
encoder : Lavc57.20.100 mjpeg
Side data:
unknown side data type 10 (24 bytes)
Stream mapping:
Stream #0:0 -> #0:0 (mpeg4 (native) -> mjpeg (native))
Press [q] to stop, [?] for help
frame=13370 fps=506 q=24.8 Lsize=N/A time=00:08:54.80 bitrate=N/A dup=6685 drop=0 speed=20.2x
video:157591kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
Entonces, un par de preguntas:
- ¿Qué estoy haciendo mal? ¿Por qué me devuelve todos los fotogramas?
- ¿Jpeg causará más pérdidas? ¿O es la misma compresión que se usa dentro del marco en mpeg4? ¿Quizás debería usar tiff en su lugar?
- ¿Cómo extraigo solo el canal Y?
- ¿Es normal que aparezca un cuadro I cada segundo cuadro? He estado leyendo un poco sobre la codificación MPEG4 y parece que no se utilizan fotogramas completos, sino bloques como referencia. ¿Estoy entonces extrayendo todos los fotogramas que contienen dichos bloques? ¿Existe un nivel superior con marcos de referencia completos "reales"?
- ¿Supongo que no hay forma de recuperar más calidad?
¡Muchas, muchas gracias por tu ayuda!
Los mejores deseos,
Rik Verdonck
Respuesta1
ffmpeg utiliza implícitamente la velocidad de fotogramas de la fuente a menos que se especifique explícitamente lo contrario. Si el número de fotogramas proporcionados por el decodificador/filtro es diferente a esa velocidad, entonces los fotogramas se duplican o eliminan para lograrlo. Esto se puede solucionar generando nuevas marcas de tiempo para cada fotograma seleccionado o especificando una velocidad de fotogramas que coincida con la frecuencia de fotogramas I por segundo de vídeo. Es más seguro hacer lo primero.
Puede utilizar TIFF, PNG o BMP en lugar de JPEG para evitar una mayor compresión. No estoy seguro de si los esquemas de predicción son los mismos para los códecs JPEG y MPEG.
Un cuadro I cada dos cuadros es inusual para un códec MPEG-4, pero usted dijo que estaban mal codificados. Alguien estableció un GOP, es decir, un intervalo de fotograma clave de 2, o un umbral de cambio de escena muy bajo, probablemente lo primero.
En resumen, utilice
ffmpeg -i movie.avi -vf "select=eq(pict_type\,I),setpts=N/25/TB" -pix_fmt gray /picture%d.png
Editado
Para la extracción directa del componente Y, utilice
ffmpeg -i movie.avi -vf "select=eq(pict_type\,I),setpts=N/25/TB,extractplanes=y" -pix_fmt gray /picture%d.png