2.2 基本视频操作

如果说单张图像是二维数据,则视频是三维数据,它增加了时间维度z,单张图像与视频序列如图2-18所示。在机器视觉和机器学习结合的许多应用中都需要处理视频,本节将介绍OpenCV的基本视频操作。

图2-18 单张图像与视频序列

2.2.1 读取和播放视频文件

1. 实现方法

使用OpenCV读取和播放视频文件几乎与显示图像一样简单,只需设置一个循环结构,每次循环读取视频文件中的一帧图像用于显示即可,当然还需要设置退出循环的条件。读取和播放视频文件如示例代码2-6所示。

示例代码2-6 读取和播放视频文件

2. 代码分析

首先,实例化一个视频控制类函数cv::VideoCapture的对象cap,用于读取和操作视频文件:

其次,使用cap.open方法从源文件所在目录读取视频文件bike.avi:

然后,创建一个用于从视频文件中读取单帧图像的cv::Mat变量frame:

接下来是关键部分,创建for循环从视频文件中依次读取单帧图像并存储在frame中:

最后,当读取的frame是空图像时,即已读取到视频文件的最后一帧,退出循环:

如果读取的frame有数据,则显示frame:

另外,增加一个退出机制,即在播放过程中当有按键被按下时,cv::waitKey函数将返回按键的ASCII码(>0),并退出循环,停止播放,否则等待33ms后继续读取下一帧图像:

当然,也可以使用while循环控制视频文件的播放。

3. 运行结果

示例代码2-6的运行结果如图2-19所示,视频中的小女孩骑车从左至右经过画面。

图2-19 示例代码2-6的运行结果

2.2.2 处理视频文件

1. 实现方法

下面通过例子展示如何使用OpenCV调用摄像头并处理视频文件。如果计算机自带摄像头或已将USB摄像头连接至计算机,则只需将示例代码2-7中的cap.open("bike.avi")改为cap.open(0)即可打开摄像头。

示例代码2-7 调用摄像头并处理视频

2. 代码分析

首先,使用cap.open(0)方法打开默认摄像头,新建两个cv::Mat变量——frame和edge_img,分别用于存放摄像头捕获的图像与边缘检测后的图像:

然后,与示例代码2-6一样,在循环中获取一帧图像:

接下来,使用cv::Canny函数检测图像的边缘图像,并将其存储于edge_img变量中:

在两个窗口中分别显示源图与边缘图像:

设置循环退出条件,当按下“ESC”、“q”或者“Q”键时退出循环:

前面从整体上分析了示例代码2-7,下面简单介绍Canny边缘检测算法。Canny边缘检测算法结合了高低阈值计算出的两个边缘分布图,可生成最优边缘分布图。具体做法是在低阈值的边缘分布图中只保留有连续路径的边缘点,同时把这些边缘点连接到属于高阈值边缘分布图的边缘上。这样一来,高阈值分布图上的所有边缘点就都保留了下来,而低阈值分布图上的边缘点的孤立链被全部移除。这是一种很好的折中方案,只要指定适当的阈值,就能获得高质量的轮廓。这种基于两个阈值获得二值分布图的策略,称为滞后化阈值,适用于任何需要用阈值获得二值分布图的场景。

cv::Canny函数是OpenCV提供的边缘检测函数。

cv::Canny函数:

函数参数:

◎ image:8bit输入图像。

◎ edges:输出边缘图像,单通道8bit图像,尺寸与输入图像一致。

◎ threshold1:滞后阈值化的第1阈值。

◎ threshold2:滞后阈值化的第2阈值。

◎ apertureSize:Sobel操作员的孔径大小。

◎ L2gradient:标志,指示是使用更精确的L2范数计算图像梯度的大小(L2gradient =true),还是使用默认的L1范数(L2gradient = false)计算图像梯度的大小。

3. 运行结果

读取摄像头并处理示例代码2-7的运行显示结果如图2-20所示。

图2-20 读取摄像头并处理示例代码2-7的运行显示结果

2.2.3 存储视频文件

1. 实现方法

使用cv::VideoWrite类可以方便地在硬盘中写入不同编码方式的视频文件。在示例代码2-7的基础上,修改代码如下。

示例代码2-8 写视频文件

2. 代码分析

与示例代码2-7相比,在增加的代码中首先使用了cv::VideoCapture类的get方法来获取摄像头图像的宽和高:

然后根据这些参数,实例化一个cv::VideoWriter类的对象out,并调用open方法完成对out的初始化:

最后,在while循环中计算图像边缘后,使用cv::VideoWriter类的write方法将边缘图像edge_img一帧接一帧地写入视频文件my_video.avi中:

另一种写入视频的操作如下:

cv::VideoWriter::VideoWriter函数:

函数参数:

◎ filename:输出视频文件名。

◎ fourcc:由4个字母组成,表示帧压缩的编码方式。例如:

VideoWriter::fourcc('P', 'I', 'M', '1') 表示MPEG-1编码。

VideoWriter::fourcc('D', 'I', 'V', 'X') 表示MPEG-4编码。

VideoWriter::fourcc('M', 'J', 'P', 'G') 表示主运动jpeg编码。

◎ fps:每秒传输视频帧数,简称帧率。

◎ frameSize:视频帧的宽和高。

◎ isColor:如果不为零,则编码器如预期一样编码彩色帧,否则编码灰度帧(该标记当前仅在Windows上受支持)。

3. 运行结果

在源文件的目录下生成一个my_video.avi的视频文件,它存储了边缘检测结果视频,存储的视频文件缩略图如图2-21所示。

图2-21 存储的视频文件缩略图