基础入门
视频是由一系列连续的图像帧组成的,这些帧按照一定的速率连续播放,从而形成动态画面。与视频相关的主要参数有:分辨率、帧率、码率、编解码器、帧类型、文件格式等,下面分别进行介绍。
1、帧率。表示每秒显示的图像帧数,该参数决定视频流畅度,更高的帧率意味着更平滑的动作。常见的帧率有:24fps(电影)、30fps(电视标准)、60fps(流畅的游戏体验)等。
2、分辨率。表示视频每一帧图像的宽度和高度,通常以像素为单位,该参数决定视频的清晰度和细节程度。常见的分辨率有:720p(1280 x 720)、1080p(1920 x 1080)、4K(3840 x 2160)等。
3、码率。也叫比特率,指每秒传送的数据量,通常以kbps (千比特/秒) 或 Mbps (兆比特/秒) 表示。该参数影响视频的质量和文件大小,高比特率意味着更好的视频质量,但文件也更大。码率可以是恒定的(CBR, Constant Bitrate),也可以是可变的(VBR, Variable Bitrate)。
4、编解码器。即Codec,是编码器和解码器的组合词,用于压缩和解压视频数据。该参数影响视频质量和文件大小,常见的编解码算法有:H.264/AVC、H.265/HEVC、MJPEG、VP9等。
5、帧类型。在H.264、H.265编码算法中,使用了不同的帧类型来提高压缩效率,主要包括I帧、P帧和B帧。
(1)I帧:也称为帧内编码帧,是一个完整的图像帧,它包含了该帧所有的图像信息,类似于一张静态图片。I帧不需要参考其他任何帧来解码,因此可以在视频的任何点开始播放。但是,由于包含完整信息,所以占用的空间较大。通常用于视频的开头或场景变化较大的地方,因为这些位置不适合使用预测编码。
(2)P帧:也称为前向预测帧,只包含相对于前一帧变化的部分信息。P帧会参考前一个I帧或P帧来进行解码,通过对前一帧的预测,P帧只需要记录变化的内容,这样就能大大减少所需的存储空间。P帧在视频中占大多数,用于高效地表示场景中变化不大的部分。
(3)B帧:也称为双向预测帧,会同时向前和向后参考相邻的I帧或P帧。B帧进一步提高了压缩效率,但增加了解码复杂度。
6、文件格式。用于存储视频数据的标准或容器,不同的视频文件格式有着不同的特性和用途。常见的视频文件格式有:MP4、AVI、MOV、FLV、MPEG、WMV等。
API接口
cv::VideoCapture是OpenCV中用于处理视频输入的核心类之一,它可以用来从摄像头设备或视频文件中捕获视频帧。cv::VideoCapture类构造函数的原型如下。
VideoCapture();
VideoCapture(int device_index);
VideoCapture(const String& filename);
各个参数的含义如下。
device_index:整数,表示设备索引,通常0表示第一个摄像头。
filename:视频文件的路径。
cv::VideoCapture类提供了很多实用性的方法来控制视频流,其导出的主要方法如下。
open用于打开摄像头或者视频文件,成功打开设备或文件返回true,失败返回false。其参数的含义与上面完全相同,故这里不再赘述。
bool open(int device_index);
bool open(const String& filename);
read用于读取一帧视频数据,成功读取帧返回true,失败返回false。参数image为一个OutputArray,通常是一个cv::Mat对象,用于接收读取到的帧。
bool read(OutputArray image);
grab会提前获取下一帧的数据,但并不立即填充到image中。如果成功获取下一帧,grab方法返回true。如果没有更多的帧可获取,或者发生错误,grab方法返回false。
bool grab();
retrieve将先前通过grab方法获取的帧数据填充到image中。如果成功填充了一帧,retrieve方法返回true,并且image将包含这一帧的数据。如果没有通过grab方法获取帧,或者发生错误,retrieve方法返回false。grab和retrieve方法通常一起使用,先通过grab获取下一帧,再通过retrieve获取该帧的数据。这种分离的设计使得处理视频流时更为灵活,可以在获取下一帧之前处理当前帧,或者处理多路视频流。
bool retrieve(OutputArray image, int stream = 0);
各个参数的含义如下。
image:一个OutputArray,通常是一个cv::Mat对象,用于接收读取的帧。
stream:整数,指定要从中检索帧的流。对于单流设备,默认为0。
isOpened用于判断视频设备或文件是否打开,如果已成功打开,则返回true,否则返回false。
bool isOpened() const;
release用于释放VideoCapture对象所使用的资源。
void release();
实战解析
在下面的实战代码中,我们首先定义了一个预处理宏VIDEO_PROCESSING_MODE,用于控制程序是打开一个视频文件还是打开默认摄像头。在主函数中,根据该宏的取值,程序尝试打开视频文件,或启动默认摄像头。如果无法打开视频文件或摄像头,程序会输出错误信息并终止。
接下里,我们创建了一个名为"Video Processing"的窗口,并设置了窗口的大小。程序随后进入一个无限循环,在每次循环中读取一帧视频,并显示该帧。如果无法读取更多帧,则跳出循环。另外,程序还监听键盘输入,如果用户按下了'q'键,则退出循环。最后,我们释放了VideoCapture对象使用的资源,并销毁所有窗口。
#include <opencv2/opencv.hpp>
using namespace cv;
#include <iostream>
using namespace std;
#define VIDEO_PROCESSING_MODE 0
int main()
{
#if VIDEO_PROCESSING_MODE == 0
// 打开视频文件
VideoCapture cap("DigitalHuman.mp4");
if (!cap.isOpened())
{
cout << "Can not open or find the video file" << endl;
return -1;
}
#else
// 打开摄像头
VideoCapture cap(0);
if (!cap.isOpened())
{
cout << "Can not open or find the camera" << endl;
return -1;
}
#endif
namedWindow("Video Processing", WINDOW_NORMAL);
resizeWindow("Video Processing", 640, 360);
Mat frame;
// 获取视频的帧率
int nFPS = (int)cap.get(CAP_PROP_FPS);
while (true)
{
// 读取一帧
if (!cap.read(frame))
{
// 如果无法读取更多帧,跳出循环
break;
}
// 显示当前帧
imshow("Video Processing", frame);
// 按q键可以退出
if (waitKey(1000 / nFPS) == 'q')
{
break;
}
}
// 释放资源
cap.release();
destroyAllWindows();
return 0;
}
执行上面的代码,运行效果可参考下图。