中值滤波器在去除尖端噪声中非常重要,是信号处理中最长用到的滤波器。图像中的一些椒盐噪声或者其它突变比较大的噪声可以使用中值滤波器去除,所以这篇文章我们来讲解FPGA实现图像的中值滤波。本次项目的简述如下:PC机通过千兆以太网发送上位机到FPGA开发板中,然后经过中值滤波缓存进DDR3中,最后经过DDR3发送到上位机中显示。
本次实验所用到的软硬件环境如下:
1、VIVADO2019.1软件环境
2、Modelsim10.7c仿真环境
3.米联客MA7035FA(100T)开发板
4、米联客USB3.0上位机软件
图像信号在形成、传输和记录的过程中,由于成像系统、 传输介质、 工作环境和记录设备等的固有缺陷,不可避免地产生各种类型的噪声,降低了图像的质量,进而影响后续处理(如边缘检测、图像分割、特征提取、模式识别等)的效果或准确性。因此,对噪声图像进行滤波是必要预处理过程。
这里需要注意中值滤波基于排序统计理论,是抑制噪声的***非线性信号处理技术***。其核心运算是以模板中的数据进行排序,使得某个亮点(暗点)的噪声在排序过程中被排在数据序列的两侧。因为数据序列中间位置上的值一般不是噪声点的值,从而达到抑制噪声的目的。
假设有图像 A, 对其进行中值滤波, 即求出该 3x3 矩阵中的排序在中间位置的那个值。
当然前面的文章中我们已经详细讲解了3*3矩阵的构建方法,那么这篇文章,我们主要集中在找上面图像矩阵中的中值,然后代替图像的数据。方法一,是对上面的矩阵进行硬件排序,硬件排序的效率非常高,但是理解与实现起来较困难,我会在后面的专门的文章中进行相应的讲解。第二种方法就是两两找最大值的方法,对齐介绍如下:
第一步: 分别求出 3 行中同一行的最大值、 最小值、 中间值
设 max_h1 为第一行的最大值, min_h1 为第一行的最小值, mid_h1 为第一
行的中间值。
同理有 max_h2, mid_h2, min_h2, max_h3, mid_h3, min_h3。
第二步: 3 行的最大值、 最小值、 中间值进行比较
max_h1, m2ax_h2, max_h3 这 3 个值比较, 得到这 3 个值的最小值 min_max。
mid_h1, mid_h2, mid_h3 这 3 个值比较, 得到这 3 个值的中间值 mid_mid。
min_h1, min_h2, min_h3 这 3 个值比较, 得到这 3 个值的最大值 max_min。
第三步: 得到 3x3 矩阵的中间值
将第二步得到的 min_max, mid_mid, max_min 进行比较, 得到的中间值,
即该 3x3 矩阵的中间值。
至于为什么上面找到的值就是图像矩阵的中值,同学们可以试着分别对上面 max_h1 ,mid_h1 ,max_h1 ,max_h2, mid_h2, min_h2, max_h3, mid_h3, min_h3进行排序的大体范围判断。然后,就可以发现排序第5位的数据就在min_max, mid_mid, max_min的中间值。
中值滤波器的实现这里因为简单的数学关系,我们同样不再给出时序图,而是直接会给出相应的代码:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : nnzhang1996@foxmail.com
// Website :
// Module Name : sobel.v
// Create Time : 2020-04-08 08:32:02
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module sobel(
//System Interfaces
input sclk ,
input rst_n ,
//Communication Interfaces
input [ 7:0] rx_data ,
input pi_flag ,
output reg [ 7:0] tx_data ,
output reg po_flag
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
parameter COL_NUM = 1024 ;
parameter ROW_NUM = 768 ;
parameter VALUE = 80 ;
wire [ 7:0] mat_row1 ;
wire [ 7:0] mat_row2 ;
wire [ 7:0] mat_row3 ;
wire mat_flag ;
reg [ 7:0] mat_row1_1 ;
reg [ 7:0] mat_row2_1 ;
reg [ 7:0] mat_row3_1 ;
reg [ 7:0] mat_row1_2 ;
reg [ 7:0] mat_row2_2 ;
reg [ 7:0] mat_row3_2 ;
reg mat_flag_1 ;
reg mat_flag_2 ;
reg mat_flag_3 ;
reg mat_flag_4 ;
reg mat_flag_5 ;
reg mat_flag_6 ;
reg mat_flag_7 ;
reg [ 7:0] max_h1 ;
reg [ 7:0] mid_h1 ;
reg [ 7:0] min_h1 ;
reg [ 7:0] max_h2 ;
reg [ 7:0] mid_h2 ;
reg [ 7:0] min_h2 ;
reg [ 7:0] max_h3 ;
reg [ 7:0] mid_h3 ;
reg [ 7:0] min_h3 ;
reg [ 7:0] min_max ;
reg [ 7:0] mid_mid ;
reg [ 7:0] max_min ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
always @(posedge sclk)
begin
mat_row1_1 <= mat_row1;
mat_row2_1 <= mat_row2;
mat_row3_1 <= mat_row3;
mat_row1_2 <= mat_row1_1;
mat_row2_2 <= mat_row2_1;
mat_row3_2 <= mat_row3_1;
end
always @(posedge sclk)
begin
mat_flag_1 <= mat_flag;
mat_flag_2 <= mat_flag_1;
mat_flag_3 <= mat_flag_2;
mat_flag_4 <= mat_flag_3;
mat_flag_5 <= mat_flag_4;
mat_flag_6 <= mat_flag_5;
mat_flag_7 <= mat_flag_6;
end
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
max_h1 = mat_row1_1 && mat_row1 >= mat_row1_2)
max_h1 = mat_row1 && mat_row1_1 >= mat_row1_2)
max_h1 <= mat_row1_1;
else
max_h1 <= mat_row1_2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
mid_h1 = mat_row1_1 && mat_row1_1 >= mat_row1_2) || (mat_row1_2 >= mat_row1_1 && mat_row1_1 >= mat_row1))
mid_h1 = mat_row1 && mat_row1 >= mat_row1_2) || (mat_row1_2 >= mat_row1 && mat_row1 >= mat_row1_1))
mid_h1 <= mat_row1;
else
mid_h1 <= mat_row1_2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
min_h1 <= 8'd0;
else if(mat_row1 <= mat_row1_1 && mat_row1 <= mat_row1_2)
min_h1 <= mat_row1;
else if(mat_row1_1 <= mat_row1 && mat_row1_1 <= mat_row1_2)
min_h1 <= mat_row1_1;
else
min_h1 <= mat_row1_2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
max_h2 = mat_row2_1 && mat_row2 >= mat_row2_2)
max_h2 = mat_row2 && mat_row2_1 >= mat_row2_2)
max_h2 <= mat_row2_1;
else
max_h2 <= mat_row2_2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
mid_h2 = mat_row2_1 && mat_row2_1 >= mat_row2_2) || (mat_row2_2 >= mat_row2_1 && mat_row2_1 >= mat_row2))
mid_h2 = mat_row2 && mat_row2 >= mat_row2_2) || (mat_row2_2 >= mat_row2 && mat_row2 >= mat_row2_1))
mid_h2 <= mat_row2;
else
mid_h2 <= mat_row2_2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
min_h2 <= 8'd0;
else if(mat_row2 <= mat_row2_1 && mat_row2 <= mat_row2_2)
min_h2 <= mat_row2;
else if(mat_row2_1 <= mat_row2 && mat_row2_1 <= mat_row2_2)
min_h2 <= mat_row2_1;
else
min_h2 <= mat_row2_2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
max_h3 = mat_row3_1 && mat_row3 >= mat_row3_2)
max_h3 = mat_row3 && mat_row3_1 >= mat_row3_2)
max_h3 <= mat_row3_1;
else
max_h3 <= mat_row3_2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
mid_h3 = mat_row3_1 && mat_row3_1 >= mat_row3_2) || (mat_row3_2 >= mat_row3_1 && mat_row3_1 >= mat_row3))
mid_h3 = mat_row3 && mat_row3 >= mat_row3_2) || (mat_row3_2 >= mat_row3 && mat_row3 >= mat_row3_1))
mid_h3 <= mat_row3;
else
mid_h3 <= mat_row3_2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
min_h3 <= 8'd0;
else if(mat_row3 <= mat_row3_1 && mat_row3 <= mat_row3_2)
min_h3 <= mat_row3;
else if(mat_row3_1 <= mat_row3 && mat_row3_1 <= mat_row3_2)
min_h3 <= mat_row3_1;
else
min_h3 <= mat_row3_2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
min_max <= 8'd0;
else if(max_h1 <= max_h2 && max_h1 <= max_h3)
min_max <= max_h1;
else if(max_h2 <= max_h1 && max_h2 <= max_h3)
min_max <= max_h2;
else
min_max <= max_h3;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
mid_mid = mid_h2 && mid_h2 >= mid_h3) || (mid_h3 >= mid_h2 && mid_h2 >= mid_h1))
mid_mid = mid_h1 && mid_h1 >= mid_h3) || (mid_h3 >= mid_h1 && mid_h1 >= mid_h2))
mid_mid <= mid_h1;
else
mid_mid <= mid_h3;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
max_min <= 8'd0;
else if(min_h1 <= min_h2 && min_h1 <= min_h3)
max_min <= min_h1;
else if(min_h2 <= min_h1 && min_h2 <= min_h3)
max_min <= min_h2;
else
max_min <= min_h3;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
tx_data = min_max && min_max >= max_min) || (max_min >= min_max && min_max >= mid_mid))
tx_data = mid_mid && mid_mid >= max_min) || (max_min >= mid_mid && mid_mid >= min_max))
tx_data <= mid_mid;
else
tx_data <= max_min;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
po_flag <= 1'b0;
else if(mat_flag_2 == 1'b1 && mat_flag_4 == 1'b1)
po_flag <= 1'b1;
else
po_flag <= 1'b0;
mat_3x3 mat_3x3_inst(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//Communication Interfaces
.rx_data (rx_data ),
.pi_flag (pi_flag ),
.mat_row1 (mat_row1 ),
.mat_row2 (mat_row2 ),
.mat_row3 (mat_row3 ),
.mat_flag (mat_flag )
);
endmodule
这里说明一下为了文章的简洁性,我们这里不再给出整个工程的代码,知识给出了中值滤波部分的程序。具体的项目工程代码查看前面的文章***基于FPGA的图像边缘检测***,至于要把这篇论文种的sobel模块换成上面的sobel便可以完成图像的中值滤波,至于这里取名字sobel也只是因为偷懒没改模块名。
测试模块的代码测试模块的代码如下:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : nnzhang1996@foxmail.com
// Website :
// Module Name : tb_sobel.v
// Create Time : 2020-04-08 09:19:44
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module tb_sobel();
//System Interfaces
reg sclk ;
reg rst_n ;
//Communication Interfaces
reg [ 7:0] rx_data ;
reg pi_flag ;
wire [31:0] tx_data ;
wire po_flag ;
reg [ 1:0] cnt ;
initial begin
sclk = 1'b0;
rst_n <= 1'b0;
pi_flag <= 1'b0;
rx_data <= 8'd0;
#(1000);
rst_n <= 1'b1;
#(10000);
gendata();
#(10000);
gendata();
end
always #5 sclk = ~sclk;
task gendata();
integer i ;
integer j ;
begin
for(j = 0;j < 768;j = j+1)
begin
for(i = 0;i < 1024;i = i+1)
begin
pi_flag = 1'b1;
#10;
pi_flag = 1'b0;
#20;
end
pi_flag = 1'b0;
#1000;
end
end
endtask
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt <= 2'd0;
else if(pi_flag == 1'b1 && cnt == 2'd2)
cnt <= 2'd0;
else if(pi_flag == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= cnt;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
rx_data <= 8'd0;
else if(cnt == 2'd2 && pi_flag == 1'b1 && rx_data == 1024-1)
rx_data <= 8'd0;
else if(cnt == 2'd2 && pi_flag == 1'b1)
rx_data <= rx_data + 1'b1;
else
rx_data <= rx_data;
//sobel sobel_inst(
// //System Interfaces
// .sclk (sclk ),
// .rst_n (rst_n ),
// //Communication Interfaces
// .rx_data (rx_data ),
// .pi_flag (pi_flag ),
// .tx_data (tx_data ),
// .po_flag (po_flag )
//);
conver_bit conver_bit_inst(
//System Interfaces
.sclk (sclk ),
.rst_n (rst_n ),
//Gigbit Interfaces
.image_data (rx_data ),
.image_data_en (pi_flag ),
//Communication Interfaces
.rgb_data (tx_data ),
.rgb_data_en (po_flag )
);
endmodule
仿真结果
我们进行相应的仿真测试,结果如下:
从上图中的关键数据可以发现我们完成了图像的中值滤波。
原图像如下:
FPGA中值滤波处理之后的图像如下:
从上面的图像中,我们可以发现图像中的突变噪声被消除,但是图像的边缘也被取消一部分,从而证明了我们实验的正确性。
创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。(txt文件、图片文件在群中)对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: