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!\n"); $display("The all time is %d \n",$time); $stop; end
    end

    

    always@(posedge CLK) begin
        if(Done_sig) begin $fwrite(fouti, "%d", Data_out, "\n"); $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;\n');
% fprintf(fid, 'MEMORY_INITIALIZATION_VECTOR=\n');


for i = 1 : N-1
    fprintf(fid_gray, '%d,\n', lena_gray(i));%使用%x表示十六進制數
end
fprintf(fid_gray, '%d;\n', data(N));                 %%輸出結尾,每個數據后面用逗號或者空格或者換行符隔開,最后一個數據后面加分號
fclose(fid_gray);                            %%關閉文件

for i = 1 : N-1
    fprintf(fid_medfilt, '%d,\n', lena_medfilt(i));%使用%x表示十六進制數
end
fprintf(fid_medfilt, '%d;\n', 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的世界。真的,還是希望大家能夠選擇去做自己喜歡的事情。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM