简介 该项目基于OV5640摄像头与ZYNQ,实现最多8个运动目标的检测,输出结果会显示在RGB LCD屏幕,开发板的四个按键功能如下
PL_KEY0:控制运动区域阈值的变化
PL_KEY1:控制运动目标间距离阈值的变化
PS_KEY0:控制输出的图像类型,可输出 灰度数据二值化帧差结果 二值化帧差的开运算结果 带运动检测边框的视频数据
PL_KEY1:第一次按下使能前三个按键,随后按下控制两个阈值的加减
按键按下后的控制信息均通过串口打印至SDK的显示终端
硬件平台采用正点原子领航者V2开发板,型号7010
整个项目思路主要参考b站up大磊fpga11_基于ZYNQ的帧差法运动目标检测_大磊FPGA图像处理_哔哩哔哩_bilibili
文章图片
https://www.bilibili.com/video/BV1Ub4y1d7Ca?spm_id_from=333.999.0.0
正文中的程序框图均来自视频中的截图或正点原子的文档
实物图展示
文章图片
文章图片
硬件架构
文章图片
整体架构 如图1,OV5640摄像头采集到的数据通过图像采集模块,输入给Video in to AXI4-Stream IP核,从而转化为AXI4-Stream Video协议的视频数据,Video协议时序图2所示(具体请考视频链接),该Viedo协议的数据会分成两路,一路送入VDMA0 IP核将视频数据写入PS端的DDR缓存,另一路送入自己编写的帧差法目标检测IP核,同时VDMA0会从DDR中读取前一帧的图像送入帧差法目标检测IP核,与当前帧进行帧差运算,生成带有运动检测边框的Video协议视频数据,送入VDMA1,写到PS端的DDR中缓存,VDMA1再从DDR读出数据送入AXI4-Stream to Video Out IP核,该IP可直接引出端口连接LCD屏幕
文章图片
图1 整体架构
文章图片
图2 AXI4-Stream Video协议时序
帧差模块的代码流程 整体流程如图3,实际上在“插值运算”->“检测方框”的中间又添加了腐蚀膨胀运算模块,即进行开运算,可以去除局部的高信号噪点
文章图片
图3 帧差模块框图 整个模块的接口有三个,均为stream video接口,s0 s1分别接收当前输入的视频数据和从VDMA读取的前一帧视频数据,m接口输出带有检测边框的视频数据
文章图片
因为s0输入的视频数据是72M摄像头数据通过IP核转换成100M时钟下的stream数据,而上一帧数据s1来自VDMA0,本身就是100M下的stream数据,因此相比s0的输入,s1会更先输入进来,因此s1进来的stream数据先缓存到fifo中,以保证前一帧数据和当前帧数据对齐
文章图片
文章图片
由于从FIFO读出的数据会比当前输入的s0数据滞后两个周期,所以需要s0数据寄存两次
文章图片
同步好前一帧和当前帧的数据后,进行转灰度,需要三个周期
文章图片
对前一帧和后一帧的每个灰度像素数据进行求差,如果差大于阈值就认为是运动区,将标志位moving_flag拉高,这个moving_flag是跟随着当前s0数据的,即当前周期s0输入数据a,那么六个周期后的moving_flag信号是对应这个数据a的,所以为了对齐当前输入和运动标志数据moving_flag,要将当前s0时序数据寄存6个周期,下图这种将valid和ready信号考虑进去的时序信号寄存方式可以使寄存后的信号更稳定不易出错
文章图片
要点:因为摄像头采集模块clken信号的存在,s0的数据valid信号不是在每一行数据中始终保持高电平,而是断断续续的(如图4),因此要尤其注意只有valid和ready信号都为高时才能对数据做处理
文章图片
图4 AXI4-Stream Video 数据的ila采样
对于moving_flag构成的二值视频信号可以再进行开运算,即先腐蚀后膨胀,能够去除高信号噪点首先用两个shift_ram ip核存储前两行数据,从而与当前行构成待处理的三行数据,shift_ram其实就是移位寄存器,在ip核设置中将寄存器长度设置为显示器行像素的个数,位宽可以设置为1以节省资源,因为要存入寄存器的数据是1位的moving_flag,例化两个shift_ram,其中shift_ram0保存第二行数据,shift_ram1保存第一行数据,当前输入的视频数据会移位到shift_ram0,从shift_ram0移出的数据会移入shift_ram1,就这样,当前从shift_ram1、shift_ram0与输入进来的视频数据构成三行一列数据用来构造3x3矩阵,下面仿真中例化了两个shift_ram,深度都为8,当ce信号拉高时开始向shift_ram0移入数据,在100ns的clk上升沿最先移入的数据是6,在400ns读出的数据分别是6 14,以及当前输入的数据21,实际上这三个数据并不是同一列的,如下图,理论上6 14 22才能够成一列,可见从ram中移出的数据需要寄存一个周期才能和当前像素数据同步为同一列
文章图片
文章图片
随后对上述的3x1数据进行寄存构成3x3矩阵,那么构成的矩阵相比moving_flag信号滞后一个周期,再判断3x3矩阵中是否都为1,是,输出腐蚀的结果是1,否则是0,这个过程会延时一个周期
同样的思路对腐蚀后的结果进行膨胀,同样要生成3x3矩阵,不同的是判断矩阵的数据是否至少有一个是1,是:输出1,否:输出0,整个膨胀过程延时两个周期随后对膨胀的结果进行多目标边界计算,整个开运算消耗4个周期,该部分请参考源码
现在获得了运动标志信号(经过开运算),那么需要当前数据的像素坐标,配合运动标志信号绘制运动方框,s0数据像素坐标根据tuser和tlast信号即可计算,理论上不需要判断valid信号了,因为上面寄存tuser和tlast时考虑了valid信号,这个坐标是同步在寄存10个周期(6+4)下的s0数据的
文章图片
接下来就是重点了,多运动检测边框的计算
多运动目标的检测思路如下
1.将第一个moving_flag为高的像素x y坐标作为第一个运动边框的上下边界和左右边界
2.检测到第二个moving_flag为高的像素时,判断条件如下,即当前像素点的y坐标与第一个运动边框的下边界差值在于之内,且当前点的x坐标与第一个运动边框的左边界或右边界差值的绝对值小于阈值,或者当前点x坐标刚好在第一个运动边框的左右边界之间,就判定该点属于第一个运动目标,随后将第一个运动边框的下边界更新为当前像素的y坐标,根据当前像素的x坐标与边框左右边界的大小更新左右边框的边界
(y-down)
4.检测到下一个moving_flag为高的像素时,同样以2中的条件判断该像素是否属于第二个运动目标,如果不是,就将该像素x y坐标作为第三个运动边框的上下边界和左右边界
5.由前述过程类推,程序中最多设置了能够检测8个运动目标
该部分代码段较长,请参考源码
边框叠加部分以及接收来自PS的控制部分均可参考源码,有详细的注释帮助理解
代码中还存在着明显不足,特别是多运动目标边框计算部分,有更好思路的小伙伴欢迎私信讨论交流
工程源码:
链接:https://pan.baidu.com/s/1FVKdVP5WrWifX-K6LCz7uQ
提取码:he67
【目标检测|基于ZYNQ的帧差法多运动目标检测(开源)】
推荐阅读
- #|python opencv 图像像素处理基础
- 神经网络|【论文导读】浅谈胶囊网络与动态路由算法
- 人工智能|涨点技巧!Kaggle图像分类项目的性能提升指南
- 图像处理|【OpenCv】图像分割——分水岭算法
- 环境搭建|tensorflow安装测试教程【一文读懂】
- 人工智能|PPF(Point Pair Features)原理及实战技巧
- 卷积|理解转置卷积与空洞卷积
- 深度学习|2021语义分割指南总结
- Anaconda问题|Anaconda安装OpenCV的方法