均值滤波的原理是使用一个3x3的卷积核,从左到右依次划窗,每划过9个数据对9个数据求一次均值,原9个数据使用这一个均值代替。由于是3x3卷积,所以一个1280x720的图像经过卷积核后,会缩减到1278x718,需要对均值滤波的图像进行填充,将其恢复原来的size.
在fpga中实现均值滤波至关重要的一点是构建出一个3x3的矩阵,这里需要用到两个fifo
理解上述的思维很重要,上一行-当前行-下一行 ,本人自己抽象了下,如下图所示,这样理解就好了。当第三行数据全部到来时,3x3矩阵会构建如图二所示,1、2、3会按照上一行-当前行-下一行的方式呈现,因为当第三行的第一个数据到来时,第一个数据会直接输出用于矩阵构建,同时也会存入fifo1,由于存进去了一个数,所以fifo1需要同步读出一个数,也就是第2行数据的第一列数据,同时读出的这个数据也需要同时用于构建3x3矩阵和存入fifo0,同理fifo0也需要同步读出一个数据,但是这个数据后续无须再存,当第三行数据完全存入fifo1时,3x3矩阵就构建完成。此时右图二的数据应当为1、2、3。此时fifo0和fifo1中的数据应当为2、3行数据。此时的划窗也整好划过一整行。当第4行数据到来时就完成划窗选择123行到选中234行。
module Median_filtering #(
parameter WEIGHT = 1280 ,
parameter HEIGHT = 720
)(
input wire sclk ,
input wire reset ,
input wire valid_i ,
input wire [23:0] data_i ,
output wire valid_o ,
output wire [23:0] data_o
);
parameter C_0 = 114 ;// 1/9*1024
reg [9:0] row_cnt ;
reg [10:0] col_cnt ;
reg wr_en_0 ;
reg [23:0] wr_data_0 ;
reg rd_en_0 ;
wire [23:0] rd_data_0 ;
reg wr_en_1 ;
reg [23:0] wr_data_1 ;
reg rd_en_1 ;
wire [23:0] rd_data_1 ;
//3x3矩阵
reg [23:0] conv0_0 ;
reg [23:0] conv0_1 ;
reg [23:0] conv0_2 ;
reg [23:0] conv1_0 ;
reg [23:0] conv1_1 ;
reg [23:0] conv1_2 ;
reg [23:0] conv2_0 ;
reg [23:0] conv2_1 ;
reg [23:0] conv2_2 ;
//求和信号
reg [9:0] mid_conv0 ;
reg [9:0] mid_conv1 ;
reg [9:0] mid_conv2 ;
reg [11:0] sum_conv ;
//求均值信号
reg [18:0] mean_conv ;
//计算打拍信号
reg reg_flag ;
reg sum_flag_0 ;
reg sum_flag_1 ;
reg sum_flag_2 ;
reg mean_flag ;
reg [23:0] reg_flag_data ;
// reg [23:0] sum_flag_0_data ;
// reg [23:0] sum_flag_1_data ;
// reg [23:0] sum_flag_2_data ;
// reg [23:0] mean_flag_data ;
wire full ;
wire empty;
wire wr_rst_busy ;
wire rd_rst_busy ;
wire [23:0] reg_data_i ;
wire reg_valid_i ;
reg [23:0] reg_data_i_1 ;
reg reg_valid_i_1 ;
reg [23:0] reg_data_i_2 ;
reg reg_valid_i_2 ;
reg [23:0] reg_data_i_3 ;
reg reg_valid_i_3 ;
wire last_valid_o ;
wire [23:0] last_data_o ;
reg [19:0] last_cnt ;
image_rgb2gray u_image_rgb2gray (
.clk ( sclk ),
.reset ( reset ),
.valid_i ( valid_i ),
.img_data_i ( data_i ),
.valid_o ( reg_valid_i ),
.img_data_o ( reg_data_i )
);
//列计数器
always @(posedge sclk or posedge reset) begin
if(reset==1'b1)
col_cnt <= 'd0 ;
else if(reg_valid_i==1'b1&&col_cnt==WEIGHT-1)
col_cnt <= 'd0 ;
else if(reg_valid_i==1'b1)
col_cnt <= col_cnt+1'b1 ;
end
//行计数器
always @(posedge sclk or posedge reset) begin
if(reset==1'b1)
row_cnt <= 'd0 ;
else if(reg_valid_i==1'b1&&row_cnt==HEIGHT-1&&col_cnt==WEIGHT-1)
row_cnt <= 'd0 ;
else if(reg_valid_i==1'b1&&col_cnt==WEIGHT-1)
row_cnt <= row_cnt +1'b1 ;
end
//读使能信号打拍
always @(*) begin
if(reset==1'b1)begin
wr_en_0 <= 0 ;
wr_en_1 <= 0 ;
end
else if(row_cnt==0&®_valid_i==1'b1)begin
wr_en_0 <= 1 ;
wr_en_1 <= 0 ;
end
else if(row_cnt>0&®_valid_i==1'b1)begin
wr_en_0 <= 1 ;
wr_en_1 <= 1 ;
end
else begin
wr_en_0 <= 0 ;
wr_en_1 <= 0 ;
end
end
//写使能信号打拍
always @(*) begin
if(reset==1'b1)begin
wr_data_0 <= 'd0 ;
wr_data_1 <= 'd0 ;
end
else if(row_cnt==0&®_valid_i==1'b1)begin
wr_data_0 <= reg_data_i ;
wr_data_1 <= 'd0 ;
end
else if(row_cnt>0&®_valid_i==1'b1)begin
wr_data_0 <= reg_data_i ;
wr_data_1 <= rd_data_0 ;
end
else if(row_cnt==HEIGHT-1&®_valid_i==1'b1)begin
wr_data_0 <= 'd0 ;
wr_data_1 <= 'd0 ;
end
end
//读使能信号打拍
always @(*) begin
if(reset==1'b1)begin
rd_en_0 <= 0 ;
rd_en_1 <= 0 ;
end
else if(row_cnt==1&®_valid_i==1'b1)begin
rd_en_0 <= 1 ;
rd_en_1 <= 0 ;
end
else if(row_cnt>=2&®_valid_i==1'b1)begin
rd_en_0 <= 1 ;
rd_en_1 <= 1 ;
end
else begin
rd_en_0 <= 0 ;
rd_en_1 <= 0 ;
end
end
//3x3矩阵构建完成标志信号
always@(posedge sclk or posedge reset)
if(reset==1'b1)
sum_flag_0<=0;
else if(row_cnt>=2&®_valid_i==1'b1&&col_cnt>=2)
sum_flag_0<=1;
else
sum_flag_0<=0;
//信号打拍,构建3x3矩阵
always @(posedge sclk or posedge reset) begin
if(reset==1'b1)begin
conv0_0 <= 'd0 ;
conv0_1 <= 'd0 ;
conv0_2 <= 'd0 ;
conv1_0 <= 'd0 ;
conv1_1 <= 'd0 ;
conv1_2 <= 'd0 ;
conv2_0 <= 'd0 ;
conv2_1 <= 'd0 ;
conv2_2 <= 'd0 ;
reg_flag <= 'd0 ;
reg_flag_data<='d0 ;
end
else if(reg_valid_i)begin
conv0_0 <= reg_data_i ;
conv0_1 <= conv0_0 ;
conv0_2 <= conv0_1 ;
conv1_0 <= rd_data_0 ;
conv1_1 <= conv1_0 ;
conv1_2 <= conv1_1 ;
conv2_0 <= rd_data_1 ;
conv2_1 <= conv2_0 ;
conv2_2 <= conv2_1 ;
reg_flag <= reg_valid_i ;
reg_flag_data<=reg_data_i ;
end
else begin
conv0_0 <= conv0_0 ;
conv0_1 <= conv0_1 ;
conv0_2 <= conv0_2 ;
conv1_0 <= conv1_0 ;
conv1_1 <= conv1_1 ;
conv1_2 <= conv1_2 ;
conv2_0 <= conv2_0 ;
conv2_1 <= conv2_1 ;
conv2_2 <= conv2_2 ;
reg_flag <= 0 ;
reg_flag_data<=reg_flag_data;
end
end
//原始信号打拍,后面用于填零
always@(posedge sclk or posedge reset) begin
if(reset)begin
reg_valid_i_1<=1'b0;
reg_valid_i_2<=1'b0;
reg_valid_i_3<=1'b0;
reg_data_i_1 <='d0;
reg_data_i_2 <='d0;
reg_data_i_3 <='d0;
end
else if(reg_flag)begin
reg_valid_i_1<=1'b1;
reg_valid_i_2<=reg_valid_i_1;
reg_valid_i_3<=reg_valid_i_2;
reg_data_i_1 <=reg_flag_data;
reg_data_i_2 <=reg_data_i_1;
reg_data_i_3 <=reg_data_i_2;
end
else begin
reg_valid_i_1<=1'b0;
reg_valid_i_2<=reg_valid_i_1;
reg_valid_i_3<=reg_valid_i_2;
reg_data_i_1 <=reg_data_i_1;
reg_data_i_2 <=reg_data_i_1;
reg_data_i_3 <=reg_data_i_2;
end
end
always @(posedge sclk or posedge reset) begin
if(reset==1'b1)begin
mid_conv0 <= 'd0 ;
mid_conv1 <= 'd0 ;
mid_conv2 <= 'd0 ;
sum_flag_1 <= 1'b0 ;
end
else if(sum_flag_0)begin
mid_conv0 <= conv0_0[7:0]+conv0_1[7:0]+conv0_2[7:0] ;
mid_conv1 <= conv1_0[7:0]+conv1_1[7:0]+conv1_2[7:0] ;
mid_conv2 <= conv2_0[7:0]+conv2_1[7:0]+conv2_2[7:0] ;
sum_flag_1 <= 1'b1 ;
end
else begin
mid_conv0 <= mid_conv0 ;
mid_conv1 <= mid_conv1 ;
mid_conv2 <= mid_conv2 ;
sum_flag_1 <= 1'b0 ;
end
end
always @(posedge sclk or posedge reset) begin
if(reset==1'b1)begin
sum_conv <= 'd0 ;
sum_flag_2 <= 0 ;
end
else if(sum_flag_1) begin
sum_conv <= mid_conv0+mid_conv1+mid_conv2;
sum_flag_2 <= 1'b1 ;
end
else begin
sum_conv <= sum_conv ;
sum_flag_2 <= 1'b0 ;
end
end
always @(posedge sclk or posedge reset) begin
if(reset==1'b1)begin
mean_conv <= 'd0 ;
mean_flag <= 1'b0 ;
end
else if(sum_flag_2)begin
mean_conv <= sum_conv*114 ;
mean_flag <= 1'b1 ;
end
else begin
mean_conv <= mean_conv ;
mean_flag <= 1'b0 ;
end
end
assign last_valid_o = mean_flag ;
assign last_data_o = mean_conv[18]?'hffffff: {mean_conv[17:10],mean_conv[17:10],mean_conv[17:10]};
always @(posedge sclk or posedge reset) begin
if(reset)
last_cnt<='d0;
else if(last_cnt==921600)
last_cnt<='d0;
else if(last_valid_o)
last_cnt<=last_cnt+1'b1;
end
move_cnter #(
.COL ( 1280 ),
.ROW ( 720 ),
.DW ( 24 ))
u_move_cnter (
.sclk ( sclk ),
.reset ( reset ),
.raw_de (reg_valid_i_3 ) ,
.raw_data (reg_data_i_3 ) ,
.i_de ( last_valid_o ),
.i_data ( last_data_o ),
.o_de ( valid_o ),
.o_data ( data_o )
);
fifo_generator_0 fifo_generator_0 (
.rst ( reset ) , // input wire rst
.wr_clk ( sclk ) , // input wire wr_clk
.rd_clk ( sclk ) , // input wire rd_clk
.din ( wr_data_0 ) , // input wire [127 : 0] din
.wr_en ( wr_en_0 ) , // input wire wr_en
.rd_en ( rd_en_0 ) , // input wire rd_en
.dout ( rd_data_0 ) , // output wire [15 : 0] dout
.full ( full ) , // output wire full
.empty ( empty ) , // output wire empty
.wr_rst_busy(wr_rst_busy),
.rd_rst_busy(rd_rst_busy) // output wire rd_rst_busy
);
fifo_generator_0 fifo_generator_1 (
.rst ( reset ) , // input wire rst
.wr_clk ( sclk ) , // input wire wr_clk
.rd_clk ( sclk ) , // input wire rd_clk
.din ( wr_data_1 ) , // input wire [127 : 0] din
.wr_en ( wr_en_1 ) , // input wire wr_en
.rd_en ( rd_en_1 ) , // input wire rd_en
.dout ( rd_data_1 ) , // output wire [15 : 0] dout
.full ( ) , // output wire full
.empty ( ) , // output wire empty
.wr_rst_busy( ) , // output wire wr_rst_busy
.rd_rst_busy( ) // output wire rd_rst_busy
);
endmodule