FPGA实现图像的线性滤波:高斯滤波
在进行数学仿真或者误差评估时,往往认为传感器所引入的噪声服从正态分布(高斯白噪声),这个时候用高斯滤波器就可以很好地消除高斯噪声。高斯滤波也是一种线性平滑滤波,通俗地讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波的具体操作:用一个模板(或称卷积、掩模)扫描图像中的每一个像素, 用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。
高斯滤波被用作为平滑滤波器的本质原因是因为它是一个低通滤波器,而且大部份基于卷积平滑滤波器都是低通滤波器。高斯模板名字的由来是二维高斯函数,即我们熟悉的二维正态分布密度函数,如下所示:


常用的 3x3 的高斯模板如下所示:

高斯滤波后图像被平滑的程度取决于标准差。它的输出是临域像素的加权平均,同时离中心越近的像素权重越高。因此,高斯滤波比均值滤波的平滑效果更柔和,而且边缘保留的也更好。高斯滤波算法克服了边界效应,因而滤波后的图像较好。高斯滤波的平滑力度不如均值滤波,因此其保留细节的能力也比均值滤波好。
一、MATLAB实现
偷下懒,用函数法实现一下:
clc; clear all; close all; RGB= imread('flower.bmp'); %读取图片 gray = rgb2gray(RGB); %灰度图 Gauss_3x3 = fspecial('gaussian',3,2); %sigma=2的3*3高斯模板 Gauss = imfilter(gray, Gauss_3x3); %高斯滤波 subplot(2,1,1); imshow(gray); title('灰度图'); subplot(2,1,2); imshow(Gauss); title('高斯滤波');
点击运行,得到如下结果:

结果和理论一致,图像变得平滑了。
二、FPGA实现

1、形成3x3矩阵
这个在前面的博客花了3篇来解释,就不多说了,我把3x3矩阵的代码用一个专门的 .v 文件写好,这里直接调用即可。输入是灰度数据,即 YCbCr格式中的 8bit Y分量,输出是矩阵数据。耗费 1 个时钟周期。
//**************************************************************************
// *** 名称 : matrix_3x3_8bit.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2020年3月
// *** 描述 : 3x3矩阵,边界采用像素复制,最大支持1024x1024,耗费1clk
//**************************************************************************
module matrix_3x3_8bit
//========================< 参数 >==========================================
#(
parameter H_DISP = 12'd480 , //图像宽度
parameter V_DISP = 12'd272 //图像高度
)
//========================< 端口 >==========================================
(
input wire clk ,
input wire rst_n ,
//input ---------------------------------------------
input wire din_vld ,
input wire [ 7:0] din ,
//output --------------------------------------------
output reg [ 7:0] matrix_11 ,
output reg [ 7:0] matrix_12 ,
output reg [ 7:0] matrix_13 ,
output reg [ 7:0] matrix_21 ,
output reg [ 7:0] matrix_22 ,
output reg [ 7:0] matrix_23 ,
output reg [ 7:0] matrix_31 ,
output reg [ 7:0] matrix_32 ,
output reg [ 7:0] matrix_33
);
//========================< 信号 >==========================================
reg [11:0] cnt_col ;
wire add_cnt_col ;
wire end_cnt_col ;
reg [11:0] cnt_row ;
wire add_cnt_row ;
wire end_cnt_row ;
wire wr_en_1 ;
wire wr_en_2 ;
wire rd_en_1 ;
wire rd_en_2 ;
wire [ 7:0] q_1 ;
wire [ 7:0] q_2 ;
wire [ 7:0] row_1 ;
wire [ 7:0] row_2 ;
wire [ 7:0] row_3 ;
//==========================================================================
//== FIFO例化,show模式,深度为大于两行数据个数
//==========================================================================
fifo_show_2048x8 u1
(
.clock (clk ),
.data (din ),
.wrreq (wr_en_1 ),
.rdreq (rd_en_1 ),
.q (q_1 )
);
fifo_show_2048x8 u2
(
.clock (clk ),
.data (din ),
.wrreq (wr_en_2 ),
.rdreq (rd_en_2 ),
.q (q_2 )
);
//==========================================================================
//== 行列划分
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt_col <= 12'd0;
else if(add_cnt_col) begin
if(end_cnt_col)
cnt_col <= 12'd0;
else
cnt_col <= cnt_col + 12'd1;
end
end
assign add_cnt_col = din_vld;
assign end_cnt_col = add_cnt_col && cnt_col== H_DISP-12'd1;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
cnt_row <= 12'd0;
else if(add_cnt_row) begin
if(end_cnt_row)
cnt_row <= 12'd0;
else
cnt_row <= cnt_row + 12'd1;
end
end
assign add_cnt_row = end_cnt_col;
assign end_cnt_row = add_cnt_row && cnt_row== V_DISP-12'd1;
//==========================================================================
//== fifo 读写信号
//==========================================================================
assign wr_en_1 = (cnt_row < V_DISP - 12'd1) ? din_vld : 1'd0; //不写最后1行
assign rd_en_1 = (cnt_row > 12'd0 ) ? din_vld : 1'd0; //从第1行开始读
assign wr_en_2 = (cnt_row < V_DISP - 12'd2) ? din_vld : 1'd0; //不写最后2行
assign rd_en_2 = (cnt_row > 12'd1 ) ? din_vld : 1'd0; //从第2行开始读
//==========================================================================
//== 形成 3x3 矩阵,边界采用像素复制
//==========================================================================
//矩阵数据选取
//---------------------------------------------------
assign row_1 = q_2;
assign row_2 = q_1;
assign row_3 = din;
//打拍形成矩阵,1clk
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
{matrix_11, matrix_12, matrix_13} <= {8'd0, 8'd0, 8'd0};
{matrix_21, matrix_22, matrix_23} <= {8'd0, 8'd0, 8'd0};
{matrix_31, matrix_32, matrix_33} <= {8'd0, 8'd0, 8'd0};
end
//------------------------------------------------------------------------- 第1排矩阵
else if(cnt_row == 12'd0) begin
if(cnt_col == 12'd0) begin //第1个矩阵
{matrix_11, matrix_12, matrix_13} <= {row_3, row_3, row_3};
{matrix_21, matrix_22, matrix_23} <= {row_3, row_3, row_3};
{matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
end
else begin //剩余矩阵
{matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_3};
{matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_3};
{matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
end
end
//------------------------------------------------------------------------- 第2排矩阵
else if(cnt_row == 12'd1) begin
if(cnt_col == 12'd0) begin //第1个矩阵
{matrix_11, matrix_12, matrix_13} <= {row_2, row_2, row_2};
{matrix_21, matrix_22, matrix_23} <= {row_2, row_2, row_2};
{matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
end
else begin //剩余矩阵
{matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_2};
{matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_2};
{matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
end
end
//------------------------------------------------------------------------- 剩余矩阵
else begin
if(cnt_col == 12'd0) begin //第1个矩阵
{matrix_11, matrix_12, matrix_13} <= {row_1, row_1, row_1};
{matrix_21, matrix_22, matrix_23} <= {row_2, row_2, row_2};
{matrix_31, matrix_32, matrix_33} <= {row_3, row_3, row_3};
end
else begin //剩余矩阵
{matrix_11, matrix_12, matrix_13} <= {matrix_12, matrix_13, row_1};
{matrix_21, matrix_22, matrix_23} <= {matrix_22, matrix_23, row_2};
{matrix_31, matrix_32, matrix_33} <= {matrix_32, matrix_33, row_3};
end
end
end
endmodule
2、高斯滤波
采用流水线思想,一级一级的往下运算,共耗费 3 个时钟周期。
//**************************************************************************
// *** 名称 : gaussian.v
// *** 作者 : 咸鱼FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2020年3月
// *** 描述 : Y分量进行gaussian高斯滤波
//**************************************************************************
module gaussian
//========================< 参数 >==========================================
#(
parameter H_DISP = 12'd480 , //图像宽度
parameter V_DISP = 12'd272 //图像高度
)
//========================< 端口 >==========================================
(
input wire clk , //时钟
input wire rst_n , //复位
//input ---------------------------------------------
input wire Y_hsync , //Y分量行同步
input wire Y_vsync , //Y分量场同步
input wire [ 7:0] Y_data , //Y分量数据
input wire Y_de , //Y分量数据使能
//output --------------------------------------------
output wire gaussian_hsync , //gaussian行同步
output wire gaussian_vsync , //gaussian场同步
output reg [ 7:0] gaussian_data , //gaussian数据
output wire gaussian_de //gaussian数据使能
);
//========================< 信号 >==========================================
//matrix_3x3 ----------------------------------------
wire [ 7:0] matrix_11 ;
wire [ 7:0] matrix_12 ;
wire [ 7:0] matrix_13 ;
wire [ 7:0] matrix_21 ;
wire [ 7:0] matrix_22 ;
wire [ 7:0] matrix_23 ;
wire [ 7:0] matrix_31 ;
wire [ 7:0] matrix_32 ;
wire [ 7:0] matrix_33 ;
//gaussian ------------------------------------------
reg [11:0] gs_1 ;
reg [11:0] gs_2 ;
reg [11:0] gs_3 ;
reg [11:0] gs ;
//同步 ----------------------------------------------
reg [ 3:0] Y_de_r ;
reg [ 3:0] Y_hsync_r ;
reg [ 3:0] Y_vsync_r ;
//==========================================================================
//== matrix_3x3_8bit,生成3x3矩阵,输入和使能需对齐,耗费1clk
//==========================================================================
//--------------------------------------------------- 矩阵顺序
// {matrix_11, matrix_12, matrix_13}
// {matrix_21, matrix_22, matrix_23}
// {matrix_31, matrix_32, matrix_33}
//--------------------------------------------------- 模块例化
matrix_3x3_8bit
#(
.H_DISP (H_DISP ),
.V_DISP (V_DISP )
)
u_matrix_3x3_8bit
(
.clk (clk ),
.rst_n (rst_n ),
.din_vld (Y_de ),
.din (Y_data ),
.matrix_11 (matrix_11 ),
.matrix_12 (matrix_12 ),
.matrix_13 (matrix_13 ),
.matrix_21 (matrix_21 ),
.matrix_22 (matrix_22 ),
.matrix_23 (matrix_23 ),
.matrix_31 (matrix_31 ),
.matrix_32 (matrix_32 ),
.matrix_33 (matrix_33 )
);
//==========================================================================
//== 高斯滤波,耗费3clk
//==========================================================================
//clk1,左中右的一列值
//---------------------------------------------------
always @ (posedge clk or negedge rst_n)begin
if(!rst_n)begin
gs_1 <= 12'd0;
gs_2 <= 12'd0;
gs_3 <= 12'd0;
end
else begin
gs_1 <= matrix_11 + matrix_12 * 2 + matrix_13;
gs_2 <= matrix_21 * 2 + matrix_22 * 4 + matrix_23 * 2;
gs_3 <= matrix_31 + matrix_32 * 2 + matrix_33;
end
end
//clk2,相加
//---------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
gs <= 12'd0;
end
else begin
gs <= gs_1 + gs_2 + gs_3;
end
end
//clk3,除以16 -> 右移4位 -> 取高8位
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
gaussian_data <= 12'd0;
end
else begin
gaussian_data <= gs[11:4];
end
end
//==========================================================================
//== 信号同步
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
Y_de_r <= 4'b0;
Y_hsync_r <= 4'b0;
Y_vsync_r <= 4'b0;
end
else begin
Y_de_r <= {Y_de_r[2:0], Y_de};
Y_hsync_r <= {Y_hsync_r[2:0], Y_hsync};
Y_vsync_r <= {Y_vsync_r[2:0], Y_vsync};
end
end
assign gaussian_de = Y_de_r[3];
assign gaussian_hsync = Y_hsync_r[3];
assign gaussian_vsync = Y_vsync_r[3];
endmodule
三、上板验证
灰度图:

高斯滤波:

从结果可以看出,图像变得更加平滑,但力度不如上篇的均值滤波,整体细节保留得非常好。
[1] OpenS Lee:FPGA开源工作室(公众号)
[2] CrazyBingo:基于VIP_Board Mini的FPGA视频图像算法(HDL-VIP)开发教程-V1.6
[3] NingHechuan:FPGA图像处理教程

浙公网安备 33010602011771号