FPGA实现图像的线性滤波:均值滤波
图像处理领域的线性滤波器主要包括均值滤波和高斯滤波等平滑滤波器,此外,还有Sobel算子、Laplas算子和梯度运算等锐化滤波器。线性滤波通常的处理方法是利用一个指定尺寸的掩模(mask)对图像进行卷积,通常,这个掩模(mask)也可以称为滤波器(filter)、 核(kernel)、模板(template)和窗(window) 等。这里给出线性滤波器的定义如下:设r为处理窗口的半径,f(i,j) 为窗口内模板的灰度值, I(x, y)为输入像素值,g(x,y)
为输出像素值,则有如下定义:
令g(x,y)滑过整幅图像,即对整幅图像做一个 卷积处理,就得到最后的滤波结果。由卷积定理可以得到,在频域将模板直接与图像进行像素相乘也可以达到相同的目的。
均值滤波是典型的线性滤波算法,主要方法为邻域平均法,即用一个图像区域的各个像素的平均值来代替原图像的各个像素值。均值滤波的主要作用是减小图像灰度值的“尖锐”变化从而达到减小噪声的目的。但是,由于图像边缘在一般情况下也是由图像灰度尖锐化引起的,因此,均值滤波也存在边缘模糊的问题。
一、理论分析
一般来说, 图像具有局部连续性质,即相邻像素的数值相近,而噪声的存在使得在噪声点处产生灰度跳跃,但- -般我们可以合理地假设偶尔出现的噪声影响并没有改变图像局部连续的性质,例如下面的局部图像 f _sub,灰色底纹标识的为噪声点,在图像中表现为亮区中的 2 个暗点:

对 f 用 3X3 的平均模板进行平滑滤波后,得到的平滑后图像 g_ sub:

显然,通过平滑滤波原局部图像 f_sub 中噪声点的灰度值得到了有效修正,像这样将每一个点用周围点的平均替代从而达到减少噪声影响的过程就称为平滑或模糊。随着模板的增大,滤波过程在平滑更多噪声的同时也使得图像变得越来越模糊,这是由平均模板的工作机理决定的。当图像细节与滤波器模板大小相近时,图像细节就会受到比较大的影响,尤其当它们的灰度值比较接近时,混合效应导致的图像模糊会更明显。随着模板地进一步增大,很多细节都会被当作噪声平滑掉。因此,我们在确定模板尺寸时应仔细考虑要滤除的噪声点的大小,有针对性地进行滤波。
以 3x3 为例,均值滤波说白了就是将图像数据生成3x3的矩阵,对这九个数求个平均值代替中间的那个数。在FPGA中我们为了简便运算只将目标像素周围八个点求和然后除以8,取代中间像素点。

二、MATLAB实现
clc; clear all; close all; RGB = imread('flower.bmp'); %读取图片 imgn = imnoise(RGB,'salt & pepper',0.05); %椒盐密度0.05 gray = im2double(rgb2gray(imgn)); %灰度图 [ROW,COL, DIM] = size(gray); %得到图像行列数 %-------------------------------------------------------------------------- % Mean Filter 均值滤波 %-------------------------------------------------------------------------- Mean_Img = zeros(ROW,COL); for r = 2:1:ROW-1 for c = 2:1:COL-1 Mean_Img(r,c) = (gray(r-1, c-1) + gray(r-1, c) + gray(r-1, c+1) + gray(r, c-1) + gray(r, c) + gray(r, c+1) + gray(r+1, c-1) + gray(r+1, c) + gray(r+1, c+1)) / 9; end end %-------------------------------------------------------------------------- % Show Image %-------------------------------------------------------------------------- subplot(2,2,1); imshow(RGB); title('原图'); subplot(2,2,2); imshow(imgn); title('椒盐噪声'); subplot(2,2,3); imshow(gray); title('灰度图'); subplot(2,2,4); imshow(Mean_Img); title('均值滤波'); % 由实验可知: % 1、均值滤波会使得图像模糊,其实是做了平滑处理,像素值高的会被拉低,像素值低的会被拉高,趋向于一个平均值。 % 2、椒盐噪声就是黑白噪声,均值滤波对椒盐噪声基本无作用,必须使用中值滤波才行。
点击运行,得到如下结果:

由结果看出:均值滤波对略去了部分噪声,但不如上篇的中值滤波效果好。中值滤波后,图片变的模糊,其实是做了平滑处理,像素值高的会被拉低,像素值低的会被拉高,趋向于一个平均值。
三、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、均值滤波
本来的均值滤波是周围8个像素包括自己共9个,相加除以9,FPGA不适合做除法,因此改成只取周围8个像素,相加除以8,即右移3位,即取高8位。共耗费 2 个时钟周期。
//**************************************************************************
// *** 名称 : mean.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2020年3月
// *** 描述 : Y分量进行mean均值滤波
//**************************************************************************
module mean
//========================< 参数 >==========================================
#(
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 mean_hsync , //mean行同步
output wire mean_vsync , //mean场同步
output wire [ 7:0] mean_data , //mean数据
output wire mean_de //mean数据使能
);
//========================< 信号 >==========================================
//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 ;
//mean ----------------------------------------------
reg [10:0] mean1 ;
reg [10:0] mean2 ;
reg [10:0] mean3 ;
reg [10:0] mean4 ;
//同步 ----------------------------------------------
reg [ 2:0] Y_de_r ;
reg [ 2:0] Y_hsync_r ;
reg [ 2: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 )
);
//==========================================================================
//== 均值滤波,耗费2clk
//==========================================================================
//clk 1
//---------------------------------------------------
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
mean1 <= 'd0;
mean2 <= 'd0;
mean3 <= 'd0;
end
else begin
mean1 <= matrix_11 + matrix_12 + matrix_13;
mean2 <= matrix_21 + + matrix_23;
mean3 <= matrix_31 + matrix_32 + matrix_33;
end
end
//clk 2
//---------------------------------------------------
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
mean4 <= 'd0;
else
mean4 <= mean1 + mean2 + mean3;
end
assign mean_data = mean4[10:3];
//==========================================================================
//== 信号同步
//==========================================================================
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
Y_de_r <= 3'b0;
Y_hsync_r <= 3'b0;
Y_vsync_r <= 3'b0;
end
else begin
Y_de_r <= {Y_de_r[1:0], Y_de};
Y_hsync_r <= {Y_hsync_r[1:0], Y_hsync};
Y_vsync_r <= {Y_vsync_r[1:0], Y_vsync};
end
end
assign mean_de = Y_de_r[2];
assign mean_hsync = Y_hsync_r[2];
assign mean_vsync = Y_vsync_r[2];
endmodule
四、上板验证
灰度图:

均值滤波后:

从实验结果看出,图片变得模糊了,其实是平滑了,实验成功。
我的板子坏了,出现一些不该有的横条,如果是好的板子则没有这些鬼东西。
[1] OpenS Lee:FPGA开源工作室(公众号)
[2] CrazyBingo:基于VIP_Board Mini的FPGA视频图像算法(HDL-VIP)开发教程-V1.6
[3] NingHechuan:FPGA图像处理教程

浙公网安备 33010602011771号