本文讲一下如何捕获视频。

从文件中捕获视频

其实使用OpenCV读取视频相当简单,只需要初始化一个VideoCapture对象,打开视频文件,逐帧读取。

程序

下面是简单的OpenCV代码:

#include <opencv2\highgui\highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char **argv)
{
	//打开视频文件
	VideoCapture cap("Girls'Generation.avi");
	if (!cap.isOpened())
	{
		cout << "不能打开视频文件" << endl;
		return -1;
	}
	//从3000ms开始播放视频
	//cap.set(CV_CAP_PROP_POS_MSEC, 3000);

	//获取视频的帧速率
	double fps = cap.get(CV_CAP_PROP_FPS);
	cout << "帧/秒:" << fps << endl;

	namedWindow("MyVideo",CV_WINDOW_AUTOSIZE);
	while (true)
	{
		Mat frame;
		//从视频中读取一个帧
		bool bSuccess = cap.read(frame);
		if (!bSuccess)
		{
			cout <<"不能从视频文件读取帧"<< endl;
			break;
		}
		//在MyVideo窗口上显示当前帧
		imshow("MyVideo", frame);
		//如果30ms没有按下键,就返回-1,当按下ESC键返回27
		if (waitKey(30)==27)
		{
			cout << "按下ESC键" << endl;
			break;
		}
	}

	return 0;
}

解释

	VideoCapture::VideoCapture(const string& filename)

这是VideoCapture类为数不多的构造函数的一个。这个构造函数打开视频文件并为读取视频流初始化VideoCapture对象。
这个类的析构函数会释放对象关联的所有内存。因此,不需要在程序中显示的释放内存。

	bool VideoCapture::IsOpened()

如果调用VideoCapture构造函数成功,这个方法返回true,否则返回false。
检查VideoCapture初始化是否成功是有必要的。如果失败,程序应该退出,否则当从VideoObject中读取帧的时候,程序就会崩溃了。

	bool VideoCapture::set(int propId, double value)

这个方法改变VideoObject对象的属性,如果成功,返回true,否则返回false。你应该在你的代码中尝试修改视频流的一些属性。在我的代码中,我改变了CV_CAP_PROP_POS_MSEC属性。

它的参数:

  • int propID - 这个参数指定你要更改的属性。这个参数有很多可选项,这里列出一部分。
    • CV_CAP_PROP_POS_MSEC - 视频的当前位置(毫秒)
    • CV_CAP_PROP_POS_FRAMES - 视频的当前位置(帧)
    • CV_CAP_PROP_FRAME_WIDTH - 视频流的宽度
    • CV_CAP_PROP_FRAME_HEIGHT - 视频流的高度
    • CV_CAP_PROP_FPS - 帧速率(帧/秒)
    • CV_CAP_PROP_FOURCC - 编码器的四字符编码
  • double value - 给propID分配的值。
	doubleVideoCapture::get(int propId)

这个函数返回指定的propId的属性值。它的可选项和set函数的propID一样。

	bool VideoCapture::read(Mat& image);

这个函数抓取视频的下一帧,解码并存储在‘image’变量中。在这个函数内部会调用VideoCapture::grap()和VideoCapture::retrieve()。可以使用这2个函数代替VideoCapture::read()函数。如果操作成功,返回true,否则返回false。

	waitKey(30)

这个函数等待30毫秒。如果在指定的时间按下键,它会返回键的ASCII值。如果这个值是27,程序就会执行if块。如果在30毫秒内没有按下键,程序返回-1,程序继续while循环。

	VideoCapture::~VideoCapture()

VideoCapture对象的析构函数会释放对象所有关联的内存。在程序退出main方法时,析构函数会隐式调用。

总结

首先,这个程序从文件中捕获一个视频,然后进入一个无限循环。在这个循环中,它从捕获的视频中连续地抓取帧,解码,然后显示在窗口中并等待30毫秒。如果视频没有更多的帧或按下了ESC键,程序就会跳出无限循环。

注意:使用waitKey(int)函数非常重要,因为imshow(string&, MAT)函数需要时间在窗口上绘制图像,正是waitKey(int)提供了这个时间。

从摄像头中采集视频

程序

下面的程序和上面的最大不同是VideoCapture构造函数的参数。这里我给VideoCapture的构造函数提供摄像头的编号来代替一个文件名。下面是OpenCV代码:

#include <opencv2\highgui\highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char **argv)
{
	// 打开第1个摄像头
	VideoCapture cap(0); 

	if (!cap.isOpened())
	{
		cout << "不能打开摄像头!" << endl;
		return -1;
	}

	//获取视频帧的宽度
	double dWidth = cap.get(CV_CAP_PROP_FRAME_WIDTH); 
	//获取视频帧的高度
	double dHeight = cap.get(CV_CAP_PROP_FRAME_HEIGHT);

	cout << "视频帧尺寸 : " << dWidth << " x " << dHeight << endl;

	namedWindow("MyVideo", CV_WINDOW_AUTOSIZE); 

	while (1)
	{
		Mat frame;

		bool bSuccess = cap.read(frame); 

		if (!bSuccess) 
		{
			cout << "不能从视频流中读取帧!" << endl;
			break;
		}

		imshow("MyVideo", frame); 

		if (waitKey(30) == 27) 
		{
			cout << "ESC键被按下" << endl;
			break;
		}
	}
	return 0;

}

解释

	VideoCapture::VideoCapture(int device)

这是VideoCapture 3个构造函数的一个。这个构造函数打开给定编号的摄像头并为读取摄像头的视频流初始化VideoCapture对象。这里的“0”表示使用编号为0的摄像头。如果你的电脑上有多个摄像头,可以使用1,2,3...代替0。

	double dWidth = cap.get(CV_CAP_PROP_FRAME_WIDTH)

这个函数获取摄像头输出帧的宽(像素)

	double dHeight = cap.get(CV_CAP_PROP_FRAME_HEIGHT)

这个函数获取摄像头输出帧的高(像素)

其他所有的函数都跟第一个程序中一样。