OpenCV计算机视觉开发实践:基于Qt C++ - 商品搜索 - 京东
构造VideoCapture对象
类VideoCapture既支持从视频文件(.avi 、*.mp4、.mpg等格式)读取,也支持直接从摄像机(比如计算机自带的摄像头)中读取。要想获取视频,需要先创建一个VideoCapture对象。VideoCapture对象的创建方式有以下3种:
(1)从视频文件中读取视频。如果是从文件(.mpg或.avi格式)中读取视频,则在定义对象的时候,可以把视频文件的路径作为参数传给构造函数。对象创建以后,OpenCV将会打开该视频文件并做好准备读取它,并且VideoCapture提供了成员函数isOpened来判断文件是否打开成功,如果成功将返回true(建议在打开视频或摄像头时都使用该成员函数判断是否打开成功)。用于读取视频文件的构造函数如下:
VideoCapture (const String &filename, int apiPreference=CAP_ANY);
其中参数filename是视频文件的文件名(可以包含路径),如果不包含路径,则在当前路径下打开文件; apiPreference表示首选使用的后端捕获API,默认是使用任意捕获API,用宏CAP_ANY来表示。比如我们定义一个VideoCapture对象并打开D盘上的test.avi文件:
cv::VideoCapture capture("D:/test.avi"); // 从视频文件读取
(2)从摄像机中读取视频。如果是从摄像机中读取视频,这种情况下,构造函数会给出一个整型索引参数,用于表示想要访问的摄像机。对于摄像机而言,这个索引参数就是一个标志数字,如果只有1个摄像机,那么索引参数就是0;如果系统中有多个摄像机,那么只要将其值向上增加即可。读取摄像头中的视频的构造函数如下:
VideoCapture (int index, int apiPreference=CAP_ANY);
其中参数index表示要打开的视频捕获设备(摄像头)的ID,如果使用默认后端的默认摄像头,只需传递0;apiPreference表示首选使用的后端捕获API,默认是使用任意捕获API,用宏CAP_ANY来表示,多数情况下,由于我们只有一个摄像机,因此没必要指定摄像机的域,此时使用cv::CAP_ANY是一种高效的方式。参数apiPreference表示摄像机的类型,这个域值可以是下面任意一个预定义常量:
CAP_ANY:自动选择API
CAP_DSHOW:通过视频输入,例如,摄像头输入
CAP_FFMPEG:使用FFMPEG库
CAP_IMAGES:打开一个图像序列(例如,img_%02d.jpg)
(3)不带参数构造一个VideoCapture对象。使用类VideoCapture的不带参数的构造函数来创建一个VideoCapture对象,然后用成员函数open打开一个视频文件或摄像头。open函数声明如下:
virtual bool open (const String &filename, int apiPreference=CAP_ANY);
virtual bool open (int index, int apiPreference=CAP_ANY);
两个open函数的参数和前面两个构造函数的参数含义一样,这里不再赘述。
判断视频是否打开成功
当我们打开一个视频文件或摄像头视频后,可以用成员函数isOpened来判断是否打开成功。该函数声明如下:
virtual bool isOpened() const;
若打开成功,则返回true,否则返回false。
比如:
VideoCapture capture;
capture.open(0, CAP_DSHOW); // 打开摄像头
if (!capture.isOpened()) // 判断打开是否成功
puts("open failed");
12.5 读取视频帧
要播放视频,肯定要把每一帧视频图像都读出来,然后显示出来。读取方式有两种,一种是用运算符>>,直接输出视频帧到Mat对象中。>>是一个重载运算符,定义如下:
virtual VideoCapture& operator >> (CV_OUT Mat& image);
例如:
Mat frame;
VideoCapture capture("test.mp4");
while (1) {
capture >> frame; // 输出一帧到Mat对象中
if (frame.empty()) { // 播放完就退出
printf("播放完成\n");
break;
}
imshow("读取视频", frame); // 显示帧
waitKey(20);
}
当输出完一帧后,下次就会输出下一帧。
另外一种方式是通过成员函数read,该函数声明如下:
virtual bool read(OutputArray image);
参数image用来存放读取到的当前视频帧。若读取成功,则返回true,否则返回false。例如:
Mat frame;
VideoCapture capture(0, CAP_DSHOW);
while (capture.read(frame))
{
imshow("video-demo", frame);
char c = waitKey(66);
if (c == 27) break;
}
播放视频文件
播放视频文件的基本步骤就是先构造VideoCapture对象,再打开视频文件,接着用一个循环逐帧读取并显示读取到的视频帧,然后间隔一段时间读取下一个视频帧并显示,依次循环,直到全部视频帧读取完毕。
【例12.1】播放MP4视频文件
新建一个控制台工程,工程名是conplay。
打开main.cpp,并输入如下代码:
#include <opencv2/opencv.hpp>
using namespace cv;
#include <QDebug>
void play(const char *filename)
{
Mat frame;
int i=0;
VideoCapture capture(filename);
if (!capture.isOpened())
{
printf("open %s failed",filename);
return;
}
// 获取整个视频帧数
long totalFrameNumber = capture.get(CAP_PROP_FRAME_COUNT);
qDebug()<<"整个视频帧数:"<<totalFrameNumber;
while (i<totalFrameNumber) {
capture >> frame;
if (frame.empty()) break; // 播放结束就跳出循环
imshow("read video", frame); // 显示帧
waitKey(1);
i++;
}
qDebug()<<"播放完成\n";
}
int main() {
play("/root/hd.mp4");
}
在上述代码中,我们首先定义了一个VideoCapture对象capture,并传入视频文件的文件名;然后利用成员函数get获取视频的帧数,这样在后面的while循环中可以控制播放的帧数。当frame为空时,说明视频帧没有了,也就是播放完毕了,此时跳出循环。每一帧其实就是一幅图片,可以用imshow函数把图片显示出来。当显示速度很快时,多个图片连续显示,看起来就是视频了。这里我们设置每个帧的显示间隔时间是1毫秒。
保存工程并运行,结果如图12-2所示。
图12-2
播放摄像头视频的主要流程与之类似,只不过在调用open的时候,需要修改一下传入的整型数据。读者可以准备一个USB接口的计算机摄像头,然后把USB口插入计算机的USB插槽中,通常是不需要安装驱动程序的。