盡可能無損地從 MPEG4 影片中提取每個 I 幀的 Y 通道

盡可能無損地從 MPEG4 影片中提取每個 I 幀的 Y 通道

我正在進行視訊追蹤實驗,但遇到了使用 MPEG4 DivX 5x/6x 編解碼器壓縮得非常糟糕的影片。我對圖像格式、編解碼器和壓縮相當陌生,但我想我發現除非我違反熱力學第二定律,否則我會堅持這種品質。

現在,為了追蹤我的昆蟲(是的,這就是我正在做的事情),我只對 I 幀感興趣(幀速率足夠高),並且我對顏色通道 U 和 V 不感興趣,因為它們只有每個區塊都有一個值,因此不會給我我想要的解析度。 Y 通道包含我感興趣的所有資訊。

現在我的問題是:如何將所有 I 幀提取為灰階(僅限 Y 通道)影像,而不會造成任何進一步的品質損失?我在 ubuntu 14.04 工作,我優先使用 ffmpeg 或 imageJ,因為它們已經存在於我的管道中。我現在所在的位置是:

我想我發現每隔一幀都是一個 I 幀,但我不確定這一點。我用了:

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

現在,我想我知道如何提取每個 I 幀:

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

但它似乎給了我所有的幀。

ls *jpg | wc -l
133370

我究竟做錯了什麼?這是 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

所以,有幾個問題:

  • 我究竟做錯了什麼?為什麼它給我所有幀?
  • jpeg會造成進一步的損失嗎?或者它與 mpeg4 中幀內使用的壓縮相同嗎?我該改用 tiff 嗎?
  • 如何只提取 y 通道?
  • 每隔一幀就收到一個 I 幀,這正常嗎?我一直在閱讀 MPEG4 編碼,似乎不是使用整個幀,而是使用區塊作為參考?然後我是否提取包含此類塊的所有幀?是否存在具有“真實”整個參考系的更高級別?
  • 我想有沒有辦法恢復更多的品質?

非常非常感謝您的幫忙!

最好的祝愿,

里克·維東克

答案1

除非另有明確指定,否則 ffmpeg 隱式使用來源的幀速率。如果解碼器/過濾器提供的幀數與該速率不同,則透過複製或丟棄幀來實現該速率。這可以透過為每個選定的幀產生新的時間戳或指定與每 1 秒影片的 I 幀頻率相符的幀速率來解決。做第一個比較安全。

您可以使用 TIFF 或 PNG 或 BMP 而不是 JPEG 以避免進一步壓縮。不確定 JPEG 和 MPEG 編解碼器的預測方案是否相同。

對於 MPEG-4 編解碼器來說,每隔一格就有一個 I 幀是不尋常的,但您確實說過這些編碼很糟糕。有人設定了 GOP,即關鍵影格間隔為 2,或設定了非常低的場景變化閾值,很可能是前者。

總而言之,使用

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

已編輯

對於直接 Y 分量提取,請使用

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

相關內容