1 中值滤波的基本原理
中值滤波是一种非线性图像处理方法,与均值滤波思想不同,中值滤波通过对邻域内像素灰度值的排序中位数来决定中心像素的灰度,具体思想为基于滑动窗口求得窗口中像素值的中间值,用该值来替代中间位置像素的值。
中值滤波对于消除脉冲干扰,对孤立的噪声即椒盐噪声,脉冲噪声有很好的滤波效果,可以保护图像的边缘特性,不会使图像出现显著的模糊。
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;