OpenCV入门(C++/Python)- 使用OpenCV读取和编写视频(二)

简介: OpenCV入门(C++/Python)- 使用OpenCV读取和编写视频(二)

在OpenCV中读取和写入视频与读取和写入图像非常相似。视频只不过是一系列通常被称为帧的图像。因此,您只需要在视频序列中循环所有帧,然后一次处理一帧。在这篇文章中,我们将演示如何从文件、图像序列和网络摄像头中读取、显示和写入视频。我们还将调查流程中可能出现的一些错误,并帮助了解如何解决这些错误。


1.读取视频


让我们先浏览一下读取视频文件的代码示例。这本质上包含从磁盘读取视频并显示它的功能。随着您进一步推进,我们将详细讨论此实现中使用的功能。


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<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.VideoCapture - 创建一个视频捕获对象,这将有助于流式传输或显示视频。

cv2.VideoWriter - 将输出视频保存到目录中。

此外,还有其他需要的功能,如cv2.imshow()、cv2.waitKey()和get()方法,该方法用于读取视频元数据,如帧高度、宽度、fps等。


1.1 从一个文件中读取


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


VideoCapture(path, apiPreference)


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");


isOpened()


现在我们有一个视频捕获对象,我们可以使用isOpened()方法来确认视频文件是否已成功打开。isOpened()方法返回一个布尔值,指示视频流是否有效。否则,您将收到一条错误消息。错误信息可能意味着很多事情。其中之一是整个视频被损坏,或者一些帧被损坏了。

假设视频文件已成功打开,我们可以使用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()函数在视频帧之间暂停20ms。调用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();


1.2 从图像序列中读取


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

在下面的示例中,使用视频捕获对象


但是,您只需指定图像序列,而不是指定视频文件

使用下图所示的符号(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等…)。

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

Python


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


C++


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


1.3 从网络摄像头读取


从网络摄像头阅读视频


从网络摄像头读取视频流也与上面讨论的示例非常相似。这怎么可能?这都要归功于OpenCV中视频捕获类的灵活性,为了方便起见,该类有几个重载功能,可以接受不同的输入参数。与其为视频文件或图像序列指定源位置,您只需给出视频捕获设备索引,如下所示。


如果您的系统有一个内置的网络摄像头,那么相机的设备索引将是“0”。


如果您的系统连接了多个摄像头,则与每个附加相机关联的设备索引会增加(例如1、2等)。


Python

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

C++

VideoCapture vid_capture(0);


2.写入视频


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


  • 使用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;


如前所述,VideoCapture()类的get()方法需要:


枚举列表中的单个参数,允许您检索与视频帧关联的各种元数据。

在这里下,通过指定3(CAP_PROP_FRAME_WIDTH)和4(CAP_PROP_FRAME_HEIGHT)来检索帧宽度和高度。在将视频文件写入磁盘时,您将在下面使用这些维度。


要编写视频文件,您需要首先从VideoWriter()类创建一个视频写入对象,如下代码所示。


VideoWriter():


VideoWriter(filename, apiPreference, fourcc, fps, frameSize[, isColor])


VideoWriter()类接受以下参数:


文件名:输出视频文件的路径名

apiPreference:API后端标识符

Fourcc:编解码器的4个字符代码,用于压缩

Fps:创建的视频流的帧速率

Frame_size:视频帧的大小

isColor:如果不是零,编码器将期望并编码颜色帧。否则,它将适用于灰度帧(该标志目前仅在Windows上受支持)。


以下代码创建视频编写器对象,从VideoWriter()类输出。使用特殊的便利功能来检索四字符编解码器,这是视频编写器对象cv2的第二个参数。


Python:VideoWriter_fourcc(‘M’、‘J’、‘P’、‘G’)

C++:VideoWriter::fourcc(‘M’, ‘J’, ‘P’, ‘G’)


视频编解码器指定了视频流是如何压缩的。它将未压缩的视频转换为压缩格式,反之亦然。要创建AVI或MP4格式:


  • 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


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


3.读取或写入视频时可能面临的错误

3.1 视频读取


在读取帧时,如果路径错误或文件损坏或帧缺失,它可能会抛出错误。这就是为什么我们在while循环中使用if语句。如果ret == True,可以在行中看到。这样,只有当框架存在时,它才会处理它。以下是在这种情况下观察到的错误日志示例。它不是完整的日志,只包含关键参数。


cap_gstreamer.cpp:890: error: (-2) GStreamer: unable to start pipeline  in function
• 1


3.2 错误的路径:


当您提供视频的错误路径时,它不会使用VideoCapture()类显示任何错误或警告。当您尝试对视频帧进行任何操作时,会出现这些问题。为此,您可以使用一个简单的if块来检查您是否像我们在示例中所做的那样阅读了视频文件。这应该打印以下消息。


Error opening the video file
• 1


3.3 视频写作


在此步骤中,可能会出现各种错误。最常见的是帧大小错误和api首选项错误。如果帧大小与视频不相似,那么即使我们在输出目录中收到一个视频文件,它也会是空白的。


如果您正在使用NumPy形状方法来检索帧大小,请记住反向输出,因为OpenCV将返回高度x宽度x通道。如

果它抛出api首选项错误,我们可能需要在VideoCapture()参数中传递CAP_ANY标志。这可以在网络摄像头示例中看到,我们使用CAP_DHOW来避免生成警告。


以下是错误日志的示例:


当CAP_DSHOW未通过时:


[WARN:0]...cap_msmf.cpp(438) …. terminating async callback
• 1


当帧尺寸不正确时:


cv2.error: OpenCV(4.5.2) :-1: error: (-5:Bad argument) in   function 'VideoWriter'
> Overload resolution failed:
>  - Can't parse 'frameSize'. Sequence item with index  0 has a wrong type
>  - VideoWriter() missing required argument 'frameSize' (pos 5)
>  - VideoWriter() missing required argument 'params' (pos 5)
>  - VideoWriter() missing required argument 'frameSize' (pos 5)VideoWriter()缺少必需的参数'frameSize'(pos 5)
相关文章
|
6天前
|
数据采集 存储 XML
Python爬虫定义入门知识
Python爬虫是用于自动化抓取互联网数据的程序。其基本概念包括爬虫、请求、响应和解析。常用库有Requests、BeautifulSoup、Scrapy和Selenium。工作流程包括发送请求、接收响应、解析数据和存储数据。注意事项包括遵守Robots协议、避免过度请求、处理异常和确保数据合法性。Python爬虫强大而灵活,但使用时需遵守法律法规。
|
7天前
|
Python
深入理解Python装饰器:从入门到实践####
本文旨在通过简明扼要的方式,为读者揭开Python装饰器的神秘面纱,从基本概念、工作原理到实际应用场景进行全面解析。不同于常规的摘要仅概述内容概要,本文将直接以一段精炼代码示例开篇,展示装饰器如何优雅地增强函数功能,激发读者探索兴趣,随后深入探讨其背后的机制与高级用法。 ####
36 11
|
3天前
|
机器学习/深度学习 人工智能 TensorFlow
人工智能浪潮下的自我修养:从Python编程入门到深度学习实践
【10月更文挑战第39天】本文旨在为初学者提供一条清晰的道路,从Python基础语法的掌握到深度学习领域的探索。我们将通过简明扼要的语言和实际代码示例,引导读者逐步构建起对人工智能技术的理解和应用能力。文章不仅涵盖Python编程的基础,还将深入探讨深度学习的核心概念、工具和实战技巧,帮助读者在AI的浪潮中找到自己的位置。
|
3天前
|
机器学习/深度学习 数据挖掘 Python
Python编程入门——从零开始构建你的第一个程序
【10月更文挑战第39天】本文将带你走进Python的世界,通过简单易懂的语言和实际的代码示例,让你快速掌握Python的基础语法。无论你是编程新手还是想学习新语言的老手,这篇文章都能为你提供有价值的信息。我们将从变量、数据类型、控制结构等基本概念入手,逐步过渡到函数、模块等高级特性,最后通过一个综合示例来巩固所学知识。让我们一起开启Python编程之旅吧!
|
3天前
|
存储 Python
Python编程入门:打造你的第一个程序
【10月更文挑战第39天】在数字时代的浪潮中,掌握编程技能如同掌握了一门新时代的语言。本文将引导你步入Python编程的奇妙世界,从零基础出发,一步步构建你的第一个程序。我们将探索编程的基本概念,通过简单示例理解变量、数据类型和控制结构,最终实现一个简单的猜数字游戏。这不仅是一段代码的旅程,更是逻辑思维和问题解决能力的锻炼之旅。准备好了吗?让我们开始吧!
|
9天前
|
Java 测试技术 持续交付
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
本文重点讲解如何搭建App自动化测试框架的思路,而非完整源码。主要内容包括实现目的、框架设计、环境依赖和框架的主要组成部分。适用于初学者,旨在帮助其快速掌握App自动化测试的基本技能。文中详细介绍了从需求分析到技术栈选择,再到具体模块的封装与实现,包括登录、截图、日志、测试报告和邮件服务等。同时提供了运行效果的展示,便于理解和实践。
42 4
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
|
3天前
|
设计模式 缓存 开发框架
Python中的装饰器:从入门到实践####
本文深入探讨了Python中装饰器的工作原理与应用,通过具体案例展示了如何利用装饰器增强函数功能、提高代码复用性和可读性。读者将学习到装饰器的基本概念、实现方法及其在实际项目开发中的实用技巧。 ####
15 3
|
6天前
|
机器学习/深度学习 数据采集 数据可视化
Python在数据科学中的应用:从入门到实践
本文旨在为读者提供一个Python在数据科学领域应用的全面概览。我们将从Python的基础语法开始,逐步深入到数据处理、分析和可视化的高级技术。文章不仅涵盖了Python中常用的数据科学库,如NumPy、Pandas和Matplotlib,还探讨了机器学习库Scikit-learn的使用。通过实际案例分析,本文将展示如何利用Python进行数据清洗、特征工程、模型训练和结果评估。此外,我们还将探讨Python在大数据处理中的应用,以及如何通过集成学习和深度学习技术来提升数据分析的准确性和效率。
|
5天前
|
机器学习/深度学习 数据挖掘 开发者
Python编程入门:理解基础语法与编写第一个程序
【10月更文挑战第37天】本文旨在为初学者提供Python编程的初步了解,通过简明的语言和直观的例子,引导读者掌握Python的基础语法,并完成一个简单的程序。我们将从变量、数据类型到控制结构,逐步展开讲解,确保即使是编程新手也能轻松跟上。文章末尾附有完整代码示例,供读者参考和实践。
|
5天前
|
人工智能 数据挖掘 程序员
Python编程入门:从零到英雄
【10月更文挑战第37天】本文将引导你走进Python编程的世界,无论你是初学者还是有一定基础的开发者,都能从中受益。我们将从最基础的语法开始讲解,逐步深入到更复杂的主题,如数据结构、面向对象编程和网络编程等。通过本文的学习,你将能够编写出自己的Python程序,实现各种功能。让我们一起踏上Python编程之旅吧!