VPF:適用於 Python 的開源影片處理框架,加速影片任務、提高 GPU 利用率

  • 2020 年 2 月 11 日
  • 筆記

近日,NVIDIA 開源了適用於 Python 的影片處理框架「VideoProcessingFramework(VPF)」。該框架為開發人員提供了一個簡單但功能強大的 Python 工具,可用於硬體加速的影片編碼、解碼和處理類等任務。

同時,由於 Python 綁定下的 C ++程式碼,它使開發者可以在數十行程式碼中實現較高的 GPU 利用率。解碼後的影片幀以 NumPy 數組或 CUDA 設備指針的形式公開,以簡化交互過程及其擴展功能。

目前,VPF 並未對 NVIDIA Video Codec SDK 附加任何限制,開發者可充分利用 NVIDIA 專業級 GPU 的功能。

Python 中的硬體加速影片處理框架 VPF


VPF 是基於 CMake 的開源跨平台框架,它依賴於 FFmpeg 庫來進行(de)muxing 和 pybind11 項目從而構建 Python 綁定。它包含了一組開源的 C ++庫和 Python 綁定,可與其封閉源程式碼 Codec SDK 進行交互。

該框架的主要功能是簡化從 Python 開發 GPU 加速影片編碼/解碼的過程,可為影片處理任務(例如解碼,編碼,程式碼轉換以及 GPU 加速的色彩空間和像素格式轉換)提供完整的硬體加速。

儘管 Python 不是性能最高的語言,但它易於使用;在 NVIDIA 發布此影片處理框架之後,它相當於在現有 Video Codec SDK C ++ 堆棧周圍的 Python wrapper,將用於在 Kepler 及更高版本上基於 GPU 的影片編碼/解碼。這使得 VPF 在利用基於 GPU 的高性能影片加速的同時,也獲得了易於閱讀/編寫的程式碼。

NVIDIA Video Codec SDK 使用效果示意圖

同時值得注意的是,VPF 還利用 NVIDIA Video Codec SDK(一套全面的 API,包括用於 Windows 和 Linux 上硬體加速影片編碼和解碼的高性能工具,示例和文檔)來提高靈活性和性能,並為開發人員提供 Python 固有的易用性。目前,該程式碼在 GitHub 上已開源。

Github 地址: https://github.com/NVIDIA/VideoProcessingFramework

程式碼示例及結果


在官網部落格宣布 VPF 時,開發者也提供了一個簡短的 Python 程式碼示例,該示例使用 PyNvCodec 模組顯示 Python 中的影片轉碼:

import PyNvCodec as nvc    gpuID = 0  encFile = "big_buck_bunny_1080p_h264.mov"  xcodeFile = open("big_buck_bunny_1080p.h264", "wb")    nvDec = nvc.PyNvDecoder(encFile, gpuID)  nvEnc = nvc.PyNvEncoder({'preset': 'hq', 'codec': 'h264', 's': '1920x1080'}, gpuID)    while True:       rawSurface = nvDec.DecodeSingleSurface()       # Decoder will return zero surface if input file is over;       if not (rawSurface.GetCudaDevicePtr()):            break        encFrame = nvEnc.EncodeSingleSurface(rawSurface)      if(encFrame.size):           frameByteArray = bytearray(encFrame)           xcodeFile.write(frameByteArray)    # Encoder is asynchronous, so we need to flush it  encFrames = nvEnc.Flush()  for encFrame in encFrames:      encByteArray = bytearray(encFrame)      xcodeFile.write(encByteArray)

儘管這一示例的設計簡單,但 VPF 仍具有良好的性能。上面顯示的程式碼轉換示例足以使 RTX 5000 GPU 上的 Nvenc 單元飽和,如下所示:

Big Buck Bunny 序列包含 14315 幀,可以在 32 秒內進行轉碼,而無需使用任何先進的技術(例如生產者-消費者模式),解碼器和編碼器將在單獨的執行緒中啟動共享解碼器隊列,從而可以在約 447fps 的速度下進行轉碼。由於所有轉碼均在 GPU 上完成,因此沒有明顯的 CPU 負載。

VPF 使用類說明


VPF 中包含了多個類,其核心部分是 PyNvDecoder 和 PyNvEncoder 類,它們是與 NVIDIA Video Codec SDK 的 Python 綁定。

PyNvDecoder 和 PyNvEncoder 類支援 NV12 像素格式,所有轉換均通過 GPU 加速,並在 VRAM 記憶體中完成,以提高性能。其中——

PyNvDecoder 類有五個主要方法:

  • DecodeSingleSurface 從輸入影片解碼單幀,返回帶有解碼像素的 Surface。下次用戶調用此方法時,先前返回的 Surface 可能會被重用。如果未解碼幀,則解碼後的 Surface 的 GetCudaDevicePtr 方法將返回零;
  • DecodeSingleFram 從輸入影片解碼單幀,返回帶有解碼像素的 NumPy 數組。下次用戶調用此方法時,將返回另一個 NumPy 數組實例。如果未解碼幀,它將返回空的 NumPy 數組。此操作將設備複製到主機記憶體;
  • Width 返回解碼的幀寬度;
  • Height 返回解碼的幀高度;
  • PixelFormat 返回解碼的幀像素格式。

用戶使用 DecodeSingleSurface 和 DecodeSingleFrame 時,不會破壞解碼器的內部狀態。解碼器類支援 H.264 和 H.265 編解碼器。

PyNvEncoder 類有六個方法:

  • EncodeSingleSurface 以原始像素獲取 NV12 Surface,對其進行編碼,然後將基本影片比特流作為 NumPy 數組返回。編碼器是非同步的,因此此方法可能會在前幾次調用時返回空數組(取決於編碼器設置),這不是編碼錯誤;
  • EncodeSingleFrame 以原始像素獲取 NumPy 數組,對其進行編碼,然後將基本影片比特流作為 NumPy 數組返回。編碼器是非同步的,因此此方法可能在前幾次調用時返回空數組(取決於編碼器設置);
  • Flush 沖洗編碼器。除非編碼器隊列中的所有原始幀都已編碼,否則它不會返回,並返回帶有基本流位元組的 NumPy 數組的列表;
  • Width 返回編碼的幀寬度;
  • Height 返回編碼的幀高度;
  • PixelFormat 返回編碼的幀像素格式。

如果用戶使用 EncodeSingleSurface 和 EncodeSingleFrame,則不會破壞編碼器的內部狀態。此外,PyNvEncoder 可以獲取任意解析度的輸入幀,並在實際編碼之前即時在 GPU 上調整其大小。

編碼器類支援 H.264 和 H.265 編解碼器,並且具有較低的延遲,因此在編碼會話結束時,應調用 Flush 刷新編碼器幀隊列。

HardwareSurface 類包含一個包裝器 CUdeviceptr:

  • GetCudaDevicePtr 將 CUdeviceptr 返回到 CUDA 記憶體對象。

對於主機和設備之間的記憶體傳輸,有兩個名為 PyFrameUploader 和 PySurfaceDownloader 的類:

  • PyFrameUploader 用於將 NumPy 數組上傳到 GPU;
  • UploadSingleFrame 將一個 numpy 數組上傳到 GPU,再將句柄返回到上傳的 Surface。下次用戶調用此方法時,先前返回的 Surface 可能會被重用。

PySurfaceDownloader 類用於從 GPU 下載 Surface,它只包含一種方法:

  • DownloadSingleSurface 將 GPU 端 Surface 下載到 CPU 端 numpy 數組中。下次用戶調用此方法時,將返回另一個 numpy 數組實例。

PySurfaceConverter 類用於 GPU 加速的色彩空間和像素格式轉換。以下是受支援的轉化列表:

  • YUV420 至 NV12
  • NV12 到 YUV420
  • NV12 轉 RGB

PySurfaceConverter 類包含一種方法:

  • Execute 在 GPU 上執行轉換,將句柄以輸出格式返回給 Surface。下次用戶調用此方法時,先前返回的 Surface 可能會被重用。

而 VPF 運行的主要數據類型有兩種:

  • 用於 CPU 端數據的 NumPy 數組;
  • 用戶透明 Surface 類,表示 GPU 端數據;

由於 GPU 端記憶體對象分配很複雜,並且會嚴重影響性能,因此所有歸還 Surface,並在下次調用時重用先前返回的 VPF 類方法。

與此不同的是,VPF 類方法每次被調用時都會返回新的 NumPy 數組實例。移動構造函數可避免記憶體複製的運行成本。

其它開源影片處理框架


一、RxFFmpeg

RxFFmpeg 是基於 ( FFmpeg 4.0 + X264 + mp3lame + fdk-aac ) 編譯的適用於 Android 平台的音影片編輯、影片剪輯的快速處理框架。

包含:影片拼接,轉碼,壓縮,裁剪,片頭片尾,分離音影片,變速,添加靜態貼紙和 gif 動態貼紙,添加字幕,添加濾鏡,添加背景音樂,加速減速影片,倒放音影片,音頻裁剪,變聲,混音,圖片合成影片,影片解碼圖片等主流特色功能。

RxFFmpeg 開源地址: https://github.com/microshow/RxFFmpeg

二、VidGear

VidGear 是一個圍繞 OpenCV 影片 I/O 模組的輕量級 python 包裝器,它使用多執行緒 Gears(又名 API)構建,每個都有獨特的開拓性功能。

這些 API 提供了易於使用,高度可擴展的多執行緒包裝器,這些包裝器圍繞著許多底層的最新 python 庫,例如 OpenCV,FFmpeg,picamera,pafy,pyzmq 和 python-mss ➶,可以在各種設備和平台上實現高速影片幀讀取功能 。它也是 imutils 庫影片模組的重新實現,修復了所有主要錯誤,並附帶了直接網路流支援。

VidGear 開源地址: https://pypi.org/project/vidgear/

VPF 部落格地址: https://devblogs.nvidia.com/vpf-hardware-accelerated-video-processing-framework-in-python/