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

    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;
    }
    
    • 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

    下面是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')
    
    • 1
    • 2

    C++

    # Create a video capture object, in this case we are reading the video from a file
    VideoCapture vid_capture("Resources/Cars.mp4");
    
    • 1
    • 2

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在检索到与视频文件关联的所需元数据后,我们现在准备从文件中读取每个图像帧。这是通过使用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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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;
            }
        }
    
    • 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

    一旦视频流完全处理或用户过早退出循环,您将使用以下代码释放视频捕获对象(vid_capture)并关闭窗口:
    Python

    # Release the objects
    vid_capture.release()
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3

    C++

    // Release video capture object
    vid_capture.release();
    destroyAllWindows();
    
    • 1
    • 2
    • 3

    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')
    
    • 1

    C++

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

    1.3 从网络摄像头读取

    从网络摄像头阅读视频

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

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

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

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

    C++

    VideoCapture vid_capture(0);
    
    • 1

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如前所述,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)
    
    • 1
    • 2

    C++

    //Initialize video writer object
    VideoWriter output("Resources/output.avi", VideoWriter::fourcc('M', 'J', 'P', 'G'),frames_per_second, frame_size);
    
    • 1
    • 2

    现在已经创建了一个视频写入器对象,请使用它将视频文件写入磁盘,一次一帧。在这里,您正在以每秒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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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;
                      }
            }
     }
    
    • 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

    最后,在下面的代码中,释放视频捕获和视频写入器对象。
    Python

    # Release the objects
    vid_capture.release()
    output.release()
    
    • 1
    • 2
    • 3

    C++

    // Release the objects
    vid_capture.release();
    output.release();
    
    • 1
    • 2
    • 3

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    PDF转PPT软件哪个好?这几款软件亲测实用
    WPF-封装自定义雷达图控件
    ubuntu环境变量配置
    DSPE-PEG-Thiol,DSPE-PEG-SH(MV:2000),磷脂-聚乙二醇-巯基低温储存
    NodeRed系列—创建mqtt broker(mqtt服务器),并使用mqttx进行消息发送验证
    如何创建自己的小程序?
    MySQL-(6)
    【面试经典150 | 算术平方根】
    大模型引领未来:探索其在多个领域的深度应用与无限可能【第二章、金融领域:大模型重塑金融生态】
    狂神说MybatisPlus学习笔记
  • 原文地址:https://blog.csdn.net/weixin_42010722/article/details/128148242