情景
图像预处理中,图像滤波可以去除一些环境中的噪声,均值滤波的主要作用是减小图像灰度值的尖锐从而达到减小噪声的目的。
方法
基于三行数据缓存进行均值滤波,可以实现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
```
仿真结果分析
容易看到当mat_flag为1时候。data_out为1,因为此时值为(0+1+2+0+2+0+1+2)/8 = 1。 而data_out_en信号是当mat_flag =1时候才置为1的,所以延迟一拍。