Extraia o canal Y de cada quadro I do filme MPEG4 da maneira mais sem perdas possível

Extraia o canal Y de cada quadro I do filme MPEG4 da maneira mais sem perdas possível

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   

informação relacionada