本文将继续讲述图像处理算法的FPGA实现,后续可能更新图像旋转(1080P)、画中画、快速DCT等算法。视频场景切换检测常用于视频编解码领域,我选用的算法是双阈值灰度直方图检测法,起初在MATLAB上实现并测出最佳双阈值,然后将其转换为verilog代码,最终在XILINX K7开发板上实现视频场景切换检测的效果。

  双阈值灰度直方图算法,顾名思义是采用两个灰度阈值来判断视频场景的切换。其中一个是渐变阈值T1,表明视频场景可能发生改变;另一个是突变阈值T2,表示阈值已经发生改变。当相邻帧灰度直方图差值比大于阈值T2时表明为场景突变帧,而当差值比大于阈值T1而小于阈值T2时则表示为场景渐变帧,差值比小于阈值T1则为连续帧。其中,相邻灰度直方图差值实际指的是一个权重,即当前灰度帧差与平均帧差的比,如公式(1)所示。这个比值越大,说明图像变化越大,就越有可能发生场景切换,反之则相反。

            Prop_CurrentFrame=FrameDiff/AveFrameDiff   (1)

  双阈值灰度算法的MATALAB实现较为简单,更多的是依靠公式进行计算,并对差值进行判别。当然由于读取的是1080P视频,又需要遍历整帧的像素点,所以程序运行的比较慢。而程序实现的思路较为清晰,即:视频从第二帧开始读取,一次选取两帧,并将读取的图像数据转YUV格式,提取其中的Y分量;然后利用imhist()函数求取其对应的灰度直方图,并计算其相邻帧差,同时统计平均帧差;最后根据统计的平均帧差和当前帧差作比较,并根据阈值跳转到渐变帧或者突变帧,记录突变帧位置和突变次数。部分代码如下所示:

  1. for i=2:FrameNum
  2. Frame_0=read(Obj,i-1);%前一帧
  3. Frame_1=read(Obj,i);%当前帧
  4. ImageYuv_0=rgb2ycbcr(Frame_0);
  5. ImageYuv_1=rgb2ycbcr(Frame_1);
  6. Y_0=ImageYuv_0(:,:,1);%Y
  7. Y_1=ImageYuv_1(:,:,1);%Y
  8. GrayHist_0=imhist(Y_0); %获取灰度直方图
  9. GrayHist_1=imhist(Y_1); %获取灰度直方图
  10. FrameDiff=sum(abs(GrayHist_1-GrayHist_0));%当前帧与前一帧灰度直方图帧差
  11. SumFrameDiff=(SumFrameDiff+FrameDiff);
  12. AveFrameDiff=SumFrameDiff/(GapFrameCnt-1);
  13. end

  最终经过大量测试,确定双阈值在分别为3和5时检测效果较好,视频场景检测效率大概为90%。

而对于视频场景切换检测算法的FPGA实现,比较关键的内容在于帧差的求取。帧差,顾名思义,两帧的像素之差,而本文使用的两帧的亮度分量(Y)差。对于1080P的图像存储,自然会使用到DDR。而计算帧差最少需要两帧数据,假如使得DDR同时输出相邻的两帧,然后对数据流分别统计求直方图,依次相减,再求和,倒的确是能得到两帧的亮度差。但是该方法DDR控制较为复杂,且最终得到像素差位宽太大,所以不采用。本文选用DDR缓存整帧图像+bram存储图像亮度直方图的方式实现。即选用两组bram乒乓操作,一组对DDR输出一帧图像数据进行抽样,在行方向每4个点抽取一次,在列方向每4列抽取一列,且像素位宽由10bit降为8bit,求取对应的灰度直方图数据,并将其写入bram中;另一组bram在当前帧做完灰度直方图统计时,依次读取上一帧缓存的对应位置灰度直方图数据,并依次做差求和,最终求出灰度直方图帧差以及平均帧差。

其中,灰度直方图的统计也是极其重要的一个环节。由前面的分析可知,为缩小数据量对一帧图像进行采样,数据量由原来的1920*1080变为现在的129600(17bit),可这仍然是一个非常大的数字,尤其在帧差计算时带来的累积求和,所以进一步降低统计数据量, 采用2级双口BRAM实现灰度直方图统计。即第一级bram采用9bit位宽,第二级bram为8bit。特选用像素灰度值寻址,当一个8bit亮度统计值计满512时则第二级bram对应地址数值加一,直到统计完所有采样点。在这之中,选用双口bram的原因在于灰度直方图的统计过程中,

当一个新的像素点传来时,需提前一拍读取该地值统计值,然后累加。部分RTL代码如下所示:

  1. //bram读地址,提前2拍,一拍是先于bram写,一拍是bram读延迟
  2. always @(posedge vid_clk or posedge reset)begin
  3. if(reset)begin
  4. BRAM_addrb_0<='h0;
  5. BRAM_addrb_1<='h0;
  6. end
  7. else if(HistData_rden)begin//外部读取直方图数据
  8. if(pos_HistData_rden)begin
  9. BRAM_addrb_0<= odd_frame ? 'h0 : BRAM_addrb_0;
  10. BRAM_addrb_1<=~odd_frame ? 'h0 : BRAM_addrb_1;
  11. end
  12. else begin
  13. BRAM_addrb_0<= odd_frame ? BRAM_addrb_0+1'b1 : BRAM_addrb_0;//奇数帧读bram0
  14. BRAM_addrb_1<=~odd_frame ? BRAM_addrb_1+1'b1 : BRAM_addrb_1;//偶数帧读bram1
  15. end
  16. end
  17. else begin//写直方图数据前提前2拍读
  18. BRAM_addrb_0<= (Hist_wren && ~odd_frame) ? Hist_addr:BRAM_addrb_0;//偶数帧写bram0
  19. BRAM_addrb_1<= (Hist_wren && odd_frame) ? Hist_addr:BRAM_addrb_1;//奇数帧读bram1
  20. end
  21. end

  最终,为更为方便观察视频场景切换的效果,特在检测到视频场景切换时,使得边框变为红色,持续0.5s。最后检测的效果与MATLAB上测试的效果相当,正确检测率在90%左右,基本检测出了视频场景的切换。

  我个人认为,双阈值灰度直方图算法有一定局限性。即当视频中的场景较为稳定时,平均帧差很小,突然来一个变动比较大的画面时,当前帧差较大,对应的帧差比就偏大,进而产生误警。当然,这也能解释该算法在一些场景下发生错误检测的现象。

视频场景切换检测的FPGA实现的更多相关文章

  1. 【Cocos2d-x 3.x】 场景切换生命周期、背景音乐播放和场景切换原理与源码分析

    大部分游戏里有很多个场景,场景之间需要切换,有时候切换的时候会进行背景音乐的播放和停止,因此对这块内容进行了总结. 场景切换生命周期 场景切换用到的函数: bool Setting::init() { ...

  2. 自制Unity小游戏TankHero-2D(5)声音+爆炸+场景切换+武器弹药

    自制Unity小游戏TankHero-2D(5)声音+爆炸+场景切换+武器弹药 我在做这样一个坦克游戏,是仿照(http://game.kid.qq.com/a/20140221/028931.htm ...

  3. Unity 中场景切换

    Unity游戏开发中,单个Scene解决所有问题似乎不可能,那么多个Scene之间的切换是必然存在.如果仅仅是切换,似乎什么都好说,但是在场景比较大的时候不想让玩家等待加载或者说场景与场景之间想通过一 ...

  4. Cocos2d-x Lua中多场景切换生命周期

    在多个场景切换时候,场景的生命周期会更加复杂.这一节我们介绍一下场景切换生命周期.多个场景切换时候分为几种情况:情况1,使用pushScene函数从实现GameScene场景进入SettingScen ...

  5. Faster-RCNN用于场景文字检测训练测试过程记录(转)

    [训练测试过程记录]Faster-RCNN用于场景文字检测 原创 2017年11月06日 20:09:00 标签: 609 编辑 删除 写在前面:github上面的Text-Detection-wit ...

  6. texturepacker打包图片,场景切换时背景图有黑边

    在使用TexturePacker打包图片之后,背景图在场景切换(有切换动画)时,明显能看到有黑边,在百度之后解决了. 知乎上边有网友贴出了两种解决方法,我抄过来如下: 第一种: 修改 ccConfig ...

  7. cocos2d-x 帧循环不严谨造成场景切换卡顿

    最近在用cocos2d-x做引导界面,2dx版本是2.2.3,场景切换加上了效果,所有资源都已经使用texturepacker打包预加载,但是在实际运行调试中,场景切换相当卡顿. 各种纠结后,无意中将 ...

  8. JavaScript强化教程 -- cocosjs场景切换

    场景切换 在main.js,将StartScene作为我们初始化运行的场景,代码如下: cc.LoaderScene.preload(g_resources, function () { cc.dir ...

  9. cocos2d-x场景切换动画

    void StartScene::beginGame() {     CCLog("beginGame");          //CCTransitionScene *trans ...

随机推荐

  1. h264和h265多维度区别

    h264和h265多维度区别 1.  概述 h265旨在在有限带宽下传输更高质量的网络视频,仅需原先的一半带宽即可播放相同质量的视频,很多朋友不知道h264和h265如何区别,下面让我们一起来了解一下 ...

  2. MindSpore模型推理

    MindSpore模型推理 如果想在应用中使用自定义的MindSpore Lite模型,需要告知推理器模型所在的位置.推理器加载模型的方式有以下三种: 加载本地模型. 加载远程模型. 混合加载本地和远 ...

  3. 重新整理 mysql 基础篇————— 介绍mysql[一]

    前言 准备整理mysql的基础篇了,前面整理了sql语句序列的的<sql 语句系列(八百章)>,感觉很多用不上,就停下来了,后续还是会继续整理. mysql 基础篇主要是对一些基础进行整理 ...

  4. Jmeter(五十一) - 从入门到精通高级篇 - jmeter之运动战(详解教程)

    1.简介 运动战是一种军事作战方式,依托较大的作战空间来换取时间移动兵力包围敌方,以优势兵力速战速决,运动战的运用归为这样一段话"避敌主力,诱敌深入,集中优势兵力逐个击破".今天宏 ...

  5. 菜鸟刷题路:剑指 Offer 06. 从尾到头打印链表

    剑指 Offer 06. 从尾到头打印链表 class Solution { public int[] reversePrint(ListNode head) { Stack<Integer&g ...

  6. 工作流引擎Activiti使用进阶!详细解析工作流框架中高级功能的使用示例

    Activiti高级功能简介 Activit的高级用例,会超越BPMN 2.0流程的范畴,使用Activiti高级功能需要有Activiti开发的明确目标和足够的Activiti开发经验 监听流程解析 ...

  7. Django基础之路由层

    内容概要 路由匹配 无名有名分组 反向解析 无名有名分组反向解析(难理解) 路由分发 名称空间 伪静态 内容详细 1 路由匹配 urls.py url()方法第一个参数其实是一个正则表达式 第一个参数 ...

  8. 「10.19」最长不下降子序列(DP)·完全背包问题(spfa优化DP)·最近公共祖先(线段树+DFS序)

    我又被虐了... A. 最长不下降子序列 考场打的错解,成功调了两个半小时还是没A, 事实上和正解的思路很近了,只是没有想到直接将前$D$个及后$D$个直接提出来 确实当时思路有些紊乱,打的时候只是将 ...

  9. Pandas高级教程之:处理缺失数据

    目录 简介 NaN的例子 整数类型的缺失值 Datetimes 类型的缺失值 None 和 np.nan 的转换 缺失值的计算 使用fillna填充NaN数据 使用dropna删除包含NA的数据 插值 ...

  10. Spring @Transaction 注解是如何执行事务的?

    前言 相信小伙伴一定用过 @Transaction 注解,那 @Transaction 背后的秘密又知道多少呢? Spring 是如何开启事务的?又是如何进行提交事务和关闭事务的呢? 画图猜测 在开始 ...