视频场景切换检测的FPGA实现
本文将继续讲述图像处理算法的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实现的更多相关文章
- 【Cocos2d-x 3.x】 场景切换生命周期、背景音乐播放和场景切换原理与源码分析
大部分游戏里有很多个场景,场景之间需要切换,有时候切换的时候会进行背景音乐的播放和停止,因此对这块内容进行了总结. 场景切换生命周期 场景切换用到的函数: bool Setting::init() { ...
- 自制Unity小游戏TankHero-2D(5)声音+爆炸+场景切换+武器弹药
自制Unity小游戏TankHero-2D(5)声音+爆炸+场景切换+武器弹药 我在做这样一个坦克游戏,是仿照(http://game.kid.qq.com/a/20140221/028931.htm ...
- Unity 中场景切换
Unity游戏开发中,单个Scene解决所有问题似乎不可能,那么多个Scene之间的切换是必然存在.如果仅仅是切换,似乎什么都好说,但是在场景比较大的时候不想让玩家等待加载或者说场景与场景之间想通过一 ...
- Cocos2d-x Lua中多场景切换生命周期
在多个场景切换时候,场景的生命周期会更加复杂.这一节我们介绍一下场景切换生命周期.多个场景切换时候分为几种情况:情况1,使用pushScene函数从实现GameScene场景进入SettingScen ...
- Faster-RCNN用于场景文字检测训练测试过程记录(转)
[训练测试过程记录]Faster-RCNN用于场景文字检测 原创 2017年11月06日 20:09:00 标签: 609 编辑 删除 写在前面:github上面的Text-Detection-wit ...
- texturepacker打包图片,场景切换时背景图有黑边
在使用TexturePacker打包图片之后,背景图在场景切换(有切换动画)时,明显能看到有黑边,在百度之后解决了. 知乎上边有网友贴出了两种解决方法,我抄过来如下: 第一种: 修改 ccConfig ...
- cocos2d-x 帧循环不严谨造成场景切换卡顿
最近在用cocos2d-x做引导界面,2dx版本是2.2.3,场景切换加上了效果,所有资源都已经使用texturepacker打包预加载,但是在实际运行调试中,场景切换相当卡顿. 各种纠结后,无意中将 ...
- JavaScript强化教程 -- cocosjs场景切换
场景切换 在main.js,将StartScene作为我们初始化运行的场景,代码如下: cc.LoaderScene.preload(g_resources, function () { cc.dir ...
- cocos2d-x场景切换动画
void StartScene::beginGame() { CCLog("beginGame"); //CCTransitionScene *trans ...
随机推荐
- Echarts-2.2.7中统计出来的统计图保存为图片
今天在做一个图形报表,有个需求是要把展现的统计图保存为图片, 图形报表用的Echarts-2.2.7, 以前有用过 Echarts,记得echarts插件是可以帮助我们把统计图保存为图片的. 只是不记 ...
- 如何在框架外部自定义C++ OP
如何在框架外部自定义C++ OP 通常,如果PaddlePaddle的Operator(OP)库中没有所需要的操作,建议先尝试使用已有的OP组合,如果无法组合出您需要的操作,可以尝试使用paddle. ...
- Ascend Pytorch算子适配层开发
Ascend Pytorch算子适配层开发 适配方法 找到和PyTorch算子功能对应的NPU TBE算子,根据算子功能计算出输出Tensor的size,再根据TBE算子原型构造对应的input/ou ...
- CodeGen融合核心扩展定制文件
CodeGen融合核心扩展定制文件 融合核心定制文件 Harmony核心环境的各个方面都可以通过创建一个定制文件来定制,该文件是一个名为Harm的JSON文件onyCoreCustomization. ...
- C#后台定义一个DataTable并手动写入静态数据(测试数据)
//创建一个DataTable,并为之添加数据(自定义DataTable) DataTable dtz = new DataTable(); //添加Table中的列 DataColumn dc1 = ...
- Java如何使用while和for嵌套循环控制输出数据,使数据奇偶行不同
/* 题目1 使用Eclipse编写控制台应用程, 使用while循环在控制台打印10行10列的如下图形 □ □ □ □ □ □ □ □ □ □ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ □ □ □ □ ...
- NX二次开发-获取WCS坐标系的原点坐标和矩阵标识
函数:UF_CSYS_ask_csys_info() 函数说明:获取工作坐标系对象的标识符. 用法: #include <uf.h> #include <uf_csys.h> ...
- markdown写ppt (史上最全)
文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...
- 【模拟8.01】matrix(DP杂题,思维题)
很神的题,感谢lnc大佬的指点. 先设1-LL[i]统称左区间,RR[i]-m为右区间 用L[i]统计从1-i列,出现的左区间端点的前缀和,R[i]是右区间.... f[i][j]中j表示当前在第i列 ...
- sleep、wait方法之间区别
sleep.wait方法之间区别 1.所属的类不同 sleep是Thread类的静态方法,而wait是Object类的成员方法 2.锁机制不一样 sleep方法:会让出资源调度器为当前线程分配的时间片 ...