参考:小梅哥的《FPGA系统设计与验证实战指南》
一、算法介绍
中值滤波法是一种非线性平滑技术,它将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值.
中值滤波是基于排序统计理论的一种能有效抑制噪声的非线性信号处理技术,中值滤波的基本原理是把数字图像或数字序列中一点的值用该点的一个邻域中各点值的中值代替,让周围的像素值接近的真实值,从而消除孤立的噪声点。方法是用某种结构的二维滑动模板,将板内像素按照像素值的大小进行排序,生成单调上升(或下降)的为二维数据序列。二维中值滤波输出为g(x,y)=med{f(x-k,y-l),(k,l∈W)}
,其中,f(x,y),g(x,y)分别为原始图像和处理后图像。W 为二维模板,通常为 33,55 区域,也可以是不同的的形状,如线状,圆形,十字形,圆环形等。
中值滤波法对消除椒盐噪声非常有效,在光学测量条纹图象的相位分析处理方法中有特殊作用,但在条纹中心分析方法中作用不大。中值滤波在图像处理中,常用于保护边缘信息,是经典的平滑噪声的方法。
要得到模板中数据的中间值,首先要将数据按大小排序,然后根据有序的数字序列来找中问值。中值滤波排序的过程有很多成熟的算法,如冒泡排序、二分排序等,大多是基于微机平台的软件算法,而适合硬件平台的排序算法则比较少。
FPGA 中值滤波实现方法:
(摘自:https://blog.csdn.net/Laplace666/article/details/83826443)
中值滤波方法是,对待处理的当前像素,选择一个模板,该模板为其邻近的若干个像素组成,对模板的像素由小到大进行排序,再用模板的中值来替代原像素的值的方法。
当我们使用3x3窗口后获取邻域中的9个像素,就需要对9个像素值进行排序,为了提高排序效率,排序算法思想如下图所示:
(1) 对窗内的每行像素按降序排序,得到最大值、中间值和最小值;
(2) 把三行的最小值相比较,取其中的最大值;
(3) 把三行的最大值相比较,取其中的最小值;
(4) 把三行的中间值相比较,再取一次中间值;
(5) 把前面得到的三个值再做一次排序,获得的中值即该窗口的中值。
二、程序设计
关于如何形成3*3的像素矩阵,参考另一篇文章:FPGA丨设计行缓存(linebuffer)对图像开窗
timescale 1ns/1ps
module median_filtering(
input clk,
input rst_n,
input [15:0] data_in,
input data_in_en,
input hs_in,
input vs_in,
output[15:0] data_out,
output data_out_en,
output hs_out,
output vs_out
);
wire [15:0] line0;
wire [15:0] line1;
wire [15:0] line2;
//--------------------------------------
//pipeline control signal
//--------------------------------------
reg hs0;
reg hs1;
reg hs2;
reg vs0;
reg vs1;
reg vs2;
reg de0;
reg de1;
reg de2;
//-------------------------------------
//pipeline data 对应3*3窗口内的九个像素
//-------------------------------------
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;
//--------------------------------------
//define line max mid min
//--------------------------------------
reg [15:0] line0_max;
reg [15:0] line0_mid;
reg [15:0] line0_min;
reg [15:0] line1_max;
reg [15:0] line1_mid;
reg [15:0] line1_min;
reg [15:0] line2_max;
reg [15:0] line2_mid;
reg [15:0] line2_min;
//----------------------------------------------------------------
// define //max of min //mid of mid// min of max
//---------------------------------------------------------------
reg [15:0] max_max;
reg [15:0] max_mid;
reg [15:0] max_min;
reg [15:0] mid_max;
reg [15:0] mid_mid;
reg [15:0] mid_min;
reg [15:0] min_max;
reg [15:0] min_mid;
reg [15:0] min_min;
//---------------------------------------------
// define mid of mid
//---------------------------------------------
reg [15:0] mid;
line3x3 line3x3_inst(
.clken(data_in_en),
.clock(clk),
.shiftin(data_in),
.shiftout(),
.taps0x(line0),
.taps1x(line1),
.taps2x(line2)
);
//----------------------------------------------------
//delay control signal
//----------------------------------------------------
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;
de0 <= data_in_en;
de1 <= de0;
de2 <= de1;
end
end
//--------------------------------------------------------------------
// Form an image matrix of three multiplied by three
//--------------------------------------------------------------------
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
line0_data0 <= 16'b0;
line0_data1 <= 16'b0;
line0_data2 <= 16'b0;
line1_data0 <= 16'b0;
line1_data1 <= 16'b0;
line1_data2 <= 16'b0;
line2_data0 <= 16'b0;
line2_data1 <= 16'b0;
line2_data2 <= 16'b0;
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
else ;
end
//--------------------------------------------------------------------------
//(line0 line1 line2) of (max mid min)
//--------------------------------------------------------------------------
always @(posedge clk or negedge rst_n) begin
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
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
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
//--------------------------------------------------------------------------
// (max_max max_mid max_min) of ((line0 line1 line2) of max)
//--------------------------------------------------------------------------
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
//--------------------------------------------------------------------------
// (mid_max mid_mid mid_min) of ((line0 line1 line2)of mid)
//--------------------------------------------------------------------------
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
//--------------------------------------------------------------------------
// (min_max min_mid min_min) of ((line0 line1 line2)of min)
//--------------------------------------------------------------------------
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
//--------------------------------------------------------------------------
// middle
//--------------------------------------------------------------------------
always @(posedge clk or negedge rst_n) begin
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_mid < mid_mid)))
mid <= max_min ;
else if(((mid_mid > max_min ) && (mid_mid < min_max)) || ((min_mid >= min_max) &&
(mid_mid < max_min )))
mid <= mid_mid;
else if(((min_max> max_min ) && (min_max< mid_mid)) || ((min_max> mid_mid) &&
(mid_min < max_min )))
mid <= min_max;
end
else ;
end
//--------------------------------------------------------------------------
//result
//--------------------------------------------------------------------------
assign data_out = mid;
assign data_out_en = de2;
assign hs_out = hs2;
assign vs_out = vs2;
endmodule
需要注意的是:
上述代码的输入图像数据默认是灰度图像的数据,数据位宽为16。关于如何得到灰度图,可以参考另一篇文章: