FPGA 图像篇(图像增强-均值滤波)

314 阅读2分钟

情景

图像预处理中,图像滤波可以去除一些环境中的噪声,均值滤波的主要作用是减小图像灰度值的尖锐从而达到减小噪声的目的。

方法

基于三行数据缓存进行均值滤波,可以实现3x3窗口的滤波操作。 具体思路为当输出三行数据有效时,即flag信号有效时,即在输出数据,延迟三个周期后,数据以及输出了三列此时可以进行滤波操作。 鉴于3x3需要除以9实现均值,本文基于周围八个像素值除以8,通过移位操作,简单实现。

细节

1 3行数据缓存模块

2 滤波模块

3行数据缓存模块:

即上一篇juejin.cn/post/710280… ,提到的行缓存,通过行缓存可以获取三行数据并并行同时开始输出,输出信号为flag,当flag信号为高电平时同时输出三行数据。

2 滤波模块

基于3行数据缓存模块,在滤波模块中我们只需要做的是通过移位寄存器获取同一行的三个数,三行获取九个数,求中间数邻域的八个数的均值。并在三个周期后使输出信号为高电平,即在第四个周期使得data_out_en <= 1'b1;,此时可以输出计算出的data_out信号。

1 通过移位寄存器的流水线思想获得3x3区域的像素值。 具体到每一行用三个寄存器连接。三行数据分别用移位寄存器实现流水线思想的数据获取,程序如下:

    always@(posedge clk or negedge rst_n)
        if(!rst_n) begin
            data0_num0 <= 9'd0;
            data0_num1 <= 9'd0;
            data0_num2 <= 9'd0;
            data1_num0 <= 9'd0;
            data1_num1 <= 9'd0;
            data1_num2 <= 9'd0;
            data2_num0 <= 9'd0;
            data2_num1 <= 9'd0;
            data2_num2 <= 9'd0;
        end
        else if(data_in_en)begin
            data0_num0 <= data0;
            data0_num1 <= data0_num0;
            data0_num2 <= data0_num1;
            data1_num0 <= data1;
            data1_num1 <= data1_num0;
            data1_num2 <= data1_num1;
            data2_num0 <= data2;
            data2_num1 <= data2_num0;
            data2_num2 <= data2_num1;    
        end

2 计算均值程序如下:

        always @(posedge clk or negedge rst_n) begin
            if(!rst_n)
                data_out <= 9'b0;
            else if(data_in_en)
                data_out <= (data0_num0 + data0_num1+data0_num2+data1_num0+data1_num2+data2_num0+data2_num1+data2_num2)>>3;
        end
            

3 输出信号的控制,使得每次计算完成后都可以输出有效数据,这里用到了控制信号,mat_flag。 通过寄存器来延迟三拍的数据来延迟flag信号,即当mat_flag4 = 1时候,数据此时为三个,计算出的均值也为正确的三行三列数据的均值。

三级寄存器延迟三拍程序如下:

        mat_flag1 <= flag;
        mat_flag2 <= mat_flag1;
        mat_flag3 <= mat_flag2;
        mat_flag4 <= mat_flag3;
    end

则mat_flag4延迟mat_flag1三拍,即延迟flag信号三拍。

顶层程序如下所示:

module Mean_filter(
    clk,
    rst_n,
    data_in_en,
    data_in,
    data_out,
    data_out_en
      );
    input clk;
    input rst_n;
    input [9:0]data_in;  //
    input data_in_en;
    output  reg [9:0]data_out;
    output reg data_out_en;
    //激励信号定义
    wire flag;
    reg mat_flag1;
    reg mat_flag2;
    reg mat_flag3;
    reg mat_flag4;
    wire [9:0]data0;
    wire [9:0]data1;
    wire [9:0]data2;
    //模板为3x3窗口,该窗口中像素值共9个。
    reg [9:0]data0_num0;
    reg [9:0]data0_num1;
    reg [9:0]data0_num2;
    reg [9:0]data1_num0;
    reg [9:0]data1_num1;
    reg [9:0]data1_num2;
    reg [9:0]data2_num0;
    reg [9:0]data2_num1;
    reg [9:0]data2_num2;
    
    
    always@(posedge clk)begin
        mat_flag1 <= flag;
        mat_flag2 <= mat_flag1;
        mat_flag3 <= mat_flag2;
        mat_flag4 <= mat_flag3;
    end
     matrix_3x3 matrix_3x3(
    .clk(clk),
    .rst_n(rst_n),
    .valid_in(data_in_en),
    .din(data_in),
    .dout_r0(data0),
    .dout_r1(data1),
    .dout_r2(data2),
    .dout(),
    .flag(flag)
);
    // 取出3x3 区域
    always@(posedge clk or negedge rst_n)
        if(!rst_n) begin
            data0_num0 <= 9'd0;
            data0_num1 <= 9'd0;
            data0_num2 <= 9'd0;
            data1_num0 <= 9'd0;
            data1_num1 <= 9'd0;
            data1_num2 <= 9'd0;
            data2_num0 <= 9'd0;
            data2_num1 <= 9'd0;
            data2_num2 <= 9'd0;
        end
        else if(data_in_en)begin
            data0_num0 <= data0;
            data0_num1 <= data0_num0;
            data0_num2 <= data0_num1;
            data1_num0 <= data1;
            data1_num1 <= data1_num0;
            data1_num2 <= data1_num1;
            data2_num0 <= data2;
            data2_num1 <= data2_num0;
            data2_num2 <= data2_num1;    
        end
        // 均值滤波最好的计算方法是计算该像素周围八个像素,其值应为九个值的均值。但求八个数的均值比较方便直接移位就可以。
        always @(posedge clk or negedge rst_n) begin
            if(!rst_n)
                data_out <= 9'b0;
            else if(data_in_en)
                data_out <= (data0_num0 + data0_num1+data0_num2+data1_num0+data1_num2+data2_num0+data2_num1+data2_num2)>>3;
        end
            
    
        always@(posedge clk or negedge rst_n)
            if(!rst_n)
                data_out_en <= 1'b0;
            else if(mat_flag4 == 1'b1)
                data_out_en <= 1'b1;
            else
                data_out_en <= 1'b0;
          
   
    
endmodule

testbench如下:

````timescale 1ns / 1ps

`define clk_period 20 module mean_filter_tb( );

reg clk,rst_n;
reg [9:0]data_in;
reg data_in_en;

wire [9:0]data_out;
wire data_out_en;



Mean_filter  Mean_filter0(
.clk(clk),
.rst_n(rst_n),
.data_in_en(data_in_en),
.data_in(data_in),
.data_out(data_out),
.data_out_en(data_out_en)
  );

initial begin 
    clk = 0;
    rst_n = 0;
    data_in_en = 0;
    #(`clk_period*10);
    rst_n = 1;
    #(`clk_period*10);
    data_in_en = 1;
    #(`clk_period*480*10);
    data_in_en = 0;
    #(`clk_period*20);
    $stop;
end

 always #(`clk_period/2) clk = ~clk;
 
 always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        data_in <= 0;
    else if(data_in == 479)
        data_in <= 0;
    else if(data_in_en == 1'b1)
        data_in <= data_in + 1;
    
   end

endmodule

```

仿真结果分析

image.png

容易看到当mat_flag为1时候。data_out为1,因为此时值为(0+1+2+0+2+0+1+2)/8 = 1。 而data_out_en信号是当mat_flag =1时候才置为1的,所以延迟一拍。