input自适应_【正点原子FPGA连载】第十一章基于OV5640的自适应二值化实验-领航者ZYNQ之HLS 开发指南...
1)摘自【正點原子】領航者ZYNQ之HLS 開發指南
2)平臺購買地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套實驗源碼+手冊+視頻下載:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html
4)對正點原子FPGA感興趣的同學可以加群討論:876744900
5)正點原子資料更新和新品發布,請加正點原子公眾號:正點原子 關注方法:微信→添加好友→公眾號→輸入:正點原子
第十一章基于OV5640的自適應二值化實驗
在數字圖像分析與處理中,一幅圖片通常含有人們感興趣和不感興趣的兩部分。為了提取或突出人們感興趣的目標,常用的方法就是對圖像進行二值化分割。二值化的方法有很多,其中自適應二值化(OTSU)是圖像二值化最常用的一種算法。本章我們將在HLS中實現圖像的自適應二值化。
本章包括以下幾個部分:
1111.1簡介
11.2實驗任務
11.3HLS設計
11.4IP驗證
11.5下載驗證
11.1簡介
圖像二值化(Image Binarization)就是將圖像上的像素點的灰度值設置為最大(白色)或最小(黑色),也就是將整個圖像呈現出明顯的黑白效果的過程。這里我們以8bit表示的灰度圖像為例(灰度值的范圍為0~255),二值化就是通過選取適當的閾值,與圖像中的256個灰度等級進行比較。亮度高于閾值的像素點設置為白色(255),低于閾值的像素點設置為黑色(0),從而明顯地反映出圖像的整體和局部特征。
我們把圖像進行二值化是為了將感興趣目標和背景分離,在數字識別和指紋識別等場景中,二值化應用非常廣泛。
圖 11.1.1 指紋識別
在指紋識別中,由于我們只關注圖像的指紋信息,所以我們需要將背景信息全部清除掉,如上圖所示,圖像經過二值化處理后只保留了關鍵的指紋信息。
二值化在數字圖像處理中占有非常重要的地位,特別是在實時的圖像處理中,通過二值圖像處理實現而構成的系統是很多的。要進行二值圖像的處理與分析,首先要把灰度圖像二值化,得到二值化圖像,這樣有利于在對圖像做進一步處理時,圖像的集合性質只與像素值為0或255的點的位置有關,不再涉及像素的多級值,使處理變得簡單,而且數據的處理和壓縮量小。為了得到理想的二值圖像,一般采用封閉、連通的邊界定義不交疊的區域。所有灰度大于或等于閾值的像素被判定為屬于特定物體,其灰度值為255表示,否則這些像素點被排除在物體區域以外,灰度值為0,表示背景或者例外的物體區域。
實現二值化有兩種方法,一種是手動指定一個閾值,通過閾值來進行二值化處理;另一種是一個自適應閾值二值化方法(OTSU算法等)。使用第一種方法計算量小速度快,但是在處理不同圖像時顏色分布差別很大;使用第二種方法適應性強,能直接觀測處圖像的輪廓,但相對計算更復雜。當圖像的直方圖分布沒有明顯的低峰和高峰時,直接指定閾值可能會導致圖像中某些像素的關鍵信息丟失,所以我們在本章采用第二種方法來實現圖像的二值化。
OTSU算法計算出來的閾值可以使圖像的前景目標區域的像素的平均灰度值、背景區域像素的平均灰度值與整幅圖像像素的平均灰度值之間的差別最大,這種差異使用方差來表示的,故名最大類間方差法,該方法由日本學者大津于1978年提出,故又名大津法。
OTSU算法的原理非常簡單,該算法假定一幅圖像根據雙模直方圖(前景圖像像素和背景圖像像素)被分為兩類像素,它要計算能將兩類分開的最佳閾值,使得它們的類間方差最大,也就是前景圖像和背景圖像間像素的離散程度最大。OTSU算法類間方差的定義如下:
圖 11.1.2 類間方差公式
如圖所示,其中w0表示前景圖像像素出現的概率,u0表示前景圖像像素的平均灰度,w1表示背景圖像像素出現的概率,u1表示背景圖像像素的平均灰度,u表示整幅圖像像素的平均灰度。
為了實現找到使類間方差最大的閾值這個目標。我們需要遍歷0~255個灰度等級,這每一個灰度等級都將圖像分為兩類像素(前景圖像像素和背景圖像像素)。然后在每一個灰度等級下算出前景圖像像素的平均值(u0)和出現概率(w0)以及背景圖像像素的平均值(u1)和出現概率(u),同時我們還要計算出整幅圖像的平均灰度(u)。最后代入上面的公式計算出類間方差σ2。我們就是要找到一個灰度值得這個類間方差最大,然后將這個灰度值作為圖像二值化的閾值。
下面給出一幅灰度圖像以及它做了指定閾值和OTSU自適應二值化之后的圖像來加深我們對OTSU自適應二值化的理解:
圖 11.1.3 圖像自適應二值化
如圖所示,左邊的這幅圖像是灰度圖,中間的這副圖像是我們手動指定閾值操作的二值化圖像,右邊的這副圖像是灰度圖做了自適應二值化算法處理后的圖像,可以發現圖像經過自適應二值化計算出來的閾值比手動指定閾值二值化的效果更好。
11.2實驗任務
本節的實驗任務是使用Vivado HLS設計OTSU自適應二值化的IP核,并在Vivado中對設計出來的IP核進行驗證。
11.3HLS設計
我們在電腦中的“F:ZYNQHigh_Level_Synthesis”目錄下新建一個名為otsu_threshold的文件夾,作為本次實驗的工程目錄。然后打開Vivado HLS工具,創建一個新的工程。設置工程名為“otsu_threshold”,選擇工程路徑為剛剛創建的文件夾。需要注意的是,工程名以及路徑只能由英文字母、數字和下劃線組成,不能包含中文、空格以及其他特殊字符。如下圖所示:
圖 11.3.1 工程配置界面
設置好工程名及路徑之后,點擊“Next”,進入如下界面設置頂層函數:
圖 11.3.2 設置頂層函數
工程創建完成后,在工程面板中的“source”目錄上點擊右鍵,然后在打開的列表中選擇“New File”新建源文件,在彈出的對話框中輸入源文件的名稱“otsu_threshold.cpp”,如圖1.3.3所示。源文件默認的保存路徑為HLS工程目錄,為方便源文件的管理,我們在工程目錄下新建一個名為“src”的文件下,將源文件保存在src目錄下。
圖 11.3.3 輸入源文件名
我們在這里使用C++語言來設計,那么后綴名需要設置為“.cpp”。設置好文件名和路徑之后,點擊“保存”。
“otsu_threshold.cpp”文件源代碼如下:
代碼的主體部分與《基于OV5640的直方圖均衡實驗》非常類似,只是在代碼的第29行我們使用的是hls::Otsu_threshold這個函數來實現自適應二值化算法。這個函數在Vivado HLS視頻庫中沒有相關定義,需要我們自己實現。
“otsu_threshold.h”代碼如下:
實現OTSU算法,需要先統計圖像各個像素值出現的個數,也就是統計圖像的直方圖。統計直方圖這部分代碼我們參考Vivado HLS視頻庫中直方圖均衡化中的部分源碼。下面簡單講解一下這個源碼的實現思路。
我們想要獲取一幅圖像的直方圖,就需要定義一個數組,這個數組可以保存每個像素值出現的個數。其中數組的地址代表的是圖像像素的灰度等級0~255,數組里存儲的就是每一個灰度等級出現的個數,在代碼的第18行就定義了“hist_out”這樣的一個數組。我們在獲取到輸入圖像像素的灰度值后,把這個灰度值作為數組的地址,從數組中取出數據,并將得到的數據加1后再寫回數組對應的地址。這樣一幀圖像輸入完成后,數組中就存儲了整幅圖像的直方圖信息,即實現了直方圖的統計。
在獲取一幀圖像的直方圖之后,需要根據圖像的直方圖來計算類間方差。在計算類間方差的時候,我們需要緩存后續輸入的圖像數據,為計算上一幀圖像的OTSU閾值留出時間。代碼的第16行“addr_win”緩存了圖像像素的灰度值,代碼的第19行“hist_win”緩存了圖像像素出現的個數,這里NUM_STATUS定義成4表示我們緩存的大小是4。
代碼的第58行是讀取輸入像素的灰度值并存儲到“tempsrc”中,代碼的第59行是通過三目運算符,判斷輸入圖像像素值與閾值之間的大小,如果輸入圖像像素值比閾值大,則將輸出圖像像素設置為255,否則將輸出圖像像素設置為0,這樣就實現了圖像的二值化操作。
在代碼的第130行到第180行就是通過OTSU算法來計算圖像的閾值。我們首先遍歷灰度級0~255,針對每一個灰度,把它作為閾值。如代碼的第130行所示:“threshold_tmp”這個變量就是我們設置的這個閾值。然后以這個閾值分類,分別計算前景圖像像素和背景圖像像素出現的個數和灰度總和。這里計算灰度總和的方式就是拿每個像素的灰度級乘以它出現的個數然后做一個累加。如代碼第144到第155行所示:“j”代表的是前景或背景圖像像素的灰度值,“hist_out”數組里存儲的是每個灰度級出現的個數。接下來我們就需要根據簡介里所介紹的類間方差公式,來分別計算公式中各個符號的值。如下圖所示:
圖 11.3.4 類間方差公式
其中“w0”代表的是前景圖像像素出現的概率,對應于代碼的第158行“front_pixel_probability”這個變量。我們在這里是拿前景圖像像素出現的個數除以一幀像素的個數來計算前景圖像像素出現的概率;“u0”代表的是前景圖像像素的平均灰度,對應于代碼的第164行“front_gray_average”這個變量。我們在這里是通過前景圖像像素的灰度總和除以前景圖像像素出現的個數來計算前景圖像像素的平均灰度。“u”代表的是整幅圖像的平均灰度,對應于代碼的第168行“total_gray_average”這個變量。我們在這里是通過整幅圖像像素的灰度總和除以一幀像素的個數來得到的。在代碼的第178行到第180行就是找出最大的類間方差,并且將最大類間方差所對應的像素值賦值給“threshold”這個變量,作為我們二值化的閾值。
我們程序用到了一些優化指令,下面對這些優化指令做一些介紹:
在代碼的第34行“pragma HLS UNROLL”是HLS優化指令,表示我們展開循環創建多個獨立的操作,這將會導致我們可以在單個時鐘周期里并行執行for循環中的操作,而基于處理器的架構導致它執行這些操作步驟都是串行執行的。在FPGA內部數據可以并行處理,這體現了用FPGA并行加速的優勢。如下圖所示:
圖 11.3.5 循環展開
左邊的這個“Rolled Loop”是滾動循環,它表示每次迭代都在單獨的時鐘周期內執行,這個實現需要四個時鐘周期,只需要用FPGA的一個乘法器就可以實現;中間的這個“Partially Unrolled Loop”是部分展開循環,在這個例子中,這個實現需要兩個時鐘周期,需要用FPGA的兩個乘法器來實現;右邊的這個“Unroolled Loop”是展開循環,在這個例子中,循環被完全展開,我們可以在單個時鐘周期內執行所有循環操作。然而這個實現需要四個乘法器,更重要的是,這個實現需要能夠在相同的時鐘周期內執行4次讀取和4次寫入操作,由于在FPGA的塊RAM最多只有兩個端口,因此我們在實現這個的時候需要對陣列進行分區。需要注意的是,循環展開后,如果循環的一次迭代中的操作需要前一次迭代的結果,那么它們不能并行執行,而是在數據可用時立即執行。
在代碼的第52行“pragma HLS PIPELINE”是為了提高吞吐率而進行的優化。“PIPELINE”指的是流水線操作,流水線操作允許操作同時發生,任務可以不必在開始下一個操作之前完成所有操作。流水線可以應用于函數和循環。函數的吞吐率改進如下圖所示:
圖 11.3.6 函數流水線操作
如圖所示,如果沒有流水線操作,這個函數每3個時鐘周期讀取一個輸入,并且每2個時鐘周期輸出一個值。這個函數的啟動間隔(initiation interval)為3,延遲(latency)為2。通過流水線操作,每個周期(initiation interval = 1)讀取一個新的輸入,而不會改變輸出延遲或使用的資源。
循環流水線操作允許循環中的操作以并發方式實現,如圖所示:
圖 11.3.7 循環流水線操作
左邊的這個圖是默認的順序操作,其中每個輸入讀取(initiation interval = 3)之間有3個時鐘周期,并且在執行最后一次輸出寫入之前需要8個時鐘周期。右邊的這個圖是循環流水線之后的結果,經過流水線處理后可以每個周期(initiation interval = 1)讀取一個新的輸入樣本,并且僅在4個時鐘周期后寫入最終輸出。在這里我們使用這個指令來提高系統的吞吐率。
在代碼的第53行“pragma HLS LOOP_FLATTEN OFF”是為了防止這個嵌套循環在FPGA實現中被展平為單個循環層次結構。在Vivado HLS中默認情況下不會將嵌套循環展開,但可以定義優化的程度,如果優化等級定義過高,在綜合的時候還是有可能將嵌套的循環結構展平為單個循環層次結構,添加這個指令,就是為了防止Vivado HLS做這種優化。
程序創建完成后,點擊工具欄中向右的綠色三角形對C++代碼進行綜合,綜合完成后在工具欄中點擊黃色的“田”字按鈕,導出RTL,如下圖所示:
圖 11.3.4 導出RTL
在彈出的對話框中保持默認設置,直接點擊“OK”,如下圖所示:
圖 11.3. 將設計導出成IP
設計導出完成后,HLS設計部分就結束了,我們在HLS工程目錄下可以找到導出的IP核,如下圖紅色方框所示:
圖 11.3.18導出得到的IP
HLS設計結束之后,我們將在Vivado中對導出的IP核進行驗證。
11.4IP驗證
在IP驗證環節,我們會使用Vivado工具的IP集成器將生成的IP核添加到Block Design中,然后完成設計后將程序下載到領航者開發板上進行驗證。
用于IP驗證的底層硬件可以在《領航者ZYNQ之HLS開發指南》第5章“OV5640 攝像頭灰度顯示”實驗的基礎上進行。打開該實驗所對應的Vivado工程“ov5640_rgb2gray_ip_test”,將其另存為“otsu_threshold_ip_test”工程。為了方便工程管理,我們將Vivado工程的目錄與HLS工程目錄保持一致,如下圖所示:
圖 11.4.1 創建Vivado工程
在通過“另存為”的方式保存工程之后,還要將原來工程中的IP庫(名為ip_repo的文件夾)復制到新的Vivado工程目錄下, 然后將HLS設計過程中導出的IP核拷貝到“ip_repo”目錄下并解壓。
在Vivado中重新將當前工程目錄下的ip_repo文件夾添加到工程的IP庫中,然后將HLS生成的IP核text_overlay添加到Block Design中,并將其STREAM接口分別連接到Video In to AXI4-Stream模塊的video_out接口與VDMA模塊的S_AXIS_S2MM接口上。最后點擊上圖中左上角的“Run Connection Automation”,讓工具自動連接該IP核的其他端口,包括時鐘、復位以及AXI-Lite從接口,最終完成的設計如下圖所示:
然后點擊“Run Connnection Automation”,下面列出了會自動連接的模塊及其接口,勾選“All Automation”, 然后點擊“OK”按鈕。最終完成的設計如下圖所示:
圖 11.4.2 完成后的Block Design
到這里我們的Block Design就設計完成了,在Diagram窗口空白處右擊,然后選擇“Validate Design”驗證設計。驗證完成后彈出對話框提示“Validation Successful”表明設計無誤,點擊“OK”確認。最后按快捷鍵“Ctrl + S”保存設計。
接下來在Source窗口中右鍵點擊Block Design設計文件“system.bd”,然后依次執行“Generate Output Products”和“Create HDL Wrapper”。
最后在左側Flow Navigator導航欄中找到PROGRAM AND DEBUG,點擊該選項中的“Generate Bitstream”,對設計進行綜合、實現、并生成Bitstream文件。
在生成 Bitstream 之后,在菜單欄中選擇 File > Export > Export hardware 導出硬件,并在彈出的對話框 中,勾選“Include bitstream”。然后在菜單欄選擇 File > Launch SDK,啟動 SDK 軟件。
在Vivado SDK中新建空的應用工程,工程名為“ov5640_otsu_threshold”。
然后找到《領航者ZYNQ之嵌入式開發指南》第二十三章“OV5640 攝像頭 LCD 顯示”實驗的Vivado工程目錄,將“21_ov7725_lcdov7725_lcd.sdkov7725_lcd”目錄下的src文件夾拷貝到新建的應用工程目錄下。
在SDK中刷新src目錄,然后將“main.c”的代碼修改為如下所示:
.
.
在代碼的第14行引入了“xov5640_otsu_threshold.h”頭文件,這個頭文件是Vivado HLS工具生成的,里面聲明了OTSU自適應二值化IP核的驅動函數。首先在代碼的34行定義了OTSU自適應二值化IP核的驅動實例otsu_threshold_inst,該變量會在后面對IP核進行配置時用到。然后在代碼的第112行通過“XOv5640_otsu_threshold_Initialize ()”函數來初始化Vivado HLS生成的OTSU自適應二值化IP核;在代碼的第116行通過傳入“vd_mode.height”形參來設置OTSU自適應二值化IP核的行數,在代碼的第114行通過傳入“vd_mode.width”形參來設置列數。這些數據類型和函數在“xov5640_otsu_threshold.h”頭文件中均有聲明。
11.5下載驗證
編譯完工程之后我們就可以開始下載程序了。將 OV5640 攝像頭模塊插在領航者Zynq開發板的“OLED/CAMERA”插座上,并將LCD的排線接頭插入開發板上的 LCD 接線座。將下載器一端連電腦,另一端與開發板上的 JTAG 端口連接,連接電源線并打開電源開關。
在SDK軟件下方的SDK Terminal窗口中點擊右上角的加號設置并連接串口。然后下載本次實驗硬件設計過程中所生成的BIT文件,來對PL進行配置。最后下載軟件程序,下載完成后在LCD上就可以看到液晶屏上顯示了經過OTSU自適應二值化處理后的圖像,如下圖所示:
圖 11.5.1 OTSU自適應二值化效果圖
總結
以上是生活随笔為你收集整理的input自适应_【正点原子FPGA连载】第十一章基于OV5640的自适应二值化实验-领航者ZYNQ之HLS 开发指南...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 标致308发动机排气VVT阀故障灯亮怎么
- 下一篇: 12年的斯柯达明锐自动挡的是多大排量?