前言

项目需要,想要实现算法中的其中一步即中值滤波,同时,因为图像处理部分中值滤波相对来说还是比较简单的,将中值滤波的硬件实现作为进入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
);

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

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

功能:

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

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

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

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

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

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

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

 `timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 09:27:48 05/18/2016
// Design Name:
// Module Name: win3by3_gen
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module win3by3_gen(
CLK,
RSTn,
center_pix_sig,
cols, // the column numbers of the input image
rows,
rom_data_win, //input-from U1;
column_addr_sig, //input-from U3; //output [9 : 0] addra;
row_addr_sig, //input-from U3; //output [9 : 0] addra;
rom_addr_sig, //output-to U1;
data_out0, //output-to U4;
data_out1,
data_out2,
data_out3,
data_out4,
data_out5,
data_out6,
data_out7,
data_out8,
win_data_done_sig //output-to U4/U3;complete the win data;
); input CLK;
input RSTn;
input [:] rom_data_win;
input [:] cols;
input [:] rows;
input center_pix_sig; //
input [:] column_addr_sig;
input [:] row_addr_sig; output [:] data_out0; //output-to U4;
output [:] data_out1;
output [:] data_out2;
output [:] data_out3;
output [:] data_out4;
output [:] data_out5;
output [:] data_out6;
output [:] data_out7;
output [:] data_out8;
output [:] rom_addr_sig;
output win_data_done_sig; /******************************************************************************************************************************/ reg [:] m; always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
m <= 'd1;
else if ( center_pix_sig )
m <= row_addr_sig[:]; /******************************************************************************************************************************/ reg [:] n; always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
n <= 'd1;
else if ( center_pix_sig )
n <= column_addr_sig[:]; /*****************************************************************************************************************************/ reg [:] i;
reg isWinDone;
reg [:] rom_addr;
reg [:] a11;
reg [:] a12;
reg [:] a13;
reg [:] a21;
reg [:] a22;
reg [:] a23;
reg [:] a31;
reg [:] a32;
reg [:] a33; /*****************************************************************************************************************************/ reg get_9point_vld; always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
get_9point_vld <= 'b0;
else if ( center_pix_sig )
get_9point_vld <= 'b1;
else if ( i=='d10 )
get_9point_vld <= 'b0; always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
isWinDone <= 'b0;
else if ( i=='d10 )
isWinDone <= 'b1;
else
isWinDone <= 'b0; always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
i <= 'd0;
else if (i == 'd10)
i <= 'd0;
else if ( get_9point_vld )
i <= i + 'b1; always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
rom_addr <= ;
else if ( get_9point_vld)
case (i)
'd0:
if(!(m== || n==)) rom_addr <= (m-)*cols + (n-) -; 'd1:
if(!(m== )) rom_addr <= (m-)*cols + n -; 'd2:
if(!(m== || n==cols)) rom_addr <= (m-)*cols + (n+) -; 'd3:
if(!(n==)) rom_addr <= (m-)*cols + (n-) -; 'd4:
rom_addr <= (m-)*cols + n -; 'd5:
if(!(n==cols)) rom_addr <= (m-)*cols + (n+) -; 'd6:
if(!(m==cols || n==)) rom_addr <= m*cols + (n-) -; 'd7:
if(!(m==cols)) rom_addr <= m*cols + n -; 'd8:
if(!(m==cols || n==cols)) rom_addr <= m*cols + (n+) -; default:; endcase always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
begin
a11 <= ;
a12 <= ;
a13 <= ;
a21 <= ;
a22 <= ;
a23 <= ;
a31 <= ;
a32 <= ;
a33 <= ;
end
else if ( get_9point_vld ) case (i) 'd2:
if ( m== || n== )
a11 <= ;
else
a11 <= rom_data_win; 'd3:
if ( m== ) a12 <= ;
else a12 <= rom_data_win; 'd4:
if ( m== || n==cols ) a13 <= ;
else a13 <= rom_data_win; 'd5:
if ( n== ) a21 <= ;
else a21 <= rom_data_win; 'd6:
a22 <= rom_data_win; 'd7:
if ( n==cols ) a23 <= ;
else a23 <= rom_data_win; 'd8:
if ( m==cols || n== ) a31 <= ;
else a31 <= rom_data_win; 'd9:
if ( m==cols ) a32 <= ;
else a32 <= rom_data_win; 'd10:
if ( m==cols || n==cols ) a33 <= ;
else a33 <= rom_data_win; default:; endcase /**********************************************************************************************/ assign win_data_done_sig = isWinDone;
assign rom_addr_sig = rom_addr; assign data_out0 = a11;
assign data_out1 = a12;
assign data_out2 = a13;
assign data_out3 = a21;
assign data_out4 = a22;
assign data_out5 = a23;
assign data_out6 = a31;
assign data_out7 = a32;
assign data_out8 = a33; /**********************************************************************************************/ endmodule

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 [:] cols; output pix_done_sig;
output [:] column_addr_sig;
output [:] row_addr_sig; /***********************************************************************************************/ reg isCtrlDone;
//reg isWinStart;
reg [:] imk; //The k-th pixel of the image
reg [:] row_addr; // The row of the centeral pixel
reg [:] 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 <= ;
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 <= 'b0;
column_addr <= 'b0;
row_addr <= 'b0;
isCtrlDone <= 'b0;
end
else if (start_sig_rising_vld)
begin
imk <= 'b1;
column_addr <= 'b1;
row_addr <= 'b1;
isCtrlDone <= 'b1;
end
else if ( nxt_pix_sig )
begin
imk <= imk + 'b1;
row_addr <= imk / cols + ;
column_addr <= imk % cols + ;
isCtrlDone <= 'b1;
end
else isCtrlDone <= 'b0; /*****************************************************************************************/ assign row_addr_sig = row_addr;
assign column_addr_sig = column_addr;
assign pix_done_sig = isCtrlDone; /*****************************************************************************************/
endmodule

4) 3*3中值滤波模块

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

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

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

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

always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
begin
a11 <= ;
a12 <= ;
a13 <= ;
a21 <= ;
a22 <= ;
a23 <= ;
a31 <= ;
a32 <= ;
a33 <= ;
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

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

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

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

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

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

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

该模块的代码:

`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 [:] data_in0; //output-to ;
input [:] data_in1;
input [:] data_in2;
input [:] data_in3;
input [:] data_in4;
input [:] data_in5;
input [:] data_in6;
input [:] data_in7;
input [:] data_in8; output medfilt_done_sig;
output [:] medfilt_data_out; /******************************************************************************/
reg [:] a11;
reg [:] a12;
reg [:] a13;
reg [:] a21;
reg [:] a22;
reg [:] a23;
reg [:] a31;
reg [:] a32;
reg [:] a33; reg [:] b11;
reg [:] b12;
reg [:] b13;
reg [:] b21;
reg [:] b22;
reg [:] b23;
reg [:] b31;
reg [:] b32;
reg [:] b33; reg [:] c11;
reg [:] c12;
reg [:] c13;
reg [:] c21;
reg [:] c22;
reg [:] c23;
reg [:] c31;
reg [:] c32;
reg [:] c33; reg [:] i;
reg [:] medfilt_data;
reg filt_done; reg cal_vld; always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
begin
a11 <= ;
a12 <= ;
a13 <= ;
a21 <= ;
a22 <= ;
a23 <= ;
a31 <= ;
a32 <= ;
a33 <= ;
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 <= 'd0;
else if( cal_vld & ( i!= ) )
i <= i + ;
else
i <= ; always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
cal_vld <= 'b0;
else if( win_data_sig )
cal_vld <= 'b1;
else if( i=='d3 )
cal_vld <= ; always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
begin
filt_done <= 'b0;
b11 <= ;
b12 <= ;
b13 <= ;
b21 <= ;
b22 <= ;
b23 <= ;
b31 <= ;
b32 <= ;
b33 <= ;
c11 <= ;
c12 <= ;
c13 <= ;
c21 <= ;
c22 <= ;
c23 <= ;
c31 <= ;
c32 <= ;
c33 <= ;
medfilt_data <= ;
end
else if( cal_vld )
case(i)
'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 'd1:
begin
c31 <= max(b31, b32, b33);
c22 <= med(b21, b22, b23);
c13 <= min(b11, b12, b13);
end 'd2:
begin
medfilt_data <= med(c13, c22, c31);
filt_done<='b1;
end 'd3:
filt_done <= 'b0; default:; endcase /************************************************************************************/ function [:] max;//if the data is signed number, please add the char signed behind key function;
input [:] a, b, c;
begin
max = (((a >= b) ? a : b) >= c ) ? ((a >= b) ? a : b) : c;
end
endfunction function [:] med;
input [:] a, b, c;
begin
med = a < b ? (b < c ? b : a < c ? c : a) : (b > c ? b : a > c ? c : a);
end
endfunction function [:] min;
input [:] 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

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 [:] Data_out; /********************************************************************/ wire [:] rom_addr; //
wire [:] 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 [:] data_out0; //output-to ;
wire [:] data_out1;
wire [:] data_out2;
wire [:] data_out3;
wire [:] data_out4;
wire [:] data_out5;
wire [:] data_out6;
wire [:] data_out7;
wire [:] data_out8;
wire win_done_sig; wire [:] column_addr_sig;
wire [:] row_addr_sig; win3by3_gen win3by3_gen_inst (
.CLK(CLK),
.RSTn(RSTn),
.center_pix_sig(win_start_sig), //input-from ;
.cols('d512), // the column numbers of the input image
.rows('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('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 [:] 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 [:] Data_out;
assign Done_sig = medfilt_done_sig;
assign Data_out = medfilt_data_wire; /**********************************************************************/
endmodule

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 [:] pix_cnt; //512*512=262144=100,0000,0000,0000,0000 // Outputs
wire Done_sig;
wire [:] 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 = ;
RSTn = ;
Start_sig = ; fouti = $fopen("medfilter2_re.txt"); // Wait 100 ns for global reset to finish
#; // To reset the system
// Add stimulus here
RSTn = ;
Start_sig = ;
pix_cnt = ; #; // To start the system
// Add stimulus here
RSTn = ;
pix_cnt = ; end always # CLK = ~CLK; always@(posedge CLK)
begin
if(Done_sig)
pix_cnt <= pix_cnt + ;
end always@(posedge CLK)
begin
if(pix_cnt == 'd262145)
begin
Start_sig <= ;
$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

整体的代码就是这样的。

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的世界。真的,还是希望大家能够选择去做自己喜欢的事情。

verilog实现中值滤波的更多相关文章

  1. verilog 实现中值滤波

    图像信号在形成.传输和记录的过程中,由于成像系统.传输介质.工作环境和记录设备等的固有缺陷,不可避免地产生各种类型的噪声,降低了图像的质量,进而影响后续处理(如边缘检测.图像分割.特征提取.模式识别等 ...

  2. 基于MATLAB的中值滤波算法实现

    在实时图像采集中,不可避免的会引入噪声,尤其是干扰噪声和椒盐噪声,噪声的存在严重影响边缘检测的效果,中值滤波是一种基于排序统计理论的非线性平滑计数,能有效平滑噪声,且能有效保护图像的边缘信息,所以被广 ...

  3. 学习 opencv---(8)非线性滤波:中值滤波,双边滤波

    正如我们上一篇文章中讲到的,线性滤波可以实现很多种不同的图像变换.然而非线性滤波,如中值滤波器和双边滤波器,有时可以达到更好的实现效果. 邻域算子的其他一些例子还有对 二值图像进行操作的形态学算子,用 ...

  4. Atitit   图像处理 平滑 也称 模糊, 归一化块滤波、高斯滤波、中值滤波、双边滤波)

    Atitit   图像处理 平滑 也称 模糊, 归一化块滤波.高斯滤波.中值滤波.双边滤波) 是一项简单且使用频率很高的图像处理方法 用途 去噪 去雾 各种线性滤波器对图像进行平滑处理,相关OpenC ...

  5. opencv实现图像邻域均值滤波、中值滤波、高斯滤波

    void CCVMFCView::OnBlurSmooth()//邻域均值滤波 { IplImage* in; in = workImg; IplImage* out = cvCreateImage( ...

  6. MATLAB灰度图、中值滤波图

    x=imread(‘x.jpg’); x=rbg2gray(x);  %转成灰度图像 k=medfilt2(x);   %中值滤波,默认为3X3矩阵 figure, imshow(k); medfil ...

  7. 关于中值滤波算法,以及C语言实现(转)

    源:关于中值滤波算法,以及C语言实现 1.什么是中值滤波? 中值滤波是对一个滑动窗口内的诸像素灰度值排序,用其中值代替窗口中心象素的原来灰度值,它是一种非线性的图像平滑法,它对脉冲干扰级椒盐噪声的抑制 ...

  8. 基于FPGA的中值滤波算法实现

    在这一篇开篇之前,我需要解决一个问题,上一篇我们实现了基于FPGA的均值滤波算法的实现,最后的显示效果图上发现有一些黑白色的斑点,我以为是椒盐噪声,然后在做基于FPGA的中值滤波算法的实验时,我发现黑 ...

  9. 基于MATLAB的中值滤波均值滤波以及高斯滤波的实现

    基于MATLAB的中值滤波均值滤波以及高斯滤波的实现 作者:lee神 1.   背景知识 中值滤波法是一种非线性平滑技术,它将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值. 中值滤 ...

随机推荐

  1. Hive QL的操作

    一.数据定义DDL操作 创建表: --create table为创建一个指定名字的表 create(external) table table_name --external关键字可以让用户创建一个外 ...

  2. 1.Liunx 文件管理

    文件管理命令 1.基本命令:ls .cd .pwd .man 2.文件操作:touch . cp . rm . mv .ln . mkdir . rmdir 3.文件查看:cat .more .les ...

  3. Qt Widgets——抽象滑块及其继承类

    三个可视类的默认外观分别如下(win7):它们的滑块都处于最小值0处. 理解QAbstractSlider时 可将它想成就是QScrollBar(该小部件的外观比较多地拥有QAbstractSlide ...

  4. JS 控制只能输入数字并且最多允许两位小数点

    <html lang="en"> <head> <meta charset="UTF-8"> <title>JS ...

  5. js代码之编程习惯

    编程习惯: 异常处理 如果你没有使用异常处理的习惯,这可能是因为你并未真正的理解它的作用.当你正确使用异常处理之后,你会发现你的代码最显著的变化就是:少了很多的 if-else 语句 . 虽然在 JS ...

  6. Linux第三周作业

    1.三个法宝 ①存储程序计算机工作模型,计算机系统最最基础性的逻辑结构: ②函数调用堆栈,堆栈完成了计算机的基本功能:函数的参数传递机制和局部变量存取 : ③中断,多道程序操作系统的基点,没有中断机制 ...

  7. POJ 2243 Knight Moves(BFS)

    POJ 2243 Knight Moves A friend of you is doing research on the Traveling Knight Problem (TKP) where ...

  8. 使用ES6的reduce函数,根据key去重

    最近很着迷于ES6的函数,让代码变得更优雅.ES6里的reduce函数,平时用的不是特别多,真正用起来发现还是挺好用的. 想要实现的效果为: 原数组: let rawArr = [{id:'123'} ...

  9. 利用awk处理学生成绩问题(难度较大)

    学生成绩表单如下: Name,Team,First Test, Second Test, Third Test Tom,Red,,, Joe,Green,,, Maria,Blue,,, Fred,B ...

  10. python 爬虫之 selenium API

    一.浏览器操作 1.浏览器最大化 driver.maximize_window() #将浏览器最大化显示 2.设置浏览器宽.高 driver.set_window_size(480, 800)#设置浏 ...