• 3x3开窗中值滤波器的FPGA硬件实现


    数字逻辑课程的自由设计中,我编写了一个3x3开窗的中值滤波器,处理一副128*128像素值的图像,并且最终可以在FPGA上板实现。

    中值滤波的本质就是对于一个n*n的窗口,将其内部的值进行排序,取中位数作为中间的点的值。通过中值滤波可以很好的减弱图片的噪声,并且报纸边缘不变。

    中值滤波的硬件实现主要分为三个模块:开窗模块、中值计算模块与存储器模块。

    【1】开窗模块主要需要利用循环同步计数器来实现,分别进行列和位置的循环,从而达到最终开窗的目的。

    开窗模块的代码如下:

    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////
    //  Q为0~127计数的循环计数器,pos为1~128的循环计数器                        //
    //  clk为输入时钟, rst_n为低电平有效的reset信号,en为高电平有效的使能端    //
    //////////////////////////////////////////////////////////////////////////////
    module Counter14(clk, rst_n, en, mm, aa, bb, cc, dd, ee, ff, gg, hh, jj);
        
        input clk, rst_n, en;
    
        output [13:0]mm,aa,bb,cc,dd,ee,ff,gg,hh,jj;
        wire [13:0]m_,Q_,a2,b2,c2,d2,e2,f2,g2,h2,j2;   
        wire [15:0]m,Q,pos,pos2,a,b,c,d,e,f,g,h,j;
        wire [6:0]i; 
        
        wire posm_w;
        
        not not1(posm_w, pos2[7]);
        assign Q_ = Q[13:0];        // 13位0~127计数器                            
    
    //    assign shamt2 = shamt[13:0];// 13位移位数据
        assign mm = m[13:0];
        assign a2 = a[13:0];
        assign b2 = b[13:0];
        assign c2 = c[13:0];
        assign d2 = d[13:0];
        assign e2 = e[13:0];
        assign f2 = f[13:0];
        assign g2 = g[13:0];
        assign h2 = h[13:0];
        assign j2 = j[13:0];
        
        wire Q0, Q1, Q2, Q3, Q4, Q5;
        wire m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12;
        wire [15:0]a_,b_,c_,d_,e_,f_,g_,h_,j_;
        wire [13:0]ya,yb,yc,yd,ye,yf,yg,yh,yj;
       
        T_trigger t1(.clk(clk), .rst_n(rst_n), .EN(en), .Q(Q[0]), .QN(Q0));
        T_trigger t2(.clk(Q0),  .rst_n(rst_n), .EN(en), .Q(Q[1]), .QN(Q1));
        T_trigger t3(.clk(Q1),  .rst_n(rst_n), .EN(en), .Q(Q[2]), .QN(Q2));
        T_trigger t4(.clk(Q2),  .rst_n(rst_n), .EN(en), .Q(Q[3]), .QN(Q3));
        T_trigger t5(.clk(Q3),  .rst_n(rst_n), .EN(en), .Q(Q[4]), .QN(Q4));
        T_trigger t6(.clk(Q4),  .rst_n(rst_n), .EN(en), .Q(Q[5]), .QN(Q5));
        T_trigger t7(.clk(Q5),  .rst_n(rst_n), .EN(en), .Q(Q[6]), .QN());    
    
        
        T_trigger mm1(.clk(clk), .rst_n(rst_n), .EN(en), .Q(m[0]), .QN(m0));
        T_trigger mm2(.clk(m0),  .rst_n(rst_n), .EN(en), .Q(m[1]), .QN(m1));
        T_trigger mm3(.clk(m1),  .rst_n(rst_n), .EN(en), .Q(m[2]), .QN(m2));
        T_trigger mm4(.clk(m2),  .rst_n(rst_n), .EN(en), .Q(m[3]), .QN(m3));
        T_trigger mm5(.clk(m3),  .rst_n(rst_n), .EN(en), .Q(m[4]), .QN(m4));
        T_trigger mm6(.clk(m4),  .rst_n(rst_n), .EN(en), .Q(m[5]), .QN(m5));
        T_trigger mm7(.clk(m5),  .rst_n(rst_n), .EN(en), .Q(m[6]), .QN(m6));    
        T_trigger mm8(.clk(m6),  .rst_n(rst_n), .EN(en), .Q(m[7]), .QN(m7));
        T_trigger mm9(.clk(m7),  .rst_n(rst_n), .EN(en), .Q(m[8]), .QN(m8));        
        T_trigger mm10(.clk(m8),  .rst_n(rst_n), .EN(en), .Q(m[9]), .QN(m9));       
        T_trigger mm11(.clk(m9),  .rst_n(rst_n), .EN(en), .Q(m[10]), .QN(m10));   
        T_trigger mm12(.clk(m10),  .rst_n(rst_n), .EN(en), .Q(m[11]), .QN(m11));   
        T_trigger mm13(.clk(m11),  .rst_n(rst_n), .EN(en), .Q(m[12]), .QN(m12));       
        T_trigger mm14(.clk(m12),  .rst_n(rst_n), .EN(en), .Q(m[13]), .QN());       
                   
        // 将Q拓展为16位
        assign Q[15:7] = 9'b000000000;
        // 将m拓展为16位
        assign m[15:14] = 2'b00;
        // 得到行数
        assign i[6:0] = m[13:7];
        // 由行数得到偏移量
    //    assign shamt[15:0] = {2'b00,i[6:0],7'b0000000};
        
        // 构造1~128循环的位置信号
        add_16_prefix add1(.A(Q), .B(16'b0000000000000001), .Cin_s(1'b0), .S(pos), .Cout());
        
        // 计算该像素点序号1~128*128 
    //    add_16_prefix add2(.A(m), .B(16'b0000000000000001), .Cin_s(1'b0), .S(m_1), .Cout());
        
        // 计算输出地址
    //    add_16_prefix add3(.A(m_1), .B(16'b0000000010000001), .Cin_s(1'b0), .S(output_des), .Cout()); 
     
        add_16_prefix add5(.A(pos), .B(16'b0000000000000001), .Cin_s(1'b0), .S(pos2), .Cout()); 
       
        
        // 计算开窗地址   
        assign a_ = m;
        add_16_prefix add02(.A(a_), .B(16'b0000000000000001), .Cin_s(1'b0), .S(b_), .Cout());
        add_16_prefix add03(.A(b_), .B(16'b0000000000000001), .Cin_s(1'b0), .S(c_), .Cout());    
        add_16_prefix add04(.A(a_), .B(16'b0000000010000000), .Cin_s(1'b0), .S(d_), .Cout());
        add_16_prefix add05(.A(d_), .B(16'b0000000000000001), .Cin_s(1'b0), .S(e_), .Cout());
        add_16_prefix add06(.A(e_), .B(16'b0000000000000001), .Cin_s(1'b0), .S(f_), .Cout());
        add_16_prefix add07(.A(d_), .B(16'b0000000010000000), .Cin_s(1'b0), .S(g_), .Cout());
        add_16_prefix add08(.A(g_), .B(16'b0000000000000001), .Cin_s(1'b0), .S(h_), .Cout());
        add_16_prefix add09(.A(h_), .B(16'b0000000000000001), .Cin_s(1'b0), .S(j_), .Cout());
        
        // 超出地址都指向0
        genvar u;
        generate 
           for (u = 0; u < 16; u = u + 1)
           begin : layer0
               and ora(a[u], ~a_[14], a_[u]);
               and orb(b[u], ~b_[14], b_[u]);
               and orc(c[u], ~c_[14], c_[u]);
               and ord(d[u], ~d_[14], d_[u]);
               and ore(e[u], ~e_[14], e_[u]);
               and orf(f[u], ~f_[14], f_[u]);
               and org(g[u], ~g_[14], g_[u]);
               and orh(h[u], ~h_[14], h_[u]);
               and orj(j[u], ~j_[14], j_[u]);
            end             
        endgenerate  
        
        genvar u2;
        generate 
           for (u2 = 0; u2 < 14; u2 = u2 + 1)
           begin : layer1
               and aa1(ya[u2], posm_w, a2[u2]);
               and aa2(yb[u2], posm_w, b2[u2]);
               and aa3(yc[u2], posm_w, c2[u2]);
               and aa4(yd[u2], posm_w, d2[u2]);
               and aa5(ye[u2], posm_w, e2[u2]);
               and aa6(yf[u2], posm_w, f2[u2]);
               and aa7(yg[u2], posm_w, g2[u2]);
               and aa8(yh[u2], posm_w, h2[u2]);
               and aa9(yj[u2], posm_w, j2[u2]);
            end             
        endgenerate  
        
    
    
    
        genvar u3;
        generate 
           for (u3 = 0; u3 < 14; u3 = u3 + 1)
           begin : layer2
               and an1(aa[u3], rst_n, ya[u3]);
               and an2(bb[u3], rst_n, yb[u3]);
               and an3(cc[u3], rst_n, yc[u3]);
               and an4(dd[u3], rst_n, yd[u3]);
               and an5(ee[u3], rst_n, ye[u3]);
               and an6(ff[u3], rst_n, yf[u3]);
               and an7(gg[u3], rst_n, yg[u3]);
               and an8(hh[u3], rst_n, yh[u3]);
               and an9(jj[u3], rst_n, yj[u3]);
            end             
        endgenerate 
       
        
    endmodule

    其中为了保持整体的一致性,我们所有模块的代码都尽量使用门级的、结构化语言来描写。在开窗模块中,还用到了T触发器这一子模块,其代码如下所示:

    `timescale 1ns / 1ps
    
    module T_trigger(clk, rst_n, EN, Q, QN);
    
        input clk, rst_n, EN;
        output reg Q;
        output QN;
    
        assign QN = ~Q;
        
    
        always@(posedge clk or negedge rst_n)
        begin
            if(!rst_n) 
                Q <= 0;
            else if(EN)
                Q <= ~Q;
            else;
        end  
    
    endmodule

    【2】取中值模块我们采用三分法的原理,对于每一行进行比较,然后再通过一轮比较得到最终结果。对于9个数字取中位数一共只用到了21个两两比较器。具体的算法如下图中所示:

    比较器模块的代码如下:

    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    
    // 
    //////////////////////////////////////////////////////////////////////////////////
    
    
    module median_comparator(
        A,B,C,D,E,F,G,H,I,
        median
        );
        input [7:0]A,B,C,D,E,F,G,H,I;
        output [7:0]median;
        wire [7:0]max1,max2,max3,med1,med2,med3,min1,min2,min3;
        wire [7:0]max_2,med_2,min_2;
        //第一层
        comparator_3_3 mycomparator_3_3_1(A,B,C,max1,med1,min1);
        comparator_3_3 mycomparator_3_3_2(D,E,F,max2,med2,min2);
        comparator_3_3 mycomparator_3_3_3(G,H,I,max3,med3,min3);
        
        //第二层
        comparator_3_3 mycomparator_3_3_max(.A(max1),.B(max2),.C(max3),.max(),.med(),.min(max_2));
        comparator_3_3 mycomparator_3_3_med(.A(med1),.B(med2),.C(med3),.max(),.med(med_2),.min());
        comparator_3_3 mycomparator_3_3_min(.A(min1),.B(min2),.C(min3),.max(min_2),.med(),.min());
        
        //第三层
        comparator_3_3 mycomparator_3_3_median(.A(max_2),.B(med_2),.C(min_2),.max(),.med(median),.min());
    endmodule

    【3】最后是一个自己写的9输入9输出的RAM,可以达到在同一个时钟上升沿来到时,同步提取所有开窗地址对应的数据:

    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    
    //////////////////////////////////////////////////////////////////////////////////
    
    
    module ram(
        CLK,rst_n,a, b, c, d, e, f, g, h, j,
        A,B,C,D,E,F,G,H,J
        );
        input CLK,rst_n;
        input [15:0]a, b, c, d, e, f, g, h, j;//输入地址
        output [7:0]A,B,C,D,E,F,G,H,J;//输出数据
        
        reg [7:0]RAM[65535:0];//深度65536,位宽8
        
        always @(posedge CLK or negedge rst_n)
            begin
                if (!rst_n) 
                    begin
                        A <= 8'b0;
                        B <= 8'b0;
                        C <= 8'b0;
                        D <= 8'b0;
                        E <= 8'b0;
                        F <= 8'b0;
                        G <= 8'b0;
                        H <= 8'b0;
                        J <= 8'b0;
                    end
                else 
                    A <= RAM[a];
                    B <= RAM[b];
                    C <= RAM[c];
                    D <= RAM[d];
                    E <= RAM[e];
                    F <= RAM[f];
                    G <= RAM[g];
                    H <= RAM[h];
                    J <= RAM[j];
            end
    endmodule

    最终我们在Matlab中手动对一幅图像添加噪声,然后再分别比较Matlab自带的中值滤波器滤波后的结果,与我们的硬件实现的中值滤波器的结果。结果比较如下:

    可以看出,硬件实现的中值滤波器与Matlab自带的中值滤波效果无差别,最终两者的像素值平均值之差为0.034,误差小于0.1%,因此可以视为高度有效的硬件实现。

  • 相关阅读:
    activeMq-1 快速入门
    netty2 案例:数据通信
    SQL学习分享之数据链接(二)
    SQL学习 (一)
    CSS的定位重叠
    CSS 伪类 学习
    Jmeter 初学(三)
    玩转codeacademy (三)
    玩转codecademy (二)
    玩转codecademy(首次体会对象的乐趣) (一)
  • 原文地址:https://www.cnblogs.com/olivermahout/p/9346353.html
Copyright © 2020-2023  润新知