赞
踩
自适应中值滤波硬件框图如下。
3x3窗口中值滤波参考比较多,这里不做介绍。
图像数据是一个一个输入进来的,要实现5x5的模板,就首先必须要保证能同时能对5行图像数据进行获取,这样就必须要对图像数据进行行缓存,咋一看,5x5模板需要缓存5行,其实不然,缓存4行后,接下来输入进来的数据就是第5行的数据了,这样就实现了5行数据同时存在的情况了,对行缓存区的要求是左端进入一个数据,右端出来一个数据,这个要求与移位寄存器有些类似。
Vivado中通过调用IP核叫RAM-base Shift Register即可实现5行数据移位寄存。因为使用的是720p图像做处理,这里使用IP核串行处理来实现1280个数据(IP核内depth最大为1088)。
代码如下:
module shift_reg_5x5( input wire taps_clk , input wire[7:0] shift_in , input wire shiftin_valid , output wire[7:0] shift_out , output wire [7:0] taps3x , output wire [7:0] taps2x , output wire [7:0] taps1x , output wire [7:0] taps0x ); wire [7:0] line3; wire [7:0] line2; wire [7:0] line1; wire [7:0] line0; //5x1 //5x2 c_shift_ram_0 c_shift_ram_0_u1 ( .D(shift_in), // input wire [7 : 0] D .CLK(taps_clk), // input wire CLK .CE(shiftin_valid), // input wire CE .Q(line3) // output wire [7 : 0] Q ); c_shift_ram_0 c_shift_ram_0_u2 ( .D(line3), // input wire [7 : 0] D .CLK(taps_clk), // input wire CLK .CE(shiftin_valid), // input wire CE .Q(taps3x) // output wire [7 : 0] Q ); //5x3 c_shift_ram_0 c_shift_ram_0_u3 ( .D(taps3x), // input wire [7 : 0] D .CLK(taps_clk), // input wire CLK .CE(shiftin_valid), // input wire CE .Q(line2) // output wire [7 : 0] Q ); c_shift_ram_0 c_shift_ram_0_u4 ( .D(line2), // input wire [7 : 0] D .CLK(taps_clk), // input wire CLK .CE(shiftin_valid), // input wire CE .Q(taps2x) // output wire [7 : 0] Q ); //5x4 c_shift_ram_0 c_shift_ram_0_u5 ( .D(taps2x), // input wire [7 : 0] D .CLK(taps_clk), // input wire CLK .CE(shiftin_valid), // input wire CE .Q(line1) // output wire [7 : 0] Q ); c_shift_ram_0 c_shift_ram_0_u6 ( .D(line1), // input wire [7 : 0] D .CLK(taps_clk), // input wire CLK .CE(shiftin_valid), // input wire CE .Q(taps1x) // output wire [7 : 0] Q ); //5x5 c_shift_ram_0 c_shift_ram_0_u7 ( .D(taps1x), // input wire [7 : 0] D .CLK(taps_clk), // input wire CLK .CE(shiftin_valid), // input wire CE .Q(line0) // output wire [7 : 0] Q ); c_shift_ram_0 c_shift_ram_0_u8 ( .D(line0), // input wire [7 : 0] D .CLK(taps_clk), // input wire CLK .CE(shiftin_valid), // input wire CE .Q(taps0x) // output wire [7 : 0] Q ); assign shift_out = taps0x ; endmodule
仿真如下,5x5窗口生成输出正确
5x5中值输出流程如下,对某一个 5x5 的图像采样窗口,5x5中值滤波器经过两个时钟周期即可实现最大值,最小值的求取,而中值需要再经过两次对角线元素的排序才能输出,也即还需要两个时钟周期才能求取,因此实现完整的滤波器功能需要4个时钟周期。在每个步骤的排序中,每行的排序是可以并行执行的,所以完成5行降幂排序只需要一个时钟周期;每列的排序也是可以并行执行的,所以完成5列降幂排序也只需要一个时钟周期;后面的对角线排序也是一个同步时钟周期即可完成。
5x5中值滤波器顶层模块代码如下:
module median_filter_5 #( parameter DATA_WIDTH = 8 ) ( input wire clk , input wire reset_p , input wire[DATA_WIDTH-1:0] data_00,data_01,data_02,data_03,data_04, data_10,data_11,data_12,data_13,data_14, data_20,data_21,data_22,data_23,data_24, data_30,data_31,data_32,data_33,data_34, data_40,data_41,data_42,data_43,data_44, input wire data_in_valid , output wire[DATA_WIDTH-1:0] data_out_max, output wire[DATA_WIDTH-1:0] data_out_med, output wire[DATA_WIDTH-1:0] data_out_min, output wire[DATA_WIDTH-1:0] dout5_xy,//中心像素点 output wire data_out_valid ); //=======line data Interface ======== wire [DATA_WIDTH-1:0] l0_min,l1_min,l2_min,l3_min,l4_min; wire [DATA_WIDTH-1:0] l0_min_next,l1_min_next,l2_min_next,l3_min_next,l4_min_next; wire [DATA_WIDTH-1:0] l0_med,l1_med,l2_med,l3_med,l4_med; wire [DATA_WIDTH-1:0] l0_max_next,l1_max_next,l2_max_next,l3_max_next,l4_max_next; wire [DATA_WIDTH-1:0] l0_max,l1_max,l2_max,l3_max,l4_max; //=======row data Interface ======== wire [DATA_WIDTH-1:0] min_min,min_next_min,med_min,max_next_min,max_min; wire [DATA_WIDTH-1:0] min_min_next,min_next_min_next,med_min_next,max_next_min_next,max_min_next; wire [DATA_WIDTH-1:0] min_med,min_next_med,med_med,max_next_med,max_med; wire [DATA_WIDTH-1:0] valid_med; wire [DATA_WIDTH-1:0] min_max_next,min_next_max_next,med_max_next,max_next_max_next,max_max_next; wire [DATA_WIDTH-1:0] min_max,min_next_max,med_max,max_next_max,max_max; //5x5模版排序需要四个时钟周期 reg data_in_valid_reg1 ; reg data_in_valid_reg2 ; reg data_in_valid_reg3 ; reg data_in_valid_reg4 ; reg [DATA_WIDTH-1:0] dout5_xy_reg1 ; reg [DATA_WIDTH-1:0] dout5_xy_reg2 ; reg [DATA_WIDTH-1:0] dout5_xy_reg3 ; reg [DATA_WIDTH-1:0] dout5_xy_reg4 ; always @(posedge clk ) begin data_in_valid_reg1<= data_in_valid ; data_in_valid_reg2<= data_in_valid_reg1 ; data_in_valid_reg3<= data_in_valid_reg2 ;// data_in_valid_reg4<= data_in_valid_reg3 ; dout5_xy_reg1 <= data_22 ; dout5_xy_reg2 <= dout5_xy_reg1 ; dout5_xy_reg3 <= dout5_xy_reg2 ; dout5_xy_reg4 <= dout5_xy_reg3 ; end //======= l0 排序 ======== sort5 sort5_u0( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg1), .i1 (data_00), .i2 (data_01), .i3 (data_02), .i4 (data_03), .i5 (data_04), .dout_1 (l0_min ), .dout_2 (l0_min_next ), //临近min的值 .dout_3 (l0_med ), .dout_4 (l0_max_next ), //临近max的值 .dout_5 (l0_max ), .dout_valid () ); //======= l1 排序 ======== sort5 sort5_u1( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg1), .i1 (data_10), .i2 (data_11), .i3 (data_12), .i4 (data_13), .i5 (data_14), .dout_1 (l1_min ), .dout_2 (l1_min_next ), //临近min的值 .dout_3 (l1_med ), .dout_4 (l1_max_next ), //临近max的值 .dout_5 (l1_max ), .dout_valid () ); //======= l2 排序 ======== sort5 sort5_u2( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg1), .i1 (data_20), .i2 (data_21), .i3 (data_22), .i4 (data_23), .i5 (data_24), .dout_1 (l2_min ), .dout_2 (l2_min_next ), //临近min的值 .dout_3 (l2_med ), .dout_4 (l2_max_next ), //临近max的值 .dout_5 (l2_max ), .dout_valid () ); //======= l3 排序 ======== sort5 sort5_u3( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg1), .i1 (data_30), .i2 (data_31), .i3 (data_32), .i4 (data_33), .i5 (data_34), .dout_1 (l3_min ), .dout_2 (l3_min_next ), //临近min的值 .dout_3 (l3_med ), .dout_4 (l3_max_next ), //临近max的值 .dout_5 (l3_max ), .dout_valid () ); //======= l4 排序 ======== sort5 sort5_u4( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg1), .i1 (data_40), .i2 (data_41), .i3 (data_42), .i4 (data_43), .i5 (data_44), .dout_1 (l4_min ), .dout_2 (l4_min_next ), //临近min的值 .dout_3 (l4_med ), .dout_4 (l4_max_next ), //临近max的值 .dout_5 (l4_max ), .dout_valid () ); //======= row0 排序 ======== sort5 sort5_u5( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg2), .i1 (l0_min), .i2 (l1_min), .i3 (l2_min), .i4 (l3_min), .i5 (l4_min), .dout_1 (min_min ), .dout_2 (min_min_next ), //临近min的值 .dout_3 (min_med ), .dout_4 (min_max_next ), //临近max的值 .dout_5 (min_max ), .dout_valid () ); //======= row1 排序 ======== sort5 sort5_u6( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg2), .i1 (l0_min_next), .i2 (l1_min_next), .i3 (l2_min_next), .i4 (l3_min_next), .i5 (l4_min_next), .dout_1 (min_next_min ), .dout_2 (min_next_min_next), //临近min的值 .dout_3 (min_next_med ), .dout_4 (min_next_max_next), //临近max的值 .dout_5 (min_next_max ), .dout_valid () ); //======= row2 排序 ======== sort5 sort5_u7( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg2), .i1 (l0_med), .i2 (l1_med), .i3 (l2_med), .i4 (l3_med), .i5 (l4_med), .dout_1 (med_min ), .dout_2 (med_min_next), //临近min的值 .dout_3 (med_med ), .dout_4 (med_max_next), //临近max的值 .dout_5 (med_max ), .dout_valid () ); //======= row3 排序 ======== sort5 sort5_u8( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg2), .i1 (l0_max_next), .i2 (l1_max_next), .i3 (l2_max_next), .i4 (l3_max_next), .i5 (l4_max_next), .dout_1 (max_next_min ), .dout_2 (max_next_min_next), //临近min的值 .dout_3 (max_next_med ), .dout_4 (max_next_max_next), //临近max的值 .dout_5 (max_next_max ), .dout_valid () ); //======= row4 排序 ======== sort5 sort5_u9( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg2), .i1 (l0_max), .i2 (l1_max), .i3 (l2_max), .i4 (l3_max), .i5 (l4_max), .dout_1 (max_min ), .dout_2 (max_min_next), //临近min的值 .dout_3 (max_med ), .dout_4 (max_max_next), //临近max的值 .dout_5 (max_max ), .dout_valid () ); // wire [DATA_WIDTH-1:0] d14,d23,d32,d41; wire [DATA_WIDTH-1:0] d15,d24,d33,d42,d51; wire [DATA_WIDTH-1:0] d25,d34,d43,d52; sort5 sort5_u10( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg3), .i1 (0), .i2 (min_max_next), .i3 (min_next_med), .i4 (med_min_next), .i5 (max_next_min), .dout_1 ( ), .dout_2 (d14), //临近min的值 .dout_3 (d23), .dout_4 (d32), //临近max的值 .dout_5 (d41), .dout_valid () ); sort5 sort5_u11( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg3), .i1 (min_max ), .i2 (min_next_max_next), .i3 (med_med ), .i4 (max_next_min_next), .i5 (max_min ), .dout_1 (d15), .dout_2 (d24), //临近min的值 .dout_3 (d33), .dout_4 (d42), //临近max的值 .dout_5 (d51), .dout_valid () ); sort5 sort5_u12( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg3), .i1 (0 ), .i2 (min_next_max), .i3 (med_max_next), .i4 (max_next_med), .i5 (max_min_next), .dout_1 (), .dout_2 (d25), //临近min的值 .dout_3 (d34), .dout_4 (d43), //临近max的值 .dout_5 (d52), .dout_valid () ); sort5 sort5_u13( .clk (clk), .reset_p (reset_p), .din_valid (data_in_valid_reg4), .i1 (0), .i2 (0), .i3 (d41), .i4 (d33), .i5 (d25), .dout_1 (), .dout_2 (), //临近min的值 .dout_3 (), .dout_4 (valid_med), //临近max的值 .dout_5 (), .dout_valid () ); //输出数据同步 reg [7:0] min_min_reg1,min_min_reg2; reg [7:0] max_max_reg1,max_max_reg2; always @(posedge clk ) begin min_min_reg1<=min_min; min_min_reg2<=min_min_reg1; max_max_reg1<=max_max; max_max_reg2<=max_max_reg1; end assign data_out_min = min_min_reg2 ; assign data_out_med = valid_med ; assign data_out_max = max_max_reg2 ; assign dout5_xy = dout5_xy_reg4; assign data_out_valid =data_in_valid_reg4; endmodule
注意3x3中值滤波器只需三个时钟周期即可输出中值,两个中值滤波器输入数据来自同一个 5x5 的图像采样窗口,是同步的,而处理速度不同,因此输出数据将不再同步,会影响到后面的自适应比较模块,需要对3x3中值滤波器输出数据做延迟处理。
自适应中值滤波顶层代码:
module adap_median_filter#( parameter DATA_WIDTH = 8 )( input wire clk , input wire reset_p , input wire [DATA_WIDTH-1:0] data_in , input wire data_in_valid , input wire data_in_hs , input wire data_in_vs , output reg [DATA_WIDTH-1:0] data_out, output wire data_out_valid , output wire data_out_hs , output wire data_out_vs ); //将3x3滤波器输出数据与5x5滤波器同步输出到自适应判断模块 wire [DATA_WIDTH-1:0] S3_max,S3_med,S3_min; //S内部最大值、中值、最小值、中心点值 wire [DATA_WIDTH-1:0] S5_max,S5_med,S5_min; // reg [2:0] S ; //窗口尺寸 wire [DATA_WIDTH-1:0] dout5_xy; reg data_in_valid_reg1,data_in_valid_reg2,data_in_valid_reg3, data_in_valid_reg4,data_in_valid_reg5; reg data_in_hs_reg1,data_in_hs_reg2,data_in_hs_reg3, data_in_hs_reg4,data_in_hs_reg5; reg data_in_vs_reg1,data_in_vs_reg2,data_in_vs_reg3, data_in_vs_reg4,data_in_vs_reg5; always@(posedge clk) begin data_in_valid_reg1 <= data_in_valid; data_in_valid_reg2 <= data_in_valid_reg1; data_in_valid_reg3 <= data_in_valid_reg2; data_in_valid_reg4 <= data_in_valid_reg3; data_in_valid_reg5 <= data_in_valid_reg4; data_in_hs_reg1 <= data_in_hs; data_in_hs_reg2 <= data_in_hs_reg1; data_in_hs_reg3 <= data_in_hs_reg2; data_in_hs_reg4 <= data_in_hs_reg3; data_in_hs_reg5 <= data_in_hs_reg4; data_in_vs_reg1 <= data_in_vs; data_in_vs_reg2 <= data_in_vs_reg1; data_in_vs_reg3 <= data_in_vs_reg2; data_in_vs_reg4 <= data_in_vs_reg3; data_in_vs_reg5 <= data_in_vs_reg4; end //======= 5x5 generate begin ======== wire [DATA_WIDTH-1:0] l0_data; //第一行数据 wire [DATA_WIDTH-1:0] l1_data; wire [DATA_WIDTH-1:0] l2_data; wire [DATA_WIDTH-1:0] l3_data; wire [DATA_WIDTH-1:0] l4_data; shift_reg_5x5 shift_reg_5x5_u( .taps_clk (clk ), .shift_in (data_in ), .shiftin_valid(data_in_valid), .shift_out ( ), .taps3x (l3_data ), .taps2x (l2_data ), .taps1x (l1_data ), .taps0x (l0_data ) ); assign l4_data = data_in; //5x5 row 1 reg [DATA_WIDTH-1:0] data_00,data_01,data_02,data_03,data_04; reg [DATA_WIDTH-1:0] data_10,data_11,data_12,data_13,data_14; reg [DATA_WIDTH-1:0] data_20,data_21,data_22,data_23,data_24; reg [DATA_WIDTH-1:0] data_30,data_31,data_32,data_33,data_34; reg [DATA_WIDTH-1:0] data_40,data_41,data_42,data_43,data_44; always@(posedge clk or posedge reset_p) begin if(reset_p)begin data_00 <= 8'd0;data_01 <= 8'd0;data_02 <= 8'd0;data_03 <= 8'd0;data_04 <= 8'd0; data_10 <= 8'd0;data_11 <= 8'd0;data_12 <= 8'd0;data_13 <= 8'd0;data_14 <= 8'd0; data_20 <= 8'd0;data_21 <= 8'd0;data_22 <= 8'd0;data_23 <= 8'd0;data_24 <= 8'd0; data_30 <= 8'd0;data_31 <= 8'd0;data_32 <= 8'd0;data_33 <= 8'd0;data_34 <= 8'd0; data_40 <= 8'd0;data_41 <= 8'd0;data_42 <= 8'd0;data_43 <= 8'd0;data_44 <= 8'd0; end else if (data_in_hs && data_in_vs) begin if (data_in_valid) begin data_04 <= l0_data; data_03 <= data_04; data_02 <= data_03; data_01 <= data_02; data_00 <= data_01; data_14 <= l1_data; data_13 <= data_14; data_12 <= data_13; data_11 <= data_12; data_10 <= data_11; data_24 <= l2_data; data_23 <= data_24; data_22 <= data_23; data_21 <= data_22; data_20 <= data_21; data_34 <= l3_data; data_33 <= data_34; data_32 <= data_33; data_31 <= data_32; data_30 <= data_31; data_44 <= l4_data; data_43 <= data_44; data_42 <= data_43; data_41 <= data_42; data_40 <= data_41; end else begin data_00 <= data_00; data_01 <= data_01; data_02 <= data_02; data_03 <= data_03; data_04 <= data_04; data_10 <= data_10; data_11 <= data_11; data_12 <= data_12; data_13 <= data_13; data_14 <= data_14; data_20 <= data_20; data_21 <= data_21; data_22 <= data_22; data_23 <= data_23; data_24 <= data_24; data_30 <= data_30; data_31 <= data_31; data_32 <= data_32; data_33 <= data_33; data_34 <= data_34; data_40 <= data_40; data_41 <= data_41; data_42 <= data_42; data_43 <= data_43; data_44 <= data_44; end end else begin data_00 <= 8'd0;data_01 <= 8'd0;data_02 <= 8'd0;data_03 <= 8'd0;data_04 <= 8'd0; data_10 <= 8'd0;data_11 <= 8'd0;data_12 <= 8'd0;data_13 <= 8'd0;data_14 <= 8'd0; data_20 <= 8'd0;data_21 <= 8'd0;data_22 <= 8'd0;data_23 <= 8'd0;data_24 <= 8'd0; data_30 <= 8'd0;data_31 <= 8'd0;data_32 <= 8'd0;data_33 <= 8'd0;data_34 <= 8'd0; data_40 <= 8'd0;data_41 <= 8'd0;data_42 <= 8'd0;data_43 <= 8'd0;data_44 <= 8'd0; end end //======= 5x5 generate end ======== median_filter_3#( .DATA_WIDTH (8) ) median_filter_3_u( .clk (clk ) , .reset_p(reset_p) , .data_00(data_11),.data_01(data_12),.data_02(data_13), .data_10(data_21),.data_11(data_22),.data_12(data_23), .data_20(data_31),.data_21(data_32),.data_22(data_33), .data_in_valid (data_in_valid), .data_out_min (S3_min), .data_out_med (S3_med), .data_out_max (S3_max), .data_out_valid ( ) ); median_filter_5#( .DATA_WIDTH (8) ) median_filter_5_u( .clk (clk) , .reset_p (reset_p) , .data_00(data_00),.data_01(data_01),.data_02(data_02),.data_03(data_03),.data_04(data_04), .data_10(data_10),.data_11(data_11),.data_12(data_12),.data_13(data_13),.data_14(data_14), .data_20(data_20),.data_21(data_21),.data_22(data_22),.data_23(data_23),.data_24(data_24), .data_30(data_30),.data_31(data_31),.data_32(data_32),.data_33(data_33),.data_34(data_34), .data_40(data_40),.data_41(data_41),.data_42(data_42),.data_43(data_43),.data_44(data_44), .data_in_valid (data_in_valid) , .data_out_max (S5_max ) , .data_out_med (S5_med ) , .data_out_min (S5_min ) , .dout5_xy (dout5_xy ) ,//中心像素点 .data_out_valid ( ) ); //======= adaptive logical begin ======== always@(posedge clk or posedge reset_p) begin if(reset_p)begin data_out<=8'd0; end else if(data_in_valid)begin if((S5_med==S5_max)||(S5_med==S5_min)) begin if ((S3_med==S3_max)||(S3_med==S3_min)) begin data_out<=S5_med; end else begin if ((dout5_xy==S3_max)||(dout5_xy==S3_min)) begin data_out<=S3_med; end else data_out<=dout5_xy; end end else begin if ((dout5_xy==S5_max)||(dout5_xy==S5_min)) begin data_out <= S5_med; end else data_out<= dout5_xy; end end end assign data_out_valid = data_in_valid_reg4; assign data_out_hs = data_in_hs_reg4; assign data_out_vs = data_in_vs_reg4; endmodule
仿真结果如下,滤波器输出数据进行了同步,并进行自适应判断
如下图,1图为含有10%浓度椒盐噪声的灰度图,2图为含有30%浓度椒盐噪声的灰度图,3图含有50%浓度椒盐噪声的灰度图,4图为原始灰度图。
3x3中值滤波器处理效果图
5x5中值滤波器处理效果图
自适应中值滤波处理
以上仅供参考(作为学习记录)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。