FPGA 图像篇(中值滤波)

527 阅读3分钟

1 中值滤波的基本原理

中值滤波是一种非线性图像处理方法,与均值滤波思想不同,中值滤波通过对邻域内像素灰度值的排序中位数来决定中心像素的灰度,具体思想为基于滑动窗口求得窗口中像素值的中间值,用该值来替代中间位置像素的值。

中值滤波对于消除脉冲干扰,对孤立的噪声即椒盐噪声,脉冲噪声有很好的滤波效果,可以保护图像的边缘特性,不会使图像出现显著的模糊。

image.png

2 基于FPGA的中值滤波的技术关键点

分析中值滤波的原理可知道,我们所要完成的是对滑动窗口中像素的排序,而滑动窗口我们可以通过之前的博客# FPGA 图像篇(数据处理-行缓存)juejin.cn/post/710280… ,之前的博客已经实现了行缓存,通过对行缓存的控制可以去实现滑动窗口的控制。

技术关键点: 对滑动窗口中像素的排序,在高级语言如c++,python等要实现对矩阵数组的排序很简单,快速排序,归并排序等都可以方便快速的实现,但对于我们FPGA来说以上的方法好像不太适合,基于FPGA的设计思想,我们的滑动窗口排序思想为:

1 求得每一行的最大值,求得每一行的最小值,求得每一行的中间值 2 最大值中取最小值,中间值中取中间值,最小值中取最大值 3 基于步骤二 获得排序后三个数中的中间值,即为中间位置像素的值。 观察发现这个方法的思想其实是类似于归并排序的,一直在缩小区间的长度。

3 FPGA 模块设计

1 行缓存模块juejin.cn/post/710280…

2 中值滤波模块

4 程序设计

数据的输入为16位的灰度图像。

module median_filter(clk,rst_n,data_in,data_in_en,HS_in,VS_in,data_out,data_out_en,HS_out,VS_out)

   input clk,rst_n;
   input [15:0]data_in;
   input data_in_en;
   input HS_in,VS_in;
   output [15:0]data_out;
   output data_out_en;
   output HS_out,VS_out;
   //获得的三行数据缓存 分比为第三行 第二行和第一行数据
   wire line0,line1,line2;
   
   // 输出控制信号的中间寄存器  流水线控制信号
  reg hs0;
  reg hs1;
  reg hs2; 
  reg vs0; 
  reg vs1; 
  reg vs2; 
  reg d0; 
  reg d; 
  reg d2;
   
   
   // 3x3窗口中的像素
   reg[15:0] line0_data0;
   reg[15:0] line0_data1;
   reg[15:0] line0_data2;
   reg[15:0] line1_data0;
   reg[15:0] line1_data1;
   reg[15:0] line1_data2;
   reg[15:0] line2_data0;
   reg[15:0] line2_data1;
   reg[15:0] line2_data2;
   // 每行像素的最大值 最小值和中值
   reg [15:0]line0_max;
   reg [15:0]line1_max;
   reg [15:0]line2_max;
   reg [15:0]line0_min;
   reg [15:0]line1_min;
   reg [15:0]line2_min;
   reg [15:0]line0_mid;
   reg [15:0]line1_mid;
   reg [15:0]line2_mid;
   // 最大值中的最小值 最小值中的最大值 中间值中的中值
   reg [15:0] max_min;
   reg [15:0] max_mid;
   reg [15:0] max_max
   reg [15:0] min_max;
   reg [15:0] min_mid;
   reg [15:0] min_mid;
   reg [15:0] mid_mid;
   reg [15:0] mid_min;
   reg [15:0] mid_max;
   // 获得最终的中值
   reg [15:0] mid;
    mat_3x3 mat
    ( 
    .clk(clk), 
    .rst_n(rst_n), 
    .valid_in(data_in_en),
    .din(data_in), 
    .dout_0(line0), 
    .dout_1(line1), 
    .dout_2(line2), 
    .dout(), 
    .flag()
    );
    
  //输出控制信号的控制
    always @(posedge clk or negedge rst_n) begin
         if(!rst_n) begin
             hs0 <= 1'b0;
             hs1 <= 1'b0;
             hs2 <= 1'b0;
	 
             vs0 <= 1'b0;
             vs1 <= 1'b0;
             vs2 <= 1'b0;
	 
             de0 <= 1'b0;
             de1 <= 1'b0;
             de2 <= 1'b0;
 end
         else if(data_in_en) begin
             hs0 <= hs_in;
             hs1 <= hs0;
             hs2 <= hs1;
	 
             vs0 <= vs_in;
             vs1 <= vs0;
             vs2 <= vs1;
	 
             d0 <= data_in_en;
             d1 <= d0;
             d2 <= d1; 
 end
end

   //获得3x3数据
   always@(posedge clk or negedge rst_n) begin
   if(!rst_n) begin
       line0_data0 <= 16'd0;
       line0_data1 <= 16'd0;
       line0_data2 <= 16'd0;
       
       line1_data0 <= 16'd0;
       line1_data1 <= 16'd0; 
       line1_data2 <= 16'd0;
       
       line2_data0 <= 16'd0;
       line2_data1 <= 16'd0; 
       line2_data2 <= 16'd0;
   end
   else if(data_in_en) begin
       line0_data0 <= line0;
       line0_data1 <= line0_data0;
       line0_data2 <= line0_data1;
       
       line1_data0 <= line1;
       line1_data1 <= line1_data0;
       line1_data2 <= line1_data1;
       
       line2_data0 <= line2;
       line2_data1 <= line2_data0;
       line2_data2 <= line2_data1;
    end
    
    
    
//                     求取 每行的最大最小和中值
    
    //获取第2行的最大最小和中值
       always@(posedge clk or negedge rst_n)
       if(!rst_n) begin
            line0_max <= 16'd0;
            line0_mid <= 16'd0;
            line0_min <= 16'd0;
       end
       else if(data_in_en) begin
       if((line0_data0 >= line0_data1) && (line0_data0 >= line0_data2)) begin
           line0_max <= line0_data0;
           if(line0_data1 >= line0_data2) begin
                line0_mid <= line0_data1;
                line0_min <= line0_data2;
        end
            else begin
                line0_mid <= line0_data2;
                line0_min <= line0_data1;
        end
        end
        
         else if((line0_data1 > line0_data0) && (line0_data1 >= line0_data2)) begin
            line0_max <= line0_data1;
             if(line0_data0 >= line0_data2) begin
                 line0_mid <= line0_data0;
                 line0_min <= line0_data2;
         end
             else begin
                 line0_mid <= line0_data2;
                 line0_min <= line0_data0;
         end
         end
         else if((line0_data2 > line0_data0) && (line0_data2 > line0_data1)) begin
            line0_max <= line0_data2;
             if(line0_data0 >= line0_data1) begin
             line0_mid <= line0_data0;
             line0_min <= line0_data1;
         end
             else begin
             line0_mid <= line0_data1;
             line0_min <= line0_data0;
         end
         end
 end
 end
         //获取第1行的最大最小和中值

         always @(posedge clk or negedge rst_n) begin
         if(!rst_n) begin
             line1_max <= 16'd0;
             line1_mid <= 16'd0;
             line1_min <= 16'd0;
         end
         else if(data_in_en) begin
         if((line1_data0 >= line1_data1) && (line1_data0 >= line1_data2)) begin
            line1_max <= line1_data0;
             if(line1_data1 >= line1_data2) begin
                 line1_mid <= line1_data1;
                 line1_min <= line1_data2;
         end
             else begin
                 line1_mid <= line1_data2;
                 line1_min <= line1_data1;
         end
         end
         else if((line1_data1 > line1_data0) && (line1_data1 >= line1_data2)) begin
            line1_max <= line1_data1;
             if(line1_data0 >= line1_data2) begin
                 line1_mid <= line1_data0;
                 line1_min <= line1_data2;
             end
             else begin
                 line1_mid <= line1_data2;
                 line1_min <= line1_data0;
         end 
         end

         else if((line1_data2 > line1_data0) && (line1_data2 > line1_data1)) begin
                line1_max <= line1_data2;
             if(line1_data0 >= line1_data1) begin
                line1_mid <= line1_data0;
                 line1_min <= line1_data1;
         end
             else begin
                 line1_mid <= line1_data1;
                 line1_min <= line1_data0;
         end 
         end
 end
 end
          //获取第0行的最大最小和中值
          always @(posedge clk or negedge rst_n) begin
          if(!rst_n) begin
                 line2_max <= 16'd0;
                 line2_mid <= 16'd0;
                 line2_min <= 16'd0;
          end
         else if(data_in_en) begin
             if((line2_data0 >= line2_data1) && (line2_data0 >= line2_data2)) begin
                    line2_max <= line2_data0;
             if(line2_data1 > line2_data2) begin
                 line2_mid <= line2_data1;
                 line2_min <= line2_data2;
             end
             else begin
                 line2_mid <= line2_data2;
                 line2_min <= line2_data1;
             end
         end
         else if((line2_data1 > line2_data0) && (line2_data1 >= line2_data2)) begin
                line2_max <= line2_data1;
             if(line2_data0 >= line2_data2) begin
                 line2_mid <= line2_data0;
                 line2_min <= line2_data2;
             end
             else begin
                 line2_mid <= line2_data2;
                 line2_min <= line2_data0;
             end 
       end
         else if((line2_data2 > line2_data0) && (line2_data2 > line2_data1)) begin
                line2_max <= line2_data2;
             if(line2_data0 >= line2_data1) begin
                 line2_mid <= line2_data0;
                 line2_min <= line2_data1;
             end
             else begin
                 line2_mid <= line2_data1;
                 line2_min <= line2_data0;
             end 
             end
 end
 end
 

    
     // 获取最大数中最大,最小,中间
     always @(posedge clk or negedge rst_n) begin
         if(!rst_n) begin
             max_max <= 16'd0;
             max_mid <= 16'd0;
             max_min <= 16'd0;
         end
         else if(data_in_en) begin
             if((line0_max >= line1_max) && (line0_max >= line2_max)) begin
            max_max <= line0_max;
                 if(line1_max >= line2_max) begin
                     max_mid <= line1_max;
                     max_min <= line2_max;
         end
                 else begin
                     max_mid <= line2_max;
                     max_min <= line1_max;
             end
         end
            else if((line1_max > line0_max) && (line1_max >= line2_max)) begin
                    max_max <= line1_max;
                 if(line0_max >= line2_max) begin
                     max_mid <= line0_max;
                     max_min <= line2_max;
                 end
                 else begin
                     max_mid <= line2_max;
                     max_min <= line0_max;
                 end
             end
           else if((line2_max > line0_max) && (line2_max > line1_max)) begin
                    max_max <= line2_max;
                 if(line0_max >= line1_max) begin
                     max_mid <= line0_max;
                     max_min <= line1_max;
                 end
                 else begin
                     max_mid <= line1_max;
                     max_min <= line0_max;
                 end
             end
 end
 end
    // 获取最中数中最大,最小,中间
 
 always @(posedge clk or negedge rst_n) begin
      if(!rst_n) begin
	 mid_max <= 16'd0;
	 mid_mid <= 16'd0;
	 mid_min <= 16'd0;
 end
     else if(data_in_en) begin
         if((line0_mid >= line1_mid) && (line0_mid >= line2_mid)) begin
                mid_max <= line0_mid;
             if(line1_mid >= line2_mid) begin
                 mid_mid <= line1_mid;
                 mid_min <= line2_mid;
         end
         else begin
                 mid_mid <= line2_mid;
                 mid_min <= line1_mid;
         end
         end
       else if((line1_mid > line0_mid) && (line1_mid >= line2_mid)) begin
                  mid_mid <= line1_mid;
             if(line0_mid >= line2_mid) begin
                 mid_mid <= line0_mid;
                 mid_min <= line2_mid;
             end
             else begin
                 mid_mid <= line2_mid;
                 mid_min <= line0_mid;
             end
             end
      else if((line2_mid > line0_mid) && (line2_mid > line1_mid)) begin
                mid_max <= line2_mid;
         if(line0_mid >= line1_mid) begin
                 mid_mid <= line0_mid;
                 mid_min <= line1_mid;
         end
         else begin
                 mid_mid <= line1_mid;
                 mid_min <= line0_mid;
         end
         end
 end
 end

       // 获取最小数中最大,最小,中间
     always @(posedge clk or negedge rst_n) begin
         if(!rst_n) begin
             min_max <= 16'd0;
             min_mid <= 16'd0;
             min_min <= 16'd0;
         end
         else if(data_in_en) begin
             if((line0_min >= line1_min) && (line0_min >= line2_min)) begin
                min_max <= line0_min;
             if(line1_min >= line2_min) begin
                 min_mid <= line1_min;
                 min_min <= line2_min;
             end
             else begin
                 min_mid <= line2_min;
                 min_min <= line1_min;
             end
             end
        else if((line1_min > line0_min) && (line1_min >= line2_min)) begin
                min_max <= line1_min;
             if(line0_min >= line2_min) begin
                 min_mid <= line0_min;
                 min_min <= line2_min;
             end
             else begin
                 min_mid <= line2_min;
                 min_min <= line0_min;
             end
             end
         else if((line2_min > line0_min) && (line2_min > line1_min)) begin
                min_max <= line2_min;
             if(line0_min >= line1_min) begin
                 min_mid <= line0_min;
                 min_min <= line1_min;
             end
             else begin
                 min_mid <= line1_min;
                 min_min <= line0_min;
         end
         end
 end
 end
 
      // 求中值
      always@(posedge clk or negedge rst_n)
          if(!rst_n)
              mid <= 16'd0;
           else if(data_in_en)  begin
   
              if(((max_min >= mid_mid) && (max_min < min_max)) || ((max_min >= min_max) &&
(max_min< mid_mid)))
                    mid <= max_min ;
             else if(((mid_mid > max_min ) && (mid_mid < min_max)) || ((mid_mid >= min_max) &&
(mid_mid < max_min )))
                    mid <= mid_mid;
             else if(((min_max> max_min ) && (min_max< mid_mid)) || ((min_max> mid_mid) &&
(min_max < max_min )))
                    mid <= min_max;
                 end
       end
       
       
       
       
assign data_out = mid;
assign data_out_en = d2;
assign HS_out = hs2;
assign VS_out = vs2;