日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

高性能视频推理引擎优化技术

發布時間:2024/4/11 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 高性能视频推理引擎优化技术 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

正文字數:9404 ?閱讀時長:15分鐘

本文整理自騰訊高級技術專家鮑金龍在LiveVideoStack線上分享上的演講。他通過自身的實踐經驗,詳細講解了高性能視頻推理引擎優化技術。

?

文 /?鮑金龍

整理 / LiveVideoStack

?

大家晚上好,非常榮幸又有這個機會來LVS,與大家一起探討一些問題。我第一次參加LVS應該是2017年,現在已經接近4年的時間了。

今天的內容是推理引擎優化技術,當然有一個前提,主要是在端上。馮諾依曼體系的存儲矛盾,幾十年以來一直都是存在的主要矛盾。對于這個問題,如果是在NVIDIA顯卡上,或者是國內的燧原NPU上,他們的解決方案是用最快的HBM內存、HBM2.5 3D堆疊的內存來增加總線的帶寬,中間加上3~6層的Cache。但是在端上這么做其實是有問題的,比如端上的芯片的功耗,以及面積來出發的話,無論是GPU,還是DSP,L2的Cache基本是1 M左右,同時內存是所有的芯片共享的,還有LPDDR3、LPDDR4,功耗非常低,但是性能跟臺式機DDR和HBM內存差10~20倍。所以,在端上的優化,還是需要從推理引擎的總體設計、算子本身的執行速度上,還有算子本身的可替換性上來入手,即從軟件開發上來進行優化,因為硬件短時間內想提高10倍、20倍,實際上是非常困難的。

?

01

優化思路

目前的優化思路,雖然有五項,實際上可以分為三類。

第一類是從Framework角度來說,比如第一、二項,思路是改變一下數據排列,使計算流水線執行得更加有效。這是從Cache的利用率角度來做優化,可以降低總線訪問,同時也能降低功耗。第三項就是算子本身做些優化,在不改變算子算法原理的情況下,看能不能執行得更快。第四項是旁路優化,就是通過等效替換的原理來進行。這里只列了兩點,更復雜的優化還有,但目前時間有限,我們探討不了太復雜的內容。第五項是,如果想使用運動補償,那么就有運動向量從哪里來的問題,后面附加有一個運動向量的估計。

?

第一部分就是數據排列。這是一個非常基本的問題,視頻數據是一個在時間上連續的二維圖像序列,在空域上即單幀圖像上,有水平和垂直兩個方向的維度,在時間上和內容上有大量的冗余。視頻數據有一個很強烈的特點就是,在幀內,比如分塊的二維數據塊內相關性比較強,比如平坦塊都是平坦塊,如果有紋理的話紋理都是連續的。第二個特點就是,臨近的二維數據塊相關性也比較強。第三個特點是,幀和幀之間,通過運動向量關聯起來,它實際上就是一個Patch,三維數組相關性比較強。那么相關性強的表現是,不只紋理特征相似,還有高頻和低頻信號也分別相似(就是頻譜特征相似)。這里是從時域和空域兩個角度來看的。相關性就造成了可以讓算法加速的很多機會,后面將逐漸展開講解這些內容。

目前見到的常規推理引擎數據處理方式實際是Planar結構,它可能是多通道的,比如RGB,也可能是三個Channel,比如YUV、420格式、444格式。執行方式是行掃描,一行從左執行到右,然后第二行從左到右,跟早年電視機的掃描方式是一樣的,掃描到一幀圖處理完。這個方式是通用的,不管是什么類型的數據都可以執行完。但它也有一些問題。第一,這是按行執行的,那么數據局部的相關性沒法從這種處理方式得到照顧。第二,這是整張圖掃完,那么數據的吞吐量非常大。整張圖掃描的方式可以理解為整張圖是一個塊,這個塊就是最大塊。一般來說,如果圖比較大,比如Y通道數據有1~2 M大小,L2 Cache基本上就1 M,這就是一個很典型的Cache顛簸現象,就是說,處理完一個Filter的時候,接著處理下一個Filter的時候,你上次讀的數據已經完全刷出去了,你需要重新再load進來。那么,問題相對來說比較嚴重了,無論是速度還是功耗,表現都非常差。

那么我們要解決這個問題,可以按照視頻的編解碼一樣將數據分塊,常規的分塊就是8*8。一般來說,8*8的塊能夠充分照顧到紋理,有局部性,同時它內部也包含足夠多的特征。塊過大或者過小都各有利弊。那么,一般的分塊存放是有跨度的。如果是8*8的塊,第一個8個像素是在第一行,依次有一個很大的跨度,如果用現在的適合人工智能、大并發的指令來處理,比如AVX512,一次能做64個int 8的乘法,DSP就更大了,一般有128~768個像素,768的數據組織是RGB 16*16的分塊,有三個通道,一次就能處理完。還有更多的,比如32*32。我們常常在網上看到一些NPU的設計,如馬斯克的汽車導航用的NPU,或者是谷歌的TPU-1、2、3代,并發程度都一直在改進,但基本上都停留在這個水平上,不會太大,也不會太小。如果你是用420格式下有跨度地來讀取,會發現這個效率是非常低的。比如一個8*8的塊,需要8次尋址,8次讀入,每次讀入8個字節,只執行了一次計算指令,有8次寫出。這樣的效率其實非常非常低。那如何改進呢?

其實這很簡單。改進方式就是,將8*8的塊取消跨度,連續存放。比如將8個行連續放到64個字節里,后面的7個行都取消了,即這一行放了8行的數據。這樣處理的優勢是,比如處理8*8的塊,只需要1次尋址,而且尋址是非常整齊的對齊地址,然后需要1次讀入,1次計算,1次寫出,這個速度就會快了7、8倍。但是,一般來說,我們達不到7、8倍的速度。原因就是,TILE格式數據想達到高效的處理,還要有數據轉換、堆疊過程,運算過程中還有數據重排(后面依次會講到)。

TILE格式是需要數據重排的。首先,輸入的數據都是常規Planar格式的、RGB或YUV 420格式的,要轉換就是取消跨度。但是一般的推理引擎都是需要復制一次,它不會在原始數據上直接處理,需要Pad-邊界填充,同時可能也存在格式轉換,重排可以和這個融合起來。轉換函數permutation是一個執行非常快的指令,整個過程跟memcpy一樣快,就是說,融合起來的代價就可以忽略了。其次,運算時候的數據并不是永遠地址對齊的,有可能需要對運動向量地址不對齊的情況去做重排。上面這個圖指示的就是如何在輸入格式轉換的時候重排得更高效。重排的時候,并不是一次讀8個字節,或者一次讀8行,而是一次讀64字節*8行,經過重排以后,形成為8個8*8的數組。那么這個效率就是最高的,指令并行度都是飽和的、最大的。

?

接下來看一個地址重排的例子,這是最簡單的例子。更多例子的大家有機會去試試。

當前假設A塊地址是(0,0),相鄰的是B、C、D,它們的間隔都是8,地址是對齊的。需要訪問G塊,地址是(6,5),所以,有一個運算向量。如果是420格式下,做一個不對齊的訪問就可以了,或者做一個對齊的簡單拼接。但是在TILE格式下,要多做一些工作。首先需要拼接到E,就是在A、C兩塊之間拼接E塊,B、D兩個塊之間拼接出F塊。但是這兩個塊跟G塊還是有區別,它們位于一個水平線上的區域內,還要進行X坐標的重排。目前,AVX512的指令是這樣的,移動64位,用三條指令來完成。如果以后有更先進的指令一次完成,那就更好。但很明顯,設計AVX512的時候,沒考慮到TILE格式。同時,注意,指令的后綴加了x,意思就是有個擴展。基本的Intel指令,比如c= 5,就是常量,但常量的效率太低了,一般用的是變量。下面有一個等價的實現,是通過另外一個permute、a,b兩個向量重排之后,形成一個等價的變量align過程。

如果在高通的HVX上,就容易得多。高通的執行同樣也是5行指令,但是前4個都是一類指令valign,它既支持常量,也支持變量,執行動作完全是一樣的。最后,是類似AVX512 Blend的一個vswap操作,融合生成一個塊。這些指令都是低延時指令,訪存要快得多,所以代價基本上非常小。如果后續執行有進一步優化,可以把代價分散一下,那效果就會更好。

02

計算流水線

接下來,我們看第二部分。想要計算的時候需要一個新的方式Framework,原來是一幀、一個Filter在整幀上以raster scan的方式執行完,比如s0、s1、s2、s3、s4執行完,實際上Cache是永不命中的狀態,Cache在移動端的速度非常慢。一般來說,根據我們實驗數據,DDR內存訪存的功耗,比芯片計算指令功耗的數據更大。要解決這個問題,我們想把所有這些過程的中間數據壓縮到最小,比如Cache利用率,能提高一些。那怎么做呢?要設計一個超級流水線。就是說,在從s0到s4的多個步驟,這些Filter可以在Pipeline上并行執行。并行指的是數據的處理順序從原來的每幀順序依賴,轉換為每行順序依賴,這樣從幀的角度來看,數據處理就是并行的。這樣,數據基本都在Cache中,讀取的時候并不進行過多的總線訪問,功耗也非常小。

我們現在是以TILE或ROW,如果是420格式的也可以采用超級流水線,那么就是一行為一ROW。如果是TILE格式,那么除了行ROW,還需要一個二級結構TILE。

我們首先講的是引擎的優化,但是,無論是普通引擎,還是優化的、特殊的、緊密堆積的引擎,數據預取是非常重要的。因為移動芯片上數據Cache非常小,如果數據不預取,基本都是不命中的狀態。常規下,我們看到行掃描下的也是一種流水線結構。首先在循環開始之前,需要預取一行的數據,然后循環中首先要預取下一行的數據,然后處理當前行的數據。process_row函數處理時間比較長,一般都會有1000個Clock以上。那么一個數據從總線開始加載到Cache中去,時間一般粗略認為是100個Clock,有可能更慢,好一點機器可能是80個Clock,差一點機器是150個Clock。所以如果process_row執行過快,那么預取的效率就不夠。比如處理的時候,prefecth_row并沒有預取到,那么預取就失敗了。但是如果處理一行,時間完全是夠的。如果有很多很多種Filter同時進行,流水線有多級,數據預取跟raster scan相比效率就好多了。如圖中所示,這個預取是依次執行的,比如黑色的一行,把預取和數據load放在一塊,那么預取就做一次,后面都不需要做預取了,因為中間數據很小。原來你做了n次預取,現在實際上只做了第一次預取。如圖所示,預取指針指向了綠色行,后面紅色處理的要比它Delay 1行,后面黃色的就是處理過的。所以預取非常重要,如果沒有預取,那么引擎就不可能達到很高的性能。

接下來是超級流水線的設計。如圖所示,看看我們現在這個比較簡單的例子,有四級。第一級是預取一個1*1的load,綠色是5*5的卷積,藍色是3*3的反卷積(你就認為有就可以了),然后黃色是7*7的后處理。這顯然對前方有一個數據依賴,如果其Tap條件不滿足,后續的操作基本上不能繼續。這里需要一個處理邏輯。看看上圖中的代碼,一次處理一個行,實際上就是使用4個process函數的指針,每次依次從上往下執行,比如執行load一行之后馬上執行第二個。那么數據完整性顯然不能滿足。因為第二級Filter是5*5的,需要第5步處理完之后,第6步才能執行,所以它就Fail了 。那么一直走到5之后再去6執行,就發現數據完備了,那就6執行了。6執行后,下一級再處理了,嘗試到9,那9顯然不行。那么就從6直接break,返回到執行7,處理完之后,8就完備了。那再繼續嘗試的時候,發現9完備。之后就是下一級到15,15并不完備,然后又返回到11。11之后是12,12完了之后發現13和14都產生了。因為這是一個上采樣操作,所以處理兩行,這時候15就可以執行了。執行之后,20還沒滿足,返回到16。這個邏輯就是依次循環下去。實際上,這里執行效率比原來的、整張處理的效率要高得多,同時代碼也非常簡單。大家可以看一下,代碼其實是很優美的,越優美的代碼效率越高。

前面并沒有明確講是420格式還是TILE格式,無論哪種格式,數據依賴關系是一樣的。TILE是一種特殊的格式,有一些特殊性。首先,預取就不應以行來執行,而是按照TILE塊來執行。如圖所示,綠色的是預取指針,紅色的是處理指針指的是后面一塊,黃色的塊是已經處理過的。所以這里一個函數process_tile_row處理一行的時候,實際上是在循環外,預取第一個TILE,然后取下一個TILE,處理當前TILE,循環就執行完了。依賴性判斷是,因為一個TILE是8*8,最大支持一個Filter的Tap是17,這很容易算出來,中心點加上兩翼8+8。無論是卷積核還是濾波器都沒有這么大,7*7、9*9就是極限了,一般是3*3、5*5就足夠了。所以,這個判斷比較簡單,只需要看前面Filter的下一個TILE處理沒有,沒有處理就返回Fail,處理了就繼續執行下去。

TILE格式下,一個TILE相當于原來的ROW的8行,Cache的消耗更大。一般來說,一個1080P單通道,在前面的流水線下,消耗是50 k。這個例子里只消耗50 k,這個應該遠遠超過大家的想象。我知道很多引擎在處理數據的時候,動輒消耗2、300 M的數據緩沖。如果設計這么一個引擎,只消耗了50 k的數據,那就是很驚人了。但實際上,這里有一個格式轉換、輸入輸出轉換,還最少需要2幀的緩沖。前面說過,(int數據類型的話)Cache算是50 k,但是數據類型有可能是浮點的、或者是int 32的,50*4 = 200 k就相對來說大一些,如果再乘8,就超過了L2的容量。那這個情況的處理很簡單,在執行的時候,把數據分為多個TILE,現在把它叫作Segment。現在這一個Segment就是原來的四分之一,那么流水線的消耗自然也是四分之一,即壓到了L2 Cache消耗范圍內。這是一個很簡單的變換操作。

03

算子

我們很快將TILE格式和流水線講完了,下面進入第三部分。首先是算子合并。計算量非常小的算子的讀取和寫出的代價相對于計算來說是太大了。這里要做一個格式轉換,比如int 8變成了int 32,float變成了int。比如簡單的加減,甚至開方,有的軟件模擬性的開方指令的計算量是比較大的,但是一般是硬件指令,這個Delay就很小。這種情況下,我們思路是,這個函數是一個加,執行就是一個循環,原來的算法是按行執行,這一行作為加寫出。指令load a需要5個周期,load b需要5個周期,add只需要1個周期,寫出store需要5個周期。效率確實很慢。實際上,如果load的時候,沒有預取,那么周期可能就是50個或100個,這就是非常惡劣的情況,我們在很多工程代碼里都看到。如果預取了,那么就是5個周期的損失。

?

圖的右邊是一個開方函數,也進行了類似的操作,讀取、計算,然后寫出。那么,我們認為開方需要消耗6個周期。這里,我們進行三個計算,對a和b分別進行開方計算,再將a和b開方的結果加起來,那么指令的代價加起來是48。

那么我們重新選一個函數,把這些操作都合并了,簡簡單單寫成一行代碼。這種情況下,我們再看一下代價,load a和load b都是5,一共是27,這樣快了一倍。這是很簡單的思路。

類似的還有一個多通道。通常,卷積是8組、16組、32組。如果我們做一個卷積,比如用AVX512、DPBUSD,在3*3的情況下,是不對稱的,所以均勻分布到3個向量里,占用每個向量的Slot里面的低3位。數據也是要同樣進行組織,每個向量經過DPBUSD計算,執行三次得出SUM。同時還有其他的代價,如讀入。所以要盡可能一次將所有卷積和全做完,因為排陣方式是有代價的。需要Swap重排,重排了之后最好把它放在寄存器中,一次用完。這里的例子是8個,卷積核可以認為是const數組,一次一個循環就全做完了。完成之后直接輸出。

前面說的算子融合,我們可能會有一個問題。我們在做模型的時候,一個Element、一個運算,單獨是一個函數,是一個靜態的依次執行。如果合并的話,那么排列組合非常非常多。假設我們現在支持180種運算,里面至少有90種是合并的,這個排列組合就是天文數字了,靜態引擎是不可能的。在讀入模型的時候,為每個模型單獨構建一個執行代碼。從時機來說,構建分三種。第一,在開發端上,比如用Mac筆記本,或者用服務器來編譯的時候,直接生成一個SO,發行的時候把SO導到APK里面去。第二種,我們把模型放在端上,譬如手機端用ARM CPU來執行編譯過程,將源代碼算子合并,最后調用LLVM編譯器,生成優化代碼,讓ARM CPU來完成。第三種,是在Device上完成,OpenCL就是在GPU上編譯的,在DSP上也是可以的,實際上,這完全看你自己的選擇。從安全性角度來說,我們才需要區分Host和Device,否則Host就可以了。但是,這里有一個問題,因為大家都用過OpenCL,在端上需要編譯代碼,編譯代碼的時間實際上比較長,一般是幾百毫秒。一般我們在做A/B實驗的時候,APP啟動多出來幾百毫秒,這是比較致命的。

?

如果你覺得這個比較費勁,那么還有另一種辦法。第二條我建議的是,優化好的二進制不需要用編譯器來編譯了,直接把每個算子變成二進制unsigned char數據,然后在運行前根據模型把這些算子unsigned char數據直接拼起來,這時候就不需要優化過程。假設拼接代碼優化水平很高的話,拼接代價是可以忽略的,一般拼接速度快到千分之一秒。

04

算子旁路優化

接下來是算子旁路優化。前面首先講的是如何讓Cache利用率更高,功耗更低,然后是讓算子執行效率更高,但是算子本身并沒有進行算法優化。旁路優化有一個基本原則。一方面是處理的簡單特征,梯度方向,當前8*8的塊分為兩部分區域,梯形的一個Edge,這些特征可以通過簡單快速算法獲取,不需要比較復雜的模型,性價比比較高。另一方面,復雜特征想要進行計算,只有用深度方法。這兩方面是相輔相成的,在一定條件下,它們可以互換,快速算法可以替換深度算法。也就是說,某些數據塊用簡單算法的輸出,誤差可以接受,或者是零誤差,那么局部替換就是可行的,即一塊變化了,整個網絡輸出不會受影響。一般來說,判斷替換需要一個算法。比如運動向量,殘差是否是0,預判算法都是有代價的,代價比收益要低很多才行。如果替換了之后,速度提升了3倍,同時有百分之十的代價,那么這兩部分比較,收益還是比較大的。否則,這件事就不用做了。我有一個親身經歷,對一個算法做了紋理復雜性的預判,用TV(Totally Variance)方法,但實現有問題。比如一個720的圖需要判斷其代價是3~6ms,如果算法不進行任何旁路全走一遍,在3~6 ms內也走完了,而經過預判后,速度反而更慢了。

接下來我們看看紋理復雜度分析。常規復雜度實際上也是一個TV算法,但是它的實現方式可以非常快,代價沒有多大。比如一個8*8的塊,首先要判斷它是平坦的,算一個平均值avg。如果它是一個平坦塊,每個點跟平均值的方差,或者等價的abs,代價小于一個值或者就是0,那么紋理就是平坦的。這樣做的好處是,比如一個卷積運算,每個點需要做一個乘法,但在平坦情況下,它會退化,avg值都是一樣的,所以提出來之后,等號右邊的參數和就變成一個常量,o(n**2)或者o(n**3)的運算退化為o(1)了。當然還有其他更復雜的情況,思路是類似的,先要判斷,在旁路成立的情況下進行等價替換。我們看看下面的例子:梯度方向的判斷,就是在一個方向上進行TV運算。比如在45度的方向取了8個點,8個點跟平均值的方差非常小,或者是0,這個方向顯然是一個邊界。也可以計算多個方向的梯度,用Sobel算子、拉普拉斯算子,但是從可靠性角度來說,還是推薦用TV的方法。

平坦性紋理可以有一個很明顯的退化。跟它類似的還有一個明顯的o(1)的退化,即運動補償。編解碼只有一個像素補償,但是深度推理引擎必須各個環節都做,不能因為像素復制了,周圍的網絡中間數據就不計算了,網絡輸出就不等價了。所以在各個層次上都需要做足運動補償。第一個是像素補償。兩個數據塊殘差為0,我們對這兩個塊分別做濾波器處理,可以認為這兩個塊的結果也是殘差為0。做完F(b0)后,F(b1)就不用做了,直接復制過來就可以了。這實際上也是o(1)的操作。第二個是卷積補償。卷積有中間輸出結果,如果只是做了運動補償,中間數據空了,那么卷積像素是有依賴的,網絡的輸出結果就不正常了,所以,卷積的中間結果也就是等價濾波器的中間結果,也需要進行運動補償。同時輸出的重要中間數據,比如featuremap,也需要補償。這些補償都是有代價的,要估算一下復制的代價,或者直接硬算的代價。一般來說,在PC上,或者沒有功耗壓力、帶寬比較高的情況下,運動補償的收益是非常大的。在移動平臺上,如果是內存總線非常慢的情況下,要衡量一下替換多復雜的濾波器。一般有一個平衡點,過了平衡點就是有收益的,如果不到的話,那么運動補償就是失敗的。

05

快速運動估計

下面進入到最后一部分,我們介紹快速運動估計。運動補償都需要一個Block Match的過程,或者是光流,每一個像素都需要一個運動向量,一般我們就使用8*8塊的運動向量。通常,我們的算法跟解碼器相結合,可以獲得一個運動向量,但是編解碼的時候都是有殘差的,這可能對你的算法的干擾非常大,導致大多數情況的運動向量并不能使用。這種情況下,你需要自己獲取這個運動向量。如果是常見的編碼器上的運動估計,代價是特別龐大的,會比你本身的深度算法還要慢,所以沒有必要用這種方法來獲取。但是,近年來涌現了一些快速算法,就是快速運動估計,這個算法有幾個特征。第一,跟原來算法都使用一個大的搜索窗口相比,快速算法窗口上有初始化的預測運動向量,因為這個預測運動向量的存在,窗口可以變得很小。我們假設匹配的目標如上圖所示,原來沒有優化過的搜索算法要使用一個很大的搜索窗口,而快速算法有了預測向量后,窗口可以做得很小,即搜索次數很小,就可以收斂了。因為預測向量是通過不同渠道獲取的,它不一定符合,它有各種變化,跟像素的復制不一樣,向量不能直接用,你要重新執行搜索過程,但是這個搜索比沒有預測的窗口要快得多。

那么,預測向量如何獲取呢?我們假設有一個序列,從frame0到frame1,mv1已經通過了搜索的方式獲取了。我們可以預測,比如從frame0到frame2做一次搜索,那么就可以認為當前這個塊是勻速運動的,那就把這個運動向量引申到frame2上,mv2就是很簡單,mv1乘以2就可以了,在此基礎上確定這個窗口,再重新進行搜索。但是,還有其他的方法,比如反向搜索,從frame1到frame0。這個點所在的Block就可以用這個運動向量,運動向量反向指向frame0的搜索,同時frame2也是可以用的,也可以反向進行。可能性很多,在新版本VVC里應該有類似的算法,這并不復雜。

接下來是第二種獲取方法,即Ankor(錨)。什么是Ankor?如圖中所示,數據分了很多TILE,但不是所有的TILE都進行搜索,我們只搜索隔行隔列,是總數的四分之一,再將這四分之一的TILE作為Ankor,進行運動搜索。剩下的四分之三,就使用臨近的Ankor獲得的運動向量作為預測向量。不同的體系結構,Ankor的選取和執行順序有變化。比如是GPU,那么Ankor之間沒有時間依賴,同時搜索就可以了。如果是DSP CPU,可以用raster scan,那么下一個Ankor可以用前一個Ankor,這樣就更快些。如果是并行,也沒有任何問題,四分之一數量Block搜索的代價比較小。

接下來還有一個問題,失敗的可能性有兩種。第一,預測的基本向量方向不對。第二,窗口不夠大。這兩種情況都可能造成Ankor搜索失敗。一般,我們不會放大窗口,或者原地轉圈換方向的操作,而是在低分辨率上進行完全一樣的搜索,就是搜索算法不變。但是因為分辨率是四分之一,實際上等價窗口是2*2,在低分辨率的TILE進行搜索,把運動向量乘以2,就作為當前TILE的初始化。我們獲得新的窗口,避免使用速度比較慢的較大的搜索窗口。實際上,可以將其看作是2級的金字塔,更復雜的會有3級。但因為我們有三種預測方法,那么3級的收益就不是很大了,用2級就可以了。

最后一點是搜索。我們現在有運動向量,有窗口了,那么搜索怎么進行呢?傳統的raster scan就是一行一行地搜索,16*16的結構,要搜索256次,這顯然是不可以接受的。我們引出了下山法搜索,用變步長。第一次步長是4。比如16*16的窗口下,搜索13個點的菱形,第一次搜索13個點,然后用殘差最小的點作為新的方向。第二次換成步長2,就是藍色的區域變成新的搜索窗口,如果得到一個非常荒唐的結果那就失敗了。如果殘差繼續縮小,步長換為1,就是綠色的窗口,最后輸出最小殘差的運動向量。

06

優化收益數據

到此為止,快速的運動估計就介紹完了。前面講了三種優化方法,更多方法這次沒有討論。一般這三種優化方法都做的話,會得到一個很明顯的收益。比如,第一種方法有3倍收益,第二種方法也有3倍收益,第三種方法有4倍收益。如圖所示,這里舉了兩個例子。第一個是超分辨率。如果是用傳統的420格式,AVX2指令優化,那就不到200 fps。因為這個算法是面向移動端的。如果在PC上,數據是比較嚇人的。但是優化之后,比如使用了TILE格式,沒有使用紋理分析運動補償的情況下,速度非常快,到1000 fps以上。那么,再進一步,把紋理分析、旁路分析都加入的時候,速度又拔升了百分之四十到百分之六十。第二個例子是傳統算法VBM3D,它的收益也很夸張,因為我們用了快速運動估計算法。它的主要運算都是在Block Match上,我們把Block Match進行加速之后,收益非常明顯。如果是420格式下,網上的開源實現,用5幀序列,AVX2優化,1080的數據的性能是1 fps,如果是8*8 AVX512,那么就是超過100 fps。如果是加上Early Skip 模式,跟運動補償不一樣,這個不是運動參考的方式。這個模式是指進行DCT三維變換的時候,某些情況下可以超前處理一部分數據。Early Skip也會產生1倍的收益,最終在1080p上達到220 fps。這基本上認為已經達到實時的程度。一個非常緩慢的1 fps的視頻算法,提升到220 fps,就是從離線場景進入到實時場景這個過程。

?

今天的分享就是這些內容。謝謝大家。

講師招募?LiveVideoStackCon 2021 北京站

LiveVideoStackCon 2021 北京站(9月3-4日)正在面向社會公開招募講師,歡迎通過?speaker@livevideostack.com?提交個人及議題資料,無論你的公司大小,title高低,老鳥還是菜鳥,只要你的內容對技術人有幫助,其他都是次要的,我們將會在24小時內給予反饋。點擊[閱讀原文]了解大會更多內容。

總結

以上是生活随笔為你收集整理的高性能视频推理引擎优化技术的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。