上一篇整理了人脸检测,这篇讲一下移动目标检测

  目前逐渐形成三种运动目标的检测算法:

   1)帧间差分法是采用视频序列中的相邻两帧图像做差的方法,来检测视频序列中 的移动目标。但是受运动目标和背景变化的影响,检测过程中有可能出现伪目标或者目标中 出现“空洞”,在目标运动不是太快时可以有效的检测到目标。

   2)背景减除法首先在没有目标的场景中获取背景图像,然后利用实时视频序列和背 景图像做差,来实现地移动目标的检测。如何获得背景是背景减除法的关键。

   3)光流法是通过给图像中每个像素点赋予一个速度矢量的方法建立光流场,利用光 流场中矢量运动的连续性来检测移动目标。该方法的计算量通常很大,难以实现实时性的检测。

  其中帧差法比较简单,可操作性较强。

 

一、帧差法原理

  帧差法是通过两帧相邻图像间做差,并选取合适的阈值对图像进行二值化,从而选取出运动的物体。设 f(x,y)为灰度差分图像,gk(x,y)、gk-1(x,y) 为相邻的两帧灰度图像,D(x,y)为侦差图像,T为差分阈值。

FPGA实现移动目标检测

1、缓存两帧灰度图像。

2、两帧灰度图像做差,将结果和设置的阈值进行比较后转二值化输出。

3、对二值化结果进行框选,确定移动目标,类似人脸检测。

FPGA实现移动目标检测 

  本设计的难点是如何能缓存两帧图像,以 SDRAM 为例,常用的方法有两种:掩码法和非掩码法,下面分别介绍一下。 

 

二、移动目标检测——掩码法

1、结构框图

FPGA实现移动目标检测

  如图所示:摄像头采集数据后,再SDRAM通道0中缓存后输出到 VGA_driver,正常的摄像头显示工程到这就结束了。而为了后续处理,我将 VGA_driver 的输出数据先不输出到VGA引脚,而是对其进行图像处理:先进行 RGB转YCbCr处理,得到 8bit 的灰度数据 Y 分量,然后将 Y 分量输入到 SDRAM的通道 1 中,利用 SDRAM 的掩码,通道 1 的读出数据包含了 2 帧的灰度数据,将这两帧数据进行帧差计算,然后进行一些图像处理。最后和人脸检测的方法类似,得到 4 个极值坐标,利用极值坐标绘制框框,将移动目标框住,并一起写入到原图中,最后一起在显示器上显示,实现移动目标的检测。

  本框图只是其中一种数据流走向,也可以改为:摄像头数据直接进行图像处理,得出极值坐标后加入到摄像头数据中,一起写入到另一个SDRAM通道中,最后直接输出。

2、SDRAM 掩码过程

  (1)摄像头数据写入通道 0,然后通过VGA_driver读出通道0的数据,再将该数据转换为 8bit 的灰度数据。

  (2)SDRAM 的数据位宽为 16bit,灰度数据为 8bit,设置通道 1 的写使能为灰度数据的数据指示信号,通道 1 的写数据为 ch1_wr_data = {Y_data[7:0],Y_data[7:0]},即高8位和低8位都存储一样的数据。有些人这一步就开始帧划分,用 8'bz 来填充丢弃的高8位或低8位,经过我实际测试发现直接写就行,因为后续SDRAM会采用掩码,在进SDRAM之前不需要高低位处理。同时设置通道 1 的读使能等于通道1的写使能。

  (3)通道 1 的SDRAM写掩码最开始设置为 2‘b10(遮掩高8位),当SDRAM写完一帧图像后则SDRAM写掩码翻转变成 01(遮掩低位),如此循环反复。通道 1 的SDRAM读过程中掩码一直为2'b00(不遮掩)。写不断切换高低掩码,读不遮掩,因此读出的 16 bit 数据里包含了两帧的 8bit 灰度数据。示意图如下:

FPGA实现移动目标检测

 (4)SDRAM 通道 1 的读使能可以设置和通道 1 的写使能一样,能把数据读出来就行,读出的数据进行高低位分离,二者相减实现侦差法。 

3、掩码法重点

(1)首先要有双通道的 4 口 SDRAM 控制器,自己写的可以,移植别人的也行。

(2)写掩码 wr_dqm 和读掩码 rd_dqm 最终都赋值给SDRAM引脚的掩码信号 sdram_dqm,我们先自行定义写掩码 wr_dqm 和读掩码 wr_dqm。

  • 写掩码 wr_dqm 初始为 2'b10(遮掩高位),当写完一帧时(找对这个点),写掩码 wr_dqm 翻转;
  • 读掩码 rd_dqm 始终为 2'b00(不遮掩);
  • 当SDRAM进行写操作时,写掩码 wr_dqm 赋值给端口掩码sdram_dqm;
  • 当SDRAM进行读操作时,读掩码 rd_dqm 赋值给端口掩码sdram_dqm。

(3)需要掩码的是通道1,正常读写的通道0的掩码必须始终为 2'b00。

(4)必须关闭乒乓操作,否则写入的数据无法拼接。

 

三、移动目标检测——非掩码法

1、结构框图

FPGA实现移动目标检测

  非掩码法的通道1直接写入 8 bit的数据即可,非掩码的关键在于通道1的读使能:通道1的读使能等于通道1的写使能,但是比通道1的写使能落后一帧时间,所以最后通道1的读写数据刚好就差了一帧,可以进行帧差计算,其他的和人脸检测相似。上面框图的数据流只是其中一种方式,实际上也可以在摄像头之后,进入SDRAM之前完成图像处理。

2、非掩码法重点

  (1)非掩码法的帧差计算实际是第一帧和第二帧,第三帧和第四帧......理论上的连贯性没有掩码法好;

  (2)可以用乒乓操作,因此成像效果比掩码法好。

 

  关于非掩码法,要感谢一下公众号《FPGA自习室》的 Beyond 前辈,他提供了一个结构框图给我,如下所示:

FPGA实现移动目标检测

  因为想有些改变,所以我上面的框图做的和这个有点不一样,这两种数据流都试过,效果都很好。

 

四、开发过程中的难点

  1、帧差法的计算不能直接二者相减,而是要判断大小,取绝对值。我一开始忘了绝对值,二者直接相减,结果效果非常差,图像全是闪动。于是开始怀疑摄像头和SDRAM控制器是不是出了问题,弄了一天,最后发现是没有取绝对值,改过来后效果立马变好了,后续的加框一气呵成。

//侦差计算
//---------------------------------------------------
always @(posedge clk_10m or negedge rst_n) begin
    if(!rst_n)
        diff_data <= 16'b0;
    else if(Y_data_1 >= Y_data_2 && Y_de_2)
        diff_data <= (Y_data_1 - Y_data_2 > 80) ? 16'hffff : 16'h0000;
    else if(Y_data_1 <  Y_data_2 && Y_de_2)
        diff_data <= (Y_data_2 - Y_data_1 > 80) ? 16'hffff : 16'h0000;
end

  2、阈值的设置要合理。移动目标检测效果和当时的光照、物体运动速度、物体和背景相似度等都有关,可根据实际情况进行阈值调整。可以引入两个外部按键,一个按键用于增加阈值,同时数码管显示当前阈值;另一个按键用于切换原图和侦差图,结果不合理时方便调试。

//按键设置阈值 0-120
//---------------------------------------------------
always @(posedge clk_10m or negedge rst_n) begin
    if(!rst_n)
        value <= 32'd0;
    else if(value > 32'd100) //100以上阈值的侦差法效果很差
        value <= 32'd0;
    else if(key_vld[0])
        value <= value + 32'd10;
end

SEG_driver u_SEG_driver
(
    .clk                    (clk                    ),
    .rst_n                  (rst_n                  ),
    .en                     (1                      ),
    .value                  (value                   ), //侦差阈值
    .SH_CP                  (SH_CP                  ),
    .ST_CP                  (ST_CP                  ),
    .DS                     (DS                     )
);

//侦差计算
//---------------------------------------------------
always @(posedge clk_10m or negedge rst_n) begin
    if(!rst_n)
        diff_data <= 8'b0;
    else if(Y_data_1 >= Y_data_2 && Y_de_2)
        diff_data <= (Y_data_1 - Y_data_2 > value) ? 8'hff : 8'h00;
    else if(Y_data_1 <  Y_data_2 && Y_de_2)
        diff_data <= (Y_data_2 - Y_data_1 > value) ? 8'hff : 8'h00;
end

  3、特别注意信号的同步。例如通道1的读写使能相同,我的 SDRAM 控制器的内部读 FIFO 是 normal 模式,即读数据会落后读使能一拍。后续帧差计算要使用到这个读数据,不能用通道1的写数据和通道1的读数据直接进行帧差计算,必须将写数据打一拍后才是和读数据对齐的,才能进行帧差计算。而且我这里帧差计算用的时序逻辑,因此数据过来是:读使能和写使能相同,读数据落后一个时钟周期,帧差计算又消耗一个时钟周期,总的消耗了两个周期。因此同步信号要特别注意对齐。

//侦差法消耗一拍,故打拍对齐
//---------------------------------------------------
always @(posedge clk_10m) begin
    diff_de    <= Y_de_2;
    diff_hsync <= Y_hsync_2;
    diff_vsync <= Y_vsync_2;
end

 

五、侦差法缺点和改进方法

  侦差法的核心思想是移动目标和背景做差,不同点即移动目标本身。如果移动目标和背景的颜色相似,检测精度将大大降低。

  侦差法的缺点难以逾越,但是也可以通过一些技术手段提高检测精度:

  1、对结果进行平滑滤波、边缘检测、形态学滤波等,提高检测精度。

  2、两帧图像做差改为三帧图像做差或四帧图像做差,提高检测精度。具体实现为增加缓存器件,或者上述的两种方法结合,实现缓存三帧、四帧图像的要求。

 

六、效果展示

  之前做图像处理时,板子上TFT屏接口的引脚击穿了,导致图像偏红色。后来去了修,修好后用了没多久,又坏了,这次变成偏蓝色了,唉。

  图像失真偏蓝是我板子的问题,只要关注结果就好了。可以看到阈值为 0 时,无法检测到移动目标;按键 1 可以调节阈值,阈值为 10 时,非常灵敏,甚至灵敏过头了,随着阈值的增加,检测精度也在不断变化,阈值为 80 的效果还不错,阈值为110以上后,基本就没法检测了。另外按键 2 可以切换显示帧差图像,方便和原图对比。

 

参考资料:

[1]OpenS Lee:FPGA开源工作室(公众号)

[2]NingHechuan:硅农(公众号)

[3]Beyond:FPGA自习室(公众号)