一、帧差法运动目标跟踪概述

1.1 基本原理

帧差法顾名思义就是对输入的前后两帧图像做差值,然后检测出两帧图像不同的地方,并且可以实时跟踪运动的目标轮廓。
本设计是基于ZYNQ7010和VIVADO2018.3实现的帧差法运动目标检测,针对运动目标检测算法在传统 PC端上实时性较差的问题,设计了一种基于 ZYNQ 硬件加速的运动目标实时检测系统。将摄像头采集的彩色视频流转换为灰度视频流并进行图像处理来实现运动目标检测,并将检测后的结果与原彩色视频流叠加来显示实时检测结果。

1.2 效果展示

本设计使用到的硬件有ZYNQ7010、768P显示屏、OV5640摄像头这些硬件,在原有的摄像头显示例子上搭建完成,关于显示环境的搭建可以参照前面的文章。
帧差法项目通过pl端两个按键分别控制帧差的阈值和输出图像的类型。按键1控制阈值为40-100递增,按键2控制输出图像类型为原始RGB图像、灰度图像、帧差结果图像以及RGB叠加检测框图像。


原始图像


灰度图像


帧差结果图像




原始图像迭代帧差结果图像

二、帧差法的实现

2.1 帧差法项目架构

这边的项目架构主要是参照了《基于ZYNQ加速的帧差法运动目标检测》这样一篇论文,大家可以去下载看看。然后视频教程的话,我是看了B站up主大磊FPGA的视频,大家也可以去看看。
下图是我的帧差法实现运动目标检测跟踪的整体框图了。大致的原理过程是这样子的,首先OV5640采集图像,将采集到的图像分别送入到vdma0和vdma1中,然后两个vdma缓存不同的帧数,达到帧差的效果;之后将两帧图像送入到自己编写的帧差模块进行差值计算,之后将结果输出到vdma2;最后经过DVI模块将信号送至hdmi显示屏上显示。

下面这张图呢是我参照b站视频里面的,帧差代码也主要完成下面这个框图。将其中一个vdma,比如vdma0中的数据送到fifo进行缓存。然后当vdma1中的数据的user来到时,从fifo中读数据,这样可以对齐信号。因为从fifo中读数据需要耗费两个时钟周期,所以vdma1的数据延迟2clk,之后就是转灰度,做差值,都比较简单。

然后这边需要说一下这个握手的协议。它是有五组信号组成,data是数据信号,user是一帧图像的开始信号,last是一帧图像每一行结束的信号,当valid和ready拉高时数据有效。

2.2 帧差法模块代码

首先是信号的定义,有三组信号,分别是vdma0和vdma1的输入,以及输出到vdma2这样的三组信号。

module  frame_diff(input clk,input rst_n,//control keyinput key0,input key1,//data stream from vdma0input [23:0]    s0_axis_tdata,input           s0_axis_tvalid,output          s0_axis_tready,input           s0_axis_tuser,input           s0_axis_tlast,//data stream from VDMA1input [23:0]    s1_axis_tdata,input           s1_axis_tvalid,output          s1_axis_tready,input           s1_axis_tuser,input           s1_axis_tlast,//data stream to VDMA2output reg [23:0]       m_axis_tdata,output reg              m_axis_tvalid,input                   m_axis_tready,output reg              m_axis_tuser,output reg              m_axis_tlast
);

接着就是信号缓存了,对vdma0来的数据到user来临时就开始存;然后当vdma1的user来临时就开始从fifo中读数据。注意vdma1的数据延迟2clk来同比,具体可以看代码。

//-------------------------------------------------------
//          fifo write and read data from vdma0
//-------------------------------------------------------reg [23:0] data_in;
reg data_wr;
reg data_wr_en;
wire data_alfull;
wire [23:0] data_out;
reg data_rd;
reg data_rd_en;
reg [23:0] s0_axis_tdata_dy1;
reg [23:0] s0_axis_tdata_dy2;//write data to fifo
assign s1_axis_tready=~data_alfull; //always ready until fifo is fullalways @(posedge clk or negedge rst_n) beginif(~rst_n)begindata_wr<=1'b0;data_wr_en<=1'b0;data_in<=24'd0;endelse beginif(s1_axis_tvalid & s1_axis_tready & s1_axis_tuser)begin  //when s0_user is come,wr_en is high,make corrsponding data_wr_en<=1'b1;endif(s1_axis_tvalid & s1_axis_tready)begin  //begin to write data to fifodata_wr<=1'b1;data_in<=s1_axis_tdata;endelse begindata_wr<=1'b0;data_in<=data_in;endend
end//read data from fifo
always @(posedge clk or negedge rst_n) beginif(~rst_n)begindata_rd<=1'b0;data_rd_en<=1'b0;endelse beginif(s0_axis_tvalid & s0_axis_tready & s0_axis_tuser)begin//unfull    //when fifo is alfull and data from camera is come,rd_en is high data_rd_en<=1'b1;endif(s0_axis_tvalid & s0_axis_tready)begindata_rd<=1'b1;endelse begindata_rd<=1'b0;endend
end//fifo define 24*1024
fifo_generator_0 u_fifo_generator_0 (.clk        (clk),                  // input wire clk.srst       (~rst_n),                // input wire srst.din        (data_in),                  // input wire [23 : 0] din.wr_en      (data_wr_en & data_wr),              // input wire wr_en.rd_en      (data_rd_en & data_rd),              // input wire rd_en.dout       (data_out),                // output wire [23 : 0] dout.full       (),                // output wire full.almost_full(data_alfull),  // output wire almost_full.empty      (),              // output wire empty.data_count ()              // output wire [10 : 0] data_count
);//read from fifo cost 2 clk, so data from camera need delay 2 clk too
always @(posedge clk or negedge rst_n) beginif(~rst_n)begins0_axis_tdata_dy1<=24'd0;s0_axis_tdata_dy2<=24'd0;endelse begins0_axis_tdata_dy1<=s0_axis_tdata;s0_axis_tdata_dy2<=s0_axis_tdata_dy1;end
end

然后我这边又定义了两个按键检测和滤波信号,其实就是为了控制输出的阈值大小和图像类型,这部分内容很简单。

//-------------------------------------------------------
//   key filter and throshold control and state chose
//-------------------------------------------------------reg [7:0] Frame_Diff_Throshold;
reg [1:0] state;
wire key0_flag;
wire key1_flag;key_filter u_key_filter0(.clk    ( clk    ),.rst_n  ( rst_n  ),.key_in ( key0 ),.key_flag  ( key0_flag  )
);key_filter u_key_filter1(.clk    ( clk    ),.rst_n  ( rst_n  ),.key_in ( key1 ),.key_flag  ( key1_flag  )
);always @(posedge clk or negedge rst_n) beginif(~rst_n)beginFrame_Diff_Throshold<=8'd70;endelse if(key0_flag==1'b1 & Frame_Diff_Throshold<8'd100)beginFrame_Diff_Throshold<=Frame_Diff_Throshold+4'd10;endelse if (key0_flag==1'b1 & Frame_Diff_Throshold>=8'd100)beginFrame_Diff_Throshold<=8'd40;endelse beginFrame_Diff_Throshold<=Frame_Diff_Throshold;end
endalways @(posedge clk or negedge rst_n) beginif(~rst_n)beginstate<=2'd0;endelse if(key1_flag==1'b1)beginstate<=state+1'b1;endelse beginstate<=state;end
end

对读出的两组信号将RGB图像转换成灰度图像,之后在把两张灰度图像进行帧差,最后注意fifo读用了两个clk、转灰度用了3clk、帧差用了1clk,所以握手信号也要延迟6clk,经过这些操作后,flag信号就是代表了帧差的结果了,接下来只需要提取出这些flag在图像上的范围和边界即可,具体代码如下所示。
更多的关于怎么找到边间画出边界,大磊FPGA里面有很详细的步骤,大家可以看视频,或者看我工程,这里就不叙述了。

//-------------------------------------------------------
//  current img data and past img data:RGB888 to RGB565
//-------------------------------------------------------wire [23:0] s0_data_gray;   //data from camera
wire [23:0] s1_data_gray;   //data from vdma0
wire [7:0] s0_data_gray1;
wire [7:0] s1_data_gray1;assign s0_data_gray1=s0_data_gray[23:16];
assign s1_data_gray1=s1_data_gray[23:16];rgb2gray u_rgb2gray1(.pclk       ( clk     ),.rst_n      ( rst_n    ),.rgb_data   ( s0_axis_tdata_dy2 ),.gray_data  ( s0_data_gray  )
);rgb2gray u_rgb2gray2(.pclk       ( clk     ),.rst_n      ( rst_n    ),.rgb_data   ( data_out ),.gray_data  ( s1_data_gray  )
);//-------------------------------------------------------
//               frame diff operation
//-------------------------------------------------------reg frame_diff_flag;
reg [10:0] x_cnt;
reg [10:0] y_cnt;always @(posedge clk or negedge rst_n) beginif(~rst_n)beginframe_diff_flag<=1'b0;endelse beginif(s0_data_gray1>s1_data_gray1)beginif( ((s0_data_gray1-s1_data_gray1) > Frame_Diff_Throshold) && (x_cnt<H_DISP-5'd5) )beginframe_diff_flag<=1'b1;endelse frame_diff_flag<=1'b0;endelse beginif( ((s1_data_gray1-s0_data_gray1) > Frame_Diff_Throshold) && (x_cnt<H_DISP-5'd5) )beginframe_diff_flag<=1'b1;endelse frame_diff_flag<=1'b0;endend
end//-------------------------------------------------------
//              for signal of s0,delay 6 clk
//-------------------------------------------------------//fifo:2clk; rgb2gray:3clk; frame diff:1clk
//so 6 clk delay needed
wire s0_axis_tvalid_dy;
wire s0_axis_tuser_dy;
wire s0_axis_tlast_dy;
reg [5:0] s0_axis_tvalid_reg;
reg [5:0] s0_axis_tuser_reg;
reg [5:0] s0_axis_tlast_reg;assign s0_axis_tvalid_dy=s0_axis_tvalid_reg[5];
assign s0_axis_tuser_dy=s0_axis_tuser_reg[5];
assign s0_axis_tlast_dy=s0_axis_tlast_reg[5];always @(posedge clk or negedge rst_n) beginif(~rst_n)begins0_axis_tvalid_reg<=6'd0;s0_axis_tuser_reg<=6'd0;s0_axis_tlast_reg<=6'd0;endelse begins0_axis_tvalid_reg<={s0_axis_tvalid_reg[4:0],s0_axis_tvalid & s0_axis_tready};s0_axis_tuser_reg<={s0_axis_tuser_reg[4:0],s0_axis_tvalid & s0_axis_tready & s0_axis_tuser};s0_axis_tlast_reg<={s0_axis_tlast_reg[4:0],s0_axis_tvalid & s0_axis_tready & s0_axis_tlast};end
end

2.3 帧差法软硬件设计

vivado工程中的block design就如下图所示了,可以看到例化了3个vdma,然后自己编写了frame diff模块,原理是和上面的架构是相同的。

综合布局布线完成后,我们把它导入到sdk设计中,sdk中c代码如下所示,就很基础,只是初始化3个vdma开启读和写通道就可以了。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xil_types.h"
#include "xil_cache.h"
#include "xparameters.h"
#include "xaxivdma.h"
#include "xaxivdma_i.h"
#include "vdma_api/vdma_api.h"//宏定义
#define VDMA_ID0          XPAR_AXIVDMA_0_DEVICE_ID
#define VDMA_ID1          XPAR_AXIVDMA_1_DEVICE_ID
#define VDMA_ID2          XPAR_AXIVDMA_2_DEVICE_ID
//frame buffer的起始地址
unsigned int const frame_buffer_addr0 =0x03000000;
unsigned int const frame_buffer_addr1 =0x06000000;
unsigned int const frame_buffer_addr2 =0x09000000;//驱动实例
XAxiVdma     vdma;int main(void)
{int i;int j;int k;//配置VDMAi=run_vdma_frame_buffer(&vdma, VDMA_ID0,1024,768,frame_buffer_addr0,0,0,BOTH);j=run_vdma_frame_buffer(&vdma, VDMA_ID1,1024,768,frame_buffer_addr1,0,0,BOTH);k=run_vdma_frame_buffer(&vdma, VDMA_ID2,1024,768,frame_buffer_addr2,0,0,BOTH);printf("%d\r\n",i);printf("%d\r\n",j);printf("%d\r\n",k);while(1);return 0;
}

三、总结

这个工程采用了前后帧差法,完成了单运动目标的检测,是比较基础的实战项目。对于后期想扩展的方向,可以使用背景帧差法来更好的定位目标,或者完成多运动目标的检测和计数。

资源下载链接:
https://download.csdn.net/download/qq_40995480/86880974

ZYNQ图像处理项目——帧差法运动目标跟踪相关推荐

  1. Python与OpenCV(一)——基于帧差法的运动目标检测程序分析

    OpenCV提供了强大的图像处理功能,与Python的结合堪称完美... 这一次,我们试一下用帧差法来完成对运动目标的检测与跟踪. 帧差法的原理是这样的:由于摄像机采集的视频序列具有连续性的特点,所以 ...

  2. matlab帧间差分法前景提取_matlab采用帧差法实现运动目标检测

    帧间差分法是一种通过对视频图像序列中相邻两帧作差分运算来获得运动目标轮廓的方法,它可以很好地适用于存在多个运动目标和摄像机移动的情况.当监控场景中出现异常物体运动时,帧与帧之间会出现较为明显的差别,两 ...

  3. 基于混合高斯模型与帧差法结合的目标跟踪算法matlab仿真

    目录 一.理论基础 二.核心程序 三.仿真测试结果 一.理论基础 目标检测:混合高斯模型与帧差法结合的算法,与单独的混合高斯模型算法作对比,体现前者的优越性 3.要求和结果:对比改进前后的算法,可以非 ...

  4. 数字图像处理拓展题目——利用Matlab实现动态目标检测 二帧差法、ViBe法、高斯混合模型法,可应用于学生递东西行为检测

    1.二帧差法实现动态目标检测 先上效果图: 利用GUI界面显示出来效果图为: 实现流程 1.利用matlab中的VideoReader函数读取视频流. 2.帧差法:获得视频帧数,用for循环对图像每相 ...

  5. 视频目标跟踪算法matlab仿真,对比二帧差法,三帧差法,混合高斯法以及Vibe算法

    目录 1.算法概述 2.仿真效果 3.MATLAB仿真源码 1.算法概述 1.1二帧差法 帧间差分法是一种通过对视频图像序列的连续两帧图像做差分运算获取运动目标轮廓的方法.当监控场景中出现异常目标运动 ...

  6. 传统目标跟踪——帧差法

    目录 一.帧差法 二.代码 2.1 二帧差法 2.2 三帧差法 三.总结 一.帧差法 计算帧之间的差异,或考虑"背景帧"与其他帧之间的差异. 当视频中存在移动物体的时候,相邻帧(或 ...

  7. Python实现视频运动目标检测——帧差法

    Python实现视频运动目标检测--帧差法 在许多场景中,我们需要对视频中的运动目标进行检测.而在这个过程中,帧差法是一种常用的方法.本文将详细阐述Python如何利用帧差法实现视频运动目标检测. 首 ...

  8. 运动目标检测之帧差法

    原理简介: 所谓帧差法也就是对连续图像帧做差分运算,其结果与定义好的阈值比较,若大于阈值则为运动目标值为1,否则值为0 . 帧差法一般分为两帧差分和三帧差分. 两帧差分: 取连续的两帧序列,用后一帧减 ...

  9. 图像处理+帧差法实现对车辆的识别框选

    每一帧一个动作,连接起形成一个动画 帧差法:对每一帧进行分析,对帧内分为动态和静态物体,前一帧与后一帧相对移动的物体识别出. opencv中有专门对帧差的函数,以识别运动中的物体 原视频图像: 函数设 ...

  10. 结合帧差法与窗口搜索的车道线跟踪方法——第一篇处女座

    还好昨天舍友的提醒,抓紧时间转投别的正规杂志. 目前已经投到正规杂志社. 代码在文章发表成功之后才会公布. 之前matlab的篇,等忙过这阵子,会发百度云链接. 然鹅!可能因为投到了假的<电子器 ...

最新文章

  1. win合适做服务器的系统,win系统做云服务器
  2. CloudStack各版本新特性
  3. #012python实验课
  4. nginx缓存服务器
  5. [Leedcode][JAVA]第[945]题
  6. python学习之路day02
  7. 安卓PHP maker汉化,android 百度地图marker添加自定义视图
  8. Android json数据解析及简单例子
  9. Hadoop学习笔记(一)——编译安装和配置
  10. Sql server 行列转换
  11. Cadence Allegro智能创建PCB封装库
  12. Win10编译OpenJDK源码
  13. SpringBoot+websocket实现私聊和群聊(可以发送文字和图片)
  14. 计算机显示器分辨率,现在电脑的主流显示器的分辨率一般是多大?
  15. MES系统质量追溯功能,到底在追什么?
  16. cilium系列之四:使用阿里云vpc作为ipam
  17. Docker常用的命令
  18. c语言编程票务系统,C语言课程设计票务管理系统
  19. java上溯造型与下溯造型
  20. 数据中心暖通系统建设交付关键路径分析

热门文章

  1. 基于ssm框架实现的企业进销存管理系统【源码+数据库+毕设】
  2. docker搭建LDAP统一用户认证
  3. uni-app项目(一)
  4. RFC 2544 标准—以太网测试仪
  5. RFC2544吞吐量测试详细步骤-MiniSMB-HurricaneII软件操作演示
  6. linux 蓝牙驱动 chi,linux下的蓝牙驱动程序详解
  7. LogViewer-Unity日志的插件
  8. JAVA数组内存分析 面向对象 类和对象 创建对象的内存分析 成员变量 局部变量
  9. 管家婆婆分销ERP V3 打印报错
  10. 如何查询Opencv的版本