|
本帖最后由 正点原子 于 2020-9-28 10:15 编辑
1)实验平台:正点原子领航者ZYNQ开发板
2)购买链接:https://item.taobao.com/item.htm?id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html
4) 正点原子官方B站:https://space.bilibili.com/394620890
5)对正点原子FPGA感兴趣的同学可以加群讨论:876744900 点击加入:
6)关注正点原子公众号,获取最新资料
第十三章基于xfOpenCV的中值滤波实验
在数字图像处理中,无论是直接获取的灰度图像,还是由彩色图像转换得到的灰度图像,里面都有噪声的存在,噪声对图像质量有很大的影响。而中值滤波是一种常用的降噪方法,它不仅可以去除孤点噪声,而且可以保持图像的边缘特性,不会使图像产生显著的模糊。本章我们将在HLS中使用xfOpenCV库来实现图像的中值滤波。
本章包括以下几个部分:
1313.1简介
13.2实验任务
13.3HLS设计
13.4IP验证
13.5下载验证
13.1简介
空间滤波是图像处理领域应用非常广泛的工具之一,它可以改善图像质量,包括去除高频噪声与干扰、图像平滑等。我们常见的空间滤波有中值滤波和均值滤波。
图像可以看成是一个定义在二维平面上的信号,该信号的幅值对应像素的灰度(彩色图像对应RGB三个分量)。图像的频率指的是空间频率,它和我们认知的物理频率是不同的。图像的频率是表征图像中灰度变化剧烈程度的指标,是灰度在平面空间上的梯度。不同频率信息在图像结构中有不同的作用。图像的主要成分是低频信息,它形成了图像的基本灰度等级,对图像结构的决定作用较小;中频信息决定了图像的基本结构,形成了图像的主要边缘结构;高频信息形成了图像的边缘和细节,是在中频信息上对图像内容的进一步强化。
我们也可以通过空间滤波器(也称为空间掩模、模板或窗口)直接作用于图像本身而对图像进行滤波处理。空间滤波器由两部分组成:(1)邻域,(2)对该邻域包围的图像像素执行的预定义操作。领域是指一个像素点及其附近像素点所组成的空间。滤波会产生一个新像素,像素的坐标就是邻域中心的坐标,像素的值就是滤波操作的结果。
中值滤波就是一种很常见的空间滤波,它是一种非线性图像平滑技术。它将每一像素点及该像素点的邻域作为一个滤波模板,计算出模板中所有像素点的灰度值的中值,然后用它代替模板中心点像素的值。下图为像素点P及其周围8个像素点所组成的3x3滤波模板:
图 13.1.1 中值滤波模板
在图 13.1.1中,中值滤波需要计算出模板中9个像素点灰度的中间值,并用这个中值作为像素点P的灰度值。
中值滤波是一种基于排序统计理论的非线性信号处理技术,它可以消除孤立的噪声点,从而让图像中的像素值更接近真实值。红外图像中的盲元就是一种孤立噪点的例子,如下图所示:
图 13.1.2 红外图像中的盲元
由于红外探测器制造过程中的缺陷,传感器中某些像元的输出可能会非常大,导致图像中对应的像素点非常亮,我们称之为盲元。图 13.1.2中红色箭头所指的白色亮点就是红外图像中的盲元,它在图像中属于脉冲噪声。中值滤波对这类脉冲噪声具有良好的滤除作用,特别是在滤除噪声的同时,能够保护信号的边缘,使之不被模糊。这些优良特性是线性滤波方法所不具备的。此外,中值滤波的算法比较简单,也易于用硬件实现。所以,中值滤波方法一经提出后,便在数字信号处理领域得到广泛的应用。
为了直观的看到中值滤波的效果,下面给出一张受脉冲噪声影响的图像,如图 13.1.3所示。虽然图中仅有2%的像素受到破坏,但足以严重降低图像质量。
图 13.1.3 包含脉冲噪声的图像
图 13.1.4 中值滤波后的图像
图 13.1.4是经3x3中值滤波器过滤后的同一图像。可以看到,与滤波前的图像相比,脉冲噪声已被完全消除,图像质量得到明显的提升。
13.2实验任务
本节的实验任务是使用Vivado HLS实现一个图像处理的IP核,该IP核能够将OV5640摄像头产生的RGB彩色图像转换成灰度图像,并对灰度图像进行中值滤波。
13.3HLS设计
在前面章节中的图像处理例程中,我们使用的是Vivado HLS自带的HLS视频库中的函数。有关HLS视频库的介绍大家可以参考Xilinx官方文档《Vivado Design Suite User Guide --High-Level Synthesis》,即UG902。需要注意的是,只有在v2018.2及之前版本的UG902文档中,才对HLS视频库作了详细介绍。而在v2018.3及其之后的版本中,只给出了一个链接htps://github.com/Xilinx/xfopencv,该链接指向的是GitHub上一个名为Xilinx xfOpenCV Library的库。也就是说,从v2018.3版本开始的UG902中,已经不再介绍HLS视频库,而是替换成了xfOpenCV库。
xfOpenCV库可以简单地理解为HLS视频库的升级版,但是两者却并不兼容。由于本章所使用的中值滤波算法在HLS视频库中并没有对应的函数可以使用,但是xfOpenCV中有现成的中值滤波函数,因此我们将在本章介绍如何在HLS中使用xfOpenCV库来进行图像处理。
与HLS视频库不同,Vivado HLS中并没有集成xfOpenCV库,我们需要从UG902给出的链接中下载xfOpenCV库文件。目前xfOpenCV库已经更新到2019.1版本,该版本修正了之前版本中的Bug,我们在本章中将使用该版本。需要注意的是,2019.1版本的xfOpenCV库与之前版本的库和工具都不兼容。也就是说,只能在2019.1及其之后的版本的Vivado HLS中使用2019.1版本的xfOpenCV库。如果大家有兴趣学习Xilinx xfOpenCV库,请到Xilinx官网自行下载2019.1版本的Vivado。
由于正点原子ZYNQ开发板的例程是基于2018.3版本的Vivado套件,因此使用xfOpenCV库可能需要另外安装2019.1的版本。如果不想再次安装Vivado或者更换Vivado版本,可以跳过本章节的学习。因为在18.3及其之后的Vivado HLS工具中,默认是支持HLS视频库的,大家依然可以使用HLS视频库中的函数进行设计开发。
GitHub上的xfOpenCV库如下图所示:
图 13.3.1 xfOpenCV库2019.1版
点击右上角的“Clone or download”可以将xfOpenCV库下载到本地,为了方便大家使用学习,我们将下载好的压缩包xfopencv-2019.1_release.zip放在了ZYNQ开发板资料盘“A盘\8_ZYNQ&FPGA参考资料\其它”目录下。将该压缩包拷贝到Vivado的安装目录下并解压,将解压后的文件夹重命名为“xfopencv”,如下图所示:
图 13.3.2 解压之后的xfopencv库
我们在电脑中的“F:\ZYNQ\High_Level_Synthesis”目录下新建一个名为ov5640_median_filter的文件夹,作为本次实验的工程目录。然后打开Vivado HLS 2019.1,创建一个新的工程“ov5640_median_filter”,选择工程路径为刚刚创建的文件夹。需要注意的是,工程名以及路径只能由英文字母、数字和下划线组成,不能包含中文、空格以及其他特殊字符。如下图所示:
图 13.3.3 工程配置界面
设置好工程名及路径之后,点击“Next”,进入如下界面设置顶层函数:
图 13.3.4 设置顶层函数
然后选择ZYNQ器件所对应的型号,如下图所示:
图 13.3.5 设置时钟周期和器件型号
工程创建完成后,在工程面板中的“source”目录上点击右键,然后在打开的列表中选择“New File”新建源文件,在弹出的对话框中输入源文件的名称“ov5640_median_filter.cpp”,如下图所示。源文件默认的保存路径为HLS工程目录,为方便源文件的管理,我们在工程目录下新建一个名为“src”的文件下,将源文件保存在src目录下。
图 13.3.6 输入源文件名
我们输入的源文件的后缀名为“.cpp”,即使用C++语言进行设计。使用xfOpenCV库同样只能使用C++来进行设计。
“ov5640_median_filter.cpp”文件源代码如下:
- 1 #include "ov5640_median_filter.h"
- 2
- 3 void ov5640_median_filter(
- 4 hls::stream< ap_axiu<24,1,1,1> >& _src,
- 5 hls::stream< ap_axiu<24,1,1,1> >& _dst
- 6 ){
- 7
- 8 #pragma HLS INTERFACE axis register both port=_src
- 9 #pragma HLS INTERFACE axis register both port=_dst
- 10 #pragma HLS INTERFACE ap_ctrl_none port=return
- 11 #pragma HLS dataflow
- 12
- 13 //定义xf::mat格式变量
- 14 xf::Mat<XF_8UC3, HEIGHT, WIDTH, XF_NPPC1> imgInput1;
- 15 xf::Mat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC1> imgGray;
- 16 xf::Mat<XF_8UC1, HEIGHT, WIDTH, XF_NPPC1> imgMedian;
- 17 xf::Mat<XF_8UC3, HEIGHT, WIDTH, XF_NPPC1> imgOutput1;
- 18
- 19 #pragma HLS stream variable=imgInput1.data dim=1 depth=1
- 20 #pragma HLS stream variable=imgGray.data dim=1 depth=1
- 21 #pragma HLS stream variable=imgMedian.data dim=1 depth=1
- 22 #pragma HLS stream variable=imgOutput1.data dim=1 depth=1
- 23
- 24 //将AXI Stream格式的视频转成xf::mat格式
- 25 xf::AXIvideo2xfMat(_src, imgInput1);
- 26
- 27 //将RGB格式图像转成灰度图像
- 28 xf::rgb2gray<XF_8UC3,XF_8UC1,HEIGHT, WIDTH, XF_NPPC1>(imgInput1, imgGray);
- 29
- 30 //对灰度图像进行中值滤波
- 31 xf::medianBlur<3,XF_BORDER_REPLICATE,XF_8UC1,HEIGHT, WIDTH, XF_NPPC1>
- 32 (imgGray, imgMedian);
- 33
- 34 //将灰度图像转成RGB三个通道的灰度图像
- 35 xf::gray2rgb<XF_8UC1,XF_8UC3,HEIGHT, WIDTH, XF_NPPC1>(imgMedian,imgOutput1);
- 36
- 37 //将xf::mata格式的图像转成AXI Stream格式
- 38 xf::xfMat2AXIvideo(imgOutput1, _dst);
- 39 }
复制代码
在代码的第1行,包含了一个名为“ov5640_median_filter.h”的头文件,这个头文件里包含了使用xfOpenCV库进行灰度转换和中值滤波所需要的头文件和宏定义。
代码的主体部分与《OV5640摄像头灰度显示实验》非常类似,只是在代码的31行多了一个中值滤波的函数xf::medianBlur(),它是xfOpenCV库中的函数,可以对输入的图像进行中值滤波,从而减小图像的噪声。在Xilinx官方文档《Xilinx OpenCV User Guide》(简称UG1233)中给出了该函数的声明,如下所示:
图 13.3.7 xfOpenCV中值滤波函数
在上图中,函数medianBlur()有两个参数,_src表示输入的图像,_dst为中值滤波之后的图像,它们的格式为xf::Mat。这也是xfOpenCV与HLS视频库的一个区别,在HLS视频库中函数使用的图像格式为hls::Mat。
尖括号<>内部为函数medianBlur()的模板,其中FILTER_SIZE表示中值滤波模板的尺寸。如代码第31行所示,本交实验中我们使用的是一个3x3模板。模板中第二个参数BORDER_TYPE表示图像边界处理的类型,目前中值滤波函数仅支持XF_BORDER_REPLICATE类型,表示直接复制边界处的像素数据。模板第三个参数TYPE表示图像像素数据的类型,这里中值滤波处理的像素数据类型为XF_8UC1,它是由RGB彩色图像经过rgb2gray函数转换后得到的灰度数据。模板中ROWS和COLS表示图像的高和宽。而最后一个参数NPC表示每个时钟处理的像素个数(Number of pixels per clock),在代码中设置为XF_NPPC1,表示每个时钟处理一个像素。
代码的19至22行的编译指令用于指示xf::Mat格式变量中的.data成员使用流(stream)数据来通信,即采用FIFO来实现,而不是默认的RAM。后面的选项dim=1表示用于转成成FIFO的数组是一维的,depth=1表示FIFO的深度为1。在应用dataflow优化时,多个函数之间以流水线的形式处理图像数据流,因为每个时钟可以处理一个像素,因些FIFO的深度没有必要太大,设置成1可以减少FPGA存储资源的消耗。
从代码中可以看出,xfOpenCV在作图像处理时与HLS视频库的处理流程非常类似,甚至连函数名都很像,但是函数的命名空间由hls::变成了xf::。除此之外,xfOpenCV库中的函数处理的数据类型是xf::Mat,而HLS视频库处理的数据类型是hls::Mat,两者最大的区别在于hls::Mat使用hls::stream存储图像,而xf::Mat使用的是指针。因些使用HLS视频库完成的设计不能直接移植到xfOpenCV库。
然后以同样的方式在src目录下创建名为“ov5640_median_filter.h”的头文件,其代码如下所示:
- 1 #ifndef _OV5640_MEDIAN_FILTER_H_
- 2 #define _OV5640_MEDIAN_FILTER_H_
- 3
- 4 #include "hls_stream.h"
- 5 #include "ap_int.h"
- 6 #include "common/xf_common.h"
- 7 #include "common/xf_utility.h"
- 8 #include "common/xf_infra.h"
- 9 #include "imgproc/xf_median_blur.hpp"
- 10 #include "imgproc/xf_cvt_color.hpp"
- 11 #include "imgproc/xf_cvt_color_1.hpp"
- 12 #include "imgproc/xf_rgb2hsv.hpp"
- 13 #include "imgproc/xf_bgr2hsv.hpp"
- 14
- 15 /* config width and height */
- 16 #define WIDTH 800
- 17 #define HEIGHT 480
- 18
- 19 void ov5640_median_filter(
- 20 hls::stream< ap_axiu<8,1,1,1> >& _src,
- 21 hls::stream< ap_axiu<8,1,1,1> >& _dst
- 22 );
- 23
- 24 #endif
复制代码
代码的第4到第13行引用了xfOpenCV库作灰度转换和中值滤波所需要的头文件。代码的第16和17行指定了处理图像的宽和高。最后在代码的第19行声明了前面我们所编写的函数ov5640_median_filter()。
代码输入完成后,按快捷键Ctrl+S保存。
接下来在工程上右击,选择“Project Settings”,如下图所示:
图 13.3.8 设置工程
在弹出的对话框左侧选择“Synthesis”,然后选择源文件ov5640_median_filter.cpp,点击右侧的“Edit CFLAGS”,如下图所示:
图 13.3.9 编辑CFLAGS
在弹出的对话框中输入-IE:/Xilinx/xfopencv/include -D__SDSVHLS__,如下图所示:
图 13.3.10 输入CFLGAS
在图 13.3.10中,-I后面的“E:/Xilinx/xfopencv/include”是我们解压出来的xfOpenCV库中include文件夹的路径。大家需要根据自己电脑上的xfOpenCV库的路径分别进行设置,路径中不能出现中文、空格或其他特殊字符。
输入CFLGAS完成后,点击OK。然后选择图 13.3.9中名为“ov5640_median_filter.h”的头文件,点击右侧的Remove将其移除。因为头文件和源文件位于同一目录,编译时工具会自动找到该头文件。移除后如下图所示:
图 13.3.11 工程设置完成
工程设置完成后点击右下角的OK关闭设置界面。
接下来点击工具栏中向右的绿色三角形对设计进行综合,综合完成后,会自动打开综合结果(solution)的报告。在综合报告中还给出了设计的性能评估、资源评估以及接口等信息。本次实验中,我们重点关注综合工具为我们生成的接口信息,如下图所示:
图 13.3.7 接口信息
从图中可以看出,设计综合出了两个“AXI4-Stream”接口,分别用于输入待处理的视频以及输出处理之后的视频流。
在综合结果正确的情况下,在工具栏中点击黄色的“田”字按钮,导出RTL。
在导出RTL结束之后,我们到工程目录所指向的文件夹中可以看到以ZIP压缩文件形式存在的IP核,如下图所示:
图 13.3.18 文件夹中的IP核
HLS设计结束之后,我们将在Vivado中对导出的IP核进行验证。
13.4IP验证
在IP验证环节,我们会使用Vivado工具的IP集成器将生成的IP核添加到Block Design中,然后完成设计后将程序下载到领航者开发板上进行验证。用于IP验证的底层硬件可以在《领航者ZYNQ之嵌入式开发指南》第二十三章“OV5640 摄像头 LCD 显示”实验的基础上进行。
参考《正点原子HLS开发指南》“OV5640摄像头灰度显示实验”中的下载验证部分,将生成的IP插入到“OV5640 摄像头 LCD 显示”实验底层硬件中的Block Design中。最终的设计如下图所示:
图 13.4.1 用于验证中值滤波IP核的最终设计
在图 13.4.1中,ov5640_capture_data模块获取OV5640摄像头采集的图像,然后通过Video In to AXI4-Stream模块将摄像头图像转换成AXI4-Stream格式的视频流。该视频流输入HLS生成的中值滤波IP核ov5640_median_filter,将RGB888格式的彩色图像转换成灰度图像,并对灰度图像进行中值滤波,然后同样以AXI4-Stream格式将处理后的视频流输出给VDMA。
如果大家对设计中其他各模块的功能不了解的话,请大家参考《领航者ZYNQ之嵌入式开发指南》第二十三章“OV5640 摄像头 LCD 显示”实验。
到这里我们的Block Design就设计完成了,在Diagram窗口空白处右击,然后选择“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。
接下来在Source窗口中右键点击Block Design设计文件“system.bd”,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。
最后在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“Generate Bitstream”,对设计进行综合、实现、并生成Bitstream文件。
在生成 Bitstream 之后,在菜单栏中选择 File > Export > Export hardware 导出硬件,并在弹出的对话框 中,勾选“Include bitstream”。然后在菜单栏选择 File > Launch SDK,启动 SDK 软件。
在本次实验中,Vivado SDK中的应用工程不需要作任何修改。
13.5下载验证
编译完工程之后我们就可以开始下载程序了。将 OV5640 摄像头模块插在领航者 Zynq 开发板的“OLED/CAMERA”插座上,并将 LCD 的排线接头插入开发板上的 LCD 接线座。将下载器一端连电脑,
另一端与开发板上的 JTAG 端口连接,连接电源线并打开电源开关。
在 SDK 软件下方的 SDK Terminal 窗口中点击右上角的加号设置并连接串口。然后下载本次实验硬件设计过程中所生成的 BIT 文件,来对 PL 进行配置。最后下载软件程序,下载完成后在LCD上就可以看到摄像头采集的彩色图像被转换成了灰度图像。我们会发现中值滤波处理后的图像与灰度转换实验得到图像几乎没有差别,这是因为现如今的彩色摄像头的采集的图像质量都很高,含有的干扰很少,因此在中值滤波前后图像差别不明显。
图 13.5.1 中值滤波下载验证结果
|
阿莫论坛20周年了!感谢大家的支持与爱护!!
曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
|