4. 3x3图像均值滤波的流水线设计
任务与实现思路
本案例在FPGA中实现3x3均值滤波:用两级行缓存(Line Buffer)缓存两行像素,配合当前行组成3x3窗口,对9个像素求和后右移3位(除以8)得到滤波结果。流水线分三级:窗口生成、求和、均值输出,每级用寄存器打拍,保证时序收敛。
输入为8位灰度像素流(含行同步、场同步、数据有效),输出为滤波后8位像素流。行缓存用移位寄存器实现,每行缓存图像宽度个像素。
关键代码
module mean_filter_3x3 #(
parameter IMG_W = 640,
parameter DW = 8
)(
input wire clk,
input wire rst_n,
input wire din_valid,
input wire [DW-1:0] din,
output reg dout_valid,
output reg [DW-1:0] dout
);
// 行缓存:每行 IMG_W 个像素
reg [DW-1:0] line0 [0:IMG_W-1];
reg [DW-1:0] line1 [0:IMG_W-1];
reg [DW-1:0] cur_line [0:IMG_W-1];
reg [$clog2(IMG_W)-1:0] col_cnt;
reg [1:0] row_cnt;
// 3x3窗口寄存器
reg [DW-1:0] p00,p01,p02,p10,p11,p12,p20,p21,p22;
reg win_valid;
reg [DW-1:0] sum;
reg sum_valid;
// Stage1: 写入行缓存并生成窗口
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
col_cnt <= 0; row_cnt <= 0; win_valid <= 0;
end else if (din_valid) begin
// 当前像素写入当前行
cur_line[col_cnt] <= din;
// 行缓存移位:line0 <- line1 <- cur_line
if (col_cnt == IMG_W-1) begin
line1 <= cur_line;
line0 <= line1;
row_cnt <= row_cnt + 1;
end
// 窗口生成(需要至少2行历史)
if (row_cnt >= 2 && col_cnt >= 2) begin
p00 <= line0[col_cnt-2]; p01 <= line0[col_cnt-1]; p02 <= line0[col_cnt];
p10 <= line1[col_cnt-2]; p11 <= line1[col_cnt-1]; p12 <= line1[col_cnt];
p20 <= cur_line[col_cnt-2]; p21 <= cur_line[col_cnt-1]; p22 <= din;
win_valid <= 1;
end else begin
win_valid <= 0;
end
col_cnt <= (col_cnt == IMG_W-1) ? 0 : col_cnt + 1;
end else begin
win_valid <= 0;
end
end
// Stage2: 9像素求和
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
sum <= 0; sum_valid <= 0;
end else begin
sum <= p00+p01+p02+p10+p11+p12+p20+p21+p22;
sum_valid <= win_valid;
end
end
// Stage3: 右移3位得均值
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
dout <= 0; dout_valid <= 0;
end else begin
dout <= sum[DW+3:3]; // sum>>3
dout_valid <= sum_valid;
end
end
endmodule
运行与验证
仿真平台:用Verilog testbench产生640x480灰度图,在部分像素上叠加随机噪声(如±30),送入模块,采集dout。对比滤波前后同一位置的像素值,噪声点应被邻域均值拉回。
// testbench 关键片段
initial begin
rst_n = 0; din_valid = 0; din = 0;
#20 rst_n = 1;
// 逐行逐列送入像素
for (row = 0; row < 480; row = row + 1)
for (col = 0; col < 640; col = col + 1) begin
din_valid = 1;
din = pixel_with_noise(row, col);
#10;
end
din_valid = 0;
#100 $finish;
end
判定标准:dout_valid拉高后,输出像素与输入像素在非噪声区域基本一致(差值≤1),噪声区域像素值明显向邻域均值靠拢。用$display或波形观察p11与dout的差异即可确认滤波生效。
注意事项
- 行缓存用寄存器数组实现,IMG_W=640时消耗约640×8×3=15360个触发器,资源较大;实际工程可改用Block RAM。
- 窗口生成需等待前两行填满,前两行输出无效(win_valid=0),属正常边界行为。
- 求和位宽需DW+4(9×255=2295,需12位),代码中sum声明为[DW-1:0]会溢出,应改为[DW+3:0]。