Opencv基础 (二):使用OpenCV读取和写入视频

Opencv基础 (二):使用OpenCV读取和写入视频_第1张图片
在OpenCV中读写视频与读写图像非常相似。视频就是一系列通常被称为帧的图像。所以,你需要做的就是循环播放视频序列中的所有帧,然后一次处理一帧。在这篇文章中,我们将演示如何从一个文件、一个图像序列和一个网络摄像头读取、显示和写入视频。我们还将研究流程中可能发生的一些错误,并帮助理解如何解决这些错误。

让我们先看一下读取视频文件的代码示例。它本质上包含了从磁盘读取视频并显示它的功能。随着您的深入,我们将详细讨论这个实现中使用的函数。

Python

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()

C++

// 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的主要功能:

  • cv2.videoccapture—创建一个视频捕获对象,它将帮助流或显示视频。
  • cv2.VideoWriter -将输出的视频保存到一个目录中。
  • 此外,我们还讨论了其他需要的函数,如cv2.imshow(), cv2.waitKey()和get()方法,用于读取视频元数据,如帧高度,宽度,帧数等。


在这个例子中,你将读取上面的视频(’ Cars.mp4 ')并显示它

现在让我们从它开始。

首先,我们导入OpenCV库。请注意,对于c++,你通常会使用cv::function(),但因为我们选择使用cv命名空间(使用cv命名空间),所以我们可以直接访问OpenCV函数,而不需要在函数名前添加cv::。

C++

// Include Libraries
#include
#include
using namespace std;
using namespace cv;

从文件中读取视频

面的代码块使用VideoCapture()类来创建VideoCapture对象,然后我们将使用它来读取视频文件。使用这个类的语法如下所示:

VideoCapture(path, apiPreference)

第一个参数是视频文件的文件名/路径。第二个参数是一个可选参数,指示API首选项。与这个可选参数相关的一些选项将在下面进一步讨论。要了解更多关于apiPreference,请访问官方文档链接videocaptureapi。

Python

# Create a video capture object, in this case we are reading the video from a file
vid_capture = cv2.VideoCapture('Resources/Cars.mp4')

C++

# 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

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)

C++

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 '键,则退出循环。

Python

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

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 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

# Release the objects
vid_capture.release()
cv2.destroyAllWindows()

C++

// Release video capture object
vid_capture.release();
destroyAllWindows();

读取图像序列

处理来自图像序列的图像帧与处理来自视频流的图像帧非常相似。只需指定正在读取的图像文件。

在下面的例子中,

  • 继续使用视频捕获对象
  • 但是,您只需指定一个图像序列,而不是指定一个视频文件
    • 使用下面所示的符号(Cars%04d.jpg),其中%04d表示四位数序列命名约定(例如Cars0001.jpg、Cars0002.jpg、Cars0003.jpg等)。
    • 如果您指定了" Race_Cars_%02d.jpg ",那么您将查找的文件的形式:
(Race_Cars_01.jpg, Race_Cars_02.jpg, Race_Cars_03.jpg, etc…). 

第一个示例中描述的所有其他代码都是相同的。
Python

vid_capture = cv2.VideoCapture('Resources/Image_sequence/Cars%04d.jpg')

C++

VideoCapture vid_capture("Resources/Image_sequence/Cars%04d.jpg");

从摄像头读取视频

从网络摄像机读取视频流也非常类似于上面讨论的例子。那怎么可能?这都要感谢OpenCV中视频捕获类的灵活性,它有几个方便接受不同输入参数的重载函数。不需要为视频文件或图像序列指定源位置,只需给出一个视频捕获设备索引,如下所示。

  • 如果你的系统有一个内置的网络摄像头,那么摄像头的设备索引将是“0”
  • 如果你有多个摄像头连接到你的系统,那么与每个额外摄像头相关联的设备索引将递增(例如1、2、3等)。

Python

vid_capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)

C++

VideoCapture vid_capture(0);

您可能想知道标志CAP_DSHOW。这是一个可选参数,因此不是必需的。CAP_DSHOW只是另一个视频捕获API首选项,它是directshow通过视频输入的简称。

写视频

现在让我们来看看如何写视频。就像视频读取一样,我们可以编写来自任何源(视频文件、图像序列或网络摄像头)的视频。写入视频文件:

  • 使用get()方法检索图像帧的高度和宽度。
  • 初始化一个视频捕获对象(如前面所讨论的),使用前面描述的任何源代码将视频流读入内存。
  • 创建一个视频写入器对象。
  • 使用视频写入器对象将视频流保存到磁盘。

继续我们的运行示例,让我们首先使用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

C++

// 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()类接受以下参数:

  • 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++.

视频编解码器指定视频流如何压缩。它将未压缩的视频转换为压缩格式,反之亦然。创建AVIMP4格式,使用以下的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();

你可能感兴趣的:(opencv,opencv,音视频,计算机视觉)