目录
- 前言
- 一、研究背景及意义
- 二、本文研究内容
- 三、硬件系统框架设计
- 1、总框架设计
- 2、原理图&PCB设计
- 3、实物设计
- 4、电路介绍
- 三、中值滤波算法研究及改进
- 1、图像噪声的产生及危害
- 2、中值滤波算法
- 3、高斯滤波算法
- 4、改进的中值滤波算法(α均值滤波算法)
- 四、改进中值滤波算法的FPGA实现
- 1、椒盐加噪模块
- 2、中值滤波模块
- 3、α均值滤波模块
- 4、改进中值滤波图像系统的实验效果
- 五、总结
- 六、工程获取
本文附带资源:
FPGA工程(Quartus II)+Modelsim完整仿真+MATLAB工程+自主设计开发板(原理图+PCB图纸)
前言
图像滤波是数字图像处理中的一项重要技术,它可以对图像进行平滑、锐化、去噪等处理,使图像更加清晰、减少噪声、去除干扰、提高图像质量和增强目标特征,以便进行后续的数字图像处理和分析。本文提出了一种改进中值滤波算法的FPGA图像处理系统。
中值滤波是一种非线性滤波方法,是基于统计排序方法的滤波器,将像素点的邻域内的所有像素点灰度值的中值作为该像素点的灰度值,从而达到噪声去除的目的。但是噪点像素使得相邻像素的点发生变化,结果中的噪点依然可见,因此对于点、线、尖顶的图像不适用中值滤波。根据此问题,采用修正的α均值滤波对传统的中值滤波算法进行改进。α均值滤波可以消除椒盐噪声、高斯噪声和离群值,同时保留原始图像的细节信息,并且能够通过调整参数的方式灵活地适应不同的应用场景。系统采用FPGA作为主控器,通过OV5640摄像头实时采集RGB565数据到开发平台,经过色彩空间转换并完成灰度化处理,灰度化的后的图像数据经过选择器进入不同的滤波模块,最后通过LCD屏幕显示原始图像、传统中值滤波图像及改进后的中值滤波图像,并进行效果对比。实验结果表明,本设计在去除椒盐噪声的同时几乎不会损失原图像的细节,并且图像实时性及鲁棒性满足一般工业环境。
一、研究背景及意义
机器视觉是人工智能领域中的重要分支,它通过模拟人类的视觉系统,利用数字图像传感器替代眼睛进行画面数据采集、计算机进行图像分析。图像通过摄像头采集被噪声损坏,脉冲噪声是在成像过程中产生的噪声之一,因为开关有缺陷,其中有短暂的瞬变。脉冲噪声的去噪是数字图像处理中的一个问题,即是在很低的脉冲噪声密度下,图像也会受到很大的影响[1]。除了脉冲噪声,类似随机噪声等噪声也是图像去燥的重点。
中值滤波算法通常用于数字图像处理中的噪声去除。它可以高效的去除数字图像中的椒盐噪声和脉冲噪声等不规则噪声,同时保留原始图片中的特征信息。因此,中值滤波算法在医学影像处理、工业检测、安防监控等各种领域都有广泛的应用。特别是在监控领域,在视频监控过程中,可能会因为光线、天气、设备等因素产生一些噪点和干扰,这些干扰会降低图像的质量,影响监控效果。而中值滤波算法可以针对这些不规则噪声进行优化,使得处理后的图像更加平滑。然而,中值滤波也存在一些缺点:
1、边缘保持效果不佳:在进行中值滤波时,一些边缘信息可能会被平滑掉,导致目标物体的边缘变得模糊。
2、无法处理高斯噪声:中值滤波是一种非参统计方法,它无法区分高斯噪声和目标信号,因此无法很好地去除高斯噪声。
3、窗口大小选择不当会影响效果:中值滤波使用的窗口大小越大,可以去除的噪声也就越多,但也会导致信号丢失,边缘模糊等问题。
因此采用修正的α均值滤波,修正的α均值滤波器将滤波范围内的像素值进行排序,并从排好的数据中,删除最低灰度级和最高灰度级的2d个数据,再用剩余的像素点求均值,修正的过程采用阈值与原图和α滤波的结果差值的绝对值做比较,从而达到自适应的过程。
二、本文研究内容
本文主要设计完成了改进中值滤波图像处理系统的硬件设计及算法设计,经过电路设计、设计输入、RTL仿真、综合优化、布局布线、时序仿真与验证、板级仿真与验证,以及芯片编程与调试,最后将码流加载到FPGA上。硬件设计主要为原理图设计、PCB设计及FPGA相关模块的电路设计,软件设计主要为α均值滤波(改进的中值滤波)算法模块、TFTLCD模块、传统中值滤波算法模块、叠加椒盐噪声模块、灰度化算法模块及其他小功能模块的代码设计及功能仿真。
三、硬件系统框架设计
1、总框架设计
本系统在图像采集之前,由于板载晶振与摄像头模块及TFT-LCD模块频率不一致,所以在系统工作之前,需要设计锁相环模块输出与传感器一致的时钟信号。时钟输入后,根据OV5640的手册进行寄存器配置,使OV5640的工作模式符合本系统的设计需求。CMOS传感器采集进来的图像经过色彩空间转换,送入到叠加椒盐噪声模块。在本系统中,为了能在LCD上对比多种图像的效果,需要设计数据选择输出模块,但是在满足选择功能的情况下,更需要满足实时性,所以选择只输入一路视频流,经过四个图像处理模块,通过按键控制进行模块使能。并且根据时钟域不同,设计了异步FIFO完成数据传输缓存。最后将处理结果通过ILI9488驱动模块显示在屏幕上,并在LED上指示当前显示的模式。
2、原理图&PCB设计
改进中值滤波系统采用独立自主设计的FPGA开发平台进行设计,本文使用Intel公司的Cyclone IV系列FPGA芯片:EP4CE10F17C8作为主控芯片,这颗芯片包含10080个LE,可以满足图像处理中的大多数逻辑功能;内置硬件乘法器,可以加速复杂算法的处理;支持多种标准接口,如PCI-E、Gigabit Ethernet和USB等,并提供了一些定制接口;相对于其他FPGA芯片而言,EP4CE10F17C8融合了高性能、底功耗和适中成本等优势,故本文选择EP4CE10作为主控芯片,并根据实际需求设计了外围电路。利用AD软件,根据上述需求及芯片特性,设计了4层PCB文件。PCB设计如图所示。
PCB顶层:
PCB底层:
原理图:
3、实物设计
将PCB文件送至代工厂进行加工生产,最后完成通过热风枪、烙铁等工具完成器件焊接,FPGA开发平台实物如图所示。
4、电路介绍
电路设计比较简单,这里就不对电路图做赘述了。
简单说一下使用到的器件:
主控:EP4CE10;
摄像头:OV5640;
显示:TFT-LCD;
本文所选用的TFT-LCD模块,内置了ILI9488芯片,是一种广泛应用于液晶显示屏的高度集成化驱动芯片,支持最大分辨率达到320x480,能够满足本次设计需求。由于OV5640摄像头是DVP电气接口与Altera开发平台的Avalon-ST接口属于不同的接口标准,DVP接口是数字视频平面接口,使用于数字视频信号的处理、传输和显示等领域,而Altera的Avalon-ST接口则是一种可编程高速流式数据传输接口,适用于高速数据传输等领域,所以需要在完成ILI9488驱动的同时,做一个Avalon-ST接口标准的数据流转换为符合DVP接口的视频数据流的设计。
根据此需求,本系统设计了ILI9488驱动模块,模块输入两个时钟信号,一个用于模块时序,另一个用于LCD屏幕时序,本模块中添加了dcfifo模块,用于数据缓存,改模块本质为FIFO结构,FIFO可以作为数据缓存模块来使用。例如,图像处理系统可能需要暂存一段时间内的数据以进行后续处理,这时可以使用FIFO来临时缓存数据,同时能够处理异步信号。输入16位sink数据,最后输出16位LCD数据,显示在屏幕上。
ILI9488驱动模块结构如图2-12所示。
三、中值滤波算法研究及改进
1、图像噪声的产生及危害
图像噪声是指在数字图像中表示图像亮度或颜色信息时,由于成像设备本身的限制以及环境和传输等各种因素的干扰,而产生的非预期和不规则的像素值波动。图像噪声会导致图像质量下降,影响图像的视觉效果,并且会影响后续的图像识别以及分割等深层次处理的工作,使得机器视觉的开发难度大大提高,所以在做机器视觉开发的时候,滤波成了图像预处理不可缺少的一部分,最大程度城区将噪声清除,提高图像清晰度,维护细节信息。
图像噪声具有多种类型,其中最常见的有4种:高斯噪声、椒盐噪声、斑点噪声及条纹噪。高斯噪声是一种随机噪声,其像素值符合正态分布。高斯噪声通常由摄像机感光元件中基本的热噪声引起,或由其他因素如传输或处理错误引起。椒盐噪声是常见的随机噪声,盐噪声通常是将某些像素设置为最大值(白色),而胡椒噪声则是将某些像素设置为最小值(黑色)。斑点噪声是一种随机噪声,通常由某些像素变暗或变亮形成。这种噪声通常由摄像机传感器表面的坏点或缺陷引起。条纹噪声是一种周期性噪声,通常由图像处理或传输过程中的干扰或错误引起。这种噪声在图像上表现为明显的条纹或痕迹。
了解图像噪声的不同类型和来源是图像处理与分析的基础之一。同时,不同种类的噪声对数字图像产生的干扰不同,需要选择不同的滤波技术来减轻或消除其影响。
2、中值滤波算法
图像在经过色彩空间转换后,首先选中图像中一个邻域内的像素值并进行排序,然后取其中间值作为中心像素的值,从而达到平滑图像和去除噪声的效果。设邻域大小为nxn,则对图像中的每一个像素(x,y),其中值滤波后的像素值为:
Inew(x,y)=median(I(x+a,y+t))
其中,I表示原始图像,Inew表示经过median后的图像,(x,y)表示当前处理的像素点坐标,(a,t)表示窗口内的偏移量,median表示取窗口内所有像素点的中值作为中心像素的新值。这个公式表示去以(x,y)为中心的n x n窗口中的所有图像数据,排序后取中间的数据作为中心数据(x,y)的新值。在实际应用中,通常采用不同大小的窗口和不同顺序的排序方法进行操作。中值滤波图像处理原理如图所示。
在MATLAB中,读取电路板原始图像,在原图中加入椒盐噪声并将图像写入文件夹,随后读取加入噪声后的索引图像。通过double()函数,使索引图片像素的灰度值为双精度浮点数,这样可以避免出现截断误差,如果使用整数进行索引,容易出现计算结果与实际像素值之间的偏差,使用浮点数索引时,可以确保计算结果更加准确,有利于提高图像处理算法的精度和可靠性。接着,按中值滤波原理,定义窗口大小为9x9并取出窗口中的像素存放在lxz数组中,通过冒泡排序法对窗口范围内的数值进行降序排序,并将排序后的中间值放入新矩阵,最后通过窗口输出新图像。中值滤波原始图像、带噪图像及滤波后图像如图所示。
Matlab程序:
I=imread('D:\MATLAB\Bishe\picture\芯片原图.png'); %读取lena图像
J=imnoise(I,'salt & pepper',0.02); %在原图中添加椒盐噪声
imwrite(J,'lena1.bmp'); %将添加椒盐噪声的图像写入文件夹
[A,map]=imread('lena1.bmp'); %读取加入噪声后的索引图像
image=double(A); %使索引图片像素的灰度值为双精度浮点数
prompt=('请输入窗口大小对应n值 例3*3窗口,n=3');%设置对话框中显示的文字
name='输入窗口大小';%设置对话框的名字
numlines=1;%设置对话框显示文字的行数
defaultanswer={'3'};%设定编辑栏的默认值
answer=inputdlg(prompt,name,numlines,defaultanswer);%将编辑栏的输入值赋给cell型变量answer
n=eval(answer{1});%将cell型变量answer转换为整型变量赋给n,作为窗口边长
z=n*n;%取出窗口中像素个数赋给z
u=zeros(1,z);%定义一个内容全零的1xz数组,用于放需要排列的数
m=(n-1)/2;%定义窗口半径
for x=m+1:255-m;
for y=m+1:255-m;%进入循环,扫描图像中每个像素点
d=1;%定义数组u的变量,并赋初值1
for w=-m:m;
for s=-m:m;%将窗口定位后,进入取值循环
u(d)=image(x+w,y+s);d=d+1;%把窗口范围内取出的值放入数组u中
end;
end
for p=1:z;
for q=1:z-p;%进入排序循环
if u(q)>u(q+1)
k=u(q);u(q)=u(q+1);u(q+1)=k;%利用冒泡排序法对数组u中的数进行升序排列
end
end
end
R(x,y)=u(d/2);%将排序之后的中间值取出放入新矩阵R,即矩阵R为新图像
end
end
subplot(221);imshow(I);title('芯片原图')
subplot(222);imshow(J);title('加入椒盐噪声图像')
subplot(223);imshow(R,map);title('中值滤波后图像')%将三个原图、噪声图以及滤波后图像作为子图排列显示在窗口上
3、高斯滤波算法
上面距离了中值滤波算法,这里就不赘述高斯滤波算法,这里就简单放一下效果图跟程序。
原始图像:
高斯噪声加噪图像
高斯滤波器抑制高斯噪声图像
椒盐盐噪声加噪图像
高斯滤波器抑制椒盐噪声图像
Matlab程序:
function varargout = JJJ(varargin)
% JJJ MATLAB code for JJJ.fig
% JJJ, by itself, creates a new JJJ or raises the existing
% singleton*.
%
% H = JJJ returns the handle to a new JJJ or the handle to
% the existing singleton*.
%
% JJJ('CALLBACK',hObject,eventData,handles,...) calls the local
% function named CALLBACK in JJJ.M with the given input arguments.
%
% JJJ('Property','Value',...) creates a new JJJ or raises the
% existing singleton*. Starting from the left, property value pairs are
% applied to the GUI before JJJ_OpeningFcn gets called. An
% unrecognized property name or invalid value makes property application
% stop. All inputs are passed to JJJ_OpeningFcn via varargin.
%
% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one
% instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES
% Edit the above text to modify the response to help JJJ
% Last Modified by GUIDE v2.5 15-Jan-2019 14:32:06
% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @JJJ_OpeningFcn, ...
'gui_OutputFcn', @JJJ_OutputFcn, ...
'gui_LayoutFcn', [] , ...
'gui_Callback', []);
if nargin && ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT
% --- Executes just before JJJ is made visible.
function JJJ_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to JJJ (see VARARGIN)
% Choose default command line output for JJJ
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);
% UIWAIT makes JJJ wait for user response (see UIRESUME)
% uiwait(handles.figure1);
% --- Outputs from this function are returned to the command line.
function varargout = JJJ_OutputFcn(hObject, eventdata, handles)
% varargout cell array for returning output args (see VARARGOUT);
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Get default command line output from handles structure
varargout{1} = handles.output;
% --------------------------------------------------------------------
function uipushtool1_ClickedCallback(hObject, eventdata, handles)
% hObject handle to uipushtool1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
[filename,pathname]=uigetfile({'*.*';'*.bmp';'*.jpg';'*.tif';'*.jpg';});
str=[pathname,filename] ;
global im;
im=imread(str);
axes(handles.axes1);
imshow(im);
% --------------------------------------------------------------------
function Untitled_1_Callback(hObject, eventdata, handles)
% hObject handle to Untitled_1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% --------------------------------------------------------------------
function Untitled_5_Callback(hObject, eventdata, handles)
% hObject handle to Untitled_5 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global im I0;
I0 = imnoise(im,'salt & pepper');
% 加入椒盐噪声并显示
axes(handles.axes2);
imshow(I0);
h1=[handles.axes2 handles.text3];
set(h1,'visible','on');
% --------------------------------------------------------------------
function Untitled_6_Callback(hObject, eventdata, handles)
% hObject handle to Untitled_6 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global im I0;
im1 = im2double(im);
I0 = imnoise(im1, 'gaussian');%加入高斯噪声
axes(handles.axes2);
imshow(I0);
h1=[ handles.axes2 handles.text4];
set(h1,'visible','on');
% --------------------------------------------------------------------
function Untitled_7_Callback(hObject, eventdata, handles)
% hObject handle to Untitled_7 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% --------------------------------------------------------------------
function Untitled_8_Callback(hObject, eventdata, handles)
% hObject handle to Untitled_8 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global I0 ;
[B,A] = butter(6,0.2,'low');
I= filter(B,A,double(I0));
axes(handles.axes3);%低通
imshow(I,[]);
h1=[ handles.axes3 handles.text4];
set(h1,'visible','on');
% --------------------------------------------------------------------
function Untitled_9_Callback(hObject, eventdata, handles)
% hObject handle to Untitled_9 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global I0 ;
I= medfilt2(I0);
axes(handles.axes3);%中值
imshow(I,[]);
h1=[ handles.axes3 handles.text4];
set(h1,'visible','on');
% --------------------------------------------------------------------
function Untitled_10_Callback(hObject, eventdata, handles)
% hObject handle to Untitled_10 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global I0 ;
a=[1 1 1
1 1 1
1 1 1];
l=1/9*a;
d = conv2(double(I0),double(l)); %算术均值滤波
axes(handles.axes3);
imshow(d,[]);
h1=[ handles.axes3 handles.text4];
set(h1,'visible','on');
% --------------------------------------------------------------------
function Untitled_11_Callback(hObject, eventdata, handles)
% hObject handle to Untitled_11 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
[filename,pathname]=uigetfile({'*.*';'*.bmp';'*.jpg';'*.tif';'*.jpg';});
str=[pathname,filename] ;
global im;
im=imread(str);
axes(handles.axes1);
imshow(im);
h1=[handles.axes1 handles.text2];
set(h1,'visible','on');
% --- Executes during object creation, after setting all properties.
function text3_CreateFcn(hObject, eventdata, handles)
% hObject handle to text3 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called
% --------------------------------------------------------------------
function Untitled_12_Callback(hObject, eventdata, handles)
% hObject handle to Untitled_12 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)】
global I0
global q
q=str2double(inputdlg('标准差','sigma的值'));
sigma=q;% 标准差大小
window=double(uint8(3*sigma)*2+1);% 窗口大小一半为3*sigma
H=fspecial('gaussian', window, sigma);% fspecial('gaussian', hsize, sigma)产生滤波模板
img_gauss=imfilter(I0,H,'replicate'); %为了不出现黑边,使用参数'replicate'
axes(handles.axes3);
imshow(img_gauss)
h1=[ handles.axes3 handles.text4];
set(h1,'visible','on');
4、改进的中值滤波算法(α均值滤波算法)
从2和3可以看出,高斯滤波器、中值滤波器等线性滤波器都会导致图像模糊,因为它们在平滑图像时是对所有像素做平均处理或简单的统计,而不考虑像素间的差异性。就两种传统滤波器而言,高斯滤波是一种线性滤波器,相比中值滤波会使图像更模糊,并且中值滤波器采用序列式的计算量相较于高斯滤波器的卷积式小得多,在处理大规模图像时能够保持较快的速度。
基于此,在中值滤波算法的基础上提出一种α均值滤波器。α均值滤波的前身是Alpha滤波,Alpha滤波又是在中值滤波的基础上提出来的,所以它们在一定程度上具有相似之处,但是也存在一些差异。
α均值滤波是一种数字图像处理中的非线性滤波方法,因为传统的均值滤波器由于不需要提交检查噪声强度,对高斯噪声有较好的处理效果,但对脉冲噪声则不理想,因此本文在传统的均值滤波的基础上,通过对滤波模板中的噪声点的像素灰度值赋予一定的权重,然后计算其均值作为结果输出,这样能更好地去除噪声,并且在保留图像细节信息的同时有效地降低噪声干扰。α均值滤波器的基本原理是对滤波器内的像素点进行排序,并从排好的数据中,删除最低灰度级和最高灰度级的2d个数据,然后对剩余的像素点进行加权平均,以得到当前像素点的滤波后的值。算法公式如式:
其中,F(x,y)表示滤波后的像素值,fi(x,y)表示图像中(x,y)像素在滤波器内的一个像素值,N表示滤波器内所有像素点的数量,α是可调参数,表示在排序中去掉的像素点数,常见取值为1≤α≤6。wi表示像素点的权重,通常取值1或者0。
在MATLAB中,通过function构建alphafilt函数,在每个像素周围使用一个指定大小的矩形滤波器:roi=A(i-padSize(1):i+padSize(1),j-padSize(2): j+padSize(2)),来计算输出像素值对于每个滤波器,通过计算其窗口内的最小值、最大值和中值,并根据中值是否位于最小值和最大值之间,分别采用修剪平均法或直接输出当前像素值的方法来计算输出像素值。为了避免在计算窗口边缘时出现超出图像边界的情况,在图像周围添加了一些边缘补丁,并在最后去除这些边缘补丁。最后得到alphafilt函数:B = alphafilt(A,m,trim),函数的输入参数为A(需要进行滤波的图像)、m(滤波窗口大小)和trim(修剪比例)。
通过输入原始图像,对原始图像分别添加噪声密度为0.02的高斯噪声和0.02的椒盐噪声,接着调用设计好的alphafilt函数,对高斯噪声图像设置5x5的滤波窗口和30%的修正比例,对椒盐噪声设置5x5的滤波窗口和10%的修正比例,输出图像得到滤波结果,滤波结果如图所示。
从实验结果看,改进后的中值滤波算法对高斯噪声和椒盐噪声都能起到抑制效果,更适用于去除随机噪声,并且图像在平滑的过程中,相比传统的中值滤波和高斯滤波,细节保留的更完整。
% 读入图片
I = imread('芯片原图.png');
P1 = imnoise(I,'gaussian',0.02); %加入高斯躁声
P2 = imnoise(I,'salt & pepper',0.02);
% alpha均值滤波器滤波
filtSize1 = [3, 3];
filtSize2 = [5, 5];
J = alphafilt(P1, filtSize2,0.3);
H = alphafilt(P2, filtSize2,0.1);
% 显示结果
figure;
%subplot(4,2,1), imshow(I), title('Original');
subplot(2,4,5),imhist(P1),title('gaussian');
subplot(2,4,7),imhist(P2),title('salt & pepper');
subplot(2,4,1), imshow(P1), title('gaussian');
subplot(2,4,3), imshow(P2), title('salt & pepper');
subplot(2,4,2), imshow(J), title(['Alpha trimmed mean filter for gaussian (', num2str(filtSize2), ')']);
subplot(2,4,4), imshow(H), title(['Alpha trimmed mean filter for salt & pepper (', num2str(filtSize2), ')']);
subplot(2,4,6),imhist(J),title(['Alpha trimmed mean filter for gaussian (', num2str(filtSize2), ')']);
subplot(2,4,8),imhist(H),title(['Alpha trimmed mean filter for salt (', num2str(filtSize2), ')']);
四、改进中值滤波算法的FPGA实现
文章篇幅较长,一下简单的模块就不赘述,如:OV5640数据采集,RGB色彩转换等。
FPGA顶层模块:
module dmk_alpha(
input clk,
input rst_n, //KEY4(o) 即OK按键作为复位
//key
input key_left, //alpha滤波算法开关,默认关闭,按一下开、按一下关...
input key_right, //常规中值滤波算法开关,默认关闭,按一下开、按一下关...
input key_up, //预留算法开关,默认关闭,按一下开、按一下关...
input key_down, //加噪算法开关,默认打开,按一下关、按一下开...
//LED
output [3:0] led,
//CMOS Port
inout cmos_scl, //cmos i2c clock
inout cmos_sda, //cmos i2c data
input cmos_vsync, //cmos vsync
input cmos_href, //cmos hsync refrence,data valid
input cmos_pclk, //cmos pxiel clock
output cmos_xclk, //cmos externl clock
input [7:0] cmos_db, //cmos data
output cmos_rst_n, //cmos reset
output cmos_pwdn, //cmos power down
//ILI9488 Port
output WR,
output RD,
output CS,
output RS,
output BL_cnt,
output [15:0] data,
output RESET
//SDRAM Port
// output sdram_clk, //sdram clock
// output sdram_cke, //sdram clock enable
// output sdram_cs_n, //sdram chip select
// output sdram_we_n, //sdram write enable
// output sdram_cas_n, //sdram column address strobe
// output sdram_ras_n, //sdram row address strobe
// output[1:0] sdram_dqm, //sdram data enable
// output[1:0] sdram_ba, //sdram bank address
// output[12:0] sdram_addr, //sdram address
// inout[15:0] sdram_dq //sdram data
);
localparam WIDTH = 480;
localparam HEIGHT= 320;
wire clk_qsys_w;
wire clk_lcd_w;//LCD驱动模块工作时钟,12.5MHz
wire pll_lock_w;
wire sys_rst_n;
wire dvp_vsync_w,dvp_hsync_w,dvp_de_w;
wire [7:0] dvp_data_w;
wire lcd_init_done;
wire [3:0] pulse;
wire [3:0] sw;
assign cmos_rst_n = 1'b1;
assign cmos_pwdn = 1'b0;
assign led[0]=~sw[0];//alpha滤波开关指示灯
assign led[1]=~sw[1];//预留算法开关指示灯
assign led[2]=~sw[2];//常规中值滤波算法开关指示灯
assign led[3]=~sw[3];//叠加椒盐噪声算法开关指示灯
//PLL模块
sys_pll u_sys_pll_0 (
.areset(!rst_n),
.inclk0(clk),
.c0(clk_qsys_w),//Qsys 100M 时钟
.c1(sdram_clk),//SDRAM 100M 时钟
.c2(cmos_xclk),//CMOS XCLK 24M 时钟
.c3(clk_lcd_w),//LCD驱动模块工作时钟,12.5MHz
.locked(pll_lock_w)
);
//延迟复位模块
delay_reset u_delay_reset_0(
.clk(clk),
.rst_n(rst_n && pll_lock_w),
.reset_n(sys_rst_n)
);
//摄像头I2C配置模块
ov5640_config u_ov5640_config_0(
.rst_n(sys_rst_n),
.clk(clk),
.i2c_scl(cmos_scl),
.i2c_sda(cmos_sda)
);
//按键控制模块
key_pulse u_key_pulse(
.clk(clk),
.rst_n(sys_rst_n),
.key({key_down,key_right,key_up,key_left}),
.pulse(pulse)
);
pulse_cnt u_pulse_cnt0(
.clk(clk),
.rst_n(sys_rst_n),
.pulse(pulse[0]),
.ocnt(sw[0])
);
pulse_cnt u_pulse_cnt1(
.clk(clk),
.rst_n(sys_rst_n),
.pulse(pulse[1]),
.ocnt(sw[1])
);
pulse_cnt u_pulse_cnt2(
.clk(clk),
.rst_n(sys_rst_n),
.pulse(pulse[2]),
.ocnt(sw[2])
);
pulse_cnt u_pulse_cnt3(
.clk(clk),
.rst_n(sys_rst_n),
.pulse(pulse[3]),
.ocnt(sw[3])
);
//摄像头采集模块,dvp转avalon-st裸流
wire [15:0] data_w0;
wire valid_w0,ready_w0,sop_w0,eop_w0;
cmos_top u_cmos_top_0(
.clk(clk),
.rst_n(sys_rst_n),
.cmos_pclk(cmos_pclk), // cmos.pclk
.cmos_vsync(cmos_vsync), // .vsync
.cmos_href(cmos_href), // .href
.cmos_data(cmos_db), // (x_cnt[0]?x_cnt[7:0]:{4'd0,x_cnt[11:8]}),// .data
.source_sop(sop_w0),
.source_valid(valid_w0),
.source_data(data_w0),
.source_eop(eop_w0),
.source_ready(ready_w0)
);
//灰度化算法模块
wire [7:0] data_w1;
wire valid_w1,ready_w1,sop_w1,eop_w1;
vip_rgb2gray_top u_vip_rgb2gray_top_0
(
.clk (clk ),
.rst_n (sys_rst_n),
//sink
.sink_sop (sop_w0),
.sink_eop (eop_w0),
.sink_valid (valid_w0),
.sink_data ({data_w0[15:11],3'b111,data_w0[10:5],2'b11,data_w0[4:0],3'b111}),
.sink_ready (ready_w0),
//source
.source_sop (sop_w1),
.source_eop (eop_w1),
.source_valid(valid_w1),
.source_data (data_w1),
.source_ready(ready_w1)
);
//叠加椒盐噪声模块
wire [7:0] data_w2;
wire valid_w2,ready_w2,sop_w2,eop_w2;
vip_na_pepper_top u_vip_na_pepper_top_0
(
.clk (clk),
.rst_n(sys_rst_n),
//enable
.proc_en(sw[3]),
//sink
.sink_sop (sop_w1),
.sink_eop (eop_w1),
.sink_valid(valid_w1),
.sink_data (data_w1),
.sink_ready(ready_w1),
//source
.source_sop (sop_w2),
.source_eop (eop_w2),
.source_valid(valid_w2),
.source_data (data_w2),
.source_ready(ready_w2)
);
//传统中值滤波算法模块
wire [7:0] data_w3,data3,data_ref3;
wire valid_w3,ready_w3,sop_w3,eop_w3;
assign data3=(sw[2])?data_w3:data_ref3;
medianfilter #(
.WIDTH(480),
.HEIGHT(320)
)u_medianfilter(
.clk(clk),
.rst_n(sys_rst_n),
.sink_sop(sop_w2),
.sink_valid(valid_w2),
.sink_data(data_w2),
.sink_eop(eop_w2),
.sink_ready(ready_w2),
.source_sop(sop_w3),
.source_valid(valid_w3),
.source_data(data_w3),
.source_data_ref(data_ref3),
.source_eop(eop_w3),
.source_ready(ready_w3)
);
//alpha滤波(改进的中值滤波)算法模块
wire [7:0] data_w4;
wire valid_w4,ready_w4,sop_w4,eop_w4;
vip_alpha_proc#(
.THR(64),
.DWIDTH(8),
.WIDTH(WIDTH),
.HEIGHT(HEIGHT),
.TAPS_NUM(3),//滤波窗口宽度
.D(2) //去掉D各最大值和D各最小值
)
u_vip_alpha_proc_0
(
.clk(clk),
.rst_n(sys_rst_n),
.proc_en(sw[0]),
.sink_sop(sop_w3),
.sink_valid(valid_w3),
.sink_data(data3),
.sink_eop(eop_w3),
.sink_ready(ready_w3),
.source_sop(sop_w4),
.source_valid(valid_w4),
.source_data(data_w4),
.source_eop(eop_w4),
.source_ready(ready_w4)
);
//TFTLCD模块
vip_ILI9488 u_vip_ILI9488_0(
.clk(clk),
.clk12p5M(clk_lcd_w),
.rst_n(sys_rst_n),
//Avalon-ST Sink
.sink_sop(sop_w4),
.sink_valid(valid_w4),
.sink_data({data_w4[7:3],data_w4[7:2],data_w4[7:3]}),
.sink_eop(eop_w4),
.sink_ready(ready_w4),
//Conduit
.lcd_intdone(lcd_init_done),
//TFTLCD interface
.WR(WR), // lcd.WR
.RD(RD), // .RD
.CS(CS), // .CS
.RS(RS), // .RS
.BL_cnt(BL_cnt), // .BL_cnt
.data(data), // .data
.RESET(RESET) // .RESET
);
endmodule
1、椒盐加噪模块
为了方便验证图像滤波效果,增加对比性,需要对色彩空间转换后的图像增加椒盐噪声。在图像系统中,可以使用随机数生成器来模拟椒盐噪声,并将其添加到输入的图像数据上,从而生成含有椒盐噪声的图像。
本系统采用M序列生成得伪随机数,m序列作为伪随机序列中最基本的序列,自相关性好,而且比较容易产生,是一类由寄存器与反馈电路构成的线性反馈移位寄存器(LFSR)所产生的序列。LFSR输出序列可以看作是一个数列,也称为伪随机序列。M序列就是一种特殊形式的LFSR,其输出序列总共有2^m-1个元素,其中m为LFSR中寄存器的位数。
在Verilog代码中,通过always代码块实现时钟边沿触发的寄存器更新,使用了一个7位宽度的寄存器作为M序列寄存器,初始值为111111(第一位为零)。然后在每一个时钟上升沿触发时,通过位移和异或运算的方式进行LFSR,从而生成新的随机数输出。需要注意的是,在复位信号rst为1时,应当将M序列寄存器的值重置为初始状态。M序列的硬件电路如图所示。
椒盐加噪模块仿真时序图:
2、中值滤波模块
很成熟的东西,不赘述。
modelsim仿真图:
3、α均值滤波模块
vip_alpha_proc模块主要完成修正的α均值滤波的功能,其硬件结构图如图所示。从结果图,可以看出椒盐加噪的图像进来后,extract_region_proc模块提取输入图像的邻域以并行方式输出,输出的邻域数据剔除D个最大和D个最小值后剩下的、参与均值运算的有效数据个数。数据进入rank模块进行像素值排序,排序操作中,从当前最小值索引min_idx开始循环遍历缓存数组,寻找未排序部分的最小值所在的索引位置,并将其赋值给最小值索引变量min_idx。然后,将找到的最小值移动到已排序部分的末尾,即将最小值与当前未排序部分的第一个元素交换。最后将最小值索引min_idx加1,继续进行排序操作。当所有元素都被排序完毕后,输出缓存数组的第一个元素作为排序后的结果到输出端口Dout。
Dout的数据输入到UnsignedAdderTreePipelined模块中,该模块是基于决策树结构的无符号加法器,可以实现对相邻的两个n位无符号整数进行相加。这个模块可以通过分段并行计算连续的加法操作,以达到提高工作效率的目的。完成加法后,数据进入下一模块UnsignedDividePipelined,并在这个模块内完成无符合除法。
在FPGA上实现无符号数除法操作是一个挑战,需要通过不断减去被除数得到商数,需要使用循环计算,这使得在FPGA上实现除法更加复杂,并且增加了设计开销,所以可以本设计使用流水线技术来实现高效的分频器模块。
该模块的输入端口包括一个16位的被除数dividend、一个16位的除数divisor、时钟信号clk和复位信号rstn,输出端口包括商quotient、余数remainder和完成信号done。
在模块中使用了四个寄存器dividend_reg、divisor_reg、remainder_reg、count_reg和一个状态寄存器state_reg。state_reg表示模块所处的状态,有三种状态:初始化状态(IDLE)、除法运算状态(DIVIDE)和计算结束状态(COMPLETE)。dividend_reg和divisor_reg分别是被除数和除数的寄存器。余数的结果remainder_reg采用右移方式获取。采用流水线技术,每次计算过程中,将被除数左移一位,并与当前余数的最高位相加,如果结果大于等于除数,则商数的当前位为1,否则为0。在最后一步中,当余数变成整个数据位全为0时,计算完成并将状态寄存器置为COMPLETE。
模块中的组合逻辑块包括一个case语句,用于根据状态寄存器选择相应的操作进行处理。例如,在IDLE状态中,首先将输入参数值保存到相应的寄存器中,然后将状态寄存器设置为DIVIDE。在DIVIDE状态中,每次都进行一定的除法运算,并更新状态寄存器来处理下一个时钟周期的运算。对于超出总位数的高位的余数,都需要移位出来,下一步才能正确计算商和余数。最后,在COMPLETE状态中,将输入寄存器值清零,以完成一次除法运算。
完成乘法后的数据,经过寄存器、比较器及延迟模块后,输出到ILI9488驱动模块,最后显示在TFT-LCD屏幕上。本模块经过Modelsim功能验证,时序及数据都正确,时序仿真结果如图所示。
阿尔法均值滤波模块:
//阿尔法滤波算法处理
module vip_alpha_proc#(
parameter THR = 64,//差值阈值,即原图和alpha滤波的结果差值的绝对值的阈值,大于该值输出alpha滤波结果,否则输出原图数据
parameter DWIDTH = 10,
parameter WIDTH = 640,
parameter HEIGHT = 480,
parameter TAPS_NUM = 5, //邻域边长
parameter D = 8 //去掉D个最大的和D个最小的,即总计去掉2D个数
)(
input clk,
input rst_n,
input proc_en,//算法使能,高有效
input sink_sop,
input sink_valid,
input [DWIDTH-1:0] sink_data,
input sink_eop,
output sink_ready,
output source_sop,
output source_valid,
output [DWIDTH-1:0] source_data,
output source_eop,
input source_ready
);
localparam WIN_NUM = TAPS_NUM*TAPS_NUM;//滤波窗口的元素个数
localparam ACT_NUM = WIN_NUM-2*D;//剔除D个最大和D个最小值后剩下的、参与均值运算的有效数据个数
wire [WIN_NUM*DWIDTH-1:0] data_w0;
wire sop_w0,eop_w0,valid_w0,ready_w0;
wire [WIN_NUM*DWIDTH-1:0] data_w1;
wire sop_w1,eop_w1,valid_w1;
wire [(ACT_NUM)*DWIDTH-1:0] data_w2;
wire sop_w2,eop_w2,valid_w2;
wire [DWIDTH-1:0] data_w3;
wire sop_w3,eop_w3,valid_w3;
wire fifo_rd_w,fifo_almost_full_w,fifo_empty_w;
wire [DWIDTH-1:0] center_data_w0;//data_w0中心点灰度值
wire [DWIDTH-1:0] origin_data_w; //和alpha滤波结果对齐的原始图像数据
reg source_valid_r;
assign fifo_rd_w = !fifo_empty_w && source_ready;
assign source_valid = source_valid_r;
assign ready_w0 = !fifo_almost_full_w;
assign center_data_w0 = data_w0[((WIN_NUM+1)/2)*DWIDTH-1:((WIN_NUM-1)/2)*DWIDTH];//原始图像
extract_region_proc#(
.DWIDTH(DWIDTH),
.WIDTH(WIDTH),
.HEIGHT(HEIGHT),
.TAPS_NUM(TAPS_NUM)//邻域边长
)u_extract_region_proc_0(
.clk(clk),
.rst_n(rst_n),
.sink_sop(sink_sop),
.sink_valid(sink_valid),
.sink_data(sink_data),
.sink_eop(sink_eop),
.sink_ready(sink_ready),
.source_sop(sop_w0),
.source_valid(valid_w0),
.source_data(data_w0),
.source_eop(eop_w0),
.source_ready(ready_w0)
);
//排序,延迟3个时钟出结果
rank#(
.WIN_WIDTH(TAPS_NUM),//滤波窗口宽度,范围2-15
.WIN_HEIGHT(TAPS_NUM),//滤波窗口高度,范围2-15
.DWIDTH(DWIDTH)//图像的像素数据宽度
)
u_rank_0(
.clk(clk),
.rst_n(rst_n),
.din_valid(valid_w0),
.din_data(data_w0),
.dout_valid(valid_w1),
.dout_data(data_w1)
);
//对sop_w0 eop_w0作3个时钟的延迟
vip_alpha_shift_regs#(
.DWIDTH(2), //数据位宽
.DELAY_DUTY(3)//延迟的时钟周期数,最小是1
)
u_vip_alpha_shift_regs_0(
.clk(clk),
.rst_n(rst_n),
.idata({sop_w0,eop_w0}),
.odata({sop_w1,eop_w1})
);
//并行加法器,延迟DELAY_STAGES个时钟
UnsignedAdderTreePipelined #(
.DATA_WIDTH(DWIDTH),
.LENGTH(ACT_NUM),
.DELAY_STAGES($clog2(ACT_NUM))
)
u_UnsignedAdderTreePipelined_0(
.clk(clk),
.reset(~rst_n),
.in_addends(data_w1[(WIN_NUM-D)*DWIDTH-1:D*DWIDTH]), //此处去掉D个最大值和D个最小值
.in_advance(1'b1),
.out_sum(data_w2)
);
//对sop_w1 eop_w1 valid_w1作 $clog2(ACT_NUM) 个时钟的延迟,适配并行加法器带来的延迟
vip_alpha_shift_regs#(
.DWIDTH(3), //数据位宽
.DELAY_DUTY($clog2(ACT_NUM))//延迟的时钟周期数,最小是1
)
u_vip_alpha_shift_regs_1(
.clk(clk),
.rst_n(rst_n),
.idata({sop_w1,eop_w1,valid_w1}),
.odata({sop_w2,eop_w2,valid_w2})
);
UnsignedDividePipelined #(
.WIDTH_N(DWIDTH + $clog2(ACT_NUM)), //被除数位宽
.WIDTH_D($clog2(ACT_NUM)), //除数位宽
.PIPELINE(8) //流水线级数固定为8
)u_UnsignedDividePipelined_0 (
.clock(clk),
.denom(ACT_NUM),//除数
.numer(data_w2),//被除数
.quotient(data_w3)//商
);
//对sop_w1 eop_w1 valid_w1作8个时钟的延迟,适配除法器带来的延迟
vip_alpha_shift_regs#(
.DWIDTH(3), //数据位宽
.DELAY_DUTY(8)//延迟的时钟周期数,最小是1
)
u_vip_alpha_shift_regs_2(
.clk(clk),
.rst_n(rst_n),
.idata({sop_w2,eop_w2,valid_w2}),
.odata({sop_w3,eop_w3,valid_w3})
);
//对center_data_w0延迟,得到origin_data_w
vip_alpha_shift_regs#(
.DWIDTH(DWIDTH), //数据位宽
.DELAY_DUTY(3+$clog2(ACT_NUM)+8)//延迟的时钟周期数,最小是1
)
u_vip_alpha_shift_regs_3(
.clk(clk),
.rst_n(rst_n),
.idata(center_data_w0),
.odata(origin_data_w)
);
wire [DWIDTH-1:0] delt_w;//原图像灰度和alpha滤波结果的差值的绝对值
wire [DWIDTH-1:0] act_data_w3;//最终输出图像数据
assign delt_w = (origin_data_w > data_w3)?origin_data_w-data_w3:data_w3-origin_data_w;
assign act_data_w3 = proc_en?((delt_w > THR)?data_w3:origin_data_w):origin_data_w;//算法使能并且差值大于阈值则输出alpha滤波结果,否则原图
vip_alpha_scfifo#(
.DWIDTH(DWIDTH+2),
.NUMWORDS(128)
)
u_vip_alpha_scfifo_0 (
.aclr(!rst_n),
.clock(clk),
.data({sop_w3,eop_w3,act_data_w3}),
.rdreq(fifo_rd_w),
.wrreq(valid_w3),
.almost_full(fifo_almost_full_w),
.empty(fifo_empty_w),
.q({source_sop,source_eop,source_data})
);
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
source_valid_r <= 1'b0;
end
else
begin
source_valid_r <= fifo_rd_w;
end
end
endmodule
4、改进中值滤波图像系统的实验效果
经过上文介绍,本文完成了改进中值滤波算法的理论验证到实物设计,完成了各个算法模块的时序仿真验证,接下来需要进行实验效果测试。将所设计的RTL在Quratus II中进行综合,布局布线,其资源如表所示,从表中可以看出,虽然系统结构繁杂但是由于流水线结构的设计,整个系统对芯片的片上资源占用并不多,这为平台后续的扩展开发奠定了基础。
FPGA开发平台上电后,将软件编译出来的码流文件固化到Flash芯片中。启动程序,摄像头采集实时图像,通过按键启动椒盐加噪模块,分别在原始图像中参入噪声密度为0.002,0.03,0.04,0.06的椒盐噪声,加噪图像如所示。
参入噪声后,接着通过按键,启动α均值模块对噪声图像进行滤波,滤波后的实时图像如图4-10所示,从图4-10(a-d)看,噪声密度0.02和0.04的实时图像,经过滤波后,保留了原有的特征细节,边缘信息完好,图像中的噪声明细减少了,结果符合理论研究。
但是,当噪声密度达到0.06的时候,滤波后的图像,虽然能保留原有的特性细节,但是图像中可以看到有约0.01的噪声点,系统仍有改进空间。
五、总结
本文对多种传统图像滤波器进行了研究分析,在中值滤波的基础上,结合均值滤波器设计了一种修正的α均值滤波器,并在自主设计的硬件开发平台上建立了图像处理系统。该系统利用现场可编程门阵列并行处理数据、结构灵活性强的特点,实现了传统中值滤波的改进,并通过流水线等设计,提高了算法模块的运算速率及整个系统的吞吐量,增强了图像系统的实时性。
具体的工作内容如下:
首先,对传统的滤波算法及硬件实现国内外现状进行了调研分析,并对传统中值滤波的优缺点进行了总结,提出了一种改进中值滤波算法的图像处理系统。
接着,对FPGA芯片特性做了介绍,阐述其做为现场可编程门阵列在图像处理领域的专有优势。基于此,围绕改进中值滤波算法的图像处理系统,完成了系统整体框架设计。根据系统框架设计了主控电路、电源电路、图像采集电路及显示电路,并对相关电路进行了仿真验证,最后将所有电路整合在PCB文件上,制作出硬件开发平台。
阐述图像噪声的产生及危害,通过MATLAB进行仿真,分析传统滤波器对图像噪声的抑制效果,引出在中值滤波算法上结合均值滤波算法的α均值滤波算法,对改进后的中值滤波算法进行仿真验证,并与其他滤波算法做对比,可知改进后的算法比传统算法在图像抑制噪声上,适应性及信号保真性更强。
最后,依据改进算法的工作原理,结合FPGA特性,在Quartus II平台上,对各个算法模块及驱动电路进行RTL设计。在设计过程中,结合算法原理,模块加入了流水线设计,加快了整个系统的运算速率及吞吐量。为了验证系统的可靠性及实时性,对系统进行了RTL行为级仿真。通过设计激励文件,并结合系统在Modelsim平台上进行测试,测试结果表明,图像处理系统的时序及数据流正确,因此对系统进行了板级验证。从验证结果看,FPGA开发平台能够实时稳定的采集图像,并对原始图像加入噪声,通过按键启动,改进后的中值滤波器进行图像处理,处理后的图像,噪声抑制明显,细节保留完好。
受篇幅和所学知识限制,对一些问题未能深入研究,比如在第四章实验结果中,当噪声密度到达0.06的时候,滤波后的图像仍有0.01的噪声,系统仍有改进空间,深度学习技术的不断进步,基于神经网络的图像滤波算法有望解决这一难题。同时,FPGA虽然能够加速图像处理并且为未知的算法提供硬件验证电路平台,但FPGA目前可以调用的IP较少,很多算法必须从底层开始做起,花费的精力会比较多,AI图像处理芯片及Python等自动化语言,可能会更具优势。
六、工程获取
1、直接点击下载:
基于阿尔法均值滤波的FPGA图像系统(Verilog+原理图+PCB+仿真)
如果点了没有跳转,就是资源还没审核好。
纯Verilog代码(使用了少部分pll,fifo IP)
2、私信我或添加邮箱获取
7折获取资料。(本人有调试好的硬件,上电可看现象,有需要可预订)
联系邮箱:bumianzhe@126.com