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

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

            Prop_CurrentFrame=FrameDiff/AveFrameDiff   (1)

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

for i=2:FrameNum
Frame_0=read(Obj,i-1);%前一帧
Frame_1=read(Obj,i);%当前帧
ImageYuv_0=rgb2ycbcr(Frame_0);
ImageYuv_1=rgb2ycbcr(Frame_1);
Y_0=ImageYuv_0(:,:,1);%Y
Y_1=ImageYuv_1(:,:,1);%Y
GrayHist_0=imhist(Y_0); %获取灰度直方图
GrayHist_1=imhist(Y_1); %获取灰度直方图
FrameDiff=sum(abs(GrayHist_1-GrayHist_0));%当前帧与前一帧灰度直方图帧差
SumFrameDiff=(SumFrameDiff+FrameDiff);
AveFrameDiff=SumFrameDiff/(GapFrameCnt-1);
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代码如下所示:

//bram读地址,提前2拍,一拍是先于bram写,一拍是bram读延迟
always @(posedge vid_clk or posedge reset)begin
if(reset)begin
BRAM_addrb_0<='h0;
BRAM_addrb_1<='h0;
end
else if(HistData_rden)begin//外部读取直方图数据
if(pos_HistData_rden)begin
BRAM_addrb_0<= odd_frame ? 'h0 : BRAM_addrb_0;
BRAM_addrb_1<=~odd_frame ? 'h0 : BRAM_addrb_1;
end
else begin
BRAM_addrb_0<= odd_frame ? BRAM_addrb_0+1'b1 : BRAM_addrb_0;//奇数帧读bram0
BRAM_addrb_1<=~odd_frame ? BRAM_addrb_1+1'b1 : BRAM_addrb_1;//偶数帧读bram1
end
end
else begin//写直方图数据前提前2拍读
BRAM_addrb_0<= (Hist_wren && ~odd_frame) ? Hist_addr:BRAM_addrb_0;//偶数帧写bram0
BRAM_addrb_1<= (Hist_wren && odd_frame) ? Hist_addr:BRAM_addrb_1;//奇数帧读bram1
end
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. cuDNN概述

    cuDNN概述 NVIDIACUDA深度神经网络库(cuDNN)是GPU加速的用于深度神经网络的原语库.cuDNN为标准例程提供了高度优化的实现,例如向前和向后卷积,池化,规范化和激活层. 全球的深度 ...

  2. 重新整理 .net core 实践篇—————路由和终结点[二十三]

    前言 简单整理一下路由和终节点. 正文 路由方式主要有两种: 1.路由模板方式 2.RouteAttribute 方式 路由约束: 1.类型约束 2.范围约束 3.正则表达式 4.是否必选 5.自定义 ...

  3. java并发编程JUC第九篇:CountDownLatch线程同步

    在之前的文章中已经为大家介绍了java并发编程的工具:BlockingQueue接口.ArrayBlockingQueue.DelayQueue.LinkedBlockingQueue.Priorit ...

  4. Linux分区,格式化概念理解

    一.分区概念: 逻辑上分成不同的存储空间. 分区类型: 主分区:最多只能有4个 扩展分区:最多只能有1个. 主分区加扩展分区最多有4个. 布恩那个写入数据,只能包含逻辑分区 逻辑分区: 主分区为什么只 ...

  5. Golang编写Windows动态链接库(DLL)及C调用范例

    一.准备. 1.GoLang在1.10版本之后开始支持编译windows动态链接库,可以打开命令行工具使用go version 查看自己的go版本. 2.你的电脑上需要gcc,如果没有的话[点击这里] ...

  6. 北京某大公司:SpringBean生命周期

    <对线面试官>系列目前已经连载25篇啦!有深度风趣的系列! [对线面试官]Java注解 [对线面试官]Java泛型 [对线面试官] Java NIO [对线面试官]Java反射 & ...

  7. 一、.Net Core 依赖注入详解及Autofac使用

    .NET中的依赖注入实际上帮助我们解耦了我们的代码,是控制反转和依赖反转原则的具体实现. .Net Core的依赖注入的好处: 1. application 更稳定,容易维护和演化: 2. 实现细节的 ...

  8. Pandas高级教程之:plot画图详解

    目录 简介 基础画图 其他图像 bar stacked bar barh Histograms box Area Scatter Hexagonal bin Pie 在画图中处理NaN数据 其他作图工 ...

  9. layui动态创建Tab、Tab右键功能

      1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 & ...

  10. Datahub 0.8.5发布! 通用的元数据搜索和发现工具

    近期Datahub 发布了最新的版本0.8.5,作为LinkedIn开源的通用的元数据搜索和发现工具.Datahub近一年来有了巨大的发展,也成为了很多公司进行元数据管理的调研方向并进行使用的选择. ...