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

1

Estou trabalhando em um experimento de rastreamento de vídeo e me deparo com vídeos bastante comprimidos com o codec MPEG4 DivX 5x / 6x. Sou relativamente novo em formatos de imagens, codecs e compressão, mas acho que descobri que estou preso a essa qualidade, a menos que eu viole a segunda lei da termodinâmica.

Agora, para rastrear meus insetos (sim, é o que estou fazendo), estou interessado apenas em quadros I (a taxa de quadros é alta o suficiente) e não estou interessado nos canais de cores U e V, eles só têm um valor para cada bloco e, portanto, não me dão a resolução que eu quero. É o canal Y que tem todas as informações que me interessam. Escrevi meu tracker e não é possível analisar o vídeo, então ele precisa de uma pasta com fotos.

Agora, minha pergunta é: como posso extrair todos os I-frames para imagens em escala de cinza (somente Y) sem perda adicional de qualidade? Estou trabalhando no Ubuntu 14.04 e prefiro usar ffmpeg ou imageJ, já que eles já estão presentes no meu pipeline. Onde estou agora é:

Eu acho que descobri que cada segundo frame é um frame, 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 em descobrir como extrair todos os quadros:

ffmpeg -i movie.avi -vf '[in]select=eq(pict_type\,I)[out]' /picture%d.jpg         

Mas parece que me deu todos os frames.

ls *jpg | wc -l
133370

O que estou fazendo de errado? Esta é a saída que o ffmpeg me dá:

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 errado? Por que isso me dá todos os frames de volta?
  • O jpeg causará mais perdas? Ou é a mesma compressão que o intra-frame usado no mpeg4? Eu deveria usar tiff em vez disso?
  • Como eu apenas extraio o canal y?
  • É normal obter um quadro I a cada segundo quadro? Eu tenho lido em codificação MPEG4 um pouco e parece que não quadros inteiros, mas sim blocos são usados como referência? Estou extraindo todos os quadros que contêm esses blocos? Existe um nível mais alto com referenciais inteiros "reais"?
  • Eu acho que não há como recuperar mais qualidade?

Muito, muito obrigado pela sua ajuda!

Felicidades,

Rik Verdonck

    
por Goat-Anti-Rabbit 29.01.2016 / 10:24

1 resposta

4

O ffmpeg usa implicitamente a taxa de quadros da fonte, a menos que explicitamente especificado de outra forma. Se o número de quadros fornecidos pelo decodificador / filtro for diferente daquela taxa, os quadros serão duplicados ou descartados para alcançá-lo. Isso pode ser solucionado gerando novos timestamps 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 ou PNG ou BMP em vez de JPEG para evitar mais compactação. Não há certeza se os esquemas de previsão são os mesmos para os codecs JPEG e MPEG.

Um quadro I de todos os outros 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 de componentes Y, use

ffmpeg -i movie.avi -vf "select=eq(pict_type\,I),setpts=N/25/TB,extractplanes=y" -pix_fmt gray /picture%d.png   
    
por 29.01.2016 / 11:01