韋存陽,賈海鵬,張云泉,曲國遠,魏大洲,張廣婷
(1.中國科學院計算技術研究所計算機體系結構國家重點實驗室,北京 100190;2.中國科學院大學計算機科學與技術學院,北京 100080;3.中國航空無線電電子研究所,上海 200241)
在圖像處理領域,目前雖然在ARM平臺上有開源的算法庫OpenCV等,但其從功能和精度上都無法替代x86平臺上的Intel IPP庫。本文對標Intel IPP的高性能算法庫,開發了一個基于ARMv8計算平臺的高性能算法庫。
色彩空間轉換、圖像插值和圖像濾波在圖像處理領域都具有非常廣泛的應用場景。各個行業都有其最適合的色彩空間,例如計算機圖形領域一般使用RGB色彩空間,視頻領域一般使用YUV或YCbCr色彩空間。各個色彩空間之間的轉換本質上是將一組數據經過線性運算后轉換為另一組數據。色彩空間轉換可有效地使圖像符合各行各業的顯示標準。目前已有很多文獻提出了色彩空間轉換的快速算法。
圖像插值函數廣泛應用于醫學、航空和動畫制作等領域。圖像插值本質上是利用插值算法,根據原始圖像的各個鄰近點計算出目標圖像的像素值。這些插值運算可以使放大或縮小后的圖像更加自然平滑。
濾波函數的主要功能包括圖像降噪和邊緣檢測等,其在圖像識別領域應用廣泛。總體上,濾波函數利用各種濾波算法,根據原始圖像的鄰近點,經過一系列運算得到目標像素值。
針對上述函數,本文設計并實現了基于ARMv8平臺的HMPP(Hyper Media Performance Primitives)算法庫,庫中包含CvtColor(Convert Color)、Filter和Resize 3個模塊。其中,CvtColor模塊提供了一系列在RGB、YUV和YCbCr色彩空間之間快速轉換的算法,對于YUV和YCbCr色彩空間還支持升采樣與壓縮采樣,此外還支持RGB與灰度圖之間的轉換;Resize模塊提供了基于Nearest、Linear、Cubic和Lanzcos插值的圖像插值算法,以及超采樣(Super)與抗鋸齒(Antialiasing)算法;Filter模塊提供了雙邊濾波、高斯濾波以及均值、中值、索貝爾和沙爾等濾波算法。
本文的主要貢獻如下所示:
(1)對標Intel IPP算法庫,開發了一個基于ARMv8計算平臺的高性能圖像算法庫,與開源OpenCV庫對比,其性能有著顯著的提升。
(2)設計了一種針對ARMv8架構的圖像處理算法優化體系。該體系在提升ARMv8平臺上圖像處理函數性能的同時,也對其他算法在ARM處理器上的實現和優化有著一定的參考意義。
當前對于圖像處理函數的優化方法研究已有很多。雖然編譯器也會進行向量化的優化操作,但有研究表明,相比較于編譯器的自動向量化優化,利用ARM Neon技術手動優化可以得到巨大的加速空間[1]。SIMD指令通過適當的代碼操作可以顯著提高圖像處理算法的性能[2,3]。在圖像色彩空間轉換領域,當前已有許多針對Intel 平臺、使用SIMD指令的優化算法[4,5],對本文所提的基于ARM平臺的SIMD優化有一定的借鑒意義。此外,文獻[6]表明,使用移位近似浮點乘法并結合查表法可顯著降低浮點運算的復雜性,提高算法性能。
圖像插值算法應用十分廣泛[7],各個插值算法的特性為不同需求的用戶提供了選擇的空間[8]。對于圖像插值算法的研究成果更是十分豐富。當前已有基于ARM Neon的若干優化方法[8]。例如,對于計算復雜性較高的Lanczos插值,文獻[9]指出可以使用查表法代替一些復雜運算來提高性能,也為本文的優化工作提供了一些新思路。
線性濾波函數的優化難點在于如何提高卷積計算的性能。有研究表明,在Intel平臺使用SIMD技術對濾波核為3×3的卷積算法可以大大提高處理速度[10]。同時,在ARM平臺也有基于ARM Neon技術對Prewitt 算子進行優化[11]。上述研究對本文中線性濾波的若干函數優化都有一定的借鑒意義。
CvtColor將圖像色彩從一種存儲格式轉化為另一種格式,是點對點的轉換,同時訪存連續,故具有很好的并行性。CvtColor涉及的轉換模式多種多樣,包含RGB、YUV和YCbCr之間的相互轉換,還有Gray和RGB之間的相互轉換。
在 RGB 模型中,每種顏色都顯示為紅色(R)、綠色(G)和藍色(B)的組合,是三通道圖像,這在圖像領域應用較為廣泛。Gray是灰度圖,是單通道的圖像。YUV模型中,Y表示明亮度(Luminance、Luma),U和V表示色度和濃度(Chrominance、Chroma)。YCbCr 是在世界數字組織視頻標準研制過程中作為ITU-R BT.601 建議的一部分,其實是YUV經過縮放和偏移的翻版。其中Y與YUV 中的Y含義一致,Cb和Cr都指色彩,只是在表示方法上不同而已。此外,對于RGB和YCbCr色彩空間,還有符合ITU-R BT.709建議的HDTV(High-Definition TV)標準及CSC(Computer Systems Consideration)標準。
特別地,對于YUV和YCbCr色彩空間,可通過減少圖像可接受的顏色描述所需的比特數來壓縮采樣。其壓縮原理是基于人眼對于亮度變化的敏感度高于對色度變化的敏感度。其本質是為每個像素分配單獨的亮度分量,同時將一個色度分量分配給一個像素組。如圖1所示,主流的采樣格式包括4∶4∶4 YUV(YCbCr)、4∶2∶2 YUV(YCbCr)、4∶1∶1 YCbCr和4∶2∶0 YUV(YCbCr)。

Figure 1 Downsampling for YUV and YCbCr
在存儲格式上還有平面圖像格式(Planar Image Formats)和像素圖像格式(Pixel-Order Image Formats)。平面圖像格式是將各通道數值存儲在3個數組中,像素圖像格式是將各個通道混合存儲在1個數組中。其中像素圖像格式存儲的圖像可以充分發揮ARM Neon指令集的優勢,相比于x86平臺,無需繁瑣的轉換就可以分離出各通道的像素。
以YUVToRGB為例,可以看出總體上CvtColor模塊由于其訪存連續,同時各個像素點之間相互獨立,因此具有極好的并行性。
從計算公式(1)上也可以看出,運算較為簡單,其性能瓶頸在于訪存。
(1)
Resize對圖像大小進行的變換,包括放大和縮小。Resize包括多種模式,最基本的是根據縮放比計算得到目標點映射在原矩陣中的坐標,然后根據該坐標從原矩陣中取數并將其直接賦值給目標點。這種方法稱為Nearest,計算如式(2)所示:
(2)
其中,目標圖像坐標為(m,n),源圖像大小為SR×SC,目標圖像大小為DR×DC,映射到原圖像的坐標為(x,y)。這種方式簡單且高效,不需要運算操作,僅需要依據映射公式取像素即可。但其缺點在于由于僅通過比例映射進行縮放,如圖2a所示,在對圖像進行縮小處理時,源圖像中存在大量的點并不參與映射,造成了數據失真的情況;如圖2b所示,在對圖像進行放大操作時,目標圖像中相鄰點映射到原矩陣中的點可能是同一點,此時就會存在明顯鋸齒。

Figure 2 Nearest Interpolation
Linear、Cubic和Lanczos插值主要是為了解決Nearest存在的問題而被提出的。與Nearest類似,Linear、Cubic和Lanczos也是根據縮放比計算得到原矩陣對應的坐標。映射公式如式(3)所示:
(3)
其中,目標圖像坐標為(m,n),源圖像大小為SR×SC,目標圖像大小為DR×DC,映射到原圖像的坐標為(x,y)。系數計算的行列公式相同。以行方向為例,Linear行方向的2系數為y′-y和1-(y′-y)。
設P為參與計算的點的坐標與x′的差,B和C是用戶定義的參數,則Cubic插值行方向系數計算公式如式(4)所示:
(4)
2-lobed Lanczos與3-lobed Lanczos插值行方向系數計算公式分別如式(5)和式(6)所示:
(5)
(6)
之后再分別取原矩陣映射點周圍的2×2,4×4和6×6規模的小矩陣,使用相應的插值算法計算系數,并通過加權計算得到目標點的數值。
這樣,在圖像縮小時,就可以緩解失真現象;在放大時,就算2目標像素映射到原矩陣的同一點,但在插值計算階段,小矩陣內各個點的加權值不同,最終計算的結果也不同,避免產生鋸齒現象。
Linear、Cubic和Lanczos這3種方法引入了較多的像素參與運算,損失了性能,但是緩解了失真與鋸齒的產生。圖2和圖3中包含了Nearest、Linear、Cubic和Lanczos所需要的原矩陣數據區域示意圖。可以看出,這4種方法訪問的原矩陣的數據區域依次增大,得到的目標圖像相對原圖像的還原度也依次提高。應用時可以根據不同需求和條件選擇不同的方法。

Figure 3 Linear,Cubic,Lanczos,Super interpolation
除此之外,本文還提出了專門應對圖像縮小時失真問題的超采樣算法(Super),根據圖像的縮放比例,將相應比例的像素點加權平均得到目標像素,對于不構成一個像素的點,使用所占面積計算其加權值(見圖3)。超采樣可以充分使用原始圖像的各個像素點,因此可以顯著緩解圖像失真情況。
在上述Nearest、Linear、Cubic、Lanczos和Super算法中,其計算方式和訪存方式都不同,并且依據映射公式可以看出其訪存都不連續。相比于CvtColor模塊,Resize模塊的可并行性降低,但由于數據之間依然相互獨立,即每個目標點的計算均與其他目標點的計算不相關,因此還是具有較好的并行性。對于Resize模塊來說,計算并不繁瑣,訪存不連續的特性決定了訪存仍是其高性能處理的瓶頸。
濾波函數主要適用于邊緣檢測、降噪、模糊和特征檢測等圖像處理操作,分為線性濾波(均值濾波和沙爾濾波)和非線性濾波(中值濾波和雙邊濾波)2種。
原理上,線性濾波是根據某一特定的二維卷積算子,與原始圖像從左到右、從上到下進行卷積操作,如式(7)所示:
(7)
其中,dst(x,y)表示目標圖像的像素,kernel(x′,y′)表示卷積算子,src(x+x′,y+y′)表示源圖像的像素,kernel.width和kernel.height表示卷積窗口的寬和高。
為了確定對應于每一目標圖像像素點的原始圖像卷積區域,還需要定義卷積核的錨點(anchor point)。一般來說,錨點即幾何中心,本文也支持一些函數自定義的錨點。
簡單來說,非線性濾波也有類似于線性濾波中鄰域的概念,但“卷積核”不是固定的。目標像素點是根據鄰域區域通過某些非線性運算得到的。
濾波函數由于具有很高的數據重用性,即2個目標點之間存在一定的數據相關性,因此不具備很好的并行性。同時訪存操作較多,并且存在一定的非連續訪存,因而訪存仍是Filter模塊高性能處理的瓶頸。
值得注意的是,如圖4所示,Resize與Filter都可能超出源圖像的邊界,如果不提前定義這些邊界情況就會發生越界操作。

Figure 4 Possible boundary problems
因此,本文所實現的HMPP庫提供了對4種邊界的支持,如圖5所示。4種邊界包括常數邊界(Constant Border)、復制邊界(Replicated Border)、鏡像邊界(Mirror Border)及混合邊界(Mix Border)。

Figure 5 Four boundary handling methods
常數邊界處理方法是將用戶指定的值作為越界后的取值。復制邊界處理方法是當越界時取最外層數值作為越界后的取值。鏡像邊界處理方法是發生越界時從邊框像素向內取值。此外,內存中預定義的邊界(Border in Memory),即傳入函數的圖像外層,還有若干層數據,這樣就允許用戶更加靈活地定義越界后的取值。
此外還支持內存中預定義的邊界及其與復制邊界的混合邊界,可以指定某條邊使用預定義邊界,其他邊使用復制邊界。常用于圖像分塊計算。
從優化角度對以上3個模塊中包含的算法進行分類:
(1)數據無關。每一個目標像素點的計算都是相互獨立的,相鄰目標像素點計算對應的源圖像,訪存是連續的,如CvtColor模塊,其對應的優化方法主要是算法優化及計算過程的優化。
(2)數據共享。相鄰目標像素點的計算可共用部分源像素點,如Filter模塊。其優化的重點在于數據共享優化,減少訪存次數。
(3)非規則訪存。一個目標像素點計算對應的源圖像,訪存不連續,或相鄰目標像素點計算對應的源圖像,訪問不連續。其優化的重點在于面向SIMD的訪存優化。
ARM Neon技術是ARM cortex-A和cortex-R系列處理器的高級單指令多數據(SIMD)架構的擴展,它可以提高多媒體程序的性能。鯤鵬920平臺擁有32個向量寄存器,向量寄存器中數據的位寬可以設置為 8,16,32 或 64,這意味著一個向量寄存器可以容納多個數據。Neon指令對整個向量寄存器進行操作,一個寄存器中的多個數據可以一起處理。因此,Neon技術可以使算法并行工作,從而提高性能。
CvtColor模塊和部分Filter模塊在計算中所使用到的系數為浮點數,若直接按照公式計算需要將整數轉換為浮點型。但是,ARM Neon中并沒有將unit 8直接轉換為浮點數的操作,需要經過unit 8?unit 16?unit 32?float 32 3次轉換才能完成浮點運算,得到結果后還要將float 32轉換為unit 8。此時數據轉換指令往往比運算指令占比更大,程序大部分時間都浪費在數據轉換上,十分不合算。
因此,本文用整數近似替換浮點數,進而減少數據轉換帶來的性能損失。若原浮點型系數為S,則使用S′=floor(S×2n)近似,n為確保計算過程中不越界的最大整數。例如,對于RGB2YUV,原始公式如式(8)所示:
(8)
使用上述方法替換后,計算公式轉換為式(9):
(9)
相較于浮點運算,這樣減少了大量的數據轉換操作。同時,在整數運算過程中還可以通過使用長指令進一步減少數據轉換操作,從而大幅提高性能。
ARM Neon Intrinsics對高位操作的指令較為豐富,廣泛地支持對寄存器高位的乘法、移位和數據類型轉換等指令,例如vmull_high_u16,vmlal_high_u16等。但是,沒有相應的對寄存器低位操作的指令。若只用ARM Neon Intrinsics指令編寫程序,對低位的操作將會較為繁瑣,需要增加若干步get_low操作。經查閱發現,ARM Neon匯編指令支持低位操作,因此本文考慮使用嵌入式匯編自定義一些類Intrinsics指令來實現低位操作,例如vmull_low_u16和vmlal_low_u16,以減少大量的get_low操作。如圖6所示,vmull_low_u16(a,b)與vmull_u16(vget_low_u16(a),vget_low_u16(b))運行的結果是等價的,但前者可以節省2條指令。即:
vmull_low_u16(a,b)?
vmull_u16(vget_low_u16(a),vget_low_u16(b))

Figure 6 Opera the lower half of register
經測試,在CvtColor模塊中,使用自定義的嵌入式匯編函數替換掉get_low操作,性能大約可提升15%。
如圖7所示,Resize模塊的特點在于各個點之間的運算相互獨立,不存在數據重用,但2個相鄰的目標像素所映射的源圖像坐標不連續。因此,Resize模塊對于并行運算相對友好,但對于并行訪存不友好。此時若要通過ARM Neon并行計算,需要取出一系列不連續的點,通過標量寄存器到向量寄存器的轉換,將所需用到的像素點存放到向量寄存器中。需要注意的是,這種標量到向量寄存器之間的轉換十分耗時。針對這一特點,本文設計了以移位代替頻繁的標量到向量寄存器轉換的方法。

Figure 7 Discontinuous mapping of the Resize module
此外,在取值時也要充分利用C語言指針的特性,如圖8所示,以Linear_8u_c1為例,計算一個像素需要周圍4個點,則需要在每一行取出連續的2個點,此時可以直接以unit 16的類型取數,再重解釋為2個unit 8類型的數,從而減少訪存指令數。

Figure 8 Optimization of loading operation
若在此基礎上使用ARM Neon優化,對于鯤鵬920平臺的128位寄存器,每次可計算8個結果,在原圖像上映射為2行,每一行16個點。對于每一行,直接進行取數操作將會有8次標量寄存器到向量寄存器的轉換。為了減少該開銷,本文使用計算來代替一部分轉換操作。具體步驟如下所示:
步驟1使用unit 16類型指針,取出8個不連續的點,并保存為unit 64格式;
步驟2每4個點作為一個單元,其中第i個點左移16×(i-1)位,并將這4個移位后的結果相加,此時步驟1中的8個點轉化為了2個單元;
步驟3將步驟2得到的2個單元的結果保存到uint64×2的向量寄存器中;
步驟4將uint64×2重解釋為uint8×16。
圖9展示了上述算法的實現過程。
此時雖然增加了若干步加法與移位操作,總指令條數甚至有所增加,但標量與向量寄存器之間的轉換操作減少為原來的1/4,經測試,性能提升了50%以上。這說明標量與向量寄存器之間的轉換操作相比于移位與加法計算,耗時更多。
此外,考慮到ARM Neon所支持的最小操作寬度為64 bit,這種方法也可用于當數據不夠64 bit的情況。
從Resize模塊坐標映射和系數計算方法可以看出,行和列的系數計算是相互獨立的。如圖10所示,對于同一列數據,映射到源圖像后也在同一列,同一行數據映射到源圖像后也在同一行,系數僅與源圖像與目標圖像的大小有關。針對該特點,可以把系數計算放到init函數中,這樣對單幅圖像來說可以減少系數重復計算。若需要對多幅大小相同的圖像進行插值,僅需要計算一次系數就可應用于這一組圖像,大大增加了計算的重用性。

Figure 10 Mapping in the column direction
Filter模塊的高斯濾波和雙邊濾波也可以使用類似的方法預先計算系數。例如,對于Bilateral_8u,計算公式如式(10)所示:
w(i,j,k,l)=
(10)
其中,Id(i,j)表示輸出點的像素值,I(i,j)表示輸入點的像素值,S(i,j)表示以(i,j)為中心的濾波窗口,w(i,j,k,l)表示空間鄰近高斯函數和像素值相似度高斯函數的乘積。σd和σr這2個參數由用戶輸入。σd為坐標空間中濾波器的sigma值,該值較大時較大的定義域空間會影響結果。σr為顏色空間過濾器的sigma值,該值較大時較大的值域空間也會影響結果。
經觀察可以發現,對于unit 8類型,I(i,j)-I(k,l)至多會有256種取值,則(i-k)2+(j-l)2在濾波核大小確定時就可確定全部情況。因此,在init函數中預先計算上述參數,在計算階段僅需要取值就可以計算最終的結果,每次計算不再需要并行化相對較復雜的exp函數。因此,使用這種方法既減少了重復計算,又增加了并行度,性能將會有極大的提升。
線性濾波的特點在于相鄰行的計算可復用,在行方向上使用向量化可以減少重復訪存。如圖11所示,在列方向上需要通過循環展開若干次來減少重復訪存,并減少重復計算。以ScharrVert為例,其濾波核為:
數據為:
則第1個數據的計算公式可簡化為式(11):
result=((a3-a1)+
(a9-a7))×3+10×(a6-a4)
(11)
顯然,第2行數據計算時可復用(a9-a7)和(a6-a4)這2個結果。

Figure 11 Column expands to merge the loading operation
對于這種濾波核固定,且濾波核具有一定對稱性的濾波函數,這樣就可以大大增加計算結果的復用性。同時考慮到循環展開過多列將會增加Cache Miss的風險,因此合理地在行方向上循環展開將大大提高數據的復用性,極大地提高性能。
此外,對于均值濾波,當濾波核較大時,可以在行方向上通過加入下一個移進來的點、減去上一個移出去的點來增加數據復用。在列方向上通過將每一行數據保存到buf中,減少Cache Miss的同時,提高計算結果復用性。
(1)整數運算:移位代替乘法。
在計算壓縮采樣、升采樣和濾波函數時,經常會遇到需要乘、除2的若干次方的情況。在鯤鵬920平臺使用移位運算的實現,比使用乘法運算更快。以Laplacian濾波為例,濾波核為:
數據為:
優化前的計算公式如式(12)所示:
2(a1+a3+a7+a9)-8(a5)
(12)
使用移位運算代替乘2和乘8后的計算如式(13)所示:
((a1+a3+a7+a9)?1)-(a5?3)
(13)
經過測試發現,在鯤鵬920平臺,Laplacian濾波使用移位運算代替整數乘2n后,性能大約提升了10%。
(2)浮點運算:乘法代替除法。
相比于乘法指令,除法指令在鯤鵬920平臺性能較低。以均值濾波為例,最后的計算結果需要除以濾波核大小計算均值,這里可以使用乘以倒數來代替除法。經測試,在大規模情況下均值濾波性能提升了25%左右。
本文依據各個模塊的特點,為每一類模塊都進行了單獨的性能優化。表1總結了各模塊所使用到的優化方法。

Table 1 Summary of optimization methods of each module
本文的HMPP庫在ARMv8架構的鯤鵬920 處理器上實現運行,處理器時鐘頻率為2.6 GHz。OpenCV庫采用當前最新版本的4.5.3。
具體實驗環境如如表2所示。

Table 2 Experimental environment
由于本文介紹的HMPP庫精度參照的是Intel IPP庫,與OpenCV并不一致,因此在對比中本文盡量選取與OpenCV結果相近的函數進行性能對比,以避免差異影響最終的分析結果。本文使用運行時間作為性能評價的指標,相同圖像規模下時間越短,則性能越高。
在CvtColor模塊,選取Gray2RGB和RGB2Gray圖像轉換算法,與OpenCV中的CvtColor()函數進行對比。其中,數據類型都為unit 8,RGB和YUV都為3通道,Gray為單通道。
實驗結果如圖12所示,這表明對于GRAY2RGB,僅需要簡單的存取操作并不需要運算。HMPP庫充分利用了ARM Neon指令集的特性,相比OpenCV性能提升最大可達198%,性能提升最小有28%, 性能提升平均有101%。在RGB2GRAY,運算復雜性有所提升,性能提升最大有25%,性能提升最小有12%,性能提升平均有18%。

Figure 12 Color Space convert between Gray and RGB
Resize模塊選取最近鄰插值與OpenCV庫的resize()函數進行對比。其中,數據類型為unit 8,并分別對比單通道、3通道和4通道的性能。圖13的結果表明,對于單通道性能提升最大有131%,性能提升最小有22%,性能提升平均有91%;3通道性能提升最大有290%,性能提升最小有31%,性能提升平均有210%;4通道性能提升最大有94%,性能提升最小有53%,性能提升平均有71%。

Figure 13 Performance of Resize_Nearest
Filter模塊選取Laplacian濾波與OpenCV庫的Laplacian()函數進行對比,其中,輸入圖像格式為unit8,單通道,輸出圖像格式為int16,單通道。如圖14所示。相比OpenCV性能提升最大有233%,性能提升最小有158%,性能提升平均有194%。

Figure 14 Performance of Filter_Laplacian
整體上,本文實現的HMPP庫性能均明顯優于OpenCV庫的。結果表明,本文設計的HMPP庫以及對算法實現的一系列SIMD優化、存取優化、計算簡化和嵌入式匯編優化等是有效的。
本文基于鯤鵬920平臺,提出了一系列針對圖像色彩空間轉換、濾波和圖像插值算法的優化方法。與現有的開源圖像處理庫相比,本文實現的HMPP庫的優勢在于與Intel IPP庫具有相當的精度以及相似的接口與功能,降低了x86用戶在國產處理器鯤鵬920上開發的難度。在優化方法上,本文充分利用各模塊的特征,ARM Neon的特點以及鯤鵬920的特性,與OpenCV相比有著較大的性能提升。
在優化過程中發現,由于本文主要使用ARM Neon Intrinsics進行優化,在某些情況下指令延遲將會造成較大開銷,因此在未來將會利用ARM neon匯編指令,盡量降低延遲開銷。