在OpenCV中读写视频与读写图像非常相似。视频就是一系列通常被称为帧的图像。所以,你需要做的就是循环播放视频序列中的所有帧,然后一次处理一帧。在这篇文章中,我们将演示如何从一个文件、一个图像序列和一个网络摄像头读取、显示和写入视频。我们还将研究流程中可能发生的一些错误,并帮助理解如何解决这些错误。
让我们先看一下读取视频文件的代码示例。它本质上包含了从磁盘读取视频并显示它的功能。随着您的深入,我们将详细讨论这个实现中使用的函数。
import 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()
// Include Libraries
#include
#include
// 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的主要功能:
在这个例子中,你将读取上面的视频(’ Cars.mp4 ')并显示它
现在让我们从它开始。
首先,我们导入OpenCV
库。请注意,对于c++,你通常会使用cv::function()
,但因为我们选择使用cv命名空间(使用cv命名空间),所以我们可以直接访问OpenCV函数,而不需要在函数名前添加cv::。
// Include Libraries
#include
#include
using namespace std;
using namespace cv;
面的代码块使用VideoCapture()
类来创建VideoCapture
对象,然后我们将使用它来读取视频文件。使用这个类的语法如下所示:
VideoCapture(path, apiPreference)
第一个参数是视频文件的文件名/路径。第二个参数是一个可选参数,指示API首选项。与这个可选参数相关的一些选项将在下面进一步讨论。要了解更多关于apiPreference,请访问官方文档链接videocaptureapi。
# Create a video capture object, in this case we are reading the video from a file
vid_capture = cv2.VideoCapture('Resources/Cars.mp4')
# 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)。可以提供数值或名称。
if (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)
if (!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 '
键,则退出循环。
while(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
while (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)
并关闭窗口,使用以下代码:
# Release the objects
vid_capture.release()
cv2.destroyAllWindows()
// Release video capture object
vid_capture.release();
destroyAllWindows();
处理来自图像序列的图像帧与处理来自视频流的图像帧非常相似。只需指定正在读取的图像文件。
在下面的例子中,
(Race_Cars_01.jpg, Race_Cars_02.jpg, Race_Cars_03.jpg, etc…).
第一个示例中描述的所有其他代码都是相同的。
Python
vid_capture = cv2.VideoCapture('Resources/Image_sequence/Cars%04d.jpg')
VideoCapture vid_capture("Resources/Image_sequence/Cars%04d.jpg");
从网络摄像机读取视频流也非常类似于上面讨论的例子。那怎么可能?这都要感谢OpenCV
中视频捕获类的灵活性,它有几个方便接受不同输入参数的重载函数。不需要为视频文件或图像序列指定源位置,只需给出一个视频捕获设备索引,如下所示。
“0”
。1、2、3
等)。vid_capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)
VideoCapture vid_capture(0);
您可能想知道标志CAP_DSHOW
。这是一个可选参数,因此不是必需的。CAP_DSHOW
只是另一个视频捕获API
首选项,它是directshow
通过视频输入的简称。
现在让我们来看看如何写视频。就像视频读取一样,我们可以编写来自任何源(视频文件、图像序列或网络摄像头)的视频。写入视频文件:
继续我们的运行示例,让我们首先使用get()
方法来获取视频帧的宽度和高度。
Python
# 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
// 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()
的语法:
VideoWriter(filename, apiPreference, fourcc, fps, frameSize[, isColor]
VideoWriter()类接受以下参数:
下面的代码创建了视频写入器对象,输出自VideoWriter()
类。一个特殊的方便函数用于检索四个字符的编解码器,需要作为视频编写器对象cv2的第二个参数。
视频编解码器指定视频流如何压缩。它将未压缩的视频转换为压缩格式,反之亦然。创建AVI
或MP4
格式,使用以下的fourcc
规格:
AVI: cv2.VideoWriter_fourcc('M','J','P','G')
MP4: cv2.VideoWriter_fourcc(*'XVID')
接下来的两个输入参数指定帧速率(FPS)和帧大小(宽度、高度)。
Python
# Initialize video writer object
output = cv2.VideoWriter('Resources/output_video_from_file.avi', cv2.VideoWriter_fourcc('M','J','P','G'), 20, frame_size)
C++
//Initialize video writer object
VideoWriter output("Resources/output.avi", VideoWriter::fourcc('M', 'J', 'P', 'G'),frames_per_second, frame_size);
在您已经创建了一个视频写入器对象,使用它将视频文件写入磁盘,一次一帧,如下面的代码所示。在这里,您正在以每秒20帧的速度将一个AVI视频文件写入磁盘。请注意我们是如何将前面的例子简化为循环的。
Python
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:
# Write the frame to the output files
output.write(frame)
else:
print(‘Stream disconnected’)
break
C++
while (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
# Release the objects
vid_capture.release()
output.release()
C++
// Release the objects
vid_capture.release();
output.release();