1、中值滤波原理

       中值滤波属于统计排序滤波,对窗口内的像素值进行排序并通过多路选择器选择使用排序后的值,可以是最大值、最小值、中值等。排序滤波用接近中间位置的排序值作为输出,进行图像的平滑滤波,能得到很好的噪声平滑性质,中值滤波对去除椒盐噪声十分有用,而形态学滤波中主要用到的算子就是最大/最小值滤波。

统计排序滤波的数学定义如下:

中值滤波 频率响应 中值滤波结果_图像处理

 上式中,Sort算子代表对i和j的有效区域进行排序运算,同时输出排序结果的n个值。由数学定义不难看出,排序滤波器主要完成对图像当前窗口内的所有像素进行排序,同时按照指定输出排序结果。若令

,则上式则变成中值滤波器,若排序结果按升序排列,n = 0 ,则为最小值滤波器。同样,若

则为最大值滤波器。

       在编写中值滤波的代码中需要解决两个问题,一个是在用滑动窗口对图像进行处理时,图像边界的问题,在本文中依然采用了和均值滤波一样的处理方法,在图像的四周补一圈0,具体的原理和操作过程可见另一篇均值滤波的文章。另一个问题就是对滑动窗口中的数据进行排序,最后取出最大值,本文以中值滤波为例,也可以改成最大值滤波和最小值滤波。

2、滑动窗口数据排序

        假如总共有n个待排序元素,将每个元素和所有元素进行比较,将比较结果存入score数组中,当前数据小于某个数据时,将score数组中放入1,反之大于时放入0,当两个数据相等时,需要判断下标的大小,若下标小于等于当前数据的下标,score数组中放入0,否则放入1。后将score中所有的数据相加就是当前数据排序的序号。序号为数据降序排列所对应的序号。针对3*3的滑动窗口而言,总共 有9个数据,最大值取排序序号0对应的数据,中值取排序序号4对应的数据,最小值取排序序号8对应的数据。将3*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<width;i=i+1) begin: hehe
        always @(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
            begin
                score[i]    <=  1'b0;
            end
            else if(step == 1'b1)
                score[i]    <=  1'b0;
            else begin
                if(comp_data < comp_data_r[i])
                    score[i]<=  1'b1;
                else if(comp_data == comp_data_r[i])
                begin
                    if(col <= i)
                        score[i]    <=  1'b0;
                    else
                        score[i]    <=  1'b1;
                end
            else
                score[i]<=  1'b0;  
            end
        end
    end 
endgenerate
//*********************************************//
//*************** 计算排名 ******************//
//*********************************************//
always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
    begin
        step    <=  1'b0;
    end
    else case(step) 
    1'b0:
        step    <=  1'b1;
    1'b1:
        step    <=  1'b0;
    
    default:step<=step;
    endcase
end

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
    begin
        comp_data_out   <=  1'b0;
        comp_done       <=  1'b0;
    end
    else case(step) 
    1'b1:begin
        comp_done       <=  1'b1;
        comp_data_out   <=  (score[0]+score[1]+score[2])+(score[3]+score[4]+score[5])+(score[6]+score[7]+score[8]);
    end
    default: begin
        comp_data_out   <=  comp_data_out;
        comp_done       <=  1'b0;
    end
    endcase
end

4、结果图

中值滤波 频率响应 中值滤波结果_最小值_02

中值滤波 频率响应 中值滤波结果_中值滤波 频率响应_03