1.1均值濾波算法介紹
首先要做的是最簡單的均值濾波算法。均值濾波是典型的線性濾波算法,它是指在圖像上對目標像素給一個模板,該模板包括了其周圍的臨近像素(以目標象素為中心的周圍8個像素,構成一個濾波模板,即去掉目標像素本身),再用模板中的全體像素的平均值來代替原來像素值。
P11 | P12 | P13 |
P21 | P23 | |
P31 | P32 | P33 |
中值濾波算法可以形象的用上述表格來描述,即對于每個3*3的陣列而言,中間像素的值,等于邊緣8個像素的平均值。算法的理論很簡單,對于C處理器而言,一幅640*480圖像的均值濾波,可以很方便的通過數組獲得3*3的陣列,但對于我們的Verilog HDL而言,著實不易。
1.23*3像素陣列的HDL實現
3*3陣列的獲取,大概有以下三種方式:
(1)通過2個或3個RAM的存儲,來實現3*3像素陣列;
(2)通過2個或3個FIFO的存儲,來實現3*3像素陣列;
(3)通過2行或3行Shift_RAM的移位存儲,來實現3*3像素陣列。
不過經驗告訴大家,最方便的實現方式,非Shift_RAM莫屬了,都感覺Shift_RAM甚至是為實現3*3陣列而生的!
在Quartus II中,可以利用下圖方式產生,很方便:
首先介紹一下Shift_RAM,宏定義模塊如下圖所示:
圖6?1 QuartusII Shift_RAM IP使用界面
Shift_RAM可定義數據寬度、移位的行數、每行的深度。這里我們固然需要8Bit,640個數據每行,同時移位寄存2行即可(原因看后邊)。同時選擇時鐘使能端口clken。詳細的關于Shift_RAM的手冊參數,可在宏定義窗口右上角Document中查看,如下:
手冊給出了一個非常形象的移位寄存示意圖,如下所示:
圖6?2移位寄存示意圖
實現3*3像素陣列的大概的思路是這樣子的,Shift_RAM中存2行數據,同時與當前輸入行的數據,組成3行的陣列。
在Vivado中就沒有類似的IP,但是難不倒我,可以利用成熟的IP核實現類似上訴的移位寄存器。
在《Image?06_OV5640_DDR3_Gray_Mean_FilterOV5640_DEMOuserlinebuffer_Wapper》
中實現的就是移位寄存器功能的IP,使用方法類似上訴QuartusII中的使用。
例化方式如下:
代碼6?1
1.//--------------------------------------- 2.//moduleofshiftramforrawdata 3.wireshift_clk_en=per_frame_clken; 4. 5.linebuffer_Wapper# 6.( 7..no_of_lines(2), 8..samples_per_line(640), 9..data_width(8) 10.) 11.linebuffer_Wapper_m0( 12..ce(1'b1), 13..wr_clk(clk), 14..wr_en(shift_clk_en), 15..wr_rst(rst_n), 16..data_in(row3_data), 17..rd_en(shift_clk_en), 18..rd_clk(clk), 19..rd_rst(rst_n), 20..data_out({row2_data,row1_data}) 21.); |
源碼VIP_Matrix_Generate_3X3_8Bit文件中實現8Bit寬度的3*3像素陣列功能。具體的實現步驟如下:
(1)首先,將輸入的信號用像素使能時鐘同步一拍,以保證數據與Shift_RAM輸出的數據保持同步,如下:
代碼6?2
1.//Generate3*3matrix 2.//-------------------------------------------------------------------------- 3.//-------------------------------------------------------------------------- 4.//-------------------------------------------------------------------------- 5.//syncrow3_datawithper_frame_clken&row1_data&raw2_data 6.wire[7:0]row1_data;//framedataofthe1throw 7.wire[7:0]row2_data;//framedataofthe2throw 8.reg[7:0]row3_data;//framedataofthe3throw 9.always@(posedgeclkornegedgerst_n) 10.begin 11.if(!rst_n) 12.row3_data<=?0;?? 13.else 14.begin 15.if(per_frame_clken) 16.row3_data<=?per_img_Y;?? 17.else 18.row3_data<=?row3_data;?? 19.end 20.end |
(2)接著,例化并輸入row3_data,此時可從Modelsim中觀察到3行數據同時存在了,HDL如下:
代碼6?3 QuartusII例化移位寄存器代碼
1.//--------------------------------------- 2.//moduleofshiftramforrawdata 3.wireshift_clk_en=per_frame_clken; 4.Line_Shift_RAM_8Bit 5.#( 6..RAM_Length(IMG_HDISP) 7.) 8.u_Line_Shift_RAM_8Bit 9.( 10..clock(clk), 11..clken(shift_clk_en),//pixelenableclock 12.//.aclr(1'b0), 13..shiftin(row3_data),//Currentdatainput 14..taps0x(row2_data),//Lastrowdata 15..taps1x(row1_data),//Uparowdata 16..shiftout() 17.); |
代碼6?4 Vivado例化移位寄存器代碼
22.//--------------------------------------- 23.//moduleofshiftramforrawdata 24.wireshift_clk_en=per_frame_clken; 25. 26.linebuffer_Wapper# 27.( 28..no_of_lines(2), 29..samples_per_line(640), 30..data_width(8) 31.) 32.linebuffer_Wapper_m0( 33..ce(1'b1), 34..wr_clk(clk), 35..wr_en(shift_clk_en), 36..wr_rst(rst_n), 37..data_in(row3_data), 38..rd_en(shift_clk_en), 39..rd_clk(clk), 40..rd_rst(rst_n), 41..data_out({row2_data,row1_data}) 42.); |
在經過Shift_RAMd移位存儲后,我們得到的row0_data,row1_data,row2_data的仿真示意圖如下所示:
圖6?3ModelSim仿真截圖
數據從row3_data輸入,滿3行后剛好唯一3行陣列的第一。從圖像第三行輸入開始,到圖像的最后一行,我們均可從row_data得到完整的3行數據,基為實現3*3陣列奠定了基礎。不過這樣做有2個不足之處,即第一行與第二行不能得到完整的3*3陣列。但從主到次,且不管算法的完美型,我們先驗證3X3模板實現的正確性。因此直接在行有效期間讀取3*3陣列,機器方便快捷的實現了我們的目的。
(3)Row_data讀取信號的分析及生成
這里涉及到了一個問題,數據從Shift_RAM存儲耗費了一個時鐘,因此3*3陣列的讀取使能與時鐘,需要進行一個clock的偏移,如下所示:
代碼6?5
1.//------------------------------------------ 2.//lag2clockssignalsync 3.reg[1:0]per_frame_vsync_r; 4.reg[1:0]per_frame_href_r; 5.reg[1:0]per_frame_clken_r; 6.always@(posedgeclkornegedgerst_n) 7.begin 8.if(!rst_n) 9.begin 10.per_frame_vsync_r<=?0;?? 11.per_frame_href_r<=?0;?? 12.per_frame_clken_r<=?0;?? 13.end 14.else 15.begin 16.per_frame_vsync_r<=???{per_frame_vsync_r[0],??per_frame_vsync};?? 17.per_frame_href_r<=???{per_frame_href_r[0],???per_frame_href};?? 18.per_frame_clken_r<=???{per_frame_clken_r[0],??per_frame_clken};?? 19.end 20.end 21.//Giveupthe1thand2throwedgedatacaculateforsimpleprocess 22.//Giveupthe1thand2thpointof1lineforsimpleprocess 23.wireread_frame_href=per_frame_href_r[0];//RAMreadhrefsyncsignal 24.wireread_frame_clken=per_frame_clken_r[0];//RAMreadenable 25.assignmatrix_frame_vsync=per_frame_vsync_r[1]; 26.assignmatrix_frame_href=per_frame_href_r[1]; 27.assignmatrix_frame_clken=per_frame_clken_r[1]; |
(4)Okay,此時根據read_frame_href與read_frame_clken信號,直接讀取3*3像素陣列。讀取的HDL實現如下:
代碼6?6
1. //---------------------------------------------------------------------------- 2. //---------------------------------------------------------------------------- 3. /****************************************************************************** 4. ----------ConvertMatrix---------- 5. [P31->P32->P33->]--->[P11P12P13] 6. [P21->P22->P23->]--->[P21P22P23] 7. [P11->P12->P11->]--->[P31P32P33] 8. ******************************************************************************/ 9. //--------------------------------------------------------------------------- 10. //--------------------------------------------------- 11. /*********************************************** 12. (1)ReaddatafromShift_RAM 13. (2)CaculatetheSobel 14. (3)SteadydataafterSobelgenerate 15. ************************************************/ 16. //wire[23:0]matrix_row1={matrix_p11,matrix_p12,matrix_p13};//Justfortest 17. //wire[23:0]matrix_row2={matrix_p21,matrix_p22,matrix_p23}; 18. //wire[23:0]matrix_row3={matrix_p31,matrix_p32,matrix_p33}; 19. always@(posedgeclkornegedgerst_n) 20. begin 21. if(!rst_n) 22. begin 23. {matrix_p11,matrix_p12,matrix_p13}<=?24'h0;?? 24. {matrix_p21,matrix_p22,matrix_p23}<=?24'h0;?? 25. {matrix_p31,matrix_p32,matrix_p33}<=?24'h0;?? 26. end 27. elseif(read_frame_href) 28. begin 29. if(read_frame_clken)//Shift_RAMdatareadclockenable 30. begin 31. {matrix_p11,matrix_p12,matrix_p13}<=?{matrix_p12,?matrix_p13,?row1_data};?//1th?shift?input?? 32. {matrix_p21,matrix_p22,matrix_p23}<=?{matrix_p22,?matrix_p23,?row2_data};?//2th?shift?input?? 33. {matrix_p31,matrix_p32,matrix_p33}<=?{matrix_p32,?matrix_p33,?row3_data};?//3th?shift?input?? 34. end 35. else 36. begin 37. {matrix_p11,matrix_p12,matrix_p13}<=?{matrix_p11,?matrix_p12,?matrix_p13};?? 38. {matrix_p21,matrix_p22,matrix_p23}<=?{matrix_p21,?matrix_p22,?matrix_p23};?? 39. {matrix_p31,matrix_p32,matrix_p33}<=?{matrix_p31,?matrix_p32,?matrix_p33};?? 40. end 41. end 42. else 43. begin 44. {matrix_p11,matrix_p12,matrix_p13}<=?24'h0;?? 45. {matrix_p21,matrix_p22,matrix_p23}<=?24'h0;?? 46. {matrix_p31,matrix_p32,matrix_p33}<=?24'h0;?? 47. end 48. end |
最后得到的matrix_p11、p12、p13、p21、p22、p23、p31、p32、p33即為得到的3*3像素陣列,仿真時序圖如下所示:
前面Shift_RAM存儲耗費了一個時鐘,同時3*3陣列的生成耗費了一個時鐘,因此我們需要人為的將行場信號、像素使能讀取信號移動2個時鐘,如下所示:
assign matrix_frame_vsync =per_frame_vsync_r[1];
assign matrix_frame_href =per_frame_href_r[1];
assign matrix_frame_clken =per_frame_clken_r[1];
至此我們得到了完整的3*3像素陣列的模塊,同時行場、使能時鐘信號與時序保持一致,Modelsim仿真圖如下所示:
1.3Mean_Filter均值濾波算法的實現
不過相對于3*3像素陣列的生成而言,均值濾波的算法實現反而難度小的多,只是技巧性的問題。
繼續分析上面這個表格。其實HDL完全有這個能力直接計算8個值相加的均值,不過為了提升電路的速度,建議我們需要通過以面積換速度的方式來實現。So這里需要3個步驟:
(1)分別計算3行中相關像素的和;
(2)計算(1)中三個結果的和;
在(2)運算后,我們不能急著用除法去實現均值的運算。記住,能用移位替換的,絕對不用乘除法來實現。這里8個像素,即除以8,可以方便的用右移動3Bit來實現。不過這里更方便的辦法是,直接提取mean_value4[10:3]。
這一步我們不單獨作為一個Step,而是直接作為結果輸出。分析前面的運算,總共耗費了2個時鐘,因此需要將行場信號、像素讀取信號偏移2個時鐘,同時像素時鐘,根據行信號使能,直接讀取mean_value4[10:3],如下所示:
這樣,我們便得到了運算后的時序,實現了均值濾波算法。
最后,在Video_Image_Processor頂層文件中例化Gray_Mean_Filter算法模塊,完成算法的添加。
最后直接將生成的post_img_Y輸入給24Bit的DDR控制器即可。
審核編輯:劉清
-
RAM
+關注
關注
8文章
1367瀏覽量
114541 -
HDL
+關注
關注
8文章
327瀏覽量
47344 -
移位寄存器
+關注
關注
2文章
258瀏覽量
22238 -
濾波算法
+關注
關注
2文章
88瀏覽量
13710 -
FIFO存儲
+關注
關注
0文章
103瀏覽量
5965
原文標題:灰度圖像的均值濾波算法的 HDL 實現
文章出處:【微信號:zhuyandz,微信公眾號:FPGA之家】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論