中值滤波"/>
verilog实现中值滤波
前言
项目需要,想要实现算法中的其中一步即中值滤波,同时,因为图像处理部分中值滤波相对来说还是比较简单的,将中值滤波的硬件实现作为进入FPGA领域的第一次尝试。虽然说网上有较多关于中值滤波的文档,可是说实话,其一没有详细地讲解实现方法及原因,其二没有关于完整过程的叙述,其三有些网站上有代码但是下载下来几乎没有用,因为你根本看不明白,俗话说得好,吃别人嚼过的馍真tm的没味儿还会难受。所以,还是需要自己静下心来分析原理、设计模块、编写实现以及仿真调试。对于FPGA新手来说,前三部分还能自己慢慢摸索,最后一步真的完全无措,这真的需要经验积累呀,那是你没办法搞定的,你觉得明明正确的语句可是在这里就是不行,就是不能这样来实现,只能先向大神求救,在这里真的要谢谢项目组的zjl,在他那里也学到很多FPGA的实现方法和注意事项。
其实刚学习FPGA那会儿,先看的语法知识,感觉很简单,后来用的时候发现还是需要查找翻书,所以只有能够在实践过程中熟练应用才说明真正地掌握,宝宝还差得远呢!那些简单的数电的组合逻辑模块看得很明白,也很简单,毕竟本渣大学的数电也不是白学的!可是,一涉及到项目特别是搞图像算法的,感觉就晕头转向,茫然无措。其实宝宝还是很想在图像处理这个方向好好努力呢。偶然看到.php,网站上有很多学习资料,而且很适合软件转到FPGA的进行学习,宝宝就是看了他们的一些书之后慢慢会进行一些编程实现,真的要谢谢他们,非常非常期待他们的图像处理书籍的完成。
实现步骤
1.查看了中值滤波实现相关的网站和paper;
2.按照某篇paper的设计思想进行编程实现;
3.对各个模块进行语法检查、波形仿真、时序设计、调试验证;
4.与matlab的中值滤波结果进行比较。
实现过程:
1.查看了中值滤波实现相关的网站和paper;
在网上看了很多中值滤波的设计,也有一些代码可以下载,也有一片讲解的,只是感觉讲解的比较模糊而且不完整,最后看了几篇硕士论文,论文竟然主要做了中值滤波的工作,发现了一些设计思路,然后就按照自己的想法进行设计。
2.按照某篇paper的设计思想进行编程实现;
整个中值滤波模块分为几个小的模块:3*3窗口生成模块、计数器控制模块、3*3中值滤波模块、顶层模块以及最后的测试模块testbench的编写。
整个框架的设计如下图所示(使用visio画的框架图):
各个模块的设计:
1)ROM IP核的生成,用于存储原始灰度图像的数据。
可以参考.html
使用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 / 1ps2 //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 Created18 // Additional Comments: 19 //20 //21 module win3by3_gen(22 CLK, 23 RSTn,24 center_pix_sig,25 cols, // the column numbers of the input image26 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 endmoduleView 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 topnxt_pix_sig, //input-from --start next center point pixelcols, column_addr_sig, //outputrow_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 imagereg [9:0] row_addr; // The row of the centeral pixelreg [9:0] column_addr; // The column of the centeral pixelreg 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 resetif (!RSTn)begin imk <= 18'b0; column_addr <= 10'b0; row_addr <= 10'b0;isCtrlDone <= 1'b0; endelse 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; endelse 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)begina11 <= 0;a12 <= 0;a13 <= 0;a21 <= 0;a22 <= 0;a23 <= 0;a31 <= 0;a32 <= 0;a33 <= 0;endelse if (win_data_sig)begina11 <= 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;beginmax = (((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)begina11 <= 0;a12 <= 0;a13 <= 0;a21 <= 0;a22 <= 0;a23 <= 0;a31 <= 0;a32 <= 0;a33 <= 0;endelse if (win_data_sig)begina11 <= 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;endalways @ ( 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)beginfilt_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;endelse if( cal_vld )case(i)3'd0:beginb11 <= 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);end3'd1:beginc31 <= max(b31, b32, b33);c22 <= med(b21, b22, b23);c13 <= min(b11, b12, b13); end3'd2:beginmedfilt_data <= med(c13, c22, c31);filt_done<=1'b1;end3'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;beginmax = (((a >= b) ? a : b) >= c ) ? ((a >= b) ? a : b) : c;end endfunctionfunction [7:0] med;input [7:0] a, b, c;beginmed = a < b ? (b < c ? b : a < c ? c : a) : (b > c ? b : a > c ? c : a);end endfunctionfunction [7:0] min;input [7:0] a, b, c;beginmin= (((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;// Inputsreg CLK;reg RSTn;reg Start_sig;reg [18:0] pix_cnt; //512*512=262144=100,0000,0000,0000,0000// Outputswire 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 InputsCLK = 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 hereRSTn = 0;Start_sig = 1;pix_cnt = 0;#100; // To start the system// Add stimulus hereRSTn = 1;pix_cnt = 1;endalways #10 CLK = ~CLK;always@(posedge CLK)beginif(Done_sig)pix_cnt <= pix_cnt + 1;endalways@(posedge CLK)beginif(pix_cnt == 19'd262145)begin Start_sig <= 0; $display("Image Medfilter Completed!\n");$display(</span><span style="color: #800000;">"</span><span style="color: #800000;">The all time is %d \n</span><span style="color: #800000;">"</span>,$time);$stop;endendalways@(posedge CLK)beginif(Done_sig)begin$fwrite(fouti, "%d", Data_out, "\n");$display("%d",pix_cnt);endendendmodule
View Code
整体的代码就是这样的。
3.对各个模块进行语法检查、波形仿真、时序设计、调试验证;
本人觉得原理清楚之后按部就班的编写代码还好,只是刚接触波形仿真和调试的时候是真心不顺心,还好有同事帮忙调试;在调试的过程中其实会学习到很多东西,很多经验,以及很简单的但你之前就是不知道的知识,这就是一个实践的过程,有时候你根本不知道错误在哪里,这怎么会是错误的呢,为什么不可以这样写,我觉得这样写才是正确的,这些就是在调试过程中本人的真实心情写照呀。可是,没有那么多为什么,verilog就是这样编程的,只是你不知道而已!这才是最伤人的,因为你不知道!
仿真调试的过程中遇到的问题以及解决方法有空专门写一篇(其实本博也写了一些)。调试的过程中最好是一个一个模块的测试,特别是关键信号的数值,最好搞懂整体模块和各个模块的时序设计过程,推荐使用TimeDesigner进行波形的设计,没有软件的可以联系博主;另外还需要有关联的两个甚至多个不同模块信号的交叉仿真验证。
4.与matlab的中值滤波结果进行比较
使用matlab编程基于自带的中值滤波函数得到处理之后的图像与数据,并将verilog得到的滤波数据转换为图像,将二者进行比较。
使用matlab自带的中值滤波函数medfilt2生成原图像的灰度图像的滤波数据;
+ View Code ?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | % 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直接产生的滤波图像进行对比,代码如下:
+ View Code ?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | % 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的方法进行处理的。
结论
中值滤波终于告一段落了!简单的问题还是需要深入进去研究的,实践的过程中你才会发现自己之前了解的东西是多么的浅薄,对已知的知识掌握的是多么的流于表面!
最后结果的数据还是很让人开心的!
转自:.html
更多推荐
verilog实现中值滤波
发布评论