Ich arbeite an einem Video-Tracking-Experiment und bin mit Videos stecken geblieben, die mit dem MPEG4 DivX 5x/6x-Codec ziemlich schlecht komprimiert sind. Ich bin ziemlich neu in Bildformaten, Codecs und Komprimierung, aber ich glaube, ich habe herausgefunden, dass ich mit dieser Qualität nicht weiterkomme, wenn ich nicht den zweiten Hauptsatz der Thermodynamik verletze.
Um meine Insekten zu verfolgen (ja, das mache ich), bin ich nur an I-Frames interessiert (die Bildrate ist hoch genug), und die Farbkanäle U und V interessieren mich nicht, da sie nur einen Wert für jeden Block haben und mir daher nicht die gewünschte Auflösung liefern. Der Y-Kanal enthält alle Informationen, die mich interessieren. Ich habe meinen Tracker selbst geschrieben und er kann kein Video analysieren, also braucht er einen Ordner mit Standbildern.
Meine Frage ist nun: Wie kann ich alle I-Frames in Graustufenbilder (nur Y-Kanal) extrahieren, OHNE dass es zu weiteren Qualitätsverlusten kommt? Ich arbeite mit Ubuntu 14.04 und würde vorzugsweise ffmpeg oder imageJ verwenden, da diese bereits in meiner Pipeline vorhanden sind. Wo ich jetzt stehe, ist:
Ich glaube, ich habe herausgefunden, dass jedes zweite Bild ein I-Bild ist, bin mir aber nicht sicher. Ich habe Folgendes verwendet:
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
Jetzt dachte ich, ich hätte herausgefunden, wie man jedes I-Frame extrahiert:
ffmpeg -i movie.avi -vf '[in]select=eq(pict_type\,I)[out]' /picture%d.jpg
Aber es scheint, als ob ich alle Frames zurückbekomme.
ls *jpg | wc -l
133370
Was mache ich falsch? Dies ist die Ausgabe, die ffmpeg mir gibt:
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
Also, ein paar Fragen:
- was mache ich falsch? Warum bekomme ich alle Frames zurück?
- Führt JPEG zu weiteren Verlusten? Oder ist es die gleiche Komprimierung wie die Intra-Frame-Komprimierung bei MPEG4? Sollte ich vielleicht stattdessen TIFF verwenden?
- Wie extrahiere ich nur den Y-Kanal?
- Ist es normal, dass ich bei jedem zweiten Frame ein I-Frame bekomme? Ich habe mich ein wenig mit der MPEG4-Kodierung beschäftigt und es scheint, dass nicht ganze Frames, sondern Blöcke als Referenz verwendet werden? Extrahiere ich dann alle Frames, die solche Blöcke enthalten? Gibt es eine höhere Ebene mit „echten“ ganzen Referenz-Frames?
- Ich nehme an, es gibt keine Möglichkeit, die Qualität wiederherzustellen?
Vielen, vielen Dank für Eure Hilfe!
Beste Wünsche,
Rik Verdonck
Antwort1
ffmpeg verwendet implizit die Bildrate der Quelle, sofern nicht ausdrücklich etwas anderes angegeben ist. Wenn die Anzahl der vom Decoder/Filter gelieferten Bilder von dieser Rate abweicht, werden Bilder dupliziert oder gelöscht, um sie zu erreichen. Dies kann behoben werden, indem für jedes ausgewählte Bild neue Zeitstempel generiert werden oder eine Bildrate angegeben wird, die der Frequenz der I-Bilder pro 1 Sekunde Video entspricht. Das Erste ist sicherer.
Sie können TIFF, PNG oder BMP anstelle von JPEG verwenden, um eine weitere Komprimierung zu vermeiden. Es ist nicht sicher, ob die Vorhersageschemata für JPEG- und MPEG-Codecs dieselben sind.
Ein I-Frame in jedem zweiten Frame ist für einen MPEG-4-Codec ungewöhnlich, aber Sie haben gesagt, diese seien schlecht codiert. Jemand hat entweder ein GOP, also ein Keyframe-Intervall von 2, oder einen sehr niedrigen Schwellenwert für Szenenwechsel eingestellt, wahrscheinlich ersteres.
Zusammenfassend verwenden
ffmpeg -i movie.avi -vf "select=eq(pict_type\,I),setpts=N/25/TB" -pix_fmt gray /picture%d.png
Herausgegeben
Für die direkte Extraktion der Y-Komponente verwenden Sie
ffmpeg -i movie.avi -vf "select=eq(pict_type\,I),setpts=N/25/TB,extractplanes=y" -pix_fmt gray /picture%d.png