你的第一款開源影片分析框架

削微寒發表於2024-04-18

現在,刷影片已經成為我們生活中的一部分,而且很容易一看就停不下來。你有沒有好奇過,它是如何在海量的影片裡,找到讓你感興趣的影片?又是如何讓你可以透過關鍵字,搜尋到與之相關的影片內容的呢?這一切都離不開計算機對影片內容的分析和理解。

計算機是如何“看懂”海量影片的呢?影片本質上是一系列連續的影像幀,按照一定的幀率播放,從而形成連續的動態效果。因此,計算機分析影片的基本原理就是:解碼(影片轉圖片)-> 分析/推理(AI 演算法)-> 編碼(結果呈現)

儘管這看起來就寥寥幾步,但其中涉及許多技術細節和複雜的演算法。比如,如何將訓練好的 AI 影像演算法模型,快速部署落地到實際應⽤場景中呢?對於沒有接觸過計算機視覺(Computer Vision,後簡稱 CV)的程式設計師,或是純搞演算法的演算法工程師,要實現+落地 AI 影片分析相關功能可能會有點難度。然而,隨著影片在日常生活中的普及和應用越來越廣泛,處理和分析影片類資料的需求也在逐漸增加。

因此,今天 HelloGitHub 帶來了一款開源的影片分析/結構化框架——VideoPipe,旨在讓開發影片分析應用像使用 Django 寫 Web 一樣方便。VideoPipe 獨創的管道視覺化顯示,讓每一步的處理狀態都可以一目瞭然。該框架能夠輕鬆整合各種 CV 領域的模型,透過即插即用的方式輕鬆實現 AI 加持下的影片分析,適用於影片結構化、圖片搜尋、人臉識別、安防領域的行為分析(⻋牌識別、交通事故檢測)等場景。

GitHub 地址:https://github.com/sherlockchou86/VideoPipe

下面,讓我們跟著該專案的作者(周智)一起來了解、上手 VideoPipe,然後深入其內部學習更多的技術細節。

一、介紹

VideoPipe 這是一個用於影片分析和結構化的框架,採用 C++ 編寫、依賴少、易上手。它就像一個管道每個節點相互獨立可自行搭配,用來構建不同型別的影片分析管道,適用於影片結構化、圖片搜尋、人臉識別、安防領域的行為分析(如交通事件檢測)等場景。

你只需準備好模型並瞭解如何解析其輸出即可,推理可以基於不同的後端實現,如 OpenCV::DNN(預設)、TensorRT、PaddleInference、ONNXRuntime 等,任何你喜歡的都可以。

透過上面的 VideoPipe 工作示意圖,可以發現它提供了以下功能:

  • 流讀取/推送:⽀持主流的影片流協議,如 udp、rtsp、rtmp、檔案。
  • 影片解碼/編碼:⽀持基於 OpenCV/GStreamer 的影片和圖片解/編碼(⽀持硬體加速)。
  • 基於深度學習的演算法推理:⽀持基於深度學習演算法的多級推理,例如⽬標檢測、影像分類、特徵提取。
  • ⽬標跟蹤:⽀持⽬標追蹤,例如 IOU、SORT 跟蹤演算法等。
  • ⾏為分析(BA):⽀持基於跟蹤的⾏為分析,例如越線、停⻋、違章等交通判斷。
  • 資料代理:⽀持將結構化資料(json/xml/⾃定義格式)以 kafka/Sokcet 等⽅式推送到雲端、檔案或其他
    第三⽅平臺。
  • 錄製:⽀持特定時間段的影片錄製,特定幀的截圖。
  • 螢幕顯⽰(OSD):支援將模型輸出結果繪製到幀上。

對比功能類似、耳熟能詳的 DeepStream(英偉達)和 mxVision(華為)框架,VideoPipe 更易於使⽤和除錯、具備更好的可移植性,它完全由原生 C++ 編寫,僅依賴於少量主流的第三方模組(如 OpenCV)。同時提供了視覺化管道,框架的執行狀態會自動在螢幕上重新整理,包括管道中每個連線點的 fps、快取大小、延遲等資訊,你可以根據這些執行資訊快速定位處理時的瓶頸所在。

名稱 是否開源 學習門檻 適用平臺 效能 三方依賴
DeepStream 僅限英偉達
mxVision 僅限華為
VideoPipe 不限平臺

二、快速上手

VideoPipe 對機器硬體沒有要求,僅用 CPU 都可以執行,不需要額外的加速卡。而且專案中還提供了豐富的示例程式碼,下面讓我們透過執行一個簡單的「人臉識別」示例,快速上手該框架。

/*
* 名稱:1-1-N sample
* 完整程式碼位於:samples/1-1-N_sample.cpp
* 功能說明:1個影片輸入,1個影片分析任務(人臉檢測和識別),2個輸出(螢幕輸出/RTMP推流輸出)
* 注意:模型和影片檔案需要自行準備
*/

int main() {
    VP_SET_LOG_INCLUDE_CODE_LOCATION(false);
    VP_SET_LOG_INCLUDE_THREAD_ID(false);
    VP_LOGGER_INIT();

    // 1、建立節點
    // 影片獲取 Node
    auto file_src_0 = std::make_shared<vp_nodes::vp_file_src_node>("file_src_0", 0, "./test_video/10.mp4", 0.6);
    // 2、模型推理 Node
    // 一級推理:人臉檢測
    auto yunet_face_detector_0 = std::make_shared<vp_nodes::vp_yunet_face_detector_node>("yunet_face_detector_0", "./models/face/face_detection_yunet_2022mar.onnx");
    // 二級推理:人臉識別
    auto sface_face_encoder_0 = std::make_shared<vp_nodes::vp_sface_feature_encoder_node>("sface_face_encoder_0", "./models/face/face_recognition_sface_2021dec.onnx");
    // 3、OSD Node
    // 處理結果繪製到幀上
    auto osd_0 = std::make_shared<vp_nodes::vp_face_osd_node_v2>("osd_0");
    // 螢幕展示
    auto screen_des_0 = std::make_shared<vp_nodes::vp_screen_des_node>("screen_des_0", 0);
    // 推流展示
    auto rtmp_des_0 = std::make_shared<vp_nodes::vp_rtmp_des_node>("rtmp_des_0", 0, "rtmp://192.168.77.60/live/10000");

    // 構建管道,將節點的處理結果關聯起來
    yunet_face_detector_0->attach_to({file_src_0});
    sface_face_encoder_0->attach_to({yunet_face_detector_0});
    osd_0->attach_to({sface_face_encoder_0});

    // 管道自動拆分,透過螢幕/推流輸出結果
    screen_des_0->attach_to({osd_0});
    rtmp_des_0->attach_to({osd_0});

    // 啟動管道
    file_src_0->start();

    // 視覺化管道
    vp_utils::vp_analysis_board board({file_src_0});
    board.display();
}

透過閱讀上面的示例程式碼,可以發現 VideoPipe 框架將影片分析/處理的步驟,抽象成了一個管道(pipe),每一步的處理都是管道中的一個節點(Node),處理流程如下:

  1. 影片讀取 Node:完成讀取影片和解碼的工作
  2. 模型推理 Node:分為人臉檢測和人臉識別兩個模型
  3. OSD Node:將模型輸出的處理結果繪製到幀上
  4. 構建管道:將上述節點依次連線,並將結果分成螢幕輸出和推流輸出,
  5. 啟動:啟動程式,並展示管道的執行情況

程式碼運⾏後,會出現上面的 3 個畫⾯。它們分別是管道運⾏狀態圖(狀態⾃動重新整理)、螢幕顯⽰結果(GUI)、播放器顯⽰結果(RTMP),至此就算上手 VideoPipe 了!

三、技術原理

接下來,將詳細介紹 VideoPipe 框架實現的技術原理和細節,乾貨來啦!在深入瞭解 VideoPipe 框架技術細節之前,我們需要先弄清楚影片的整體處理流程。

3.1 影片結構化應⽤的核⼼環節

影片結構化是將非結構化資料(影片/圖片)轉換為結構化資料的過程。非結構化資料通常包括:影片、影像、⾳頻、⾃然語⾔文字,⽽結構化資料主要包括諸如 JSON、XML 或資料庫中的資料表等,這些資料可以直接由機器(程式)處理。具體到影片(含圖片,下同)結構化的過程,主要涉及以下核⼼部分:

  • 讀取流:從⽹絡或本地機器獲取影片流。
  • 解碼:將位元組流解碼為幀,因為演算法只能作⽤於影像。
  • 推理:對影像進⾏深度學習推理,如檢測、分類或特徵提取。
  • 跟蹤:跟蹤影片中的⽬標。
  • ⾏為分析/邏輯處理:分析⽬標的軌跡、屬性。
  • OSD:在影像上顯⽰結果,⽤於除錯或得到直觀效果。
  • 訊息代理:將結構化資料推送到外部,供業務平臺使⽤。
  • 編碼:對包含結果的幀進⾏編碼,以便傳輸、儲存。
  • 推送流:將位元組流推送到外部或直接儲存

上述每個環節對應 VideoPipe 中的⼀種外掛型別,即程式碼中的 Node 物件。下面我們將逐一講解 VideoPipe 的 Node、資料流、鉤子的技術細節和實現。

3.2 Node

VideoPipe 中的每個 Node 負責⼀種任務(嚴格遵循單⼀職責原則),例如解碼或推理。我們可以將許多節點串在⼀起構建成管道,並讓影片資料流經整個管道。每個 Node 內部都有兩個佇列,⼀個⽤於快取上游節點推送的資料,另⼀個⽤於快取等待被推送到下游節點的資料。我們可以在兩個佇列之間編寫邏輯程式碼,這是典型的⽣產者-消費者模式。

VideoPipe 中有三種型別的節點,分別是:

  1. SRC節點:源節點,資料被建立的地⽅(內部只有⼀個佇列,⽤於快取被推送到下游節點的資料)。
  2. MID節點:中間節點,資料將在此處理。
  3. DES節點:⽬標節點,資料消失的地⽅(內部只有⼀個佇列,⽤於快取來⾃上游節點的資料)。

每個節點本⾝具有合併多個上游節點和拆分成多個下游節點的能⼒。注意,預設情況下節點在將資料從⼀個節點傳輸到另⼀個節點時使⽤淺拷⻉和等值拷⻉。如果您需要深拷⻉或希望按通道索引傳輸資料(希望資料不混淆),則在分裂點新增⼀個 vp_split_node 型別節點。

3.3 資料流

影片是一種重量級資料,因此頻繁進行深複製會降低管道的效能。實際上,VideoPipe 中兩個節點之間傳遞的資料預設使用智慧指標,一旦資料由源節點建立,資料內容在整個管道中大多數時間不會被複制。但如果需要,我們可以指定深度複製模式,使用 vp_split_node 型別節點。

影片由連續的幀組成,因此 VideoPipe 逐幀處理這些幀,所以幀後設資料中的幀索引也會連續增加。

3.4 鉤子

鉤子是一種機制,讓主體在發生某些事件時通知檢測者,VideoPipe 也支援鉤子。管道觸發回撥函式 std::function 與外部程式碼通訊,例如實時推送管道自身的 fps、延遲和其他狀態資訊。我們在編寫回撥函式內部程式碼時,不允許有阻塞出現,否則影響整個管道效能。

鉤子有助於除錯我們的應用程式,並快速找出整個管道中的瓶頸,VideoPipe 框架中自帶的視覺化工具 vp_analysis_board 就是依賴於鉤子機制實現的。

3.5 如何實現新的 Node 型別

首先 vp_node 是 VideoPipe 中所有節點的基類,我們可以定義一個從 vp_node 派生的新節點類,並重寫一些虛擬函式:

  • handle_frame_meta:處理流經當前節點的幀資料。
  • handle_control_meta:處理流經當前節點的控制指令資料。

幀資料指的是 VideoPipe 中的 vp_frame_meta,其中包含與幀相關的資料,如幀索引、資料緩衝區、原始寬度等等。控制指令資料指的是 VideoPipe 中的 vp_control_meta,其中包含與命令相關的資料,例如記錄影片、記錄影像等。並非所有流經當前節點的資料都應該被處理,只需要處理我們感興趣的內容。

四、最後

目前,基於深度學習的影片分析技術的入門門檻還是比較高的,一些成熟的框架比如 DeepStream、mxVision 等,它們大多晦澀難懂、上手門檻高、對於新手不太友好。所以,我就花了兩年的業餘時間建立了 VideoPipe 影片分析框架,我的想法很簡單就是想讓初學者能夠快速瞭解影片分析相關技術棧,輕鬆地在自己機器上跑通一個人臉識別的應用,讓更多人掌握影片分析相關技術,同時搞清楚應該從哪裡開始。

我深知這是一件道阻且長的事情,所以 VideoPipe 在誕生之初就是完全開源,我希望能夠藉助開源的力量讓它“發光發熱”,真正地做到降低開發影片分析應用的門檻,幫助更多的開發者進入到影片分析的領域。

GitHub 地址:https://github.com/sherlockchou86/VideoPipe

最後,感謝「HelloStar 計劃」提供的機會,能夠讓更多人瞭解 VideoPipe 框架。我作為開源生態的受益者,深知開源的力量和責任,此舉也是希望 VideoPipe 專案能夠成為一座連線對影片分析、結構化技術感興趣的小夥伴的橋樑,能夠和大家一起交流學習、共同進步、回饋開源社群!

相關文章