4. 3x3图像均值滤波的流水线设计

任务与实现思路

本案例在FPGA中实现3x3均值滤波:用两级行缓存(Line Buffer)缓存两行像素,配合当前行组成3x3窗口,对9个像素求和后右移3位(除以8)得到滤波结果。流水线分三级:窗口生成、求和、均值输出,每级用寄存器打拍,保证时序收敛。

输入为8位灰度像素流(含行同步、场同步、数据有效),输出为滤波后8位像素流。行缓存用移位寄存器实现,每行缓存图像宽度个像素。

3x3均值滤波流水线结构 像素输入 din/valid 行缓存 Line0 (移位) Line1 (移位) 当前行 3x3窗口生成 9像素求和 sum = Σp[i] 右移3位 dout = sum>>3 输出 Stage1: 窗口 Stage2: 求和 Stage3: 均值

关键代码

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]。