基于FPGA的图像处理4--线性滤波-均值滤波

Github:https://github.com/zgw598243565/AverageFliter

在图像预处理中,最基础也最重要的处理方法是图像滤波与增强。图像滤波可以很好地消除测量成像或者环境带来的随机噪声,高斯噪声和椒盐噪声等。图像增强可以增强图像细节,提高图像对比度。

滤波器的种类有很多种。按照输出和输入之间是否有唯一且确定的传递函数,我们可以把滤波器分为线性滤波器和非线性滤波器两种。我们介绍线性滤波器中的均值滤波器。

均值滤波器是典型的线性滤波算法,主要方法为邻域平均法,即用一个图像区域的各个像素的平均值来替代原图像的各个像素值。

        均值滤波器的主要作用是减小图像灰度值的“尖锐”变化从而达到减小噪声的目的。但是,由于图像边缘在一般情况下也是由图像灰度尖锐化引起的,因此,均值滤波也存在边缘模糊的问题。

        均值滤波的数学定义为: 

        其中r为处理窗口的半径, 为待求的窗口内像素均值,I(x,y)为输入像素值,g(x,y)为输出像素值。 

基于FPGA的图像处理4--线性滤波-均值滤波_第1张图片

图 1 

        图1为均值滤波的电路结构图。该电路主要由四部分组成,第一个部分是用于求和的加法树ADD_Tree,第二个部分是用于产生加法树输出数据是否有效的dvalid标志信号的Tvalid_Counter,第三部分使用来抓取经过累加求和后的有效数据的Data_Capture,第四部分是用求均值的Average_Op。

基于FPGA的图像处理4--线性滤波-均值滤波_第2张图片

图 2

        图2所示是均值滤波电路中的加法树ADD_Tree模块的电路结构图。该均值滤波使用3x3大小的核进行滤波。因此,在ADD_Tree模块中,先进行3x3大小的开窗操作,即将行列对齐后的3行数据在寄存器中打3拍,这样在9个寄存器中的值就是3x3滤波核所需要的对应图像数据值了。当滤波窗口数据有效后,9个数据同时进入后续的加法数进行累加操作。这里需要注意的是,我们知道2个8位的数相加,可能会产生一个9位的数,因此,每一级的和比前一级加数的位宽大1,这样能避免和值的溢出。 

基于FPGA的图像处理4--线性滤波-均值滤波_第3张图片

图 3 

基于FPGA的图像处理4--线性滤波-均值滤波_第4张图片

图 4 

        图3和图4是ADD_Tree模块的仿真图,从仿真结果看,Tvalid_counter模块在开端产生的dvalid信号是可以和ADD_Tree模块产生的累加和add_data值对应上,但是在因为设计中的计数器的关系,Tvalid_counter模块比ADD_Tree模块早一个时钟周期拉低了dvalid信号,但这时ADD_Tree模块产生的add_data信号是有效。因此,我设计了一个Data_Capture电路来对ADD_Tree模块产生的add_data和Tvalid_counter模块产生的dvalid信号,进行采样后重新生成。此时,Tvalid_counter模块锁存来自ADD_Tree模块的有效数据有两种情况,第一种情况是:只要Tvalid_counter模块输出的dvalid信号有效(高电平),则锁存数据输出cap_data,并输出cap_valid为有效(高电平);第二种情况是:当Tvalid_counter模块输出的dvalid信号无效(低电平),且其上一拍dvalid信号有效,也锁存数据输出cap_data,并输出cap_valid为有效(高电平);除了上述两种场景外的其他场景,Tvalid_counter模块不锁存数据,同时将cap_valid置为无效(低电平)。

图 5 

         图5所示为Data_Capture模块电路输出的仿真图,可以看到输出值是正确的求和累加时序。        

基于FPGA的图像处理4--线性滤波-均值滤波_第5张图片

图 6

        图6所示为3x3大小的均值滤波的求均值电路结构图。该电路结构的设计思想是这样的:采用的是近似计算的原理。因为是3x3大小的求均值,因此是执行din_data/9的运算。对于这个除法操作可以使用除法电路来做也可以使用移位运算近似获得。这里,我使用的移位运算的近似逼近。因为9不是2的任何次幂,因此无法直接使用移位操作来实现。所以我将din_data/9做了如下转换: 

基于FPGA的图像处理4--线性滤波-均值滤波_第6张图片

        依照如上所示的转换公式,便将din_data/9操作转换成了两级移位运算和两级加法运算,其运算结构如图6所示。 

图 7 

        图7所示为Average_Op模块的仿真波形图,可以看到因为是整型定点数移位操作,所以会有精度的丢失。均值滤波具体实现如下所示:

module Average3x3 #(
    parameter LINE_NUM = 3,
    parameter PIXEL_WIDTH = 14,
    parameter KX_WIDTH =3,
    parameter IMAGE_WIDTH = 128
)(clk,arstn,data_in,din_valid,data_out,dout_valid);
function integer clogb2(input integer bit_depth);
    begin
        for(clogb2 = 0;bit_depth >0; clogb2 = clogb2 + 1)
            bit_depth = bit_depth >> 1;
    end
endfunction

localparam CNT_WIDTH = clogb2(IMAGE_WIDTH-1);
localparam DATA_WIDTH = PIXEL_WIDTH * LINE_NUM;
input clk;
input arstn;
input [DATA_WIDTH-1:0]data_in;
input din_valid;
output [PIXEL_WIDTH+4-1:0]data_out;
output dout_valid;

reg [PIXEL_WIDTH-1:0]k00_reg;
reg [PIXEL_WIDTH-1:0]k01_reg;
reg [PIXEL_WIDTH-1:0]k02_reg;
reg [PIXEL_WIDTH-1:0]k10_reg;
reg [PIXEL_WIDTH-1:0]k11_reg;
reg [PIXEL_WIDTH-1:0]k12_reg;
reg [PIXEL_WIDTH-1:0]k20_reg;
reg [PIXEL_WIDTH-1:0]k21_reg;
reg [PIXEL_WIDTH-1:0]k22_reg;
reg [CNT_WIDTH:0]cnt_reg;
reg [CNT_WIDTH:0]cnt;
always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                k00_reg <= 0;
                k01_reg <= 0;
                k02_reg <= 0;
            end
        else
            begin
                if(din_valid)
                    begin
                        k00_reg <= data_in[PIXEL_WIDTH-1:0];
                        k01_reg <= data_in[2*PIXEL_WIDTH-1:PIXEL_WIDTH];
                        k02_reg <= data_in[3*PIXEL_WIDTH-1:2*PIXEL_WIDTH];
                    end
            end
    end

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                k10_reg <= 0;
                k11_reg <= 0;
                k12_reg <= 0;
            end
       else
            begin
                k10_reg <= k00_reg;
                k11_reg <= k01_reg;
                k12_reg <= k02_reg;
            end
    end

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                k20_reg <= 0;
                k21_reg <= 0;
                k22_reg <= 0;
            end
         else
            begin
                k20_reg <= k10_reg;
                k21_reg <= k11_reg;
                k22_reg <= k12_reg;
            end
    end

/* first add pipe */
reg [PIXEL_WIDTH+1-1:0]add_delay_00;
reg [PIXEL_WIDTH+1-1:0]add_delay_01;
reg [PIXEL_WIDTH+1-1:0]add_delay_02;
reg [PIXEL_WIDTH+1-1:0]add_delay_03;
reg [PIXEL_WIDTH+1-1:0]add_delay_04;

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                add_delay_00 <= 0;
                add_delay_01 <= 0;
                add_delay_02 <= 0;
                add_delay_03 <= 0;
                add_delay_04 <= 0;
            end
        else
            begin
                add_delay_00 <= k00_reg + k01_reg;
                add_delay_01 <= k02_reg + k10_reg;
                add_delay_02 <= k11_reg + k12_reg;
                add_delay_03 <= k20_reg + k21_reg;
                add_delay_04 <= k22_reg;
            end
    end

/* second add pipe */
reg [PIXEL_WIDTH+2-1:0]add_delay_10;
reg [PIXEL_WIDTH+2-1:0]add_delay_11;
reg [PIXEL_WIDTH+2-1:0]add_delay_12;
always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                add_delay_10 <= 0;
                add_delay_11 <= 0;
                add_delay_12 <= 0;
            end
        else
            begin
                add_delay_10 <= add_delay_00 + add_delay_01;
                add_delay_11 <= add_delay_02 + add_delay_03;
                add_delay_12 <= add_delay_04;
            end
    end
    
/* Third add pipe */
reg [PIXEL_WIDTH+3-1:0]add_delay_20;
reg [PIXEL_WIDTH+3-1:0]add_delay_21;
always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                add_delay_20 <= 0;
                add_delay_21 <= 0;
            end
        else
            begin
                add_delay_20 <= add_delay_10 + add_delay_11;
                add_delay_21 <= add_delay_12;
            end
    end

/* Fourth add pipe */
reg [PIXEL_WIDTH+4-1:0]add_delay_30;
always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            add_delay_30 <= 0;
        else
            add_delay_30 <= add_delay_20 + add_delay_21;
    end
 assign data_out = add_delay_30;
 always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            cnt_reg <= 0;
        else
            cnt_reg <= cnt;
    end

always@(*)
    begin
        if(din_valid)
            begin 
                if(cnt_reg == IMAGE_WIDTH - 1)
                    cnt = 0;
                else
                    cnt = cnt_reg + 1'b1;  
            end
        else
            cnt = cnt_reg;
    end   

/* dout_valid pipe*/
reg tvalid;
reg tvalid_delay_0;
reg tvalid_delay_1;
reg tvalid_delay_2;
reg tvalid_delay_3;
always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
           tvalid <= 0;
        else 
            begin
                if(cnt == KX_WIDTH)
                    tvalid <= 1'b1;
                else if(cnt_reg == IMAGE_WIDTH - 1)
                    tvalid <= 1'b0;
                else
                    tvalid <= tvalid;
            end  
    end

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                tvalid_delay_0 <= 0;
                tvalid_delay_1 <= 0;
                tvalid_delay_2 <= 0;
                tvalid_delay_3 <= 0;
            end
        else
            begin
                tvalid_delay_0 <= tvalid;
                tvalid_delay_1 <= tvalid_delay_0;
                tvalid_delay_2 <= tvalid_delay_1;
                tvalid_delay_3 <= tvalid_delay_2;
            end
    end
assign dout_valid = tvalid_delay_3;

endmodule

你可能感兴趣的:(FPGA图像处理,fpga开发,图像处理,均值算法)