FPGA图像处理之高斯滤波


一、什么是高斯滤波

  高斯滤波是一种常用的图像处理技术,主要用于平滑图像、去除噪声和减少细节。高斯分布就是常说的正态分布,其高斯函数的波形如下所示:

在这里插入图片描述
  高斯函数分为一维高斯和二维高斯,其函数为:
{ 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π σ1e2σ2x2,一维高斯分布函数= 2π σ21e2σ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生成加了高斯白噪声的图像,如下所示:

在这里插入图片描述
  运行仿真,生成了滤波后的图像,我们打开对比一下:

在这里插入图片描述
  看得出来降噪效果还是很明显,如果高斯函数的 σ σ σ越大,降噪效果越好,但是图像也会变得模糊。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱奔跑的虎子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值