Estou trabalhando em um experimento de rastreamento de vídeo e fiquei preso com vídeos que estão mal compactados com o codec MPEG4 DivX 5x/6x. Sou bastante novo em formatos de imagem, codecs e compactação, mas acho que descobri que ficarei preso a essa qualidade, a menos que viole a segunda lei da termodinâmica.
Agora, para rastrear meus insetos (sim, é isso que estou fazendo), estou interessado apenas nos quadros I (a taxa de quadros é alta o suficiente) e não estou interessado nos canais de cores U e V, já que eles só têm um valor para cada bloco e, portanto, não me dá a resolução que desejo. É o canal Y que contém todas as informações que me interessam. Eu mesmo escrevi meu rastreador e ele não consegue analisar vídeo, por isso precisa de uma pasta com fotos.
Agora, minha pergunta é: como posso extrair todos os quadros I para imagens em escala de cinza (apenas canal Y) SEM qualquer perda adicional de qualidade? Estou trabalhando no Ubuntu 14.04 e usaria preferencialmente ffmpeg ou imageJ, pois eles já estão presentes no meu pipeline. Onde estou agora é:
Acho que descobri que cada segundo quadro é um quadro I, mas não tenho certeza disso. Eu usei:
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
Agora, pensei ter descoberto como extrair cada quadro I:
ffmpeg -i movie.avi -vf '[in]select=eq(pict_type\,I)[out]' /picture%d.jpg
Mas parece me devolver todos os quadros.
ls *jpg | wc -l
133370
O que estou fazendo de errado? Esta é a saída que o ffmpeg me fornece:
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
Então, algumas perguntas:
- O que estou fazendo de errado? Por que isso me devolve todos os frames?
- O JPEG causará mais perdas? Ou é a mesma compactação usada intra-frame no mpeg4? Talvez eu deva usar o tiff?
- Como faço para extrair apenas o canal y?
- É normal que eu receba um quadro I a cada segundo quadro? Tenho lido um pouco sobre a codificação MPEG4 e parece que nem quadros inteiros, mas sim blocos são usados como referência? Estou então extraindo todos os quadros que contêm esses blocos? Existe um nível mais alto com referenciais inteiros "reais"?
- Acho que não há como recuperar mais qualidade?
Muito, muito obrigado pela sua ajuda!
Muitas felicidades,
Rick Verdonck
Responder1
ffmpeg usa implicitamente a taxa de quadros da fonte, a menos que seja explicitamente especificado de outra forma. Se o número de quadros fornecidos pelo decodificador/filtro for diferente dessa taxa, os quadros serão duplicados ou descartados para alcançá-lo. Isso pode ser remediado gerando novos carimbos de data/hora para cada quadro selecionado ou especificando uma taxa de quadros que corresponda à frequência de quadros I por 1 segundo de vídeo. É mais seguro fazer o primeiro.
Você pode usar TIFF, PNG ou BMP em vez de JPEG para evitar compactação adicional. Não tenho certeza se os esquemas de previsão são os mesmos para codecs JPEG e MPEG.
Um quadro I a cada dois quadros é incomum para um codec MPEG-4, mas você disse que eles estavam mal codificados. Alguém definiu um GOP, ou seja, intervalo de quadro-chave de 2 ou um limite de mudança de cena muito baixo, provavelmente o primeiro.
Em resumo, use
ffmpeg -i movie.avi -vf "select=eq(pict_type\,I),setpts=N/25/TB" -pix_fmt gray /picture%d.png
Editado
Para extração direta do componente Y, use
ffmpeg -i movie.avi -vf "select=eq(pict_type\,I),setpts=N/25/TB,extractplanes=y" -pix_fmt gray /picture%d.png