你的第一款開源視頻分析框架

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

計算機是如何“看懂”海量視頻的呢?視頻本質上是一系列連續的圖像幀,按照一定的幀率播放,從而形成連續的動態效果。因此,計算機分析視頻的基本原理就是:解碼(視頻轉圖片)-> 分析/推理(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 項目能夠成爲一座連接對視頻分析、結構化技術感興趣的小夥伴的橋樑,能夠和大家一起交流學習、共同進步、回饋開源社區!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章