今天给大侠带来基于FPGA的中值滤波器设计,附源码,获取源码,请在“FPGA技术江湖”公众号内回复“中值滤波器设计源码”,可获取源码文件。话不多说,上货。
设计背景
在图像采集、转换和传输的过程中,由于成像系统、传输介质和工作环境等固有的缺陷,不可避免地产生各种类型的噪声,导致获取的图像往往与实际图像有差异。图像质量的下降使得图像后续处理(如边缘检测、图像分割、特征提取、模式识别等)产生困难,因此对噪声图像进行滤波是必要预处理过程,这可以使处理后的图像更适合观察或提取有用信息。但滤波算法在去除噪声的同时难免对图像造成一定程度的模糊,造成细节信息的丢失。中值滤波是对图像的低通滤波,可有效滤除高频噪声,增强图像清晰度。
设计原理
中值滤波是对一个滑动窗口内的诸像素灰度值排序,用其中值代替窗口中心象素的原来灰度值,它是一种非线性的图像平滑法,它对脉冲干扰级椒盐噪声(脉冲噪声)的抑制效果好,在抑制随机噪声的同时能有效保护边缘少受模糊。
本设计采用3*3的滑动窗口,先将3*3窗口中每一列数据进行从大到小的排序,列排序后,再对窗口中每一行的数据从大到小进行排序,之后再对窗口中对角线上的数据进行排序,得到中间值,即为9个数的中值。其示意图如下:
这种滤波算法,极大减少了比较的次数,提高了图像处理的速度,在FPGA上,不仅易于实现,而且占用了更少的片上资源。
这种滤波算法,极大减少了比较的次数,提高了图像处理的速度,在FPGA上,不仅易于实现,而且占用了更少的片上资源。
设计架构图
本设计可分为四个模块,分别是:ROM模块,用于存储处理图像的信息;3*3窗口生成模块,用于生成滤波的滑动窗口,得到窗口内的所有元素数据;计数器控制模块,主要用于获得中心像素点的地址信息;3*3中值滤波模块,主要用于得到某一中心像素点的3*3滑动窗口区域的灰度值的中值,作为中心像素点的值。
设计代码
medfilter顶层模块代码:
代码语言:javascript复制module medfilter
(
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 ;
// );
rom_512by512 rom_512by512_inst(
.address(rom_addr), //input clka;
.clock(CLK), //input-from;
.q(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
rom_512by512设计模块代码:
代码语言:javascript复制// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
module rom_512by512 (
address,
clock,
q);
input [17:0] address;
input clock;
output [7:0] q;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
tri1 clock;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif
wire [7:0] sub_wire0;
wire [7:0] q = sub_wire0[7:0];
altsyncram altsyncram_component (
.address_a (address),
.clock0 (clock),
.q_a (sub_wire0),
.aclr0 (1'b0),
.aclr1 (1'b0),
.address_b (1'b1),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clock1 (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.data_a ({8{1'b1}}),
.data_b (1'b1),
.eccstatus (),
.q_b (),
.rden_a (1'b1),
.rden_b (1'b1),
.wren_a (1'b0),
.wren_b (1'b0));
defparam
altsyncram_component.address_aclr_a = "NONE",
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.init_file = "medfilter2_re.mif",
altsyncram_component.intended_device_family = "Cyclone IV E",
altsyncram_component.lpm_hint = "ENABLE_RUNTIME_MOD=NO",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 262144,
altsyncram_component.operation_mode = "ROM",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_reg_a = "UNREGISTERED",
altsyncram_component.widthad_a = 18,
altsyncram_component.width_a = 8,
altsyncram_component.width_byteena_a = 1;
endmodule
counter_ctrl模块代码:
代码语言:javascript复制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
win3by3_gen模块代码:
代码语言:javascript复制 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 [7:0] rom_data_win;
input [9:0] cols;
input [9:0] rows;
input center_pix_sig; //
input [9:0] column_addr_sig;
input [9:0] row_addr_sig;
output [7:0] data_out0; //output-to U4;
output [7:0] data_out1;
output [7:0] data_out2;
output [7:0] data_out3;
output [7:0] data_out4;
output [7:0] data_out5;
output [7:0] data_out6;
output [7:0] data_out7;
output [7:0] data_out8;
output [17:0] rom_addr_sig;
output win_data_done_sig;
/******************************************************************************************************************************/
reg [9:0] m;
always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
m <= 10'd1;
else if ( center_pix_sig )
m <= row_addr_sig[9:0];
/******************************************************************************************************************************/
reg [9:0] n;
always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
n <= 10'd1;
else if ( center_pix_sig )
n <= column_addr_sig[9:0];
/*****************************************************************************************************************************/
reg [3:0] i;
reg isWinDone;
reg [17:0] rom_addr;
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 get_9point_vld;
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
get_9point_vld <= 1'b0;
else if ( center_pix_sig )
get_9point_vld <= 1'b1;
else if ( i==4'd10 )
get_9point_vld <= 1'b0;
always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
isWinDone <= 1'b0;
else if ( i==4'd10 )
isWinDone <= 1'b1;
else
isWinDone <= 1'b0;
always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
i <= 4'd0;
else if (i == 4'd10)
i <= 4'd0;
else if ( get_9point_vld )
i <= i 1'b1;
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
rom_addr <= 0;
else if ( get_9point_vld)
case (i)
4'd0:
if(!(m==1 || n==1)) rom_addr <= (m-2)*cols (n-1) -1;
4'd1:
if(!(m==1 )) rom_addr <= (m-2)*cols n -1;
4'd2:
if(!(m==1 || n==cols)) rom_addr <= (m-2)*cols (n 1) -1;
4'd3:
if(!(n==1)) rom_addr <= (m-1)*cols (n-1) -1;
4'd4:
rom_addr <= (m-1)*cols n -1;
4'd5:
if(!(n==cols)) rom_addr <= (m-1)*cols (n 1) -1;
4'd6:
if(!(m==cols || n==1)) rom_addr <= m*cols (n-1) -1;
4'd7:
if(!(m==cols)) rom_addr <= m*cols n -1;
4'd8:
if(!(m==cols || n==cols)) rom_addr <= m*cols (n 1) -1;
default:;
endcase
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 ( get_9point_vld )
case (i)
4'd2:
if ( m==1 || n==1 )
a11 <= 0;
else
a11 <= rom_data_win;
4'd3:
if ( m==1 ) a12 <= 0;
else a12 <= rom_data_win;
4'd4:
if ( m==1 || n==cols ) a13 <= 0;
else a13 <= rom_data_win;
4'd5:
if ( n==1 ) a21 <= 0;
else a21 <= rom_data_win;
4'd6:
a22 <= rom_data_win;
4'd7:
if ( n==cols ) a23 <= 0;
else a23 <= rom_data_win;
4'd8:
if ( m==cols || n==1 ) a31 <= 0;
else a31 <= rom_data_win;
4'd9:
if ( m==cols ) a32 <= 0;
else a32 <= rom_data_win;
4'd10:
if ( m==cols || n==cols ) a33 <= 0;
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
medfilter3by3模块代码:
代码语言:javascript复制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
仿真测试
仿真测试medfilter_tb模块代码:
代码语言:javascript复制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)
medfilter 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
仿真图如下:
END