上一篇博客介绍了如何实现3x3均值滤波,本次就来实现3x3中值滤波。
中值滤波常用于去除图像的噪声,对椒盐噪声比较有效。
在每个3x3窗口中通过排序获取中间值作为输出即可。
在FPGA中以流水线方式获取3x3窗口的中值:
图中详细描述了取得中值的步骤,同时也描述了每一级流水线需要完成的任务:
为了简单起见,输入输出都是valid+data形式:
module mid_filter(
input clk,
input rst_n,
input iValid,
input [7:0] iData,
output oValid,
output [7:0] oData
);
之前有介绍如何获取3x3卷积模板,感兴趣的可以看看
需要注意获取3x3卷积模板消耗了2时钟周期。
filter_3x3 inst_filter_3x3(
.clk (clk),
.rst_n (rst_n),
.iValid (iValid),
.iData (iData),
.oValid (filter_oValid),
.oData_11 (filter_11), .oData_12 (filter_12), .oData_13 (filter_13),
.oData_21 (filter_21), .oData_22 (filter_22), .oData_23 (filter_23),
.oData_31 (filter_31), .oData_32 (filter_32), .oData_33 (filter_33)
);
分析上述步骤,每一步都是对3个数进行排序,所以需要一个模块来完成这个任务,该模块消耗1时钟周期。
module sort_3(
input clk,
input rst_n,
input [7:0] data1, data2, data3,
output reg [7:0] max, mid, min
);
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
max <= 0;
mid <= 0;
min <= 0;
end else begin
// max
if(data1 >= data2 && data1 >= data3)
max <= data1;
else if(data2 >= data1 && data2 >= data3)
max <= data2;
else
max <= data3;
// mid
if((data1 >= data2 && data1 <= data3) || (data1 >= data3 && data1 <= data2))
mid <= data1;
else if((data2 >= data1 && data2 <= data3) || (data2 >= data3 && data2 <= data1))
mid <= data2;
else
mid <= data3;
// min
if(data1 <= data2 && data1 <= data3)
min <= data1;
else if(data2 <= data1 && data2 <= data3)
min <= data2;
else
min <= data3;
end
end
endmodule
对3x3窗口的每一行进行排序
// sort of 3x3 line1
sort_3 inst_sort_3_11(
.clk (clk),
.rst_n (rst_n),
.data1 (filter_11),
.data2 (filter_12),
.data3 (filter_13),
.max (max1),
.mid (mid1),
.min (min1)
);
// sort of 3x3 line2
sort_3 inst_sort_3_12(
.clk (clk),
.rst_n (rst_n),
.data1 (filter_21),
.data2 (filter_22),
.data3 (filter_23),
.max (max2),
.mid (mid2),
.min (min2)
);
// sort of 3x3 line3
sort_3 inst_sort_3_13(
.clk (clk),
.rst_n (rst_n),
.data1 (filter_31),
.data2 (filter_32),
.data3 (filter_33),
.max (max3),
.mid (mid3),
.min (min3)
);
对3个max排序,取最小,对3个mid排序,取mid,对3个min排序,取max
// min of max1 max2 max3
sort_3 inst_sort_3_21(
.clk (clk),
.rst_n (rst_n),
.data1 (max1),
.data2 (max2),
.data3 (max3),
.max (),
.mid (),
.min (min_of_max)
);
// mid of mid1 mid2 mid3
sort_3 inst_sort_3_22(
.clk (clk),
.rst_n (rst_n),
.data1 (mid1),
.data2 (mid2),
.data3 (mid3),
.max (),
.mid (mid_of_mid),
.min ()
);
// max of min1 min2 min3
sort_3 inst_sort_3_23(
.clk (clk),
.rst_n (rst_n),
.data1 (min1),
.data2 (min2),
.data3 (min3),
.max (max_of_min),
.mid (),
.min ()
);
对第二级产生的三个数进行排序,得到中值
sort_3 inst_sort_3_3(
.clk (clk),
.rst_n (rst_n),
.data1 (min_of_max),
.data2 (mid_of_mid),
.data3 (max_of_min),
.max (),
.mid (mid_of_nine),
.min ()
);
获取3x3卷积模板消耗了2时钟周期,中值滤波消耗了3时钟周期,所以输出值延后输入值5个时钟周期,oValid信号也要进行延迟,比iValid慢5个时钟周期。
和之前类似,仿真3行像素,每行640个像素点。
`timescale 1ps/1ps
module mid_filter_tb;
reg clk = 1'b1;
always #10 clk = ~clk;
reg rst_n = 1'b0;
reg iValid = 1'b0;
reg [7:0] iData = 0;
wire oValid;
wire [7:0] oData;
initial begin
#20 rst_n <= 1'b1;
#20 iValid <= 1'b1;
// 第一行数据
repeat(640) begin
iData <= $random;
#20;
end
iValid <= 1'b0;
// 第二行数据
#100 iValid <= 1'b1;
repeat(640) begin
iData <= $random;
#20;
end
iValid <= 1'b0;
// 第三行数据
#100 iValid <= 1'b1;
repeat(640) begin
iData <= $random;
#20;
end
#500 $stop;
end
mid_filter inst_mid_filter(
.clk (clk),
.rst_n (rst_n),
.iValid (iValid),
.iData (iData),
.oValid (oValid),
.oData (oData)
);
endmodule
第一行前几个像素为36,129,9,99,13
第二行前几个像素为235,248,242,137,111
第三行前几个像素为121,205,146,69,169
当146出现2个时钟周期后,窗口更新为36,129,9,235,248,242,121,205,146,再经过3个时钟周期的中值滤波流水线操作,输出为146,手算结果也为146,验证正确。
当69出现2个时钟周期后,窗口更新为129,9,99,248,242,137,205,146,69,再经过3个时钟周期的中值滤波流水线操作,输出为137,手算结果也为137,验证正确。