一、前言

视频摘要又称视频浓缩,是对视频内容的一个简单概括,先通过运动目标分析,提取运动目标,然后对各个目标的运动轨迹进行分析,将不同的目标拼接到一个共同的背景场景中,并将它们以某种方式进行组合。视频摘要在视频分析和基于内容的视频检索中扮演着重要角色。

视频摘要主要运用在对长时间的监控视频的压缩上,它可以将不同时刻场景内目标的运动显示在同一时刻,这样大量减少了整个场景事件的时间跨度。一般的视频摘要的步骤可以总结为:

视频读取$ \to $背景建模  $\to$ 前景提取$ \to$ 目标轨迹跟踪$ \to$ 目标的时序与空间规划 $\to$ 生成浓缩视频

但是本文并不讨论上面的这些主题,这里我只想通过一个简单的去除视频里非运动帧来实现一个简单的视频压缩的功能。视频摘要不是本文的主题,文章想通过做一个简单的视频摘要程序对OpenCV下面几个功能进行介绍:

1)OpenCV与XML数据通信

2)视频的读取与写入

3)如何在没有OpenCV的环境中运行编译好的程序

二、与XML的交互

很多程序都需要有一个配置文件,可以手动的去调整一些运行中的参数,xml文件格式就是我们常用到的一种配置文件格式。opencv中提供了一个处理xml的类用来与xml文件进行简单的数据存储与读取通信。但这个类的功能有限,如果需要更多的功能可以利用第三方的库,比如libxml等。

我们所设计的视频摘要程序,跟常规的视频摘要不同,这里只是通过删除一些无运动目标的帧来达到视频压缩的目的,所以我们的算法可以设计如下:

1,定义一个目标运动的兴趣区域,作为检测区域。
2,遍历指定目录下的所有视频文件,并逐一的进行视频处理。
3,针对视频的每一帧,在检测区域内运用帧差法检测前景移动。
4,如果检测区域内前景的面积超过区域面积的10%,则说明有运动物体,则此帧进行保留,写入压缩视频。否则,该帧直接舍弃。
5,所有视频处理结束,则程序终止。

那么,我们需要一个配置文件,这个文件里需要保存下面几个内容:

1,检测区域的参数

2,视频文件的目录

3,视频文件的后缀格式

4,生存视频的保存目录

1 <?xml version="1.0"?>
2 <opencv_storage>
3 <roi> 3 460 1250 480</roi>
4 <videoReadPath>D:\ExtractKeyImages\video\</videoReadPath>
5 <videoSuffix>*.mp4</videoSuffix>
6 <videoSavePath>../result.avi</videoSavePath>
7 </opencv_storage>

注意所有的节点都保存在opencv_storage节点下。

在OpenCV中定义了一个叫FileStorage的类,提供了一些简单的打开与读取xml文件内容的操作。

我们先来看xml文件数据的读取:

1,用FileStorage的构造函数可以打开一个xml或yml文件,也可以用FileStorage::open()来打开一个数据文件。

FileStorage::FileStorage(); // 默认构造函数
FileStorage::FileStorage(const string& source, int flags, const string& encoding = string());

上面第二个构造函数中有三个参数。

第一个参数source指定读取文件的路径。

第二个参数flag指定操作的模式,可以设置为READ说明以只读的方式打开一个文件,或者设置为WRITE,这种情况下,如果文件不存在,则创建一个文件,如果文件已经存在,则会清空当前文件里的内容。还可以设置为APPEND用来打开一个存在的文件,并且可以在原来基础上写入。

第三个参数用来指定文件的编码格式,一般都为UTF-8。

而open成员函数的接口与第二个构造函数接口一致。

bool FileStorage::open(const string& filename, int flags, const string& encoding=string())

2,读取文件内的数据,FileStorage重载的操作符[],用来获得指定的节点内容。

FileNode FileStorage::operator[](const string& nodename) const
FileNode FileNode::operator[](const string& nodename) const

上面两个操作符都返回FileNode类型,它是一个子节点类型。

比如:我们想读取<book>结点下的<name>结点,则可以:

FileStorage fs("../config.xml", FileStorage::READ);
string book_name;
fs["book"] ["name"]>> book_name;

如果要取出A节点下的B结点下的C结点则为fs["A"]["B"]["C"]>>content;要记住所有节点都是在根结点opencv_storage下的,但是访问时忽略它。

而如果需要将数据写入,则简单的写入可以直接用<<运算符,比如增加一个节点为book,内容为theOpenCV:

string book_name=”theOpenCV”;
fs<<”book”<<book_name;

最后给出我们程序中读取配置参数的代码,我们需要4项配置项,上面已经介绍过了:

 1 FileStorage fs("../config.xml", FileStorage::WRITE);
2
3 string videoPath;
4 string videoSuffix;
5 Rect roiRect;
6 string imgSavePath;
7
8 fs["videoReadPath"] >> videoPath;
9 fs["videoSuffix"] >> videoSuffix;
10 fs["imgSavePath"] >> imgSavePath;
11 fs["roi"] >> roiRect;

 

二、检测区域的运动检测

这里我们要进行简单的视频压缩就是想把完全静止不动的视频帧从原视频里删除,我们的兴趣目标一般是在移动的视频里。所以我们可以用帧差法来检测移动物体,它的原理是利用视频中物体的移动将引起相邻视频帧内容的不同,从而显示出移动的前景。

两帧之间的帧差图像可以这样定义:

$$imgDif(x,y)=abs(imgCur(x,y)-imgPre(x,y))$$

其中imgCur代表当前帧的图像,imgPre代表前一帧图像。

在得到帧差图像后,我们并不能得到很明显的判断条件,所以我们需要对帧差图像进行二值化,我们设置一个阈值T

$$imgBw(i,j)=

\left\{

\begin{array}{c}

1 ,\ if\ \ imgDif(x,y)\ge T\\

0 ,\ if\ \ imgDif(x,y)\lt T

\end{array}

\right.

$$

然后我们只需遍历图像求出图像中所有白点的个数,即是运动前景的面积,计算一下面积比例即可以确定当前帧是否有物体移动。

当然我们得到的前景目标并不移动的物体的轮廓,而是与前一帧相比目标移动的部分。

下面为这一部分的OpenCV实现,相关的视频读取和写入的操作可以参考OpenCV成长之路中的相关文章。

 1 // 查找文件目录下的所有视频文件
2 vector<string> videoPathStr = FindAllFile((videoPath + videoSuffix).c_str(), true);
3 // 先读取一个视频文件,用于获取相关的参数
4 VideoCapture capture(videoPathStr[0]);
5 // 视频大小
6 Size videoSize(capture.get(CV_CAP_PROP_FRAME_WIDTH), capture.get(CV_CAP_PROP_FRAME_HEIGHT));
7 // 创建一个视频写入对象
8 VideoWriter writer("../result.avi", CV_FOURCC('M', 'J', 'P', 'G'), 25.0, videoSize);
9
10 for (auto videoName : videoPathStr)
11 {
12 capture.open(videoName); // 读入路径下的视频
13
14 Mat preFrame;
15 bool stop(false);
16
17 double totleFrameNum = capture.get(CV_CAP_PROP_FRAME_COUNT); // 获取视频总帧数
18
19 for (int frameNum = 0; frameNum < totleFrameNum; frameNum++)
20 {
21 Mat imgSrc;
22 capture >> imgSrc; // 读一视频的一帧
23 if (!imgSrc.data)
24 break;
25 Mat frame;
26 cvtColor(imgSrc, frame, CV_BGR2GRAY);
27 ++frameNum;
28 if (frameNum == 1)
29 {
30 preFrame = frame;
31 }
32 Mat frameDif;
33 absdiff(frame, preFrame, frameDif); // 帧差法
34 preFrame = frame;
35
36 threshold(frameDif, frameDif, 30, 255, THRESH_BINARY); // 二值化
37
38 Mat imgRoi = frameDif(roiRect);
39 double matArea = computeMatArea(imgRoi); // 计算区域面积
40
41 if (matArea / (imgRoi.rows*imgRoi.cols) > 0.1) // 面积比例大于10%
42 {
43 writer << frameDif;// 写入视频
44 }
45 }
46 }
47 capture.release();
48 writer.release();

三、在没有OpenCV的环境下运行程序

这里是指基于windows系统下VS平台的程序,很多时候我们编译好的程序需要在别人的电脑上运行,而别人电脑上是没有OpenCV的基本库的,而我们的编译的opencv程序一般是动态链接一些dll的。

有两种方法:一种是拷贝用到的dll到release目录下,另一种是把相关的源文件加入工程中一起编译。

下面主要介绍第一种方法,因为看起来简单,很多人还是运行不了。

我们从openCV的环境配置开始说起:

首先,我们先找到我们下载并解压后的OpenCV目录下的这几个目录:

头文件目录:F:\EvProjects\OpenCV\OpenCV248\build\include

运行库目录:F:\EvProjects\OpenCV\OpenCV248\build\x86\vc12\lib

上面的vc12指定你的vs的版本,这里是vs2013

然后我们在我们新建的工程中找到属性管理器:

然后分别在DeBug和Release下配置属性表:

我们可以新建一个名字为opencv248_debug.props的属性表,以后新建的工程,直接拷贝添加即可。

然后右键配置opencv248_debug.props的属性,在VC目录下配置两项:

一项是包含目录,加入:F:\EvProjects\OpenCV\OpenCV248\build\include

第二项是在库目录下加入:F:\EvProjects\OpenCV\OpenCV248\build\x86\vc12\lib

最后我们需要在链接器->输入->附加依赖项中加入一些常用到的库文件

opencv_core248d.lib
opencv_imgproc248d.lib
opencv_highgui248d.lib
opencv_ml248d.lib
opencv_video248d.lib
opencv_features2d248d.lib
opencv_calib3d248d.lib
opencv_objdetect248d.lib
opencv_contrib248d.lib
opencv_legacy248d.lib
opencv_flann248d.lib

注意上面的248说明了我的opencv版本,你的可能是246或247。

也可以把F:\EvProjects\OpenCV\OpenCV248\build\x86\vc12\lib目录里的lib文件都加入,注意只加入带d的表示debug库。

这样的话debug下就配置完了,我们按相同方法,在release下配置一个属性表opencv248_release.props,与debug不同的是,在链接器的配置里加入的库名,都是不包含d的。

OK,属性表都配置好后,我们把当前的编译环境改为Release:

在解决方案里,右键项目名->属性->配置管理器

然后把活动解决方案配置改为release即可。

所有的环境配置好后,只需要编译好程序,然后在release下找到exe文件,这个就是我们的可执行文件,但是它不能单独运行,我们需要把它需要依赖的一些dll拷贝过来,dll在opencv的F:\EvProjects\OpenCV\OpenCV248\build\x86\vc12\bin目录下,如果你不确定你的程序里需要哪些库,你就把全部都拷贝过来。或者可以用一个依赖库查看软件查看你的程序所依赖的库,把对应的dll拷贝过来即可。

另外值得注意,如果是VS的较高版本,如VS2012,VS2013你还安装对应的运行库。

OpenCV进阶之路:一个简化的视频摘要程序的更多相关文章

  1. OpenCV进阶之路:神经网络识别车牌字符

    1. 关于OpenCV进阶之路 前段时间写过一些关于OpenCV基础知识方面的系列文章,主要内容是面向OpenCV初学者,介绍OpenCV中一些常用的函数的接口和调用方法,相关的内容在OpenCV的手 ...

  2. opencv学习之路(1)、示例程序

    一.介绍 工欲善其事必先利其器,首先当然是配置环境安装软件啦.  我安装的vs2012+opencv2.48以及opencv3.0.具体安装步骤按照浅墨大神的博客进行即可:http://blog.cs ...

  3. OpenCV成长之路(10):视频的处理

    视频中包含的信息量要远远大于图片,对视频的处理分析也越来越成为计算机视觉的主流,而本质上视频是由一帧帧的图像组成,所以视频处理最终还是要归结于图像处理,但在视频处理中,有更多的时间维的信息可以利用.本 ...

  4. opencv学习之路(2)、读取视频,读取摄像头

    一.介绍 视频读取本质上就是读取图像,因为视频是由一帧一帧图像组成的.1秒24帧基本就能流畅的读取视频了. ①读取视频有两种方法: A. VideoCapture cap; cap.open(“1.a ...

  5. 进阶之路 | 奇妙的Animation之旅

    前言 本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍: 我的GIthub博客 学习清单: 动画的种类 自定义View动画 View动画的特殊使用场景 属性动画 使用动画的注意事项 一.为什 ...

  6. 【SSH进阶之路】Hibernate映射——多对一单向关联映射(四)

    [SSH进阶之路]Hibernate基本原理(一) ,小编介绍了Hibernate的基本原理以及它的核心,採用对象化的思维操作关系型数据库. [SSH进阶之路]Hibernate搭建开发环境+简单实例 ...

  7. OpenCV成长之路:图像直方图的应用

    OpenCV成长之路:图像直方图的应用 2014-04-11 13:57:03 标签:opencv 图像 直方图 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否 ...

  8. Scala进阶之路-并发编程模型Akka入门篇

    Scala进阶之路-并发编程模型Akka入门篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Akka Actor介绍 1>.Akka介绍 写并发程序很难.程序员不得不处 ...

  9. GO语言的进阶之路-爬虫进阶之路

    GO语言的进阶之路-爬虫进阶之路 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 网络爬虫是一种自动获取网页内容的程序,是搜索引擎的重要组成部分.我们今天要介绍的就是一个简单的网络爬 ...

随机推荐

  1. easyUI validate函数【总结篇-部分转】

    以下是自己总结和修改别人的帖子和资源整理出来的一些常用验证函数,备用,交流. <body>邮箱验证:<input type="text" validtype=&q ...

  2. 2016 年 Python 开发者调查结果

    1.在团队中工作 vs 独立工作 有趣的是,半数的受访者大部分时间在团队中工作,而另外半数的受访者则独立的做项目. 在公司中工作 vs 独立从事自己的项目 大约80%的受访者告诉我们,他们在公司里面工 ...

  3. 9月12日JavaScript脚本语言

    JS脚本语言 JS脚本语言全称JavaScript,是网页里面使用的脚本语言,也是一门非常强大的语言. 一.基础语法 1.注释语法 单行注释:// 多行注释:/**/ 2.输出语法 ①alert(信息 ...

  4. Bitmap动画

    http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html htt ...

  5. Hermite Curve

    http://paulbourke.net/miscellaneous/interpolation/ http://fivedots.coe.psu.ac.th/Software.coe/Java%2 ...

  6. iTunes访问自己应用的沙盒

  7. Robot Framework--05 案例设计之流程与数据分离

    转自:http://blog.csdn.net/tulituqi/article/details/7651049 这一讲主要说一下案例设计了.还记得我们前面做的case么?先打开浏览器访问百度,输入关 ...

  8. 居于Web的进度条实现思路(下载百分比)

    http://www.cnblogs.com/wfyfngu/p/4866434.html 在传统桌面项目中,进度条随处可见,但作为一个很好的用户体验,却没有在如今主流的B/S程序中得到传承,不能不说 ...

  9. Python之迭代器和生成器

    Python 迭代器和生成器 迭代器 Python中的迭代器为类序列对象(sequence-like objects)提供了一个类序列的接口,迭代器不仅可以对序列对象(string.list.tupl ...

  10. PHP 短连接生成

    <?php #短连接生成算法 class Short_Url { #字符表 public static $charset = "0123456789ABCDEFGHIJKLMNOPQR ...