文章目录
- 一、什么是中值滤波?
- 二、均值滤波和中值滤波对比
- 三、FPGA实现
- 3.1 Verilog代码
- 3.2 仿真验证
一、什么是中值滤波?
在前一篇《FPGA图像处理之均值滤波》中,我们了解到了图像处理中常遇到的一些噪声类型以及均值滤波的原理以及实现。我们知道均值滤波实际上没有去掉噪声,而是把噪声平均到了每一个像素点上。这样就会导致图像变得很模糊,而且如果噪声是椒盐噪声的话,均值滤波后放大看图像会有一些灰度值不均匀的小块。
而中值滤波就能很好的消除噪声,特别是椒盐噪声。中值滤波算法就是取滤波窗口的中间值来替换掉原来的值,因为噪声通常是亮度异常的点,过亮或者过暗。因此在一个滤波窗口内灰度值排序的话,噪点就要么靠前要么靠后,此时我们使用中间值来替换掉滤波窗口中心像素值的话,噪点就不存在了,理论上能消除噪声。
二、均值滤波和中值滤波对比
以下是中值滤波和均值滤波对椒盐噪声降噪的对比,matlab代码如下:
% 读取原始图像
originalImage = imread('yuanyang.bmp'); % 替换为你的图像文件名
% 将图像转换为灰度图像
grayImage = rgb2gray(originalImage);
% 添加椒盐噪声(2% 椒盐噪声)
noisyImage = imnoise(grayImage, 'salt & pepper', 0.02);
% 应用均值滤波
meanFilteredImage = imfilter(noisyImage, fspecial('average', [3 3]), 'replicate');
% 应用中值滤波
medianFilteredImage = medfilt2(noisyImage, [3 3]); % 使用 3x3 的窗口
% 显示图像
figure;
subplot(1, 3, 1);
imshow(noisyImage);
title('添加噪声后的图像');
subplot(1, 3, 2);
imshow(meanFilteredImage);
title('均值滤波后的图像');
subplot(1, 3, 3);
imshow(medianFilteredImage);
title('中值滤波后的图像');
我们可以很明显看到,中值滤波对椒盐噪声的降噪更好,我们把中值滤波和均值滤波放大来看一下
三、FPGA实现
对于中值滤波最重要的是找出滤波窗口里几个数的中间值。最经典的算法就是冒泡排序法,其基本思想是:在待排序的一组数中,将相邻的两个数进行比较,若前面的数比后面的数大就交换两数,否则不交换;如此下去,直至最终完成排序。就像气泡在水里往上冒一样,但是冒泡排序法的时间复杂度为
O
(
N
2
)
O(N^2)
O(N2) ,虽然我们只需要排序一半即可,但是也需要很长的时间周期。例如一个3*3的滤波窗口,我们对需要在9个灰度值取出中间值,我们需要比较 8 + 7 + 6 + 5 = 26个周期。
因此我们可以充分利用FPGA并行的优势以面积换时间:
- 在得到一个3*3矩阵后,一个时钟周期可以比较出每一行的最大值,最小值以及中间值,总共得到3行的最大,最小以及中间值。
- 在第二个时钟周期我们可以比较出每一行的最大值之间的最大值,最小值之间的最小值,和中间值之间的中间值,一共得到了三个数,这三行数据中的 最大值,中间值,和最小值。
- 第三个时钟周期我们找出中间值然后替换掉滤波窗口的中心像素即可。
我们利用FPGA的优势可以将中值滤波的算法从26个时钟周期变成3个时钟周期。
3.1 Verilog代码
前面的图像扩展以及矩阵生成还是用均值滤波里面的,主要是后面的找中值。我们先定义9个寄存器,来存放每一行的最大值,最小值以及中间值。
reg [7:0] row1_min_data ; //第一行3个数最小的值
reg [7:0] row1_mid_data ; //第一行3个数中间的值
reg [7:0] row1_max_data ; //第一行3个数最大的值
reg [7:0] row2_min_data ; //第二行3个数最小的值
reg [7:0] row2_mid_data ; //第二行3个数中间的值
reg [7:0] row2_max_data ; //第二行3个数最大的值
reg [7:0] row3_min_data ; //第三行3个数最小的值
reg [7:0] row3_mid_data ; //第三行3个数中间的值
reg [7:0] row3_max_data ; //第三行3个数最大的值
//延迟一拍计算3*3矩阵中,每一行的中间值和最大值和最小值
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
row1_min_data <= 'd0;
else if((w_matrix_11 <= w_matrix_12)&&(w_matrix_11 <= w_matrix_13))
row1_min_data <= w_matrix_11;
else if((w_matrix_12 <= w_matrix_11)&&(w_matrix_12 <= w_matrix_13))
row1_min_data <= w_matrix_12;
else
row1_min_data <= w_matrix_13;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
row1_mid_data <= 'd0;
else if(((w_matrix_11 <= w_matrix_12)&&(w_matrix_11 >= w_matrix_13))||((w_matrix_11 >= w_matrix_12)&&(w_matrix_11 <= w_matrix_13)))
row1_mid_data <= w_matrix_11;
else if(((w_matrix_12 <= w_matrix_11)&&(w_matrix_12 >= w_matrix_13))||((w_matrix_12 >= w_matrix_11)&&(w_matrix_12 <= w_matrix_13)))
row1_mid_data <= w_matrix_12;
else
row1_mid_data <= w_matrix_13;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
row1_max_data <= 'd0;
else if((w_matrix_11 >= w_matrix_12)&&(w_matrix_11 >= w_matrix_13))
row1_max_data <= w_matrix_11;
else if((w_matrix_12 >= w_matrix_11)&&(w_matrix_12 >= w_matrix_13))
row1_max_data <= w_matrix_12;
else
row1_max_data <= w_matrix_13;
end
//在这一拍内,同时计算第二行的中间值和最大值和最小值
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
row2_min_data <= 'd0;
else if((w_matrix_21 <= w_matrix_22)&&(w_matrix_21 <= w_matrix_23))
row2_min_data <= w_matrix_21;
else if((w_matrix_22 <= w_matrix_21)&&(w_matrix_22 <= w_matrix_23))
row2_min_data <= w_matrix_22;
else
row2_min_data <= w_matrix_23;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
row2_mid_data <= 'd0;
else if(((w_matrix_21 <= w_matrix_22)&&(w_matrix_21 >= w_matrix_23))||((w_matrix_21 >= w_matrix_22)&&(w_matrix_21 <= w_matrix_23)))
row2_mid_data <= w_matrix_21;
else if(((w_matrix_22 <= w_matrix_21)&&(w_matrix_22 >= w_matrix_23))||((w_matrix_22 >= w_matrix_21)&&(w_matrix_22 <= w_matrix_23)))
row2_mid_data <= w_matrix_22;
else
row2_mid_data <= w_matrix_23;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
row2_max_data <= 'd0;
else if((w_matrix_21 >= w_matrix_22)&&(w_matrix_21 >= w_matrix_23))
row2_max_data <= w_matrix_21;
else if((w_matrix_22 >= w_matrix_21)&&(w_matrix_22 >= w_matrix_23))
row2_max_data <= w_matrix_22;
else
row2_max_data <= w_matrix_23;
end
//在这一拍内,同时计算第三行的中间值和最大值和最小值
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
row3_min_data <= 'd0;
else if((w_matrix_31 <= w_matrix_32)&&(w_matrix_31 <= w_matrix_33))
row3_min_data <= w_matrix_31;
else if((w_matrix_32 <= w_matrix_31)&&(w_matrix_32 <= w_matrix_33))
row3_min_data <= w_matrix_32;
else
row3_min_data <= w_matrix_33;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
row3_mid_data <= 'd0;
else if((w_matrix_valid == 1'b1)&&(((w_matrix_31 <= w_matrix_32)&&(w_matrix_31 >= w_matrix_33)))||((w_matrix_31 >= w_matrix_32)&&(w_matrix_31 <= w_matrix_33)))
row3_mid_data <= w_matrix_31;
else if((w_matrix_valid == 1'b1)&&(((w_matrix_32 <= w_matrix_31)&&(w_matrix_32 >= w_matrix_33)))||((w_matrix_32 >= w_matrix_31)&&(w_matrix_32 <= w_matrix_33)))
row3_mid_data <= w_matrix_32;
else
row3_mid_data <= w_matrix_33;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
row3_max_data <= 'd0;
else if((w_matrix_valid == 1'b1)&&(w_matrix_31 >= w_matrix_32)&&(w_matrix_31 >= w_matrix_33))
row3_max_data <= w_matrix_31;
else if((w_matrix_valid == 1'b1)&&(w_matrix_32 >= w_matrix_31)&&(w_matrix_32 >= w_matrix_33))
row3_max_data <= w_matrix_32;
else
row3_max_data <= w_matrix_33;
end
我们找到每一行的最大值,最小值,中间值后,再找出 其中的最大值,中间值,以及最小值:
reg [7:0] mix_min_data ; //三行最小的值
reg [7:0] mix_mid_data ; //三行中间的值
reg [7:0] mix_max_data ; //三行最大的值
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
mix_min_data <= 'd0;
else if((row1_min_data <= row2_min_data)&&(row1_min_data <= row3_min_data))
mix_min_data <= row1_min_data;
else if((row2_min_data <= row1_min_data)&&(row2_min_data <= row3_min_data))
mix_min_data <= row2_min_data;
else
mix_min_data <= row3_min_data;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
mix_mid_data <= 'd0;
else if(((row1_mid_data <= row2_mid_data)&&(row1_mid_data >= row3_mid_data))||((row1_mid_data >= row2_mid_data)&&(row1_mid_data <= row3_mid_data)))
mix_mid_data <= row1_mid_data;
else if(((row2_mid_data <= row1_mid_data)&&(row2_mid_data >= row3_mid_data))||((row2_mid_data >= row1_mid_data)&&(row2_mid_data <= row3_mid_data)))
mix_mid_data <= row2_mid_data;
else
mix_mid_data <= row3_mid_data;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
mix_max_data <= 'd0;
else if((row1_max_data >= row2_max_data)&&(row1_max_data >= row3_max_data))
mix_max_data <= row1_max_data;
else if((row2_max_data >= row1_max_data)&&(row2_max_data >= row3_max_data))
mix_max_data <= row2_max_data;
else
mix_max_data <= row3_max_data;
end
最后再把中间值复制给输出像素点:
reg [7:0] matrix_mid_data ; //一个3*3矩阵中中间的值
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
matrix_mid_data <= 'd0;
else if(((mix_min_data <= mix_mid_data)&&(mix_min_data >= mix_max_data))||((mix_min_data >= mix_mid_data)&&(mix_min_data <= mix_max_data)))
matrix_mid_data <= mix_min_data;
else if(((mix_mid_data <= mix_min_data)&&(mix_mid_data >= mix_max_data))||((mix_mid_data >= mix_min_data)&&(mix_mid_data <= mix_max_data)))
matrix_mid_data <= mix_mid_data;
else
matrix_mid_data <= mix_max_data;
end
3.2 仿真验证
仿真输入图像依然用均值滤波那幅加了椒盐噪声的图片,然后仿真生成滤波后的图像,再做对比。
仿真读取文件成功,然后直接运行
可以看出,椒盐噪声很好的被去除了,我们放大一下细节,对比一下和均值滤波的区别。
左边为中值滤波,右边为均值滤波,可以看出效果对比还是很明显。