MediaPipe加速流程和原理
目錄1. MediaPipe加速流程1.1 OpenGL ES準(zhǔn)備1.2 CameraX 準(zhǔn)備1.3 Android平臺上加速流程2. MediaPipe加速原理2.1 MediaPipe源碼結(jié)構(gòu)2.2 框架加速組件和原理2.3 人手姿態(tài)估計示例
1. MediaPipe加速流程
1.1 OpenGL ES準(zhǔn)備
(1) OpenGL上下文(Context)
在調(diào)用任何OpenGL指令之前,需要創(chuàng)建一個OpenGL上下文,該上下文是一個非常龐大的狀態(tài)機,保存了OpenGL的各種狀態(tài)。由于OpenGL上下文是一個巨大的狀態(tài)機,因此切換上下文需要較大的開銷,但是不同的繪制模塊需要完全獨立的狀態(tài)管理。因此可以通過在應(yīng)用程序中分別創(chuàng)建多個不同上下文,在不同線程中使用不同上下文,同時上下文之間共享紋理,緩沖區(qū)等資源,避免反復(fù)切換上下文,造成較大開銷。(OpenGL的一個上下文不能被多個線程同時訪問。此外,在同一線程上切換上下文可能會很慢。因此更有效的做法是為每個上下文設(shè)置一個專用線程,每個線程發(fā)出GL命令,在其上下文建立一個串行命令隊列,然后由GPU異步執(zhí)行)
(2) 幀緩沖區(qū)(FrameBuffer)
OpenGL可以理解成圖形API,因此所有運算和結(jié)果輸出都需要通過圖像進行輸出。繪圖需要有一塊畫板,那么幀緩沖區(qū)就是這塊畫板。
(3) 附著(Attachment)
附著可以理解成畫板上的夾子,夾住哪塊畫布,就往對應(yīng)的畫布上輸出數(shù)據(jù)。
(4) 紋理(Texture)和渲染緩沖區(qū)(RenderBuffer)
幀緩沖區(qū)并不是實際存儲數(shù)據(jù)的地方,實際存儲圖像數(shù)據(jù)數(shù)據(jù)的對象就是紋理和渲染緩沖區(qū)。(注意,一般來說渲染緩沖區(qū)和紋理不能同時掛載在同一幀緩沖區(qū)上)
(5) 頂點數(shù)組(VertexArray)和頂點緩沖區(qū)(VertexBuffer)
有了畫板,夾子,畫布就可以開始繪畫了,畫圖先畫好圖像骨架,然后往骨架里面添加顏色,頂點數(shù)據(jù)就是要畫的圖像的骨架,OpenGL圖像都是由圖元組成的(點,線,三角形),頂點數(shù)據(jù)預(yù)先傳入顯存當(dāng)中,這部分顯存稱為頂點緩沖區(qū)。
(6) 索引數(shù)組(ElementArray)和索引緩沖區(qū)(ElementBuffer)
索引數(shù)據(jù)的目的是為了實現(xiàn)頂點復(fù)用,在繪制圖像時,總是會有一些頂點被多個圖元共享,避免反復(fù)對這個頂點進行計算。索引數(shù)據(jù)存儲在顯存中,這部分顯存稱為索引緩沖區(qū)。
(7) 著色器程序(Shader)
頂點著色器(VertexShader)
頂點著色器是OpenGL中用于計算頂點屬性的程序。頂點著色器是逐頂點運算的程序,每個頂點數(shù)據(jù)都會執(zhí)行一次頂點著色器,每個頂點執(zhí)行時并行的,并且頂點著色器運算過程中無法訪問其他頂點數(shù)據(jù)。
片段著色器(FragmentShader)
片段著色器是OpenGL中用于計算像素顏色的程序。每個像素都會執(zhí)行一次片段著色器,每個像素運行時同樣也是并行的。
(8) 逐片段操作(Per-Fragment Operation)
測試(Test)
著色器程序完成后,我們就得到了像素數(shù)據(jù),這些數(shù)據(jù)必須通過測試才能最終繪制到畫布,也就是幀緩沖上的顏色附著上。
混合(Blending)
抖動(Dithering)
抖動是針對可用顏色較少的系統(tǒng),可以犧牲分辨率為代價,通過顏色值的抖動來增加可用顏色數(shù)量的技術(shù)。機器分辨率足夠高的情況下,激活抖動操作沒有太大意義。
(9) 渲染到紋理
OpenGL程序并不希望渲染出來的圖像立即顯示在屏幕上,而是需要多次渲染。其中一次的渲染結(jié)果作為下一次渲染的輸入。如果幀緩沖區(qū)的顏色附著設(shè)置為一張紋理,那么渲染完成之后,可以重新構(gòu)造新的幀緩沖區(qū),并將上次渲染出來的紋理作為輸入,重復(fù)上述流程。
(10) 渲染上屏/交換緩沖區(qū)(SwapBuffer)
無
1.2 CameraX 準(zhǔn)備
在 Android 應(yīng)用中要實現(xiàn) Camera 功能還是比較困難的,為了保證在各品牌手機設(shè)備上的兼容性、響應(yīng)速度等體驗細節(jié),Camera 應(yīng)用的開發(fā)者往往需要花很大的時間和精力進行測試,甚至需要手動在數(shù)百種不同設(shè)備上進行測試。CameraX 正是為解決這個痛點而誕生的。
優(yōu)勢:
CameraX和生命周期結(jié)合在一起,方便開發(fā)者管理生命周期,相比camera2減少了大量的樣板代碼的使用
CameraX是基于Camera2 API實現(xiàn)的,兼容Andorid L(API 21),保證兼容市面上的絕大多數(shù)手機
開發(fā)者可以通過擴展形式使用和原生攝像頭應(yīng)用相同的功能(人像,夜間模式,HDR,濾鏡,美顏)
Google對CameraX進行了深度測試,確保能夠給覆蓋到更加廣泛的設(shè)備中。
(1) 添加CameraX依賴
(2) 顯示相機預(yù)覽
(3) 拍照和存儲圖片
(4) 實時分析圖像幀
1.3 Android平臺上加速流程
(1) 設(shè)置
系統(tǒng)中安裝MediaPipe
安裝Android Development SDK和Android NDK
Android設(shè)備開發(fā)模式
設(shè)置Bazel編譯部署Android應(yīng)用
(2) 特定功能的圖結(jié)構(gòu)
(3) 初始最小應(yīng)用程序設(shè)置
(4) 調(diào)用CameraX相機驅(qū)動
相機權(quán)限
相機訪問
(5) 設(shè)置外部紋理轉(zhuǎn)換器
表面紋理(SurfaceTexture)從流中捕獲圖像幀作為OpenGL ES紋理。要使用MediaPipe圖形,從攝像機捕獲的幀應(yīng)該存儲在一個常規(guī)的Open GL紋理對象中。MediaPipe提供了一個名為ExternalTextureConverter的類,用于將存儲在SurfaceTexture對象中的圖像轉(zhuǎn)換為常規(guī)OpenGL紋理對象。要使用ExternalTextureConverter,我們需要EglManager對象創(chuàng)建和管理EGLContext。向構(gòu)建(BUILD)文件添加依賴項以使用EglManager。
計算攝像頭的幀在設(shè)備屏幕上的適當(dāng)?shù)娘@示尺寸
傳入previewFrameTexture和displaySize到convert現(xiàn)在攝像頭獲取到的圖像幀已傳輸?shù)組ediaPipe graph中了。
(6) 在Android中調(diào)用MediaPipe圖結(jié)構(gòu)
添加相關(guān)依賴
主活動MainActivity中使用圖
2. MediaPipe加速原理
2.1 MediaPipe源碼結(jié)構(gòu)
MediaPipe整個技術(shù)棧如圖所示
MediaPipe中核心源碼的結(jié)構(gòu)如下,BUILD為Bazel編譯文件、calculators為圖結(jié)構(gòu)的計算單元、docs為開發(fā)文檔、examples為mediapipe的應(yīng)用示例、framework為框架包含計算單元屬性,上下文環(huán)境,數(shù)據(jù)流管理,調(diào)度隊列,線程池,時間戳等、gpu為OpenGL的依賴文件、graphs為mediapipe各項示例的圖結(jié)構(gòu)(邊緣檢測,人臉檢測,姿態(tài)追蹤等等)、java為安卓應(yīng)用開發(fā)的依賴項、MediaPipe.tulsiproj為相關(guān)配置文件、models為各個應(yīng)用的tflite模型,modules為示例組件、objc為 objective-c語言相關(guān)文件、util為工具代碼。
2.2 框架加速組件和原理
框架的加速部分主要在framework中,源碼中包括計算單元基類,計算單元數(shù)據(jù)類型定義、計算單元的狀態(tài)控制、計算單元的上下文環(huán)境管理、圖結(jié)構(gòu)中輸入流和輸出流、調(diào)度器隊列、線程池、時間戳同步等。下面主要分析調(diào)度器隊列(scheduler_queue),線程池(thread_pool),時間戳(timestamp)怎樣通過調(diào)度數(shù)據(jù)流實現(xiàn)時數(shù)據(jù)流時間戳同步,再GPU計算渲染,從而達到mediapipe管線的最大數(shù)據(jù)吞吐量。
調(diào)度器機制
優(yōu)先級隊列、線程池的原理可以看這個鏈接:https://www.cnblogs.com/zhongzhaoxie/p/13630795.html
MediaPipe圖是由計算單元構(gòu)成的,整個調(diào)度機制決定何時運行每個計算單元。每個圖至少有一個調(diào)度隊列,每個調(diào)度隊列只有一個執(zhí)行器。默認情況下,執(zhí)行器是一個線程池,根據(jù)系統(tǒng)的能力決定線程數(shù)量。每個計算單元作為一個節(jié)點都有一個調(diào)度狀態(tài)(未就緒,就緒或者正在運行)。
對于源節(jié)點,沒有數(shù)據(jù)流輸入的節(jié)點成為源節(jié)點,源節(jié)點總是處于準(zhǔn)備運行的狀態(tài),一直到整個圖結(jié)構(gòu)沒有數(shù)據(jù)輸出,源節(jié)點才會關(guān)閉。對于非源節(jié)點有要處理的輸入時,根據(jù)節(jié)點的輸入策略(下面將討論),形成一個有效的輸出集,此時非源節(jié)點保持準(zhǔn)備狀態(tài)。當(dāng)一個節(jié)點準(zhǔn)備就緒時,意味著一個任務(wù)添加到優(yōu)先調(diào)度程序隊列中,優(yōu)先級函數(shù)目前時固定的,考慮到節(jié)點的靜態(tài)屬性及其圖中的拓撲排序。靠近圖輸出端的節(jié)點具有更高的優(yōu)先級,而源節(jié)點具有最低的優(yōu)先級。
時間戳同步
MediaPipe圖結(jié)構(gòu)執(zhí)行時去中心化的:沒有全局鎖,不同的節(jié)點能夠在同一時間處理不同時間戳的數(shù)據(jù)。這允許管道有更高的吞吐量。然而時間信息對于許多感知工作流非常重要。同時接收多個輸入流的節(jié)點需要以某種方式取協(xié)調(diào)它們。例如,一個目標(biāo)檢測器可能產(chǎn)生一系列候選框,然后再將這個信息輸送到渲染節(jié)點,該節(jié)點應(yīng)該與原始幀一起處理。
因此MediaPipe主要功能之一就是讓節(jié)點輸入同步。就框架而言,時間戳的主要作用是充當(dāng)同步鍵。此外,MediaPipe被設(shè)計為支持確定性操作,這在許多場景(測試、模擬、批處理等)中非常重要,同時允許圖設(shè)計者在需要滿足實時約束的地方放松確定性。
同步和決定論這兩個目標(biāo)是幾種設(shè)計選擇的基礎(chǔ)。值得注意的是,推入給定流的數(shù)據(jù)包必須有單調(diào)遞增的時間戳:這不僅是對許多節(jié)點有用的假設(shè),而且同步邏輯也依賴于此。每個數(shù)據(jù)流有時間戳限制,這是數(shù)據(jù)流上新包允許的最低時間戳。當(dāng)一個時間戳為T的數(shù)據(jù)包到達時,邊界自動推進到T+1,反映了單調(diào)要求。這允許框架確定沒有時間戳小于T的數(shù)據(jù)包會到達。
輸入策略
由DefaultInputStreamHandler定義的默認輸入策略提供了確定性的輸入同步,可以保證多個輸入流上具有相同時間戳的數(shù)據(jù)包,輸入數(shù)據(jù)流嚴格按照時間戳升序處理。基于計算單元的方法使圖可以控制在哪里丟棄數(shù)據(jù)包,并允許根據(jù)資源約束靈活的適應(yīng)和定制圖行為。
GPU計算和渲染
MediaPipe支持用于GPU計算和渲染的計算單元節(jié)點,并允許合并多個GPU節(jié)點,以及將它們與基于CPU的計算單元節(jié)點混合。MediaPipe中GPU設(shè)計原則是保證GPU計算單元可以出現(xiàn)在圖的任何地方,幀數(shù)據(jù)在GPU計算單元到另一個計算單元應(yīng)該不需要復(fù)制操作,CPU和GPU之間的數(shù)據(jù)傳輸應(yīng)該高效。
MediaPipe允許圖在多個GL上下文中運行OpenGL。舉例來說,這可能是在圖結(jié)構(gòu)中非常有用,結(jié)合較慢的GPU推理路徑(例如,在10幀/秒)和更快的GPU渲染路徑(如30 FPS):因為一個GL上下文對應(yīng)于一個連續(xù)的命令隊列,所以這兩個任務(wù)使用相同的上下文將會減少渲染的幀速率。
MediaPipe使用多個上下文解決的一個挑戰(zhàn)是跨它們進行通信的能力。比如這樣一個示例場景,同時發(fā)送輸入視頻到顯示和推理路徑,顯示需要先訪問推理的結(jié)果。
一個OpenGL上下文不能被多個線程同時訪問。此外,在某些Android設(shè)備上,在同一線程上切換活動GL上下文可能會很慢。因此,我們的方法是為每個上下文設(shè)置一個專用線程。每個線程發(fā)出GL命令,在其上下文上建立一個串行命令隊列,然后由GPU異步執(zhí)行。
2.3 人手姿態(tài)估計示例
參考文獻:
[1] https://zhuanlan.zhihu.com/p/56693625
[2] OPENGL ES 3.0編程指南
[3] https://codelabs.developers.google.com/codelabs/camerax-getting-started/
[4] https://zhuanlan.zhihu.com/p/110411044
總結(jié)
以上是生活随笔為你收集整理的MediaPipe加速流程和原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微软必应全球宕机事件涟漪,波及 Chat
- 下一篇: 关于vim、nvim的折腾