中值滤波属于统计排序滤波,对窗口内的像素值进行排序并通过多路选择器选择使用排序后的值,可以是最大值、最小值、中值等。排序滤波用接近中间位置的排序值作为输出,进行图像的平滑滤波,能得到很好的噪声平滑性质,中值滤波对去除椒盐噪声十分有用,而形态学滤波中主要用到的算子就是最大/最小值滤波。
上式中,Sort算子代表对i和j的有效区域进行排序运算,同时输出排序结果的n个值。由数学定义不难看出,排序滤波器主要完成对图像当前窗口内的所有像素进行排序,同时按照指定输出排序结果。若令,则上式则变成中值滤波器,若排序结果按升序排列,n = 0 ,则为最小值滤波器。同样,若则为最大值滤波器。
在编写中值滤波的代码中需要解决两个问题,一个是在用滑动窗口对图像进行处理时,图像边界的问题,在本文中依然采用了和均值滤波一样的处理方法,在图像的四周补一圈0,具体的原理和操作过程可见另一篇均值滤波的文章。另一个问题就是对滑动窗口中的数据进行排序,最后取出最大值,本文以中值滤波为例,也可以改成最大值滤波和最小值滤波。
假如总共有n个待排序元素,将每个元素和所有元素进行比较,将比较结果存入score数组中,当前数据小于某个数据时,将score数组中放入1,反之大于时放入0,当两个数据相等时,需要判断下标的大小,若下标小于等于当前数据的下标,score数组中放入0,否则放入1。后将score中所有的数据相加就是当前数据排序的序号。序号为数据降序排列所对应的序号。针对3*3的滑动窗口而言,总共 有9个数据,最大值取排序序号0对应的数据,中值取排序序号4对应的数据,最小值取排序序号8对应的数据。将3*3的数据送入排序模块,即可输出最大值、中值和最小值。
中值滤波模块
module median_filter(
input clk,
input rst_n,
input per_frame_vsync,
input per_frame_href,
input per_frame_clken,
input [7:0] per_img_Y,
output post_vsync,
output post_href,
output post_clken,
output [7:0] post_img_data
);
parameter [10:0] delay=11'd1310;
wire [7:0] post1_img_data;
wire post_frame_vsync;
wire post_frame_href;
wire post_frame_clken;
//-----------------------------
//generate 3×3 picture matrix
//-----------------------------
wire matrix_frame_clken;
wire matrix_frame_href;
wire matrix_frame_vsync;
wire [7:0] matrix_p11;
wire [7:0] matrix_p12;
wire [7:0] matrix_p13;
wire [7:0] matrix_p21;
wire [7:0] matrix_p22;
wire [7:0] matrix_p23;
wire [7:0] matrix_p31;
wire [7:0] matrix_p32;
wire [7:0] matrix_p33;
wire [7:0] max_out;
wire [7:0] mid_out;
wire [7:0] min_out;
wire sort_done;
generate_3_3 #(delay) u_generate_3_3(
.clk ( clk ),
.rst_n ( rst_n ),
.per_frame_vsync ( per_frame_vsync ),
.per_frame_href ( per_frame_href ),
.per_frame_clken ( per_frame_clken ),
.per_img_Y ( per_img_Y ),
.matrix_frame_vsync ( matrix_frame_vsync ),
.matrix_frame_href ( matrix_frame_href ),
.matrix_frame_clken ( matrix_frame_clken ),
.matrix_p11 ( matrix_p11 ),
.matrix_p12 ( matrix_p12 ),
.matrix_p13 ( matrix_p13 ),
.matrix_p21 ( matrix_p21 ),
.matrix_p22 ( matrix_p22 ),
.matrix_p23 ( matrix_p23 ),
.matrix_p31 ( matrix_p31 ),
.matrix_p32 ( matrix_p32 ),
.matrix_p33 ( matrix_p33 )
);
sort_udc u_sort_udc(
.clk(clk),
.rst_n(rst_n),
.udc_0(matrix_p11),
.udc_1(matrix_p12),
.udc_2(matrix_p13),
.udc_3(matrix_p21),
.udc_4(matrix_p22),
.udc_5(matrix_p23),
.udc_6(matrix_p31),
.udc_7(matrix_p32),
.udc_8(matrix_p33),
.sort_done(sort_done),
.max_out(max_out),
.mid_out(mid_out),
.min_out(min_out)
);
assign post1_img_data=mid_out;
//-----------------------------
//clk signal synchronization
//-----------------------------
reg [2:0] post_clken_dy;
reg [2:0] post_href_dy;
reg [2:0] post_vsync_dy;
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
post_clken_dy<=2'd0;
post_href_dy<=2'd0;
post_vsync_dy<=2'd0;
end
else begin
post_clken_dy<={post_clken_dy[1:0],matrix_frame_clken};
post_href_dy<={post_href_dy[1:0],matrix_frame_href};
post_vsync_dy<={post_vsync_dy[1:0],matrix_frame_vsync};
end
end
assign post_frame_clken=post_clken_dy[2];
assign post_frame_href=post_href_dy[2];
assign post_frame_vsync=post_vsync_dy[2];
//在这里做一个延时处理,先延时一行,是因为在输出图像第二行的数据时,3*3窗口中放的是前3行的数据,所以输出的数据要比送入3*3窗口的数据晚到一行的时间
//再延时一个输出像素时钟周期(两个clk时钟周期),这是因为输出的每一行的第n个数据时,这时3*3窗口中送入的是n-1、n、n+1这三列的数据,所以输出的第n个数据比送入3*3的n+1这一列数据晚到一个输出像素时钟周期
reg [delay+1:0] post_clken_dl;
reg [delay+1:0] post_href_dl;
reg [delay+1:0] post_vsync_dl;
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
post_clken_dl<=0;
post_href_dl<=0;
post_vsync_dl<=0;
end
else begin
post_clken_dl<={post_clken_dl[delay:0],post_frame_clken};
post_href_dl<={post_href_dl[delay:0],post_frame_href};
post_vsync_dl<={post_vsync_dl[delay:0],post_frame_vsync};
end
end
assign post_clken=post_clken_dl[delay+1];
assign post_href=post_href_dl[delay+1];
assign post_vsync=post_vsync_dl[delay+1];
assign post_img_data=post_href?post1_img_data:0;
endmodule
排序模块
module sort_udc #(
parameter width = 4'd9
)
(
//clk and reset
input clk ,
input rst_n ,
//input signals
input [7:0] udc_0 ,
input [7:0] udc_1 ,
input [7:0] udc_2 ,
input [7:0] udc_3 ,
input [7:0] udc_4 ,
input [7:0] udc_5 ,
input [7:0] udc_6 ,
input [7:0] udc_7 ,
input [7:0] udc_8 ,
//output signals
output [3:0] sort_out0 ,
output [3:0] sort_out1 ,
output [3:0] sort_out2 ,
output [3:0] sort_out3 ,
output [3:0] sort_out4 ,
output [3:0] sort_out5 ,
output [3:0] sort_out6 ,
output [3:0] sort_out7 ,
output [3:0] sort_out8 ,
output [7:0] max_out ,
output [7:0] mid_out ,
output [7:0] min_out ,
output sort_done
);
//*********************************************//
//***************内部端口声明******************//
//*********************************************//
wire[7:0] comp_data[8:0];
wire[3:0] comp_data_out[8:0];
wire[7:0] comp_done;
//*********************************************//
//***************接收数据*********************//
//*********************************************//
assign comp_data[0] = udc_0;
assign comp_data[1] = udc_1;
assign comp_data[2] = udc_2;
assign comp_data[3] = udc_3;
assign comp_data[4] = udc_4;
assign comp_data[5] = udc_5;
assign comp_data[6] = udc_6;
assign comp_data[7] = udc_7;
assign comp_data[8] = udc_8;
//*********************************************//
//******* 各和其它单元电压的比较结果**********//
//*********************************************//
generate
genvar col;
for(col = 0;col < width; col = col + 1) begin :hehe
comp_method comp_1 (
.clk (clk ),
.rst_n (rst_n ),
.comp_data (comp_data[col] ),
.udc_0 (udc_0 ),
.udc_1 (udc_1 ),
.udc_2 (udc_2 ),
.udc_3 (udc_3 ),
.udc_4 (udc_4 ),
.udc_5 (udc_5 ),
.udc_6 (udc_6 ),
.udc_7 (udc_7 ),
.udc_8 (udc_8 ),
.col (col ),
.comp_data_out (comp_data_out[col] ),
.comp_done (comp_done[col] )
);
defparam comp_1.width = width;
end
endgenerate
//*********************************************//
//***************输出排序结果******************//
//*********************************************//
assign sort_out0 = comp_data_out[0];
assign sort_out1 = comp_data_out[1];
assign sort_out2 = comp_data_out[2];
assign sort_out3 = comp_data_out[3];
assign sort_out4 = comp_data_out[4];
assign sort_out5 = comp_data_out[5];
assign sort_out6 = comp_data_out[6];
assign sort_out7 = comp_data_out[7];
assign sort_out8 = comp_data_out[8];
assign sort_done = comp_done[0];
reg [7:0] udc_0_dl,udc_1_dl,udc_2_dl,udc_3_dl,udc_4_dl,udc_5_dl,udc_6_dl,udc_7_dl,udc_8_dl, udc_0_dy,udc_1_dy,udc_2_dy,udc_3_dy,udc_4_dy,udc_5_dy,udc_6_dy,udc_7_dy,udc_8_dy;
always @(posedge clk)begin
udc_0_dy<=udc_0;
udc_1_dy<=udc_1;
udc_2_dy<=udc_2;
udc_3_dy<=udc_3;
udc_4_dy<=udc_4;
udc_5_dy<=udc_5;
udc_6_dy<=udc_6;
udc_7_dy<=udc_7;
udc_8_dy<=udc_8;
udc_0_dl<=udc_0_dy;
udc_1_dl<=udc_1_dy;
udc_2_dl<=udc_2_dy;
udc_3_dl<=udc_3_dy;
udc_4_dl<=udc_4_dy;
udc_5_dl<=udc_5_dy;
udc_6_dl<=udc_6_dy;
udc_7_dl<=udc_7_dy;
udc_8_dl<=udc_8_dy;
end
assign max_out=sort_done?((sort_out0==4'd0)?udc_0_dl:((sort_out1==4'd0)?udc_1_dl:((sort_out2==4'd0)?udc_2_dl:(sort_out3==4'd0)?udc_3_dl:((sort_out4==4'd0)?udc_4_dl:((sort_out5==4'd0)?udc_5_dl:((sort_out6==4'd0)?udc_6_dl:((sort_out7==4'd0)?udc_7_dl:((sort_out8==4'd0)?udc_8_dl:0)))))))):max_out;
assign mid_out=sort_done?((sort_out0==4'd4)?udc_0_dl:((sort_out1==4'd4)?udc_1_dl:((sort_out2==4'd4)?udc_2_dl:(sort_out3==4'd4)?udc_3_dl:((sort_out4==4'd4)?udc_4_dl:((sort_out5==4'd4)?udc_5_dl:((sort_out6==4'd4)?udc_6_dl:((sort_out7==4'd4)?udc_7_dl:((sort_out8==4'd4)?udc_8_dl:4)))))))):mid_out;
assign min_out=sort_done?((sort_out0==4'd8)?udc_0_dl:((sort_out1==4'd8)?udc_1_dl:((sort_out2==4'd8)?udc_2_dl:(sort_out3==4'd8)?udc_3_dl:((sort_out4==4'd8)?udc_4_dl:((sort_out5==4'd8)?udc_5_dl:((sort_out6==4'd8)?udc_6_dl:((sort_out7==4'd8)?udc_7_dl:((sort_out8==4'd8)?udc_8_dl:0)))))))):min_out;
endmodule
排序计算模块
module comp_method #(
parameter width = 4'd9
)
(
//clk and reset
input clk,
input rst_n,
//input signals
input [7:0] udc_0 ,
input [7:0] udc_1 ,
input [7:0] udc_2 ,
input [7:0] udc_3 ,
input [7:0] udc_4 ,
input [7:0] udc_5 ,
input [7:0] udc_6 ,
input [7:0] udc_7 ,
input [7:0] udc_8 ,
input [7:0] comp_data ,
input [3:0] col ,
//output signals
output reg[3:0] comp_data_out,
output reg comp_done
);
wire[7:0] comp_data_r[8:0];
reg [7:0] score[8:0];
reg [0:0] step;
//*********************************************//
//************初始化比较数据******************//
//*********************************************//
assign comp_data_r[0] = udc_0;
assign comp_data_r[1] = udc_1;
assign comp_data_r[2] = udc_2;
assign comp_data_r[3] = udc_3;
assign comp_data_r[4] = udc_4;
assign comp_data_r[5] = udc_5;
assign comp_data_r[6] = udc_6;
assign comp_data_r[7] = udc_7;
assign comp_data_r[8] = udc_8;
//*********************************************//
//********比较大小并输出比较结果**************//
//*********************************************//
generate
genvar i;
for(i=0;i