zoukankan      html  css  js  c++  java
  • verilog实现中值滤波

    前言

      项目需要,想要实现算法中的其中一步即中值滤波,同时,因为图像处理部分中值滤波相对来说还是比较简单的,将中值滤波的硬件实现作为进入FPGA领域的第一次尝试。虽然说网上有较多关于中值滤波的文档,可是说实话,其一没有详细地讲解实现方法及原因,其二没有关于完整过程的叙述,其三有些网站上有代码但是下载下来几乎没有用,因为你根本看不明白,俗话说得好,吃别人嚼过的馍真tm的没味儿还会难受。所以,还是需要自己静下心来分析原理、设计模块、编写实现以及仿真调试。对于FPGA新手来说,前三部分还能自己慢慢摸索,最后一步真的完全无措,这真的需要经验积累呀,那是你没办法搞定的,你觉得明明正确的语句可是在这里就是不行,就是不能这样来实现,只能先向大神求救,在这里真的要谢谢项目组的colleague,在他那里也学到很多FPGA的实现方法和注意事项。

     其实刚学习FPGA那会儿,先看的语法知识,感觉很简单,后来用的时候发现还是需要查找翻书,所以只有能够在实践过程中熟练应用才说明真正地掌握,宝宝还差得远呢!那些简单的数电的组合逻辑模块看得很明白,也很简单,毕竟本渣大学的数电也不是白学的!可是,一涉及到项目特别是搞图像算法的,感觉就晕头转向,茫然无措。其实宝宝还是很想在图像处理这个方向好好努力呢。偶然看到FPGA的学习网站黑金,网站上有很多学习资料,而且很适合软件转到FPGA的进行学习,宝宝就是看了他们的一些书之后慢慢会进行一些编程实现,真的要谢谢他们,非常非常期待他们的图像处理书籍的完成。

    实现步骤

    1.查看了中值滤波实现相关的网站和paper;

    2.按照某篇paper的设计思想进行编程实现;

    3.对各个模块进行语法检查、波形仿真、时序设计、调试验证;

    4.与matlab的中值滤波结果进行比较。

    实现过程:

    1.查看了中值滤波实现相关的网站和paper;

    在网上看了很多中值滤波的设计,也有一些代码可以下载,也有一片讲解的,只是感觉讲解的比较模糊而且不完整,最后看了几篇硕士论文,论文竟然主要做了中值滤波的工作,发现了一些设计思路,然后就按照自己的想法进行设计。

    2.按照某篇paper的设计思想进行编程实现;

    整个中值滤波模块分为几个小的模块:3*3窗口生成模块、计数器控制模块、3*3中值滤波模块、顶层模块以及最后的测试模块testbench的编写。

    整个框架的设计如下图所示(使用visio画的框架图):

     

    各个模块的设计:

    1)ROM IP核的生成,用于存储原始灰度图像的数据。

    可以参考使用matlab和ISE 创建并仿真ROM IP核

    使用matlab生成.coe图像数据文件,然后使用Xilinx ISE工具将.coe文件添加到ROM核进行数据初始化,按步骤得到ROM模块,参考生成的.v文件在顶层模块直接调用即可。

      rom_512by512 rom_512by512_inst
      (
        .clka(CLK),          //input clka;
        .addra(rom_addr),   //input-from 
        .douta(rom_data)     //output-to 
      );
    View Code

    注意ROM的存储空间的大小;

    2)3*3窗口生成模块,用于生成滤波的滑动窗口,得到窗口内的所有元素数据。

    功能:

    (1)根据中心像素点得到所在其所在的行、列位置;

    (2)根据该模块的开始信号设计得到获取数据的有效时间序列;

    (3)在读取数据的有效时序内,得到窗口内的所有元素数据;

    (4)窗口数据的获取按照一定的时序顺序来获得,类似于黑金推荐的“仿顺序操作”,这个比较适合my style;不过后来发现调试的过程中被项目组的硬件人员改动了一些,甚至说不好,感觉可能是本人还没有理解掌握吃透“仿顺序操作”的精髓吧。

    (5)根据中心像素点的行、列位置信息得到每个窗口元素的ROM地址,根据某一时刻ROM地址,下一时刻调用ROM模块得到对应的元素数据,下一时刻将数据锁存,然后再读取该地址的数据;所以要注意地址和数据的获取不是在同一时刻,而是需要延迟两个时刻;

    (6)还需要注意的是图像的边界问题的特殊化处理;一般图像处理都会遇到边界问题,这个需要谨慎;

    (7)对matlab的中值滤波函数medfilt2原理的深入掌握对我们编写这一模块非常重要。matlab并没有主要过程的代码,看注释默认情况下边界元素设置为0,这也可以通过结果反推回去发现的。

      1 `timescale 1ns / 1ps
      2 //////////////////////////////////////////////////////////////////////////////////
      3 // Company: 
      4 // Engineer: 
      5 // 
      6 // Create Date:    09:27:48 05/18/2016 
      7 // Design Name: 
      8 // Module Name:    win3by3_gen 
      9 // Project Name: 
     10 // Target Devices: 
     11 // Tool versions: 
     12 // Description: 
     13 //
     14 // Dependencies: 
     15 //
     16 // Revision: 
     17 // Revision 0.01 - File Created
     18 // Additional Comments: 
     19 //
     20 //////////////////////////////////////////////////////////////////////////////////
     21 module win3by3_gen(
     22   CLK, 
     23   RSTn,
     24   center_pix_sig,
     25   cols,   // the column numbers of the input image
     26   rows,
     27   rom_data_win,   //input-from U1; 
     28   column_addr_sig,    //input-from U3; //output [9 : 0] addra; 
     29   row_addr_sig,         //input-from U3; //output [9 : 0] addra;
     30   rom_addr_sig,            //output-to U1; 
     31   data_out0,           //output-to U4;
     32   data_out1,
     33   data_out2,
     34   data_out3,
     35   data_out4,
     36   data_out5,
     37   data_out6,
     38   data_out7,
     39   data_out8,
     40   win_data_done_sig            //output-to U4/U3;complete the win data;
     41     );
     42 
     43   input CLK; 
     44   input RSTn;
     45   input [7:0] rom_data_win;
     46   input [9:0] cols;
     47   input [9:0] rows;
     48   input center_pix_sig;  // 
     49   input [9:0] column_addr_sig;
     50   input [9:0] row_addr_sig;
     51   
     52   output [7:0] data_out0;           //output-to U4;
     53   output [7:0] data_out1;
     54   output [7:0] data_out2;
     55   output [7:0] data_out3;
     56   output [7:0] data_out4;
     57   output [7:0] data_out5;
     58   output [7:0] data_out6;
     59   output [7:0] data_out7;
     60   output [7:0] data_out8;
     61   output [17:0] rom_addr_sig;
     62   output win_data_done_sig;
     63   
     64 /******************************************************************************************************************************/ 
     65   
     66   reg [9:0] m;
     67   
     68   always @ ( posedge CLK or negedge RSTn )
     69     if ( !RSTn )
     70        m <= 10'd1;
     71      else if (  center_pix_sig )
     72        m <= row_addr_sig[9:0];  
     73        
     74   /******************************************************************************************************************************/ 
     75   
     76   reg [9:0] n;
     77   
     78   always @ ( posedge CLK or negedge RSTn )
     79     if ( !RSTn )
     80        n <= 10'd1;
     81      else if (  center_pix_sig )
     82        n <= column_addr_sig[9:0];    
     83         
     84   /*****************************************************************************************************************************/ 
     85   
     86   reg [3:0] i; 
     87   reg isWinDone;
     88   reg [17:0] rom_addr;
     89   reg [7:0] a11;
     90   reg [7:0] a12;
     91   reg [7:0] a13;
     92   reg [7:0] a21;
     93   reg [7:0] a22;
     94   reg [7:0] a23;
     95   reg [7:0] a31;
     96   reg [7:0] a32;
     97   reg [7:0] a33;
     98      
     99 /*****************************************************************************************************************************/ 
    100 
    101 reg get_9point_vld;
    102 
    103 always @ ( posedge CLK or negedge RSTn )
    104     if (!RSTn)
    105            get_9point_vld <= 1'b0;
    106      else if ( center_pix_sig )  
    107             get_9point_vld <= 1'b1;
    108      else if ( i==4'd10 ) 
    109             get_9point_vld <= 1'b0;
    110 
    111 
    112 always @ ( posedge CLK or negedge RSTn )
    113     if ( !RSTn )
    114            isWinDone <= 1'b0;
    115      else if ( i==4'd10 )  
    116             isWinDone <= 1'b1;
    117      else 
    118             isWinDone <= 1'b0;
    119 
    120 
    121 
    122 always @ ( posedge CLK or negedge RSTn )
    123     if ( !RSTn )
    124            i <= 4'd0;
    125      else if (i == 4'd10)  
    126             i <= 4'd0;
    127      else if ( get_9point_vld ) 
    128             i <= i + 1'b1;
    129 
    130 
    131 
    132         
    133 always @ ( posedge CLK or negedge RSTn )
    134     if (!RSTn) 
    135             rom_addr <= 0;
    136      else if ( get_9point_vld)
    137        case (i)
    138           4'd0:
    139             if(!(m==1 || n==1)) rom_addr <= (m-2)*cols + (n-1) -1;   
    140           
    141           4'd1:
    142           if(!(m==1 )) rom_addr <= (m-2)*cols + n -1; 
    143            
    144           4'd2:
    145             if(!(m==1 || n==cols)) rom_addr <= (m-2)*cols + (n+1) -1; 
    146           
    147           4'd3:
    148             if(!(n==1)) rom_addr <= (m-1)*cols + (n-1) -1; 
    149           
    150           4'd4:
    151             rom_addr <= (m-1)*cols + n -1; 
    152           
    153           4'd5:
    154             if(!(n==cols)) rom_addr <= (m-1)*cols + (n+1) -1; 
    155           
    156           4'd6:
    157             if(!(m==cols || n==1)) rom_addr <= m*cols + (n-1) -1; 
    158           
    159           4'd7:
    160             if(!(m==cols)) rom_addr <= m*cols + n -1; 
    161           
    162           4'd8:
    163             if(!(m==cols || n==cols)) rom_addr <= m*cols + (n+1) -1; 
    164              
    165           default:;
    166 
    167         endcase
    168         
    169 always @ ( posedge CLK or negedge RSTn )
    170     if (!RSTn)
    171        begin
    172           a11 <= 0; 
    173           a12 <= 0; 
    174           a13 <= 0; 
    175           a21 <= 0; 
    176           a22 <= 0; 
    177           a23 <= 0; 
    178           a31 <= 0; 
    179           a32 <= 0;
    180           a33 <= 0;
    181         end
    182      else if ( get_9point_vld )
    183      
    184        case (i)
    185   
    186           4'd2:
    187           if ( m==1 || n==1 ) 
    188                 a11 <= 0;     
    189           else 
    190                 a11 <= rom_data_win;      
    191           
    192           4'd3:
    193           if ( m==1 )  a12 <= 0; 
    194           else a12 <= rom_data_win;       
    195           
    196           4'd4:
    197           if ( m==1 || n==cols ) a13 <= 0; 
    198           else a13 <= rom_data_win; 
    199           
    200           4'd5:
    201           if ( n==1 ) a21 <= 0; 
    202           else  a21 <= rom_data_win; 
    203           
    204           4'd6:
    205           a22 <= rom_data_win; 
    206                     
    207           4'd7:
    208           if ( n==cols ) a23 <= 0; 
    209           else a23 <= rom_data_win;
    210           
    211           4'd8:
    212           if ( m==cols || n==1 ) a31 <= 0;
    213           else a31 <= rom_data_win;  
    214           
    215           4'd9:
    216           if ( m==cols ) a32 <= 0;
    217           else a32 <= rom_data_win;
    218           
    219           4'd10:
    220           if ( m==cols || n==cols ) a33 <= 0; 
    221           else a33 <= rom_data_win;  
    222           
    223           default:;
    224           
    225       endcase
    226   
    227 /**********************************************************************************************/
    228   
    229   assign win_data_done_sig = isWinDone;
    230   assign rom_addr_sig = rom_addr;
    231   
    232   assign data_out0 = a11;
    233   assign data_out1 = a12;
    234   assign data_out2 = a13;
    235   assign data_out3 = a21;
    236   assign data_out4 = a22;
    237   assign data_out5 = a23;
    238   assign data_out6 = a31;
    239   assign data_out7 = a32;
    240   assign data_out8 = a33;
    241   
    242 /**********************************************************************************************/ 
    243  
    244 endmodule
    View Code

     

    3)计数器控制模块,主要用于获得中心像素点的地址信息。

    (1)系统模块开始信号之后开始获取第一个中心像素点,注意初始化信号值和系统开始的信号值的区别;

    (2)该时刻得到的的数据将在下一个时刻产生结果,该时刻的数据并没有改变;

    (3)注意中心像素点的行、列位置信息的计算;

    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    // Company: 
    // Engineer: 
    // 
    // Create Date:    09:28:59 05/18/2016 
    // Design Name: 
    // Module Name:    counter_ctrl 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //////////////////////////////////////////////////////////////////////////////////
    module counter_ctrl(
      CLK,
      RSTn,
      start_sig,  //input-from top
      nxt_pix_sig,     //input-from --start next center point pixel
      cols,                          
      column_addr_sig,  //output
      row_addr_sig,     //output-to 
      pix_done_sig   //output-to
        );
         
      input CLK;
      input RSTn;
      input start_sig;
      input nxt_pix_sig;
      input [9:0] cols;
      
      output pix_done_sig;  
      output [9:0] column_addr_sig;    
      output [9:0] row_addr_sig;   
      
     /***********************************************************************************************/
      
      reg isCtrlDone;
      //reg isWinStart;
      reg [17:0] imk;   //The k-th pixel of the image
      reg [9:0] row_addr;  // The row of the centeral pixel
      reg [9:0] column_addr;   // The column of the centeral pixel
      
      reg start_sig_d;
      
      wire start_sig_rising_vld;
      
       always @ (posedge CLK or negedge RSTn)   //Asynchronous reset  
        if (!RSTn)
            start_sig_d <= 0;
         else 
            start_sig_d <= start_sig;
      
      assign start_sig_rising_vld = start_sig & (~start_sig_d);
      
      always @ (posedge CLK or negedge RSTn)   //Asynchronous reset
        if (!RSTn)
           begin  
            imk <= 18'b0; 
            column_addr <= 10'b0; 
            row_addr <= 10'b0;
            isCtrlDone <= 1'b0;    
            end
         else if (start_sig_rising_vld)
           begin  
            imk <= 18'b1; 
            column_addr <= 10'b1; 
            row_addr <= 10'b1;
            isCtrlDone <= 1'b1;    
            end    
         else if ( nxt_pix_sig )
           begin  
              imk <= imk + 1'b1;
              row_addr <= imk / cols + 1;   
              column_addr <= imk % cols + 1;   
            isCtrlDone <= 1'b1; 
            end
         else isCtrlDone <= 1'b0;    
              
    /*****************************************************************************************/
      
      assign row_addr_sig = row_addr;
      assign column_addr_sig = column_addr;
      assign pix_done_sig = isCtrlDone;
      
    /*****************************************************************************************/
    endmodule
    View Code

     

    4) 3*3中值滤波模块

    功能:得到某一中心像素点的3*3滑窗区域的灰度值的中值,作为中心像素点的值;

    中值滤波原理,网上有很多,大家可以查看一下。

    本项目采用的是快速中值滤波的方法。

    (1)若是3*3窗口生成模块完成之后就计算下一个中心像素点,需要将该中心像素点的窗口元素锁存起来,以防计算过程中将这些元素掩盖,不能正确进行中值滤波的计算;

    always @ ( posedge CLK or negedge RSTn )
        if (!RSTn)
          begin
                a11 <= 0;
                a12 <= 0;
                a13 <= 0;
                a21 <= 0;
                a22 <= 0;
                a23 <= 0;
                a31 <= 0;
                a32 <= 0;
                a33 <= 0;
            end
         else if (win_data_sig)
           begin
                a11 <= data_in0;
                a12 <= data_in1;
                a13 <= data_in2;
                a21 <= data_in3;
                a22 <= data_in4;
                a23 <= data_in5;
                a31 <= data_in6;
                a32 <= data_in7;
                a33 <= data_in8;
            end
    View Code

    (2)需要在时序的有效区域内进行计算,怎么设计信号的有效性;

      always @ ( posedge CLK or negedge RSTn )
        if (!RSTn)
                cal_vld <= 1'b0;
         else if( win_data_sig )
                cal_vld <= 1'b1;
         else if( i==3'd3 )
                cal_vld <= 0;    
    View Code

    (3)仿顺序操作可以分开进行;每一个时刻只进行一个操作,这样可能更明了(代码中没有这样做);

      always @ ( posedge CLK or negedge RSTn )
        if (!RSTn)
                i <= 3'd0;
         else if( cal_vld & ( i!=3 ) )
                i <= i + 1;
         else 
                i <= 0;
    View Code

    (4)verilog编程调用函数的方法,指出输入信号,函数内可以使用其他定义声明的信号,最后的输出信号作为调用函数的结果(突然想起来,如果输出信号有多个元素呢,又该怎么办呢?大家可以想想);

    function [7:0] max;//if the data is signed number, please add the char signed behind key function; 
      input [7:0] a, b, c;
      begin
        max = (((a >= b) ? a : b) >= c ) ?  ((a >= b) ? a : b) : c;
      end
    endfunction
    View Code

    该模块的代码:

    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    // Company: 
    // Engineer: 
    // 
    // Create Date:    09:28:20 05/18/2016 
    // Design Name: 
    // Module Name:    medfilter3by3 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //////////////////////////////////////////////////////////////////////////////////
    module medfilter3by3(
      CLK,
      RSTn,
      win_data_sig,  //input-from module of win3by3_gen; 
      medfilt_done_sig,   //output-to top;
      data_in0,        //input-from module of win3by3_gen;
      data_in1,
      data_in2,
      data_in3,
      data_in4,
      data_in5,
      data_in6,
      data_in7,
      data_in8,
      medfilt_data_out    //output-to top; 
        );
    
      input CLK;
      input RSTn;
      input win_data_sig;
      input [7:0] data_in0;           //output-to ;
      input [7:0] data_in1;
      input [7:0] data_in2;
      input [7:0] data_in3;
      input [7:0] data_in4;
      input [7:0] data_in5;
      input [7:0] data_in6;
      input [7:0] data_in7;
      input [7:0] data_in8;
    
      output medfilt_done_sig;
      output [7:0] medfilt_data_out;
    
    /******************************************************************************/ 
      reg [7:0] a11;
      reg [7:0] a12;
      reg [7:0] a13;
      reg [7:0] a21;
      reg [7:0] a22;
      reg [7:0] a23;
      reg [7:0] a31;
      reg [7:0] a32;
      reg [7:0] a33;
      
      reg [7:0] b11;
      reg [7:0] b12;
      reg [7:0] b13;
      reg [7:0] b21;
      reg [7:0] b22;
      reg [7:0] b23;
      reg [7:0] b31;
      reg [7:0] b32;
      reg [7:0] b33;
      
      reg [7:0] c11;
      reg [7:0] c12;
      reg [7:0] c13;
      reg [7:0] c21;
      reg [7:0] c22;
      reg [7:0] c23;
      reg [7:0] c31;
      reg [7:0] c32;
      reg [7:0] c33;
      
      reg [2:0] i;
      reg [7:0] medfilt_data;
      reg filt_done;
      
      reg cal_vld;
    
     
    always @ ( posedge CLK or negedge RSTn )
        if (!RSTn)
          begin
                a11 <= 0;
                a12 <= 0;
                a13 <= 0;
                a21 <= 0;
                a22 <= 0;
                a23 <= 0;
                a31 <= 0;
                a32 <= 0;
                a33 <= 0;
            end
         else if (win_data_sig)
           begin
                a11 <= data_in0;
                a12 <= data_in1;
                a13 <= data_in2;
                a21 <= data_in3;
                a22 <= data_in4;
                a23 <= data_in5;
                a31 <= data_in6;
                a32 <= data_in7;
                a33 <= data_in8;
            end
      
      always @ ( posedge CLK or negedge RSTn )
        if (!RSTn)
                i <= 3'd0;
         else if( cal_vld & ( i!=3 ) )
                i <= i + 1;
         else 
                i <= 0;
                
      always @ ( posedge CLK or negedge RSTn )
        if (!RSTn)
                cal_vld <= 1'b0;
         else if( win_data_sig )
                cal_vld <= 1'b1;
         else if( i==3'd3 )
                cal_vld <= 0;            
    
    
      always @ ( posedge CLK or negedge RSTn )
        if (!RSTn)
           begin
            filt_done <= 1'b0;
            b11 <= 0;
            b12 <= 0;
            b13 <= 0;
            b21 <= 0;
            b22 <= 0;
            b23 <= 0;
            b31 <= 0;
            b32 <= 0;
            b33 <= 0;
            c11 <= 0;
            c12 <= 0;
            c13 <= 0;
            c21 <= 0;
            c22 <= 0;
            c23 <= 0;
            c31 <= 0;
            c32 <= 0;
            c33 <= 0;
            medfilt_data <= 0;
            end
         else if( cal_vld )
           case(i)
              3'd0:
                begin
                 b11 <= max(a11, a21, a31); 
                 b12 <= max(a12, a22, a32); 
                 b13 <= max(a13, a23, a33);
                 b21 <= med(a11, a21, a31); 
                 b22 <= med(a12, a22, a32); 
                 b23 <= med(a13, a23, a33);
                 b31 <= min(a11, a21, a31); 
                 b32 <= min(a12, a22, a32); 
                 b33 <= min(a13, a23, a33);
                end
                 
              3'd1:
                begin
                 c31 <= max(b31, b32, b33);
                 c22 <= med(b21, b22, b23);
                 c13 <= min(b11, b12, b13); 
                 end
              
              3'd2:
                 begin
                 medfilt_data <= med(c13, c22, c31);
                 filt_done<=1'b1;
                 end
     
              3'd3:
                 filt_done <= 1'b0; 
    
                default:;
    
            endcase
            
    /************************************************************************************/ 
    
    function [7:0] max;//if the data is signed number, please add the char signed behind key function; 
      input [7:0] a, b, c;
      begin
        max = (((a >= b) ? a : b) >= c ) ?  ((a >= b) ? a : b) : c;
      end
    endfunction
    
    function [7:0] med;
      input [7:0] a, b, c;
      begin
         med = a < b ? (b < c ? b : a < c ? c : a) : (b > c ? b : a > c ? c : a);
      end
    endfunction
    
    function [7:0] min;
      input [7:0] a, b, c;
      begin
         min= (((a <= b) ? a : b) <= c ) ?  ((a <= b) ? a : b) : c;
      end
    endfunction
              
    /************************************************************************************/ 
     
      assign medfilt_data_out = medfilt_data;
      assign medfilt_done_sig = filt_done;
    
    /**********************************************************************************/ 
     
    endmodule
    View Code

    5)顶层模块,用于将低层的各个功能/控制模块衔接起来,得到结果;

    注意输入输出信号,以及不同模块之间是如何进行连线的。

    信号的名称尽量有其特别的意义,不要重复使用同一个信号名称,容易造成混乱;

    区别wire和reg类型数据的使用情况;

    `timescale 1ns / 1ps
    //////////////////////////////////////////////////////////////////////////////////
    // Company: 
    // Engineer: 
    // 
    // Create Date:    09:29:33 05/18/2016 
    // Design Name: 
    // Module Name:    medfilter2 
    // Project Name: 
    // Target Devices: 
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //////////////////////////////////////////////////////////////////////////////////
    
    module medfilter2  
    (
      CLK, 
      RSTn,
      Start_sig,
      Done_sig,
      Data_out
    );
         
      input CLK;
      input RSTn;
      input Start_sig;
      output Done_sig;
      output [7:0] Data_out;
      
      /********************************************************************/
      
      wire [17:0] rom_addr; //
      wire [7:0] rom_data;  // 
    
      rom_512by512 rom_512by512_inst
      (
        .clka(CLK),          //input clka;
        .addra(rom_addr),   //input-from; 
        .douta(rom_data)     //output-to ; 
      );
    
      /******************************************************************************/
      
      //wire [7:0] win_data[8:0];
      wire [7:0] data_out0;           //output-to ;
      wire [7:0] data_out1;
      wire [7:0] data_out2;
      wire [7:0] data_out3;
      wire [7:0] data_out4;
      wire [7:0] data_out5;
      wire [7:0] data_out6;
      wire [7:0] data_out7;
      wire [7:0] data_out8;
      wire win_done_sig;
      
     wire [9:0] column_addr_sig;
     wire [9:0] row_addr_sig;
     
      win3by3_gen win3by3_gen_inst (
      .CLK(CLK), 
      .RSTn(RSTn),
      .center_pix_sig(win_start_sig), //input-from ; 
      .cols(10'd512),   // the column numbers of the input image
      .rows(10'd512),   // the row numbers of the input image
      .rom_data_win(rom_data),    //input-from ; 
      .column_addr_sig(column_addr_sig),    //input-from ; //output [9 : 0] addra; 
      .row_addr_sig(row_addr_sig),         //input-from ; //output [9 : 0] addra;
      .rom_addr_sig(rom_addr),   //output-to ; 
      .data_out0(data_out0),           //output-to ;
      .data_out1(data_out1),
      .data_out2(data_out2),
      .data_out3(data_out3),
      .data_out4(data_out4),
      .data_out5(data_out5),
      .data_out6(data_out6),
      .data_out7(data_out7),
      .data_out8(data_out8),
      .win_data_done_sig(win_done_sig)  //output-to U4/U3; 
        );
      
      /******************************************************************************/ 
       
      counter_ctrl counter_ctrl_inst(
      .CLK(CLK),
      .RSTn(RSTn),
      .start_sig(Start_sig),  //input-from top 
      .nxt_pix_sig(win_done_sig),  //input-from 
      .cols(10'd512), 
      .column_addr_sig(column_addr_sig),  //output-to 
      .row_addr_sig(row_addr_sig),     //output-to 
      .pix_done_sig(win_start_sig)   //output-to    
      );
      
    /*****************************************************************************/
     
     wire medfilt_done_sig;
     wire [7:0] medfilt_data_wire;
     
     medfilter3by3 medfilter3by3_inst
    (
      .CLK(CLK),
      .RSTn(RSTn), 
      .win_data_sig(win_done_sig),  //input-from; 
      .medfilt_done_sig(medfilt_done_sig), //output-to;
      .data_in0(data_out0),        //input-from ;
      .data_in1(data_out1),
      .data_in2(data_out2),
      .data_in3(data_out3),
      .data_in4(data_out4),
      .data_in5(data_out5),
      .data_in6(data_out6),
      .data_in7(data_out7),
      .data_in8(data_out8),
      .medfilt_data_out(medfilt_data_wire)     //output-to top; 
    ); 
    
    /*********************************************************************/
     wire Done_sig;
     wire [7:0] Data_out;
     assign Done_sig = medfilt_done_sig;
     assign Data_out = medfilt_data_wire;
     
    /**********************************************************************/
    endmodule
    View Code

    6)测试模块

    如何将数据写入文件,需要定义文件的名称和类型;

    integer fouti;

    需要在初始化部分打开文件:

    fouti = $fopen("medfilter2_re.txt");

    代码如下:

    `timescale 1ns / 1ps
    
    ////////////////////////////////////////////////////////////////////////////////
    // Company: 
    // Engineer:
    //
    // Create Date:   13:57:14 05/24/2016
    // Design Name:   medfilter2
    // Module Name:   E:/stereo_match_pro/stereo_match_FPGA0518/medfilter_tb.v
    // Project Name:  stereo_match_FPGA0518
    // Target Device:  
    // Tool versions:  
    // Description: 
    //
    // Verilog Test Fixture created by ISE for module: medfilter2
    //
    // Dependencies:
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    ////////////////////////////////////////////////////////////////////////////////
    
    module medfilter_tb;
    
        // Inputs
        reg CLK;
        reg RSTn;
        reg Start_sig;
        reg [18:0] pix_cnt;   //512*512=262144=100,0000,0000,0000,0000
        
    
        // Outputs
        wire Done_sig;
        wire [7:0] Data_out;
        integer fouti;
            
    
        // Instantiate the Unit Under Test (UUT)
        medfilter2 uut (
            .CLK(CLK), 
            .RSTn(RSTn), 
            .Start_sig(Start_sig), 
            .Done_sig(Done_sig), 
            .Data_out(Data_out)
        );
        
        //assign Data_out = 0;
       //assign Done_sig = 0;
    
        initial begin
            // Initialize Inputs
            CLK = 0;
            RSTn = 1;
            Start_sig = 0;
            
            fouti = $fopen("medfilter2_re.txt");
    
            // Wait 100 ns for global reset to finish
            #100;   // To reset the system
            // Add stimulus here
            RSTn = 0;
            Start_sig = 1;
            pix_cnt = 0;
            
            #100;   // To start the system
            // Add stimulus here
            RSTn = 1;
            pix_cnt = 1;
        
        end
        
        always #10 CLK = ~CLK;
        
        always@(posedge CLK)
        begin
            if(Done_sig)
               pix_cnt <= pix_cnt + 1;
        end
        
        always@(posedge CLK)
        begin
            if(pix_cnt == 19'd262145)
               begin 
                  Start_sig <= 0; 
                  $display("Image Medfilter Completed!
    ");
                  $display("The all time is %d 
    ",$time);
                  $stop;
                end
        end
    
        
    
        always@(posedge CLK)
        begin
            if(Done_sig)
                begin
                  $fwrite(fouti, "%d", Data_out, "
    ");
                  $display("%d",pix_cnt);
                end
        end
         
    endmodule
    View Code

    整体的代码就是这样的。

     3.对各个模块进行语法检查、波形仿真、时序设计、调试验证;

     本人觉得原理清楚之后按部就班的编写代码还好,只是刚接触波形仿真和调试的时候是真心不顺心,还好有同事帮忙调试;在调试的过程中其实会学习到很多东西,很多经验,以及很简单的但你之前就是不知道的知识,这就是一个实践的过程,有时候你根本不知道错误在哪里,这怎么会是错误的呢,为什么不可以这样写,我觉得这样写才是正确的,这些就是在调试过程中本人的真实心情写照呀。可是,没有那么多为什么,verilog就是这样编程的,只是你不知道而已!这才是最伤人的,因为你不知道!

       仿真调试的过程中遇到的问题以及解决方法有空专门写一篇(其实本博也写了一些)。调试的过程中最好是一个一个模块的测试,特别是关键信号的数值,最好搞懂整体模块和各个模块的时序设计过程,推荐使用TimeDesigner进行波形的设计,没有软件的可以联系博主;另外还需要有关联的两个甚至多个不同模块信号的交叉仿真验证。

    4.与matlab的中值滤波结果进行比较

    使用matlab编程基于自带的中值滤波函数得到处理之后的图像与数据,并将verilog得到的滤波数据转换为图像,将二者进行比较。

    使用matlab自带的中值滤波函数medfilt2生成原图像的灰度图像的滤波数据;

    % mcode to median filter for one jpg image, and create a image data file
    src = imread('lena.jpg');
    gray = rgb2gray(src);
    
    medfilt2im = medfilt2( gray );
    [m, n] = size( medfilt2im );                  % m行 n列
    
    N = m*n;                               %%数据的长度,即存储器深度。
    word_len = 8;                          %%每个单元的占据的位数,需自己设定
    lena_gray = reshape(gray', 1, N);% 1行N列
    lena_medfilt = reshape(medfilt2im', 1, N);% 1行N列
    
    fid_gray=fopen('lena_gray.txt', 'wt');       %打开文件
    fid_medfilt=fopen('lena_medfilt.txt', 'wt');       %打开文件
    % fprintf(fid, 'MEMORY_INITIALIZATION_RADIX=16;
    ');
    % fprintf(fid, 'MEMORY_INITIALIZATION_VECTOR=
    ');
    
    
    for i = 1 : N-1
        fprintf(fid_gray, '%d,
    ', lena_gray(i));%使用%x表示十六进制数
    end
    fprintf(fid_gray, '%d;
    ', data(N));                 %%输出结尾,每个数据后面用逗号或者空格或者换行符隔开,最后一个数据后面加分号
    fclose(fid_gray);                            %%关闭文件
    
    for i = 1 : N-1
        fprintf(fid_medfilt, '%d,
    ', lena_medfilt(i));%使用%x表示十六进制数
    end
    fprintf(fid_medfilt, '%d;
    ', lena_medfilt(N));                 %%输出结尾,每个数据后面用逗号或者空格或者换行符隔开,最后一个数据后面加分号
    fclose(fid_medfilt);                            %%关闭文件
    

    将medfilt2函数和verilog产生的滤波数据转换为图像,并与matlab直接产生的滤波图像进行对比,代码如下:

    % code to create image data from txt file
    clc; 
    clear all; 
    close all;
    I_rgb = imread('lena.jpg');
    subplot(2, 3, 1), imshow(I_rgb), title('lena-rgb')
    
    I_gray = rgb2gray(I_rgb);
    subplot(2, 3, 2), imshow(I_gray), title('lena-gray')
    
    medfilt_m_load = load('.lena_medfilt.txt');
    %medfilt_m_load = load('.lena.coe');
    medfilt_v_load = load('.medfilter2_reV1.txt'); % verilog 产生的中值滤波之后数据
    
    medfilt2im = medfilt2( I_gray );
    subplot(2, 3, 3), imshow(medfilt2im), title('lena-medfilt2')
    
    m = 512;
    n = 512;
    medfilt_m = reshape(medfilt_m_load, m, n);
    medfilt_v = reshape(medfilt_v_load, m, n);
    medfilt_m = uint8(medfilt_m');
    medfilt_v = uint8(medfilt_v');
    
    aa = medfilt2im - medfilt_m;
    bb = medfilt2im - medfilt_v;
    cc = medfilt_m - medfilt_v;
    
    subplot(2, 3, 5), imshow(medfilt_m), title('medfilt-matlab');
    subplot(2, 3, 6), imshow(medfilt_v), title('medfilt-verilog');
    

    显示的结果如下图所示:

    结果:两种滤波产生的图像数据完全一致,不过感觉函数直接产生的图像颜色更深一些,不知道为什么。

    这里需要了解一下medfilt2这个函数的原理。结果数据表明,默认情况下该函数对图像边界采用的是补0的方法进行处理的。

    结论

    中值滤波终于告一段落了!简单的问题还是需要深入进去研究的,实践的过程中你才会发现自己之前了解的东西是多么的浅薄,对已知的知识掌握的是多么的流于表面!

    最后结果的数据还是很让人开心的!

    后记

    20190529

    当初做这部分工作的时候基本是从零开始的,其实当时是非常痛苦的,所以也非常理解那种无助感,最后博主的兴趣也不在这里,浪费了几个月还是回归了自己喜欢的领域,虽然目前还没有什么成就,希望自己一直坚持自己喜欢的事情。关于FPGA这部分内容,后来有colleague是专门做FPGA的,好像是非常非常容易就实现了这个函数,记得只使用了几行代码,具体的细节不太清楚,反正宝宝是真不懂FPGA的世界。真的,还是希望大家能够选择去做自己喜欢的事情。

  • 相关阅读:
    学习进度7
    《机器学习十讲》学习报告六
    《机器学习十讲》学习报告五
    《机器学习十讲》学习报告四
    《机器学习十讲》学习报告三
    华为机试题 仿苹果
    C++ STL 六大组件的交互关系
    C++ STL 源码 阅读
    抽象类和接口的区别
    重载 & 重写 在java 中
  • 原文地址:https://www.cnblogs.com/happyamyhope/p/5577898.html
Copyright © 2011-2022 走看看