3、OpenCV播放视频、保存、暂停视频,打开摄像头

一、加载视频或摄像头

1、原理理解
所谓的视频播放,无非就是将一堆有序的图片序列,按照顺序,以一定的间隔显示出来。这个间隔的多少与我们所听到的帧率相关。有了这个意识后,我们其实按照自己的思想都可以实现视频的播放:视频文件是由一帧帧具有先后顺序的图片组成,我们只需要将视频的每一帧按照原有的顺序显示出来即可。为此OpenCV提供了专用的对象和API以供加载视频并按顺序提取视频的每一帧。

2、视频管理类 cv::VideoCapture加载视频或摄像头
Opencv提供了用于获取视频文件的类 cv::VideoCapture,这个类提供了c++ API,用于从摄像机捕捉视频,或者读取视频文件和图像序列。实例化该类的对象后,便可以通过该类提供的方法获取视频帧和获得视频文件的相关信息。

通常使用如下两种方法构造 cv::VideoCapture对象。
方法一:先声明 cv::VideoCapture的对象,再调用该对象的open方法打开指定的视频文件

	VideoCapture vc1;
	vc1.open("O:\\image\\1.mp4");

注意cv::VideoCapture对象的open方法的函数原型为:

    virtual bool cv::VideoCapture::open	(const String & filename,
                                         int apiPreference = CAP_ANY)	

open方法的第一个参数指定视频文件,第二个参数可忽略。open方法的另一个常用的重载为:

    virtual bool cv::VideoCapture::open	(int index,
                                         int apiPreference = CAP_ANY)	

此时,第一个参数用于绑定使用摄像头的编号,从0开始计数。

方法二:声明 cv::VideoCapture对象的时候,调用有参构造函数直接绑定视频文件

    VideoCapture vc1("O:\\image\\1.mp4");

该构造函数的原型为:

    cv::VideoCapture::VideoCapture	(const String & filename,
                                     int apiPreference = CAP_ANY)	

其中,第一个参数指定视频文件,第二个参数可忽略。其对应的打开摄像头的重载为:

    cv::VideoCapture::VideoCapture	(int index,
                                     int apiPreference = CAP_ANY)	

此时,第一个参数还是指定摄像头编号,从0开始计数。

二、获取视频帧或摄像头的帧

1、在获取视频帧之前,通常我们需要判断一下视频或者摄像头有无打开(初始化):

	if (!vc1.isOpened())
	{
		fprintf(stderr, "failed to open %s\n", "O:\\image\\1.mp4");
		return EXIT_FAILURE;
	}

如果视频或者摄像头初始化成功,该方法会返回 true

2、获取视频帧或者摄像头的帧内容有两种方式
方法一:

	Mat frame;
	vc1.read(frame);

该函数将VideoCapture::grab()VideoCapture::retrieve() 组合在一个调用中。这是最方便的读取视频文件或捕获数据解码和返回方法。如果没有捕获帧(相机已断开连接,或没有更多帧),方法返回false,函数返回空图像。

方法二:

	Mat frame;
	vc1 >> frame;

这主要是基于cv::VideoCapture类重载了 >> 操作符。

三、循环逻辑控制视频显示

结合原理理解,现在已经有方法获取视频的每一帧内容了,接下来要做的就是写好循环逻辑来逐帧显示图像即可。

	Mat frame;
	vc1 >> frame;
	while (!frame.empty())
	{
		imshow("demo", frame);
		vc1 >> frame;
		waitKey(30);
	}
	destroyAllWindows();

四、暂停视频播放和中断视频播放

前面已经实现了视频的连续播放,接下来要做的就是实现按下空格键(space,其ASCII值为32)时视频暂停播放,再次按下的时候继续播放。按下退出键(ESC,ASCII值为27)时中断视频播放。这儿会使用到一点简单的编程技巧,直接看代码:

	Mat frame;
	vc1 >> frame;
	while (!frame.empty())
	{
		imshow("demo", frame);
		vc1 >> frame;
		int k = waitKey(30);
		
		if (k == 27)
			break;
		else if (k == 32)
		{
			while (waitKey(0) != 32)
				waitKey(0);
		}
	}
	destroyAllWindows();

在每隔30ms显示一帧图像后会等待用户按键,如果在30ms内用户没按键,则waitKey(30) 的返回值为-1,程序继续显示下一帧,循环继续。如果用户按下了ESC键,则直接break退出循环,视频播放结束,如果按下空格后,则程序会一直等待用户再次按下空格后才继续。

五、视频保存

Opencv实现视频的保存需要定义编解码器并创建cv::VideoWriter对象。
1、实例化视频写对象cv::VideoWriter

    VideoWriter writer;

cv::VideoWriter类有很多个重载的构造方法,可以在声明对象的同时定义编解码器,也可以在后面调用该对象的fourcc 方法指定编解码器。

    int code = writer.fourcc('M', 'J', 'P', 'G');
    writer.open("O:\\image\\result.avi", code, 40, Size(1280, 720), true);

声明对象的同时绑定文件和编解码器,其函数原型为:

    CV_WRAP VideoWriter(const String& filename, int fourcc, double fps,
                        Size frameSize, bool isColor = true);
  • 参数 filename:指定保存视频的文件名(路径+名称)
  • 参数 fourcc:指定编解码器
  • 参数 fps:指定保存视频的帧率
  • 参数 frameSize:指定保存视频的尺寸,为Size对象
  • 参数 isColor:指定保存视频是否为彩色,默认为true

调用实例为:

    string fileName = "O:\\image\\result.avi";
	VideoWriter writer = VideoWriter(fileName, VideoWriter::fourcc('M', 'J', 'P', 'G'), 30, Size(1280, 720), true);

其它构造函数基本一致,感兴趣的小伙伴自行查阅资料。

2、在视频显示的循环帧中,将每一帧保存即可
调用 cv::VideoWriter对象的write函数:

    writer.write(frame);

在写完视频后,记得释放资源:

    writer.release();

六、完整代码示例

#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    // 初始化视频管理对象,直接指定视频文件的路径
	VideoCapture vc1("O:\\image\\1.mp4");
    
    // 判断视频是否初始化成功
	if (!vc1.isOpened())
	{
		fprintf(stderr, "failed to open %s\n", "O:\\image\\1.mp4");
		return EXIT_FAILURE;
	}

    // 声明视频写入对象
	string fileName = "O:\\image\\result.avi";
	VideoWriter writer = VideoWriter(fileName, VideoWriter::fourcc('M', 'J', 'P', 'G'), 30, Size(1280, 720), true);
    
    // 循环处理视频的每一帧,先显示,再写入
	Mat frame;
	vc1 >> frame;
	while (!frame.empty())
	{
		imshow("demo", frame);
		// 写入视频帧
		writer.write(frame);
		vc1 >> frame;
		int k = waitKey(30);
		// 实现按键暂停和退出视频播放功能		
		if (k == 27)
			break;
		else if (k == 32)
		{
			while (waitKey(0) != 32)
				waitKey(0);
		}
	}
	// 释放资源,清空缓存
	destroyAllWindows();
	writer.release();

	system("pause");
	return EXIT_SUCCESS;
}

七、致谢

1、常规的谢天谢地谢父母,谢朋友
2、感兴趣的小伙伴欢迎入群一起探讨学习 :725027506。 去群飞机票

  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值