ZYNQ图像处理(6)——均值滤波和中值滤波
一、均值和中值濾波基本原理
首先要做的是最簡單的均值濾波算法。均值濾波是典型的線性濾波算法,它是指在圖像上對目標像素給一個模板,該模板包括了其周圍的臨近像素(以目標象素為中心的周圍 8 個像素,構成一個濾波模板,即去掉目標像素本身),再用模板中的全體像素的平均值來代替原來像素值。中值濾波算法可以形象的用上述表格來描述,即對于每個 33 的陣列而言,中間像素的值,等于邊緣 8 個像素的平均值。
無論是直接獲取的灰度圖像,還是由彩色圖像轉換得到的灰度圖像,里面都有噪聲的存在,噪聲對圖像質量有很大的影響。進行中值濾波不僅可以去除孤點噪聲,而且可以保持圖像的邊緣特性,不會使圖像產生顯著的模糊,比較適合于實驗中的人臉圖像。中值濾波算法與均值濾波非常的相似,但濾波的效果卻有很大的差別,區別如下:(1) 均值濾波相當于低通濾波,有將圖像模糊化的趨勢,對椒鹽噪聲基本無能力。(2) 中值濾波的有點事可以很好的過濾椒鹽噪聲,缺點是容易造成圖像的不連續。中值濾波的算法非常簡單,只要求得 33 像素陣列的中間值即可,這樣就有效的移植了最大值與最小值,圖像會變得均勻,對椒鹽噪聲有很好的濾除效果!
《基于FPGA的實時中值濾波器的硬件實現》這篇文章中介紹了一種適合FPGA實現的快速尋找中值的算法,算法過程是這樣的:首先計算每一行的最大值、中值以及最小值;之后計算第一列最大值中的最小值,第二列中間值中的中間值,第三列最小值中的最大值;最后再次計算這三個值的中間值就可以得到中值了。
二、3×3圖像矩陣的提取
可以發現,無論是均值濾波還是中值濾波,一個必要的步驟就是得到3×3的圖像矩陣。這個圖像矩陣在matlab中很容易得到,因為matlab讀取的是整一張的圖片。但是在FPGA中對圖像的實時處理,則需要先緩存兩到三行的數據,然后從這3行數據中獲得3×3圖像矩陣。
查閱了一些資料,發現在VIVADO中實現3×3矩陣有以下幾種方法。(1)第一種是利用vivado的IP核:ram based shift register,這個ip核在緩存數據量小的時候可以用。但是數據量很大,為幾百個以上的時候,緩存數量就不準了,不知道是不是bug,因此不推薦使用。(2)第二種是利用RAM(3)第三種是利用fifo實現,利用fifo實現野火教程很詳細。我這邊是利用了兩個ram實現數據的緩存和3×3矩陣的提取,接下來介紹實現過程。
2.1、例化ram ip核
首先自然是先需要例化一個ram的IP核,這個ram的配置信息如下如所示,是一個雙端口的ram,寬度8位,深度1024位,優先寫操作。詳細的可以看配置的圖。
2.2、基于ram的移位寄存器
基于ram 的移位寄存器代碼如下圖所示。輸入端口有時鐘信號、時鐘使能信號、行同步信號、數據輸入信號。輸出端口有前一行數據、前前一行的數據。具體實現是這樣的,當行同步和時鐘使能到來時,地址開始累加了,當行同步結束,地址清零。之后對時鐘信號、地址信號、數據信號都延遲三拍。最后兩個ram分別存儲了前一行和前前一行的圖像。
比較關鍵的一點是,ram是先開始讀,延遲幾個時鐘后才開始寫,因為剛開始是讀不到數據的,只有下一個行同步信號來的時候,前一行數據才會被讀出來,這里比較繞。
我這邊結合仿真說一下,當clken[2]信號為高時,開始往里邊寫信號,可以看到第一次是把十六進制51寫到了地址0中,但是可以發現讀地址是已經到了3了,所以讀速度是比寫速度快了三個時鐘周期,因為下一個行同步來時才可以得到上一行數據。
可以看到,下面這張圖就是下一個行同步時間內,下一行數據從taps0x中讀了出來。taps1x也是同樣的道理,這邊不在敘述。
2.3、3×3圖像矩陣提取
3×3圖像矩陣提取的代碼如下圖所示,代碼本身比較簡單,就是將輸入信號緩存,然后提取出矩陣。這里需要注意信號的同步,得到3×3矩陣信號要比進來的信號慢了兩個時鐘周期。所以需要延遲兩拍。
module VIP_matrix_generate (input clk, input rst_n, input per_frame_vsync,input per_frame_href,input per_frame_clken,input [7:0] per_img_Y,output matrix_frame_vsync,output matrix_frame_href,output matrix_frame_clken,output reg [7:0] matrix_p11, output reg [7:0] matrix_p12,output reg [7:0] matrix_p13,output reg [7:0] matrix_p21, output reg [7:0] matrix_p22, output reg [7:0] matrix_p23,output reg [7:0] matrix_p31, output reg [7:0] matrix_p32, output reg [7:0] matrix_p33 );//wire define wire [7:0] row1_data; wire [7:0] row2_data; wire read_frame_href ; wire read_frame_clken;//reg define reg [7:0] row3_data; reg [1:0] per_frame_vsync_r; reg [1:0] per_frame_href_r; reg [1:0] per_frame_clken_r;assign read_frame_href = per_frame_href_r[0] ; assign read_frame_clken = per_frame_clken_r[0]; 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];//present signal always@(posedge clk or negedge rst_n) beginif(!rst_n)row3_data <= 0;else begin if(per_frame_clken)row3_data <= per_img_Y ;elserow3_data <= row3_data ;end endline_shift_RAM_8bit1 u_line_shift_RAM_8bit1(.clock ( clk ),.clken ( per_frame_clken ),.per_frame_href ( per_frame_href ),.shiftin ( per_img_Y ),.taps0x ( row2_data ),.taps1x ( row1_data ) );//delay 2 tclk always@(posedge clk or negedge rst_n) beginif(!rst_n) begin per_frame_vsync_r <= 0;per_frame_href_r <= 0;per_frame_clken_r <= 0;endelse begin per_frame_vsync_r <= { per_frame_vsync_r[0], per_frame_vsync };per_frame_href_r <= { per_frame_href_r[0], per_frame_href };per_frame_clken_r <= { per_frame_clken_r[0], per_frame_clken };end end//generate the 3X3 matrix always@(posedge clk or negedge rst_n) beginif(!rst_n) begin {matrix_p11, matrix_p12, matrix_p13} <= 24'h0;{matrix_p21, matrix_p22, matrix_p23} <= 24'h0;{matrix_p31, matrix_p32, matrix_p33} <= 24'h0;endelse if(read_frame_href) beginif(read_frame_clken) begin {matrix_p11, matrix_p12, matrix_p13} <= {matrix_p12, matrix_p13, row1_data};{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p22, matrix_p23, row2_data};{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p32, matrix_p33, row3_data};endelse begin {matrix_p11, matrix_p12, matrix_p13} <= {matrix_p11, matrix_p12, matrix_p13};{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p21, matrix_p22, matrix_p23};{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p31, matrix_p32, matrix_p33};end endelse begin {matrix_p11, matrix_p12, matrix_p13} <= 24'h0;{matrix_p21, matrix_p22, matrix_p23} <= 24'h0;{matrix_p31, matrix_p32, matrix_p33} <= 24'h0;end endendmodule仿真的結果如下面三張圖所示,當pei_img_Y數據來到的時候,延遲兩個時鐘周期進入到row3_data中,之后在延遲一個周期數據送到p31、p32以及p33中。row2_data和row1_data的得到也是同樣的原理。
三、均值濾波的實現
前面已經清楚描述了均值濾波的實現過程,主要就是將圖像像素點周圍8個像素的值加起來然后除以8就可以了,下面式均值濾波的實現代碼。
module mean_filter(input clk,input rst_n,input [23:0] pre_data,input per_vsync,input per_href,input per_clken,output [23:0] post_data,output post_vsync,output post_href,output post_clken );//----------------------------- //generate 3×3 picture matrix //----------------------------- wire matrix_frame_clken; wire matrix_frame_href; wire matrix_frame_vsync; wire [7:0] matrix_p11; wire [7:0] matrix_p12; wire [7:0] matrix_p13; wire [7:0] matrix_p21; wire [7:0] matrix_p22; wire [7:0] matrix_p23; wire [7:0] matrix_p31; wire [7:0] matrix_p32; wire [7:0] matrix_p33;VIP_matrix_generate u_VIP_matrix_generate(.clk ( clk ),.rst_n ( rst_n ),.per_frame_vsync ( per_vsync ),.per_frame_href ( per_href ),.per_frame_clken ( per_clken ),.per_img_Y ( pre_data[7:0] ),.matrix_frame_vsync ( matrix_frame_vsync ),.matrix_frame_href ( matrix_frame_href ),.matrix_frame_clken ( matrix_frame_clken ),.matrix_p11 ( matrix_p11 ),.matrix_p12 ( matrix_p12 ),.matrix_p13 ( matrix_p13 ),.matrix_p21 ( matrix_p21 ),.matrix_p22 ( matrix_p22 ),.matrix_p23 ( matrix_p23 ),.matrix_p31 ( matrix_p31 ),.matrix_p32 ( matrix_p32 ),.matrix_p33 ( matrix_p33 ) );//----------------------------- //mean filter function //----------------------------- reg [11:0] add_p1; reg [11:0] add_p2; reg [11:0] add_p3; reg [11:0] add_all; //step1:add every href always @(posedge clk or negedge rst_n) beginif(~rst_n)beginadd_p1<=12'd0;add_p2<=12'd0;add_p3<=12'd0;endelse beginadd_p1<=matrix_p11+matrix_p12+matrix_p13;add_p2<=matrix_p21+matrix_p23;add_p3<=matrix_p31+matrix_p32+matrix_p33;end end //step2:add all the data always @(posedge clk or negedge rst_n) beginif(~rst_n)beginadd_all<=12'd0;endelse beginadd_all<=add_p1+add_p2+add_p3;end end //step3:shift to get mean filter data assign post_data={3{add_all[10:3]}};//----------------------------- //clk signal synchronization //----------------------------- reg [1:0] post_clken_dy; reg [1:0] post_href_dy; reg [1:0] post_vsync_dy; always @(posedge clk or negedge rst_n) beginif(~rst_n)beginpost_clken_dy<=2'd0;post_href_dy<=2'd0;post_vsync_dy<=2'd0;endelse beginpost_clken_dy<={post_clken_dy[0],matrix_frame_clken};post_href_dy<={post_href_dy[0],matrix_frame_href};post_vsync_dy<={post_vsync_dy[0],matrix_frame_vsync};end end assign post_clken=post_clken_dy[1]; assign post_href=post_href_dy[1]; assign post_vsync=post_vsync_dy[1];endmodule然后下面兩張圖分別是我用vivado仿真的加了椒鹽噪聲之后的圖片以及均值濾波之后的圖片。可以發現,均值濾波片讓圖片變得模糊了,而且椒鹽噪聲也沒有有效濾除。這邊的vivado仿真圖像處理是用verilog去模擬了攝像頭的產生時序,實現過程是參考了b站up主大磊FPGA。
四、中值濾波的實現
中值濾波的實現和均值濾波相似,也是需要先提取3×3的圖像矩陣,然后提取9個值中的中值,提取的方法在上面也講到了。
首先下面是對三個輸出信號進行排序的代碼,分別排序并且輸出最大值,中間值以及最小值。
之后是中值濾波的代碼,這部分實現的原理就是上文說到的提取中值的過程。
module median_filter(input clk,input rst_n,input [23:0] per_data,input per_vsync,input per_href,input per_clken,output [23:0] post_data,output post_vsync,output post_href,output post_clken );//----------------------------- //generate 3×3 picture matrix //----------------------------- wire matrix_frame_clken; wire matrix_frame_href; wire matrix_frame_vsync; reg [2:0] post_clken_dy; reg [2:0] post_href_dy; reg [2:0] post_vsync_dy; wire [7:0] matrix_p11; wire [7:0] matrix_p12; wire [7:0] matrix_p13; wire [7:0] matrix_p21; wire [7:0] matrix_p22; wire [7:0] matrix_p23; wire [7:0] matrix_p31; wire [7:0] matrix_p32; wire [7:0] matrix_p33;VIP_matrix_generate u_VIP_matrix_generate(.clk ( clk ),.rst_n ( rst_n ),.per_frame_vsync ( per_vsync ),.per_frame_href ( per_href ),.per_frame_clken ( per_clken ),.per_img_Y ( per_data[7:0] ),.matrix_frame_vsync ( matrix_frame_vsync ),.matrix_frame_href ( matrix_frame_href ),.matrix_frame_clken ( matrix_frame_clken ),.matrix_p11 ( matrix_p11 ),.matrix_p12 ( matrix_p12 ),.matrix_p13 ( matrix_p13 ),.matrix_p21 ( matrix_p21 ),.matrix_p22 ( matrix_p22 ),.matrix_p23 ( matrix_p23 ),.matrix_p31 ( matrix_p31 ),.matrix_p32 ( matrix_p32 ),.matrix_p33 ( matrix_p33 ) );//--------------------------------------------------- // midian filter function //--------------------------------------------------- //[a11 a12 a13] [max1 med1 min1] //[a21 a22 a23] to [max2 med2 min2] //[a31 a32 a33] [max3 med3 min3] // to // [min_of_max med_of_med max_of_min] // to // [med] wire [7:0] max_data_1; wire [7:0] mid_data_1; wire [7:0] min_data_1; wire [7:0] max_data_2; wire [7:0] mid_data_2; wire [7:0] min_data_2; wire [7:0] max_data_3; wire [7:0] mid_data_3; wire [7:0] min_data_3; wire [7:0] max_data_4; wire [7:0] mid_data_4; wire [7:0] min_data_4; wire [7:0] max_data_5; wire [7:0] mid_data_5; wire [7:0] min_data_5; wire [7:0] max_data_6; wire [7:0] mid_data_6; wire [7:0] min_data_6; wire [7:0] max_data_7; wire [7:0] mid_data_7; wire [7:0] min_data_7;//step1:every line find max,mid and min data sort_three u_sort_three_1(.clk ( clk ),.rst_n ( rst_n ),.data1 ( matrix_p11 ),.data2 ( matrix_p12 ),.data3 ( matrix_p13 ),.max_data ( max_data_1 ),.mid_data ( mid_data_1 ),.min_data ( min_data_1 ) ); sort_three u_sort_three_2(.clk ( clk ),.rst_n ( rst_n ),.data1 ( matrix_p21 ),.data2 ( matrix_p22 ),.data3 ( matrix_p23 ),.max_data ( max_data_2 ),.mid_data ( mid_data_2 ),.min_data ( min_data_2 ) ); sort_three u_sort_three_3(.clk ( clk ),.rst_n ( rst_n ),.data1 ( matrix_p31 ),.data2 ( matrix_p32 ),.data3 ( matrix_p33 ),.max_data ( max_data_3 ),.mid_data ( mid_data_3 ),.min_data ( min_data_3 ) ); //step2:every line find min_of_max,med_of_med,max_of_min sort_three u_sort_three_4(.clk ( clk ),.rst_n ( rst_n ),.data1 ( max_data_1 ),.data2 ( max_data_2 ),.data3 ( max_data_3 ),.max_data ( max_data_4 ),.mid_data ( mid_data_4 ),.min_data ( min_data_4 ) ); sort_three u_sort_three_5(.clk ( clk ),.rst_n ( rst_n ),.data1 ( mid_data_1 ),.data2 ( mid_data_2 ),.data3 ( mid_data_3 ),.max_data ( max_data_5 ),.mid_data ( mid_data_5 ),.min_data ( min_data_5 ) ); sort_three u_sort_three_6(.clk ( clk ),.rst_n ( rst_n ),.data1 ( min_data_1 ),.data2 ( min_data_2 ),.data3 ( min_data_3 ),.max_data ( max_data_6 ),.mid_data ( mid_data_6 ),.min_data ( min_data_6 ) ); //step3:find median value sort_three u_sort_three_7(.clk ( clk ),.rst_n ( rst_n ),.data1 ( min_data_4 ),.data2 ( mid_data_5 ),.data3 ( max_data_6 ),.max_data ( max_data_7 ),.mid_data ( mid_data_7 ),.min_data ( min_data_7 ) ); assign post_data={3{mid_data_7}};//----------------------------- // signal synchronization //----------------------------- assign post_clken=post_clken_dy[2]; assign post_href=post_href_dy[2]; assign post_vsync=post_vsync_dy[2]; always @(posedge clk or negedge rst_n) beginif(~rst_n)beginpost_clken_dy<=3'd0;post_href_dy<=3'd0;post_vsync_dy<=3'd0;endelse beginpost_clken_dy<={post_clken_dy[1:0],matrix_frame_clken};post_href_dy<={post_href_dy[1:0],matrix_frame_href};post_vsync_dy<={post_vsync_dy[1:0],matrix_frame_vsync};end endendmodule接下來是中值濾波的仿真結果,我打個比方,比如下面69這個數值是輸出的中值。獲得中值需要消耗三個時鐘周期,因此矩陣數據比中值要快三個時鐘周期,所以和這個中值對應的矩陣數據是:36、36、36、d7、bc、36、ff、f5、69。對應中值就是69,這邊顯示696969是把8位數據拼接成了24位的。
最后給出的這三張圖片一次是加了椒鹽噪聲的RGB圖片,均值濾波后的圖片以及中值濾波后的圖片。可以明顯看到,中值濾波對椒鹽噪聲的濾除作用明顯。
五、總結
均值濾波和中值濾波是圖像處理的基本操作,主要核心都是提取3×3的圖像矩陣。因為現在的攝像頭含噪聲很少,所有沒有放到FPGA開發板中進行測試,只是對其進行了matlab和vivado的仿真。后面做完這部分基礎的圖像處理后我整理下代碼并供大家參考。
總結
以上是生活随笔為你收集整理的ZYNQ图像处理(6)——均值滤波和中值滤波的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VC定时器SetTimer函数
- 下一篇: k2p拆机ttl刷breed_【1.10