文章目录
一、什么是高斯滤波
高斯滤波是一种常用的图像处理技术,主要用于平滑图像、去除噪声和减少细节。高斯分布就是常说的正态分布,其高斯函数的波形如下所示:
高斯函数分为一维高斯和二维高斯,其函数为:
{
G
(
x
)
=
1
2
π
σ
e
−
x
2
2
σ
2
,一维高斯分布函数
G
(
x
)
=
1
2
π
σ
2
e
−
x
2
+
y
2
2
σ
2
,二维高斯分布函数
\left\{ \begin{aligned} G(x)& = \ \frac{1}{\sqrt{2π}σ}e^{-\frac{x^2}{2σ^2}} ,一维高斯分布函数 \\ G(x)& = \ \frac{1}{\sqrt{2π}σ^2}e^{-\frac{x^2+y^2}{2σ^2}},二维高斯分布函数 \\ \end{aligned} \right.
⎩
⎨
⎧G(x)G(x)= 2πσ1e−2σ2x2,一维高斯分布函数= 2πσ21e−2σ2x2+y2,二维高斯分布函数
其中
σ
σ
σ越大,高斯函数分布越平缓,相邻的像素权重就会变大,反之
σ
σ
σ越小,高斯函数越集中在中心区域,图像中心的像素值权重就会变大。我们前面学习的均值滤波和中值滤波,每个滑动窗口中的像素权重值都是一样,而高斯滤波可以调整。对于白噪声而言,图像每个像素值都被噪声污染,此时再使用均值滤波和中值滤波,效果有效,matlab代码如下所示:
clc;
close all;
% 读取彩色图像
img = imread('yuanyang.bmp'); % 替换为你的图像文件路径
% 添加高斯白噪声
noise_variance = 0.08; % 噪声方差
noisy_img = imnoise(img, 'gaussian', 0, noise_variance); % 添加噪声
% 使用均值滤波
mean_filtered_img = imfilter(noisy_img, fspecial('average', [3 3]), 'replicate');
% 使用中值滤波
median_filtered_img = noisy_img; % 初始化中值滤波结果
for channel = 1:3
median_filtered_img(:, :, channel) = medfilt2(noisy_img(:, :, channel), [3 3]);
end
% 使用高斯滤波
sigma = 2; % 高斯滤波的标准差
gaussian_filtered_img = imgaussfilt(noisy_img, sigma);
% 创建一个新的图形窗口
figure;
% 显示原始图像
subplot(3, 2, 1);
imshow(img);
title('原始图像');
% 显示加噪声后的图像
subplot(3, 2, 2);
imshow(noisy_img);
title('加噪声后的图像');
% 显示均值滤波后的图像
subplot(3, 2, 3);
imshow(mean_filtered_img);
title('均值滤波后的图像');
% 显示中值滤波后的图像
subplot(3, 2, 4);
imshow(median_filtered_img);
title('中值滤波后的图像');
% 显示高斯滤波后的图像
subplot(3, 2, 5);
imshow(gaussian_filtered_img);
title('高斯滤波后的图像');
% 调整布局
sgtitle('图像去噪处理比较'); % 添加总标题
二、高斯滤波的实现
高斯函数也属于一种低通滤波器,而高斯白噪声在通信领域在每一个频率位置都有,因此高斯滤波也就是一种卷积运算。能够滤除掉图像中高频部分的噪声。在实现高斯滤波之前,我们依然需要使用前面中值滤波和均值滤波都要使用的滤波模板,33和55都可以。如果使用33模板,直接调用之前的程序皆可。这次我们使用55的滑动窗口,因此需要生成55的高斯模板以及55的图像窗口,生成5*5的高斯模板matlab代码如下:
function function_kernel(size, sigma)
% 创建一个 size x size 的高斯核
kernel = zeros(size, size);
center = floor(size / 2); % 计算中心索引
% 生成高斯核
for x = 1:size
for y = 1:size
x_offset = x - center - 1; % 相对于中心的偏移量
y_offset = y - center - 1; % 相对于中心的偏移量
kernel(x, y) = (1 / (2 * pi * sigma^2)) * exp(-(x_offset^2 + y_offset^2) / (2 * sigma^2));
end
end
% 归一化
kernel = kernel / sum(kernel(:));
% 显示结果
disp(kernel);
end
然后在命令窗口调用函数,可以看到生成的高斯模板。
由于fpga不擅长处理小数,因此我们需要对小数进行定点化,这里我们扩大1024倍,到时候取结果的时候再除以1024即可,代码添加这:
% 归一化
kernel = kernel / sum(kernel(:));
kernel = kernel * 1024;
% 显示结果
disp(kernel);
三、FPGA实现
整体思路和前面的滤波一样,就是用滑动出来的像素与高斯函数卷积。
3.1 图像五行缓存
前面的《FPGA图像处理之三行缓存》文章中,我们讲了图像三行缓存的原理以及实现,我们知道要实现图像的三行缓存,只需要两个FIFO皆可,同理我们要实现5行缓存,就需要4个FIFO,FIFO例化如下:
img_line_buffer_fifo u0_img_line_buffer_fifo (
.clk (sys_clk ), // input wire clk
.srst (sys_rst ), // input wire srst
.din (fifo1_wr_data ), // input wire [23 : 0] din
.wr_en(fifo1_wr_en ), // input wire wr_en
.rd_en(rd_en ), // input wire rd_en
.dout (fifo1_q ), // output wire [23 : 0] dout
.full (), // output wire full
.empty() // output wire empty
);
img_line_buffer_fifo u1_img_line_buffer_fifo (
.clk (sys_clk ), // input wire clk
.srst (sys_rst ), // input wire srst
.din (fifo2_wr_data ), // input wire [23 : 0] din
.wr_en(fifo2_wr_en ), // input wire wr_en
.rd_en(rd_en ), // input wire rd_en
.dout (fifo2_q ), // output wire [23 : 0] dout
.full (), // output wire full
.empty() // output wire empty
);
img_line_buffer_fifo u2_img_line_buffer_fifo (
.clk (sys_clk ), // input wire clk
.srst (sys_rst ), // input wire srst
.din (fifo3_wr_data ), // input wire [23 : 0] din
.wr_en(fifo3_wr_en ), // input wire wr_en
.rd_en(rd_en ), // input wire rd_en
.dout (fifo3_q ), // output wire [23 : 0] dout
.full (), // output wire full
.empty() // output wire empty
);
img_line_buffer_fifo u3_img_line_buffer_fifo (
.clk (sys_clk ), // input wire clk
.srst (sys_rst ), // input wire srst
.din (fifo4_wr_data ), // input wire [23 : 0] din
.wr_en(fifo4_wr_en ), // input wire wr_en
.rd_en(rd_en ), // input wire rd_en
.dout (fifo4_q ), // output wire [23 : 0] dout
.full (), // output wire full
.empty() // output wire empty
);
原理和三行一样,前面四行分别写入四个FIFO,第五行输入写入FIFO4中,同时输出所有的FIFO,这样就能实现5行图像数据同时输出。
3.2 图像拓展4行4列
前面我们使用33的滑动窗口知道,图像最终大小会缩小两行两列。对于55的滑动窗口,图像最终会缩小四行四列,因此我们要将图像提前扩展四行四列,原理如下:
扩展后在用5*5的滑动窗口,生成出来的图像大小才不会变。
3.3 生成5*5的滑动窗口
在前面实现五行缓存以及图像扩展后,我们需要生成图像的5*5滑动窗口模板,矩阵最终的输出如上所示,第一个矩阵为(66,66,66,66,66,66,66,66,66,66,66,66,0,1,2,66,66,50,51,52,66,66,100,101,102),其它的依次类推。
3.3.1 5*5滑动窗口的仿真验证
我们照样输入50*50大小的图像数据,从上到下,从左到右依次为0-2499,仿真代码如下:
`timescale 1ns / 1ps
module tb_img_5line_buffer();
reg sys_clk ;
reg sys_rst ;
reg i_img_data_valid ;
reg [23:0] i_img_data ;
reg [12:0] cnt ;
reg [5:0] waite_cnt ;
initial begin
sys_clk =0;
sys_rst = 1;
i_img_data_valid = 0;
i_img_data = 'd0;
#200;
sys_rst = 0;
end
always #5 sys_clk = ~sys_clk;
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
waite_cnt <= 'd0;
else if(cnt == 49)
waite_cnt <= 'd0;
else if(i_img_data_valid == 1'b0)
waite_cnt <= waite_cnt + 1'b1;
else
waite_cnt <= waite_cnt;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
i_img_data_valid <= 1'b0;
else if(cnt == 49)
i_img_data_valid <= 1'b0;
else if((waite_cnt == 10) && (i_img_data <= 2499))
i_img_data_valid <= 1'b1;
else
i_img_data_valid <= i_img_data_valid;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
cnt <= 'd0;
else if(cnt == 49)
cnt <= 'd0;
else if(i_img_data_valid == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
end
always @(posedge sys_clk) begin
if(sys_rst == 1'b1)
i_img_data <= 'd0;
//else if(i_img_data == 49)
// i_img_data <= 'd0;
else if(i_img_data_valid == 1'b1)
i_img_data <= i_img_data + 1'b1;
else
i_img_data <= i_img_data;
end
generate_5X5_24bit#(
.IMG_WIDTH ( 50 ),
.IMG_HEIGHT ( 50 )
)u_generate_5X5_24bit(
.sys_clk ( sys_clk ),
.sys_rst ( sys_rst ),
.i_img_data ( i_img_data ),
.i_img_data_valid ( i_img_data_valid ),
.o_matrix_11 ( ),
.o_matrix_12 ( ),
.o_matrix_13 ( ),
.o_matrix_14 ( ),
.o_matrix_15 ( ),
.o_matrix_21 ( ),
.o_matrix_22 ( ),
.o_matrix_23 ( ),
.o_matrix_24 ( ),
.o_matrix_25 ( ),
.o_matrix_31 ( ),
.o_matrix_32 ( ),
.o_matrix_33 ( ),
.o_matrix_34 ( ),
.o_matrix_35 ( ),
.o_matrix_41 ( ),
.o_matrix_42 ( ),
.o_matrix_43 ( ),
.o_matrix_44 ( ),
.o_matrix_45 ( ),
.o_matrix_51 ( ),
.o_matrix_52 ( ),
.o_matrix_53 ( ),
.o_matrix_54 ( ),
.o_matrix_55 ( ),
.o_matrix_valid ( )
);
endmodule
3.3.2 仿真观察
我们直接运行仿真,先看输入的图像数据:
输入的图像数据为0-2499连续的数,符合预期,我们直接看矩阵输出。
我们可以看到第一个矩阵输出为(66,66,66,66,66,66,66,66,66,66,66,66,0,1,2,66,66,50,51,52,66,66,100,101,102),和我们预期的一样,其它的矩阵输出也是一样。
3.4 高斯模板的卷积
我们用生成出来的5*5矩阵和高斯函数模板相乘,
// [32,38,40,38,32]
// [38,45,47,45,38]
// [40,47,50,47,40]
// [38,45,47,45,38]
// [32,38,40,38,32]
//第一拍计算各自乘法
always @(posedge sys_clk)
begin
mult_result11 <= w_matrix_11[23:16] * 6'd32;
mult_result12 <= w_matrix_12[23:16] * 6'd38;
mult_result13 <= w_matrix_13[23:16] * 6'd40;
mult_result14 <= w_matrix_14[23:16] * 6'd38;
mult_result15 <= w_matrix_15[23:16] * 6'd32;
mult_result21 <= w_matrix_21[23:16] * 6'd38;
mult_result22 <= w_matrix_22[23:16] * 6'd45;
mult_result23 <= w_matrix_23[23:16] * 6'd47;
mult_result24 <= w_matrix_24[23:16] * 6'd45;
mult_result25 <= w_matrix_25[23:16] * 6'd38;
mult_result31 <= w_matrix_31[23:16] * 6'd40;
mult_result32 <= w_matrix_32[23:16] * 6'd47;
mult_result33 <= w_matrix_33[23:16] * 6'd50;
mult_result34 <= w_matrix_34[23:16] * 6'd47;
mult_result35 <= w_matrix_35[23:16] * 6'd40;
mult_result41 <= w_matrix_41[23:16] * 6'd38;
mult_result42 <= w_matrix_42[23:16] * 6'd45;
mult_result43 <= w_matrix_43[23:16] * 6'd47;
mult_result44 <= w_matrix_44[23:16] * 6'd45;
mult_result45 <= w_matrix_45[23:16] * 6'd38;
mult_result51 <= w_matrix_51[23:16] * 6'd32;
mult_result52 <= w_matrix_52[23:16] * 6'd38;
mult_result53 <= w_matrix_53[23:16] * 6'd40;
mult_result54 <= w_matrix_54[23:16] * 6'd38;
mult_result55 <= w_matrix_55[23:16] * 6'd32;
end
因为生成高斯模板时候扩大了1024倍,所以最后取像素点的时候在右移10位。
3.5 仿真观测
我们还是用中值滤波,均值滤波的仿真代码,先用matlab生成加了高斯白噪声的图像,如下所示:
运行仿真,生成了滤波后的图像,我们打开对比一下:
看得出来降噪效果还是很明显,如果高斯函数的
σ
σ
σ越大,降噪效果越好,但是图像也会变得模糊。