在OpenCV中读写视频与读写图像非常相似。视频就是一系列通常被称为帧的图像。所以,你需要做的就是循环播放视频序列中的所有帧,然后一次处理一帧。在这篇文章中,我们将演示如何从一个文件、一个图像序列和一个网络摄像头读取、显示和写入视频。我们还将研究流程中可能发生的一些错误,并帮助理解如何解决这些错误。
让我们先看一下读取视频文件的代码示例。它本质上包含了从磁盘读取视频并显示它的功能。随着您的深入,我们将详细讨论这个实现中使用的函数。
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37import cv2 # Create a video capture object, in this case we are reading the video from a file vid_capture = cv2.VideoCapture('Resources/Cars.mp4') if (vid_capture.isOpened() == False): print("Error opening the video file") # Read fps and frame count else: # Get frame rate information # You can replace 5 with CAP_PROP_FPS as well, they are enumerations fps = vid_capture.get(5) print('Frames per second : ', fps,'FPS') # Get frame count # You can replace 7 with CAP_PROP_FRAME_COUNT as well, they are enumerations frame_count = vid_capture.get(7) print('Frame count : ', frame_count) while(vid_capture.isOpened()): # vid_capture.read() methods returns a tuple, first element is a bool # and the second is frame ret, frame = vid_capture.read() if ret == True: cv2.imshow('Frame',frame) # 20 is in milliseconds, try to increase the value, say 50 and observe key = cv2.waitKey(20) if key == ord('q'): break else: break # Release the video capture object vid_capture.release() cv2.destroyAllWindows()
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72// Include Libraries #include<opencv2/opencv.hpp> #include<iostream> // Namespace to nullify use of cv::function(); syntax using namespace std; using namespace cv; int main() { // initialize a video capture object VideoCapture vid_capture("Resources/Cars.mp4"); // Print error message if the stream is invalid if (!vid_capture.isOpened()) { cout << "Error opening video stream or file" << endl; } else { // Obtain fps and frame count by get() method and print // You can replace 5 with CAP_PROP_FPS as well, they are enumerations int fps = vid_capture.get(5); cout << "Frames per second :" << fps; // Obtain frame_count using opencv built in frame count reading method // You can replace 7 with CAP_PROP_FRAME_COUNT as well, they are enumerations int frame_count = vid_capture.get(7); cout << " Frame count :" << frame_count; } // Read the frames to the last frame while (vid_capture.isOpened()) { // Initialise frame matrix Mat frame; // Initialize a boolean to check if frames are there or not bool isSuccess = vid_capture.read(frame); // If frames are present, show it if(isSuccess == true) { //display frames imshow("Frame", frame); } // If frames are not there, close it if (isSuccess == false) { cout << "Video camera is disconnected" << endl; break; } //wait 20 ms between successive frames and break the loop if key q is pressed int key = waitKey(20); if (key == 'q') { cout << "q key is pressed by the user. Stopping the video" << endl; break; } } // Release the video capture object vid_capture.release(); destroyAllWindows(); return 0; }
以下是我们将在这篇博文中讨论的OpenCV视频I/O的主要功能:
- cv2.videoccapture—创建一个视频捕获对象,它将帮助流或显示视频。
- cv2.VideoWriter -将输出的视频保存到一个目录中。
- 此外,我们还讨论了其他需要的函数,如cv2.imshow(), cv2.waitKey()和get()方法,用于读取视频元数据,如帧高度,宽度,帧数等。
在这个例子中,你将读取上面的视频(’ Cars.mp4 ')并显示它
现在让我们从它开始。
首先,我们导入OpenCV
库。请注意,对于c++,你通常会使用cv::function()
,但因为我们选择使用cv命名空间(使用cv命名空间),所以我们可以直接访问OpenCV函数,而不需要在函数名前添加cv::。
C++
1
2
3
4
5
6// Include Libraries #include<opencv2/opencv.hpp> #include<iostream> using namespace std; using namespace cv;
从文件中读取视频
面的代码块使用VideoCapture()
类来创建VideoCapture
对象,然后我们将使用它来读取视频文件。使用这个类的语法如下所示:
1
2VideoCapture(path, apiPreference)
第一个参数是视频文件的文件名/路径。第二个参数是一个可选参数,指示API首选项。与这个可选参数相关的一些选项将在下面进一步讨论。要了解更多关于apiPreference,请访问官方文档链接videocaptureapi。
Python
1
2
3# Create a video capture object, in this case we are reading the video from a file vid_capture = cv2.VideoCapture('Resources/Cars.mp4')
C++
1
2
3# Create a video capture object, in this case we are reading the video from a file VideoCapture vid_capture("Resources/Cars.mp4");
现在我们有了一个视频捕获对象,我们可以使用isopen()
方法来确认视频文件已成功打开。isopen()
方法返回一个布尔值,指示视频流是否有效。否则,您将得到一条错误消息。错误消息可以包含许多内容。其中之一就是整个视频都损坏了,或者一些帧损坏了。假设视频文件已成功打开,我们可以使用get()
方法检索与视频流相关联的重要元数据。请注意,此方法不适用于网络摄像机。get()方法从这里记录的选项枚举列表中获取一个参数。在下面的例子中,我们提供了数值5和7,它们对应于帧速率(CAP_PROP_FPS)和帧计数(CAP_PROP_FRAME_COUNT)。可以提供数值或名称。
Python
1
2
3
4
5
6
7
8
9
10
11
12if (vid_capture.isOpened() == False): print("Error opening the video file") else: # Get frame rate information fps = int(vid_capture.get(5)) print("Frame Rate : ",fps,"frames per second") # Get frame count frame_count = vid_capture.get(7) print("Frame count : ", frame_count)
C++
1
2
3
4
5
6
7
8
9
10
11
12
13if (!vid_capture.isOpened()) { cout << "Error opening video stream or file" << endl; } else { // Obtain fps and frame count by get() method and print int fps = vid_capture.get(5): cout << "Frames per second :" << fps; frame_count = vid_capture.get(7); cout << "Frame count :" << frame_count; }
在获取与视频文件相关联的所需元数据之后,我们现在就可以从文件中读取每个图像帧了。这是通过创建一个循环并使用vid_capture.read()
方法从视频流中每次读取一帧来实现的。
vid_capture.read()
方法返回一个元组,其中第一个元素是布尔值,下一个元素是实际的视频帧。当第一个元素为True
时,它表示视频流包含一个要读取的帧。
如果有要读取的帧,那么可以使用imshow()
在窗口中显示当前帧,否则退出循环。注意,您还使用waitKey()
函数在视频帧之间暂停20
毫秒。调用waitKey()
函数可以监视键盘以获取用户输入。例如,在本例中,如果用户按下' q '
键,则退出循环。
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14while(vid_capture.isOpened()): # vCapture.read() methods returns a tuple, first element is a bool # and the second is frame ret, frame = vid_capture.read() if ret == True: cv2.imshow('Frame',frame) k = cv2.waitKey(20) # 113 is ASCII code for q key if k == 113: break else: break
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29while (vid_capture.isOpened()) { // Initialize frame matrix Mat frame; // Initialize a boolean to check if frames are there or not bool isSuccess = vid_capture.read(frame); // If frames are present, show it if(isSuccess == true) { //display frames imshow("Frame", frame); } // If frames are not there, close it if (isSuccess == false) { cout << "Video camera is disconnected" << endl; break; } //wait 20 ms between successive frames and break the loop if key q is pressed int key = waitKey(20); if (key == 'q') { cout << "q key is pressed by the user. Stopping the video" << endl; break; } }
一旦视频流被完全处理或用户提前退出循环,你释放视频捕获对象(vid_capture)
并关闭窗口,使用以下代码:
Python
1
2
3
4# Release the objects vid_capture.release() cv2.destroyAllWindows()
C++
1
2
3
4// Release video capture object vid_capture.release(); destroyAllWindows();
读取图像序列
处理来自图像序列的图像帧与处理来自视频流的图像帧非常相似。只需指定正在读取的图像文件。
在下面的例子中,
- 继续使用视频捕获对象
- 但是,您只需指定一个图像序列,而不是指定一个视频文件
- 使用下面所示的符号(Cars%04d.jpg),其中%04d表示四位数序列命名约定(例如Cars0001.jpg、Cars0002.jpg、Cars0003.jpg等)。
- 如果您指定了" Race_Cars_%02d.jpg ",那么您将查找的文件的形式:
1
2(Race_Cars_01.jpg, Race_Cars_02.jpg, Race_Cars_03.jpg, etc…).
第一个示例中描述的所有其他代码都是相同的。
Python
1
2vid_capture = cv2.VideoCapture('Resources/Image_sequence/Cars%04d.jpg')
C++
1
2VideoCapture vid_capture("Resources/Image_sequence/Cars%04d.jpg");
从摄像头读取视频
从网络摄像机读取视频流也非常类似于上面讨论的例子。那怎么可能?这都要感谢OpenCV
中视频捕获类的灵活性,它有几个方便接受不同输入参数的重载函数。不需要为视频文件或图像序列指定源位置,只需给出一个视频捕获设备索引,如下所示。
- 如果你的系统有一个内置的网络摄像头,那么摄像头的设备索引将是
“0”
。 - 如果你有多个摄像头连接到你的系统,那么与每个额外摄像头相关联的设备索引将递增(例如
1、2、3
等)。
Python
1
2vid_capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)
C++
1
2VideoCapture vid_capture(0);
您可能想知道标志CAP_DSHOW
。这是一个可选参数,因此不是必需的。CAP_DSHOW
只是另一个视频捕获API
首选项,它是directshow
通过视频输入的简称。
写视频
现在让我们来看看如何写视频。就像视频读取一样,我们可以编写来自任何源(视频文件、图像序列或网络摄像头)的视频。写入视频文件:
- 使用get()方法检索图像帧的高度和宽度。
- 初始化一个视频捕获对象(如前面所讨论的),使用前面描述的任何源代码将视频流读入内存。
- 创建一个视频写入器对象。
- 使用视频写入器对象将视频流保存到磁盘。
继续我们的运行示例,让我们首先使用get()
方法来获取视频帧的宽度和高度。
Python
1
2
3
4
5
6# Obtain frame size information using get() method frame_width = int(vid_capture.get(3)) frame_height = int(vid_capture.get(4)) frame_size = (frame_width,frame_height) fps = 20
C++
1
2
3
4
5
6// Obtain frame size information using get() method Int frame_width = static_cast<int>(vid_capture.get(3)); int frame_height = static_cast<int>(vid_capture.get(4)); Size frame_size(frame_width, frame_height); int fps = 20;
如前所述,videoccapture()类的get()方法需要:
- 来自枚举列表的单个参数,它允许您检索与视频帧关联的各种元数据。
可用的元数据非常广泛,可以在这里找到。
- 在本例中,您通过指定
3 (CAP_PROP_FRAME_WIDTH)和4 (CAP_PROP_FRAME_HEIGHT)
来检索帧的宽度和高度。当将视频文件写入磁盘时,您将在下面进一步使用这些尺寸。
为了编写视频文件,首先需要从VideoWriter()
类中创建一个视频编写器对象,如下面的代码所示。
以下是VideoWriter()
的语法:
1
2VideoWriter(filename, apiPreference, fourcc, fps, frameSize[, isColor]
VideoWriter()类接受以下参数:
- filename:输出视频文件的路径名
- apiPreference: API后端标识符
- fourcc: 4个字符的编解码器代码,用于压缩帧(fourcc)
- fps:创建的视频流的帧率
- frame_size:视频帧的大小
- isColor:如果不是0,编码器将期望并编码彩色帧。否则它将与灰度帧工作(该标志目前仅在Windows上支持)。
下面的代码创建了视频写入器对象,输出自VideoWriter()
类。一个特殊的方便函数用于检索四个字符的编解码器,需要作为视频编写器对象cv2的第二个参数。
- VideoWriter_fourcc(‘M’, ‘J’, ‘P’, ‘G’) in Python.
- VideoWriter::fourcc(‘M’, ‘J’, ‘P’, ‘G’) in C++.
视频编解码器指定视频流如何压缩。它将未压缩的视频转换为压缩格式,反之亦然。创建AVI
或MP4
格式,使用以下的fourcc
规格:
1
2
3AVI: cv2.VideoWriter_fourcc('M','J','P','G') MP4: cv2.VideoWriter_fourcc(*'XVID')
接下来的两个输入参数指定帧速率(FPS)和帧大小(宽度、高度)。
Python
1
2
3# Initialize video writer object output = cv2.VideoWriter('Resources/output_video_from_file.avi', cv2.VideoWriter_fourcc('M','J','P','G'), 20, frame_size)
C++
1
2
3//Initialize video writer object VideoWriter output("Resources/output.avi", VideoWriter::fourcc('M', 'J', 'P', 'G'),frames_per_second, frame_size);
在您已经创建了一个视频写入器对象,使用它将视频文件写入磁盘,一次一帧,如下面的代码所示。在这里,您正在以每秒20帧的速度将一个AVI视频文件写入磁盘。请注意我们是如何将前面的例子简化为循环的。
Python
1
2
3
4
5
6
7
8
9
10
11
12
13while(vid_capture.isOpened()): # vid_capture.read() methods returns a tuple, first element is a bool # and the second is frame ret, frame = vid_capture.read() if ret == True: # Write the frame to the output files output.write(frame) else: print(‘Stream disconnected’) break
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36while (vid_capture.isOpened()) { // Initialize frame matrix Mat frame; // Initialize a boolean to check if frames are there or not bool isSuccess = vid_capture.read(frame); // If frames are not there, close it if (isSuccess == false) { cout << "Stream disconnected" << endl; break; } // If frames are present if(isSuccess == true) { //display frames output.write(frame); // display frames imshow("Frame", frame); // wait for 20 ms between successive frames and break // the loop if key q is pressed int key = waitKey(20); if (key == ‘q’) { cout << "Key q key is pressed by the user. Stopping the video" << endl; break; } } }
最后,在下面的代码中,释放视频捕获和视频编写对象。
Python
1
2
3
4# Release the objects vid_capture.release() output.release()
C++
1
2
3
4// Release the objects vid_capture.release(); output.release();
最后
以上就是贤惠御姐最近收集整理的关于Opencv基础 (二):使用OpenCV读取和写入视频的全部内容,更多相关Opencv基础内容请搜索靠谱客的其他文章。
发表评论 取消回复