使用 OpenCV 读写视频

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

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

Python

import cv2

# Create a video capture object, in this case we are reading the video from a file
vid_capture = cv2.VideoCapture('person_test.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()

这些是我们将在这篇博文中讨论的 OpenCV 视频 I/O 中的主要功能:

  1. cv2.VideoCapture创建一个视频捕获对象,这将有助于流式传输或显示视频。
  2. cv2.VideoWriter将输出视频保存到目录。
  3. 此外,我们还讨论了其他需要的函数cv2.imshow()cv2.waitKey()以及get()用于读取帧高度、宽度、fps 等视频元数据的方法。

从文件中读取视频

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

VideoCapture(path, apiPreference)

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

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

现在我们有了一个视频捕获对象,我们可以使用该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")
# 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)

在检索到与视频文件关联的所需元数据后,我们现在准备从文件中读取每个图像帧。这是通过创建一个循环并使用该vid_capture.read()方法从视频流中一次读取一帧来完成的。 

vid_capture.read()方法返回一个元组,其中第一个元素是布尔值,下一个元素是实际的视频帧。当第一个元素为 True 时,表示视频流包含要读取的帧。 

如果有要读取的帧,则可以使用imshow()在窗口中显示当前帧,否则退出循环。请注意,您还使用该waitKey()函数在视频帧之间暂停 20 毫秒。调用该waitKey()函数可让您监视键盘以获取用户输入。在这种情况下,例如,如果用户按下q' 键,则退出循环。

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

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

Python

# Release the video capture object
vid_capture.release()
cv2.destroyAllWindows()

从网络摄像头读取视频 

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

  • 如果您的系统具有内置网络摄像头,则摄像头的设备索引将为“ 0 
  • 如果您有多个摄像头连接到您的系统,则与每个附加摄像头关联的设备索引会递增(例如12

Python

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

您可能想知道 flag CAP_DSHOW。这是一个可选参数,因此不是必需的。CAP_DSHOW只是另一个视频捕获 API 首选项,它是通过视频输入直接显示的缩写。

写视频

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

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

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

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

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

可用的元数据非常广泛,可以在此处找到 

  • 在这种情况下,您通过指定3CAP_PROP_FRAME_WIDTH) 4CAP_PROP_FRAME_HEIGHT) 来检索框架的宽度和高度。在将视频文件写入磁盘时,您将在下面进一步使用这些尺寸。

为了编写视频文件,您需要首先从VideoWriter()该类中创建一个 video-writer 对象,如下面的代码所示。 

这是VideoWriter()的语法:

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

该类VideoWriter()采用以下参数: 

  • filename: 输出视频文件的路径名
  • apiPreference: API 后端标识符
  • fourcc:编解码器的4字符代码,用于压缩帧(fourcc
  • fps: 创建的视频流的帧率
  • frame_size:视频帧的大小
  • isColor:如果不为零,编码器将期望并编码彩色帧。否则它将适用于灰度帧(该标志目前仅在 Windows 上受支持)。

以下代码outputVideoWriter()类创建视频编写器对象。一个特殊的便利函数用于检索四字符编解码器,需要作为视频编写器对象的第二个参数,cv2.

  • VideoWriter_fourcc('M', 'J', 'P', 'G') Python 中。
  • VideoWriter::fourcc('M', 'J', 'P', 'G')  C++ 中。

视频编解码器指定如何压缩视频流。它将未压缩的视频转换为压缩格式,反之亦然。要创建 AVI MP4 格式,请使用以下 Fourcc 规范:

视频:cv2.VideoWriter_fourcc('M','J','P','G')

MP4cv2.VideoWriter_fourcc(*'XVID')

接下来的两个输入参数指定以 FPS 为单位的帧速率和帧大小(宽度、高度)。

Python

# Initialize video writer object
output = cv2.VideoWriter('Resources/output_video_from_file.mp4', cv2.VideoWriter_fourcc(*'XVID'), 20, frame_size)

现在您已经创建了一个视频写入器对象,使用它来将视频文件写入磁盘,一次一帧,如下面的代码所示。在这里,您正在以每秒 20 帧的速度将 MP4视频文件写入磁盘。请注意我们如何从前面的示例中简化为循环。

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:
    output.write(frame)

最后,在下面的代码中,释放 video capture video-writer 对象。

Python

# Release the video capture object
vid_capture.release()
output.release()

阅读或编写视频时可能遇到的错误

视频阅读

在读取帧时,如果路径错误或文件损坏或帧丢失,它可能会引发错误。这就是我们在循环中使用if语句的原因。while可以在该行中看到,如果ret == True。这样它只会在帧存在时处理它。以下是在这种情况下观察到的错误日志示例。它不是完整的日志,仅包含关键参数。

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

对于错误的路径

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

Error opening the video file

视频写作

在此步骤中可能会出现各种错误。最常见的是帧大小错误api 首选项错误。如果帧大小与视频不相似,那么即使我们在输出目录中得到一个视频文件,它也会是空白的。如果您使用 NumPy 形状方法来检索帧大小,请记住反转输出,因为 OpenCV 将返回height x width x channels。如果它抛出 api 首选项错误,我们可能需要在参数中传递CAP_ANY标志。VideoCapture()它可以在网络摄像头示例中看到,我们使用它CAP_DHOW来避免生成警告。

以下是错误日志的示例:

CAP_DSHOW 未通过时:

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

当帧大小不正确时:

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)

扩展你的知识

概括

在本博客中,您学习了使用视频捕获对象读取和显示来自三个不同来源的视频流。甚至看到了如何使用视频捕获对象从视频流中检索重要的元数据。我们还演示了如何使用 video-writer 对象将视频流写入磁盘。您可能还会发现调整视频帧的大小或使用形状和文本对其进行注释很有帮助。为此,只需修改单个图像帧。

既然您知道如何阅读和编写视频,并且熟悉使用 OpenCV,请跟上步伐。并不断学习。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值