張燦宇,封岸松,張華良,易 星,王俊彭
(1.沈陽化工大學 信息工程學院,遼寧 沈陽 110142;2.中國科學院 沈陽自動化研究所,遼寧 沈陽 110016;3.中國科學院 網絡化控制系統重點實驗室,遼寧 沈陽 110016)
圖像處理算法[1,2]日益復雜,使用普通的計算平臺無法解決算法計算量越來越大、內存需求越來越高的問題,而現場可編程門陣列(field-programmable gate array,FPGA)可以很好地解決這個問題。當前主流的圖像處理計算平臺有:GPU(graphics processing unit,GPU)、ASIC(application specific integrated circuit,ASIC)[3,4]。GPU可以很好地對算法進行硬件加速,但使用GPU對環境和庫的依賴性大,應用場景受限不適合在移動應用;ASIC可為具有特定功能的應用進行設計開發,但ASIC的設計和開發周期長,研究成本高,靈活性差導致其并不適合圖像處理算法的部署[5]。
而FPGA不僅內部包含大量的乘法單元,同時還可以對大量變量進行邏輯運算和賦值實現并行計算,讓圖像處理算法更加適合在FPGA上部署。FPGA有著豐富的片上資源和良好的內存帶寬,兼具高性能、低功耗、適應性強的優點,而且根據FPGA的可重構性,在不需要更換芯片的情況下就可以讓用戶自己設計算法架構和接口類型,極大地方便了開發人員后續的維護工作[6]。然而FPGA使用硬件描述語言開發周期長,調試手段不足導致硬件相關的研究進展緩慢,在HLS(high-level synthesis,HLS)高層次綜合技術出現之后突破了以往使用硬件描述語言的局限性,可以快速地描述和實現所需的硬件結構,加速了OpenCV算法的實現過程。
目前已有許多研究人員利用FPGA進行算法的硬件加速并取得了一定的研究成果。Nguyen等[7]采用了硬件RTL電路對YOLOv2算法進行硬件加速,使用了較少的DSP完成了算法的復雜計算任務,Nakahara等[8]使用FPGA進行目標檢測算法的硬件加速達到一定的效果,Yu等[9]將卷積和反卷積運算設計在FPGA上采用硬件描述語言進行硬件加速。
本文以FAST角點檢測算法和Sobel邊緣檢測算法為例,將這兩種算法部署在FPGA上進行硬件加速,采用PYNQ-Z2開發板,使用HLS高層次綜合技術進行算法的實現及優化,充分利用FPGA的片上資源,同時在Xilinx公司提供的Vivado 2018.3開發工具進行系統模塊集成,最后進入Jupyter Notebook計算環境,完成系統模塊的調用及驅動,實現系統功能。
在Edward Rosten和Tom Drummond發表的《Machine learning for high-speed corner detection》文章中提出了一種FAST特征點,隨后又對文章進行修改后再次發表了《Feature From Accelerated Segment Test》,其中對FAST角點進行了定義:任取一個像素點與其周圍足夠多的像素點進行比較,若這個點與周圍點的不同符合某種要求時,則該點就是角點。判定某一點是否為角點,先確定一個角點響應函數,如式(1)所示
N=∑|I(x)-I(p)|
(1)
式中:I(x) 為圓周上任一像素點的灰度值;I(p) 為中心像素點的灰度值,Th為設定的閾值;circlez(p) 是以p點為中心的圓上點的集合。研究后發現,選取的圓半徑為3時FAST角點檢測的效率和精度能達到一個很好的平衡。FAST角點檢測的原理就是任取一像素點作為圓心畫圓,圓的半徑為3,周長為16像素。FAST角點檢測原理如圖1所示。

圖1 FAST角點檢測原理
第一步:像素點編號。在圓上按照順時鐘方向對像素點依次進行編號,一共16個。
第二步:準角點選取。選取其中的第1、第5、第9、第13像素點分別按照式(2)進行比較,若4個像素點中至少有3個符合,則該中心點p為準角點。遍歷圖像中所有的像素點,所有符合要求的點均為準角點。
第三步:準角點判斷。當所有的準角點被篩選出來后,還要進行下一步的判斷看是否為角點,即利用式(1)角點響應函數依次與圓上的像素點進行判斷,當有至少9個像素點滿足要求時,則該認為點為角點,否則,p點不為角點。
第四步:引入非極大值抑制。在經過上述步驟進行角點檢測之后,會導致在圖像中某一處多次重復出現角點形成聚簇效應,所以通過引入非極大值抑制的方法來解決這一問題。非極大值抑制就是如果在角點p為中心的3×3鄰域內出現一個角點,那么就保留這個角點,當存在多個角點時,則需要計算除角點p以外的角點的分值Vi,分值計算如式(2)所示
(2)
式中:Vi為角點p鄰域內圓上點Ii(x) 與點Ii(x) 對應的中心點Ii(p) 差值的絕對值總和。最后,保留V值大的角點作為鄰域內的角點,V值較小的角點則被刪除。
FAST角點檢測算法IP通過Xilinx公司提供的Vivado HLS工具進行設計,在算法設計的過程中,需要將OpenCV(open source computer vision library,OpenCV)函數鏈替換為HLS視頻庫里提供的函數鏈[10]。Vivado HLS進行圖像處理設計具體流程如下:①HLS在處理圖像時需要將存儲的OpenCV格式的圖像數據轉換為AXI-Stream視頻流格式,在HLS中通過IplImage2AXIvideo函數實現轉換;②再將視頻流格式的圖像數據轉換為Mat格式,通過相應的AXIvideo2Mat函數將其轉換;③調用HLS視頻庫中對應的圖像處理函數進行圖像處理;④經過處理之后再轉換為AXI-Stream視頻流格式;⑤最后還原成OpenCV格式將其保存,通過以上操作就完成了使用HLS對圖像的處理。最后經過綜合和仿真測試將算法打包成IP核放在Xilinx提供的Vivado工具進行系統模塊設計。OpenCV與HLS視頻庫圖像處理設計流程如圖2所示。

圖2 OpenCV與HLS視頻庫圖像處理設計流程
在Vivado HLS中完成了FAST角點檢測算法的IP設計,在算法中設置閾值為40,對圖3(a)進行角點檢測,仿真結果如圖3(b)所示。

圖3 Vivado HLS原圖及仿真結果
由表1所列,FAST角點檢測算法硬件加速設計的時鐘頻率為9.4 ns,算法運行的時鐘周期為1021321,則FAST角點檢測算法運行的時間為9.6 ms。

表1 FAST硬件加速設計的時鐘頻率/ns
實驗采用Sobel邊緣檢測算法進行圖像的邊緣檢測,Sobel算子引用類似局部平均的運算,對噪聲有平滑效果,同時還能提供精準的邊緣信息。Sobel算子在像素的上、下、左、右處進行灰度值加權差計算,對噪聲具有一定的平滑作用,在要求不是很高的情況下,Sobel邊緣檢測算法是足以滿足需求的。
Sobel邊緣檢測的原理是通過將圖片劃分為多個不同的3×3矩陣,以被處理像素為中心對其鄰域進行灰度分析的一種算法。具體操作方法是通過X方向算子和Y方向算子與相鄰區域像素點構成的矩陣作卷積來計算該像素點的水平梯度和垂直梯度,然后根據向量的方法求出梯度值,然后將梯度值與設定的閾值比較,如果梯度值大于閾值,這個像素點就為邊緣點,就是非邊緣點。Sobel算子模板及像素矩陣如圖4所示。

圖4 Sobel算子模板及像素矩陣
對3×3矩陣分別與Sobel X方向、Y方向算子進行卷積得到像素點的水平梯度、垂直梯度分別如式(3)、式(4)所示
Gx=P1×(-1)+P2×0+P3×1+P4×(-2)+
P5×0+P6×2+P7×(-1)+P8×(-9)+P9×1
(3)
Gy=P1×1+P2×2+P3×1+P4×0+P5×0+
P6×0+P7×(-1)+P8×(-2)+P9×(-1)
(4)

Sobel邊緣檢測算法IP與FAST角點檢測設計流程類似需將OpenCV中Sobel函數替換為HLS視頻庫里的Sobel函數,具體的設計流程如下:①在HLS中通過IplI-mage2AXIvideo函數實現轉換,將存儲的OpenCV格式的圖像數據轉換為AXI-Stream視頻流格式;②然后通過AXIvideo2Mat函數再將視頻流格式的圖像數據轉換為Mat格式;③調用HLS視頻庫中Sobel算子函數進行邊緣檢測處理;④處理完畢之后還需將其再次轉換為AXI-Stream視頻流格式;⑤最后還原成OpenCV格式將其保存在DDR3中。在對算法進行仿真時添加一張圖像進行驗證,最后將算法封裝為一個RTL級IP核,通過Vivado軟件進行硬件Block Design設計時使用。最終Vivado HLS原圖及檢測結果如圖5所示。
由Vivado HLS綜合生成的報告可得到硬件加速設計的時鐘頻率如表2所列,時鐘的頻率為10.049 ns,算法運行的時鐘周期為1149198,則Sobel邊緣檢測算法運行的時間為11.5 ms。

表2 Sobel硬件加速設計的時鐘頻率/ns
HLS(high-level synthesis,HLS)即高層次綜合,通過Xilinx官方提供的Vivado HLS工具可以將使用的高層次語言(如:C、C++、System C)可自動轉換成低層次語言(如:Verilog、VHDL、SystemVerilog)[11,12],由邏輯結構向RTL硬件電路模型的這一轉換,突破了以往對FPGA使用硬件描述語言(hardware description language,HDL)設計的局限性,相比之下HLS代碼可讀性高,便于維護,可以更加方便實現各種接口的協議,同時HLS的使用提高了對IP核(intellectual property core)的重用率,還能從更抽象的層次對算法進行功能性驗證,開發效率更高[13]。除此之外,在Vivado HLS工具里提供了HLS視頻庫,可以直接去調用所需的圖像處理函數,然后將其封裝為IP核以便調用。
在開發FPGA項目時,可在Matlab中進行算法的功能性驗證,當算法的功能能夠準確顯示出來,就可以在Vivado HLS中使用C、C++代碼實現該算法,為了能夠充分地利用FPGA的內部資源,發揮FPGA的并行計算能力,以獲取更高的性能,在完成算法的綜合和仿真后還需進行優化,直到最后的設計能夠滿足要求為止[14]。
Vivado HLS為用戶提供了多種優化方法,通過使用不同的優化方法進而調整和控制FPGA的內部邏輯和I/O行為。Vivado HLS中包含兩種優化方法:一是使用Directives,在Directives控制欄中將所需變量進行優化設置,以編譯選項的方式插入到代碼中,為開發人員帶來了極大便利;二是在代碼中插入#pragma命令來確定變量實現類型和結構。Vivado HLS優化指令見表3。

表3 Vivado HLS優化指令
一般優化分為串行和并行兩種方式,串行方式使用的硬件資源少,但加速效率差;并行方式以消耗硬件資源加速任務執行,效率高。為了實現對算法的硬件加速,通常采用并行處理的方式。并行處理的優化方法有以下幾方面:①模塊內部進行展開(unroll);②對模塊內部進行流水化(pipeline)操作;③對模塊之間進行流水化操作;④數據流優化(Dataflow)。循環展開:對for循環來說,可以通過添加directives指令進行展開,可以將一個for循環展開成N個for循環,每個循環執行1/N的運算,而這N個循環可以同時進行計算處理,減少了算法的處理時間,以消耗硬件資源的方式獲取更短的計算時間。從圖6可知,將原先的一個for循環一分為二,兩個for循環并行執行,從而節省了一半的時間。

圖6 循環優化Unroll處理前后對比
流水化(pipeline)處理:對任務內部進行流水化操作,它能使II(initiation interval,II)和Latency最大化降低,同時還能使子任務(函數、循環)最大程度地并行執行,II指的是數據初始化間隔,表示模塊在連續接收兩次輸入數據的時間間隔,當II越小,意味著數據吞吐量越大;Latency指的是從輸入數據到接收所有輸出數據的延遲時鐘數,當Latency越小,同樣意味著計算速度越快。由圖7可以看出,原先的任務在沒有pipeline時,整個的計算是按照先后順序執行的,需要執行N次花費3N個時鐘周期才能完成計算,而在進行pipeline處理后整個計算過程只花費了N+2時鐘周期,因為現在的計算操作不用再等前一次的循環執行結束后再去執行下一個循環,而是當前一次循環執行完一個階段就可以執行下一次了,這樣開始大大提高了計算效率。
由圖8可知,在沒有使用Dataflow優化指令時,3個循環是按照順序執行的,再加入Dataflow指令后,當LOOP A有輸出時就可以利用這個輸出去執行LOOP B,不需要等到LOOP A執行完后再開始執行,通過加入Dataflow指令后,多個任務的執行就有了交疊部分,從而降低了Latency,提高了數據的吞吐率。

圖8 循環優化Dataflow處理前后對比
通過直接計算和進行流水化、循環展開后可得到表4。

表4 結果對比
由表4可知,在進行流水化處理后并不會增加硬件資源的消耗而能高效完成計算任務,在進行循環展開后資源消耗成倍增加的同時效率也會成倍增加,另外,電路的時延因在進入和退出循環各需要一個時鐘周期而比像素數多2,這種就是以犧牲硬件資源消耗換取計算效率達到硬件加速效果。
系統硬件平臺是基于Xilinx 公司的PYNQ-Z2,FPGA主芯片為ZYNQ XC7Z020-1CLG400C,開發板包含1個650 MHz的雙核ARM Cortex-A9處理器、1個630 KB的Block RAM(block random access memory)、220 DSP(digital signal processing)切片和1個外部512MB DDR3(double-data-rate three synchronous dynamic random access memory)。
整個系統可分為兩個部分:PS(processing system)、PL(programmable logic)。PYNQ的PS端在ZYNQ的基礎上內置了Liunx操作系統和Python的編譯環境,PL端的各種IP核都被內置為“Overlays”以方便調用,這種形式類似于軟件庫。PYNQ使用Overlay可以對連接到PS端的接口進行解析,進而控制FPGA 邏輯資源及IO(Input/Output)。
在PS端負責對OV5640攝像頭進行初始化設置以及VDMA和對HDMI顯示驅動,PL端負責視頻圖像數據的采集、角點及邊緣檢測處理和結果顯示。
3.1.1 PL部分
PL端分為3個模塊:圖像采集、處理以及顯示模塊。
(1)圖像采集模塊:圖像采集模塊采用OmniVision公司的OV5640攝像頭作為圖像傳感器,也就是數據采集前端,它可以將模擬量轉換成數字量,支持90 fps VGA(640×480)分辨率的圖像采集,支持輸出的圖像格式為RGB565等。由于圖像傳感器只能感受光的強弱,無法感受光的波長,而光的顏色是由波長決定的,導致最后畫面不能出現顏色。為解決這個問題,引入Bayer插值補償算法(Bayer interpolation),也就對應于Sensor Demosaic IP。
在使用OV5640時,需要將其上電初始化,而后使用I2C總線對寄存器參數進行配置初始化,當經過兩步初始化后就可以進行圖像采集工作。I2C總線是一種雙向的二進制串行總線,它只需兩根線就可在連接在總線上的器件之間進行信息傳遞,即數據線SDA和時鐘線SCL。I2C支持多機通訊,也支持多控模塊,但前提是在同一時刻下主控只有一個。
(2)圖像處理模塊:圖像處理模塊包含兩個IP:FAST邊緣檢測和Sobel邊緣檢測,當圖像數據由VDMA(video direct memory access)傳過來時就可以對數據進行處理。VDMA包含4個基本參數:
1)Memory Map接口(64位):與AXI HP接口進行數據交互,讀取在PS端DDR緩存的圖像數據。根據AXI HP接口是64位,所以此接口也設置成64位。
2)Stream Data Width(24位):通過此接口將所需傳輸的圖像數據傳至HDMI接口,因在此使用的是RGB圖像數據格式,所以數據位數設置成24位。
3)Frame Buffers:通過設置此參數來圖像幀數,可緩存多幀,根據自己的需求即可。
4)Line Buffer Depth(4096):它以Stream Data Width作為基本單位,設置數值越大,緩存的數據也就越多,這種緩存方式類似于FIFO緩存。
(3)圖像顯示模塊:圖像數據處理完后通過HDMI顯示器進行顯示,圖像顯示模塊包含輸出時序控制模塊和RGBtoDVI模塊,輸出時序模塊可通過Vivado軟件自帶的IP核VTC(video timing controller)添加到硬件設計中,通過設置VTC的輸出視頻分辨率和時鐘大小就可以將場同步信號和行同步信號引出,RGBtoDVI模塊是將RGB888格式的圖像數據轉換成TMDS數據輸出給HDMI顯示器。

在系統搭建后添加Vivado所需的.xdc約束文件進行引腳約束,.xdc文件是一系列的tcl語句,可以直接放在工程里,在綜合和布局布線的時候進行調用,Vivado block design設計如圖9所示。

圖9 Vivado模塊設計
3.1.2 PS部分
PL端設計完成后還需從Vivado中導出PYNQ所需的.tcl腳本文件、.bit比特流文件、.hwh文件進行PS端的開發。PS端集成了Linux操作系統和Python編譯環境,將PYNQ鏡像文件燒錄到SD卡中,通過網線以及USB線連接PC端,同時使用PuTTY串行接口連接軟件進入到Jupyter Notebook環境下進行系統測試。Jupyter Notebook是一個基于Python內核和Websocket協議的交互式計算環境,它的使用要求簡單,只需要一個兼容Web的瀏覽器就可以進入,在Jupyter Notebook里運行Python對PYNQ進行PS端的開發。
PS部分包含使用I2C初始化攝像頭、使能算法IP、調用VDMA和對HDMI顯示驅動等與PL端進行數據交互,顯示處理結果等,實驗最后的實時視頻檢測可達56幀/s,滿足實時圖像檢測的要求。Jupyter開發環境如圖10所示。

圖10 Jupyter開發環境
系統框架如圖11所示,整個系統的工作流程:PS端通過ARM處理器對OV5640攝像頭進行初始化并通過I2C接口對OV5640攝像頭進行配置,配置完畢之后將進行圖像數據的采集,將采集到的圖像數據通過Video In to AXI-Stream IP核轉化成AXI4-Stream格式,經過VDMA,再經過AXI_HP接口傳至DDR3外部存儲器進行存儲,然后讀取DDR3中的數據傳至image processing IP,通過使能不同的算法IP從而進行FAST角點檢測或Sobel邊緣檢測,圖像處理完畢之后數據再通過VDMA傳至顯示器進行顯示。

圖11 系統框架
相較于文獻[15,16]的圖像處理系統,如圖12所示,使用USB攝像頭采集圖像數據后需要將數據從PS端傳回PL端,在圖像處理模塊處理之后還需要將圖像數據從PL端傳回PS端,而本系統在PL端實現了完整的視頻通路,可對圖像數據直接進行處理,避免了圖像數據多次從PS端傳給PL端、PL端再回傳給PS端的負載問題,提高了性能和降低了時延。

圖12 其它系統架構
3.2.1 資源消耗對比
當在FPGA上實現算法時,需要平衡“面積”和“速度”,在這里“面積”指的是資源消耗的多少,“速度”也就是算法的性能,包含算法的處理速度、延時、吞吐量等。當減少“面積”的使用,算法的運行性就會降低;如果要求算法的“速度”快,就要犧牲“面積”。在Vivado HLS中進行綜合后得出算法資源消耗情況,同時與其它資源消耗情況進行對比,對比情況如表5所列。與文獻[17,18]實現的FAST角點檢測算法相比,在HLS中算法的各種資源消耗較為合理介于二者之間,同時可以明顯地看出在3種資源消耗情況中差異最大的是BRAM的使用,在進行圖像處理中BRAM_18K用于數據量大的存儲,HLS技術的使用能夠在保持高性能的情況下盡量減少FPGA的資源消耗。

表5 FAST角點檢測算法的資源對比消耗情況
Sobel邊緣檢測算法在Vivado HLS中進行綜合后得出算法資源消耗情況如表6所列,在圖像處理的過程中,LUT查找表(Look Up Table)和FF觸發器(Flip Flop)用于數據量小的存儲,BRAM_18K可用于數據量大的存儲,DSP數字信號處理在圖像處理的過程中主要用于乘加運算。與文獻[19]實現的Sobel算法相比,可以清晰地看到本文的各種資源消耗較少;與文獻[20]相比,最明顯的資源消耗為FF觸發器,消耗量為本文的FF觸發器的使用量的3.6倍,LUT查找表的消耗量是本文的1.45倍。

表6 Sobel邊緣檢測算法的資源消耗對比情況
3.2.2 處理時間對比
OpenCV(open source computer vision library)是一個跨平臺的開源數據庫,為比較OpenCV軟件實現的處理結果與本系統實現的結果,將在PC上使用Microsoft Visual Studio軟件分別調用OpenCV函數庫中FAST角點檢測函數和Sobel邊緣檢測函數,使用同樣的圖片進行處理。如表7所示,是使用OpenCV FAST進行角點檢測分別在閾值為40和50的情況下所需的平均時間為12.282 ms和113.349 ms,而使用Vivado HLS進行FAST角點檢測只需9.6 ms,Vivado HLS進行FAST角點檢測算法硬件加速設計相比于軟件算法實現快了11倍。如表8所示,同樣使用OpenCV Sobel邊緣檢測所需的平均時間為110.418 ms,而采用硬件設計實現圖像處理只需11.5 ms,比在軟件上實現快了9.6倍。

表7 使用OpenCV FAST角點檢測所需的時間/ms

表8 使用OpenCV Sobel邊緣檢測所需的時間/ms
由此可知,使用FPGA進行算法的硬件加速會大幅度地縮短了算法的執行時間,提高了算法的吞吐量。同時根據Vivado提供的功耗報告可知,系統的總功耗為1.9 W,其中動態功耗為1.743 W,靜態功耗為0.157 W,系統滿足低功耗的要求。
該文設計并實現了一套完整的實時圖像處理系統,將FAST角點檢測算法和Sobel邊緣檢測算法部署到FPGA上進行硬件加速,并采用HLS高層次綜合技術對兩種算法進行設計優化,同時,在FPGA上實現了全部視頻輸入輸出接口和圖像算法的完整通路,提高了性能、降低了時延。實驗結果表明,在硬件上實現的算法檢測速度比在軟件上實現快了9~11倍,同時系統實時視頻幀率可達56幀/s,滿足實時性要求。FPGA在未來的應用將會越來越廣泛,將進一步研究在FPGA上實現更加復雜的算法,充分利用FPGA的片上資源來提高系統性能、降低功耗等。