用Python保存OpenCV视频

92

我试图保存视频,但是没有成功。 我按照OpenCV文档中的说明操作了。

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640,480))

while(cap.isOpened()):
    ret, frame = cap.read()
    if ret==True:
        frame = cv2.flip(frame,0)


        out.write(frame)

        cv2.imshow('frame',frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break
cap.release()

out.release()

cv2.destroyAllWindows()

有什么问题吗?


1
它在我的电脑上运行良好。事实上,被接受的答案并不起作用。 - Trect
3
请确保您尝试写入的目录存在,否则它将会悄无声息地失败。 - inostia
在我的系统上,只有“DIVX”可以正常工作,而在opencv文档示例中提供的编码,即“M”,“J”,“P”,“G”,则会静默地不写入任何文件。我希望有一种方法来验证编解码器,以便我的代码在编解码器可用性方面更加安全地可移植。 - matanster
这段代码对我来说运行良好。 - Ghulam
18个回答

55

试试这个。对我来说(在Windows 10上)它有效。

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

# Define the codec and create VideoWriter object
#fourcc = cv2.cv.CV_FOURCC(*'DIVX')
#out = cv2.VideoWriter('output.avi',fourcc, 20.0, (640,480))
out = cv2.VideoWriter('output.avi', -1, 20.0, (640,480))

while(cap.isOpened()):
    ret, frame = cap.read()
    if ret==True:
        frame = cv2.flip(frame,0)

        # write the flipped frame
        out.write(frame)

        cv2.imshow('frame',frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break

# Release everything if job is finished
cap.release()
out.release()
cv2.destroyAllWindows()

30
对我来说不起作用。只创建了一个空的视频文件(大小为5.6kB)。 - Petr Pecha
7
对于那些无法生成视频的人,请更改fourcc参数。 - Alan
3
无法创建output.avi文件。 - Trect
1
问题中的代码片段运行得非常好。 - Trect
2
虽然问题中的代码片段运行良好,但这个答案却不行。是操作系统相关吗?我使用的是Ubuntu。 - XFCC
1
这个答案应该在Windows上正常工作,如此所述 fourcc = -1 会打开一个用于压缩质量的Windows对话框,但是,这段代码在Ubuntu上对我不起作用,甚至没有创建一个空的output.avi文件。此外,问题中的代码片段对我也不起作用,似乎如下回答所述:必须使用与输入视频/图像相同的帧宽度和高度。 - aspiring1

32

jveitchmichaelis在https://github.com/ContinuumIO/anaconda-issues/issues/223中提供了详细的答案。这里是他的回答:

OpenCV文档(隐藏在某处)中提到只有使用OpenCV3才能将视频写入avi文件。我无法确定这是否正确,但我尝试过无法将视频写入其他格式。

然而,OpenCV主要是一个计算机视觉库,而不是视频流、编解码器及写入库。因此,开发人员尝试使其尽可能简单。由于这个原因,OpenCV仅支持第一个版本的avi格式。

来自:http://docs.opencv.org/3.1.0/d7/d9e/tutorial_video_write.html

我的设置:我使用MSVC 2015从源代码构建了OpenCV 3,包括ffmpeg。我还从Cisco下载并安装了XVID和openh264,并将它们添加到了我的PATH环境变量中。我正在运行Anaconda Python 3。我还下载了最近版本的ffmpeg,并将bin文件夹添加到了我的路径中,但这似乎没有什么区别,因为它已经包含在OpenCV中。

我正在运行Win 10 64位操作系统。

下面这段代码在我的计算机上似乎可以正常工作。它将生成一个包含随机静态的视频:

writer = cv2.VideoWriter("output.avi",
cv2.VideoWriter_fourcc(*"MJPG"), 30,(640,480))

for frame in range(1000):
    writer.write(np.random.randint(0, 255, (480,640,3)).astype('uint8'))

writer.release()

通过试错方法我学到了以下几件事:

  • 仅使用“.avi”,这只是一个容器,编解码器才是重要的。
  • 注意指定帧大小。在构造函数中,您需要将帧大小作为(列,行)传递,例如640x480。但是您传递的数组按(行,列)索引。看看上面的示例如何切换?

  • 如果您的输入图像与VideoWriter大小不同,则会失败(通常是静默的)

  • 仅传递8位图像,如果必须,请手动转换您的数组(.astype('uint8'))
  • 事实上,不管怎样都进行强制类型转换。即使您使用cv2.imread加载图像,也需要转换为uint8...
  • MJPG会失败,如果没有传递3通道、8位图像。我至少会因此获得一次断言失败。
  • XVID也需要一个3通道图像,但如果您不这样做,则会静默失败。
  • H264似乎对单通道图像没问题
  • 如果您需要原始输出,例如从机器视觉相机获取,则可以使用“DIB”、“RAW”或者空编解码器。奇怪的是,如果我使用DIB,我会得到一个ffmpeg错误,但视频保存得很好。如果我使用RAW,则没有错误,但Windows视频播放器无法打开它。在VLC中都可以。

最终我认为关键点是OpenCV并不是一个设计用于视频捕获的库——它甚至不支持声音。VideoWriter很有用,但99%的情况下,最好将所有图像保存到文件夹中,并使用ffmpeg将其转换为有用的视频。


7
(列,行),例如640x480。然而,你传递的数组被索引为(行,列)。由于这个问题,我浪费了一天的工作时间。谢谢! - Learning from masters

24

在我的情况下,我发现 Writer 的尺寸必须与相机或文件的帧大小匹配。因此,我首先读取帧大小,然后将其应用于以下的写入设置。

(grabbed, frame) = camera.read()
fshape = frame.shape
fheight = fshape[0]
fwidth = fshape[1]
print fwidth , fheight
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi',fourcc, 20.0, (fwidth,fheight))

1
相机未定义,什么是相机? - nycynik
如果您直接使用相机而不是保存的视频,则需要处理相机。 - amitection
1
这是唯一对我有效的解决方案。要么OpenCV已经改变了,要么其他人似乎都有640x480的网络摄像头。 - makeworld

16
您需要像这样获取捕获的确切尺寸:
import cv2

cap = cv2.VideoCapture(0)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH) + 0.5)
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) + 0.5)
size = (width, height)
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('your_video.avi', fourcc, 20.0, size)

while(True):
    _, frame = cap.read()
    cv2.imshow('Recording...', frame)
    out.write(frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
out.release()
cv2.destroyAllWindows()

谢谢,这对我有用!但是你能告诉我为什么我们需要“exact”大小吗? - Xabnord
@Mayan 精确尺寸指的是原始视频帧的宽度和高度。 - tsveti_iko
这个在我使用的Windows 10和Python 3.7上有效。 - Talha Anwar
+ 0.5的意义是什么? - 101
可以确认这个答案即使没有+0.5也是有效的。很好的回答。 - Herbz

8
我也遇到了同样的问题,但当我使用'MJPG'代替'XVID'时问题得到了解决。
我使用了
fourcc = cv2.VideoWriter_fourcc(*'MJPG')

代替
fourcc = cv2.VideoWriter_fourcc(*'XVID')

8
请确保设置正确的宽度和高度。您可以像下面这样设置:

请务必将宽度和高度设置正确。您可以像下面这样设置:

cv2.VideoWriter('output.avi', fourcc, 20.0, (int(cap.get(3)), int(cap.get(4))))

6

这篇答案涵盖了变量相关的内容,特别重要的是,输出大小应该与输入帧和视频大小相同。

import cv2

save_name = "output.mp4"
fps = 10
width = 600
height = 480
output_size = (width, height)
out = cv2.VideoWriter(save_name,cv2.VideoWriter_fourcc('M','J','P','G'), fps , output_size )

cap = cv2.VideoCapture(0) # 0 for webcam or you can put in videopath
while(True):
    _, frame = cap.read()
    cv2.imshow('Video Frame', frame)
    out.write(cv2.resize(frame, output_size ))
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
out.release()
cv2.destroyAllWindows()

5

这个答案只在MacOS中进行了测试,但很可能也适用于Linux和Windows。

import numpy as np
import cv2

cap = cv2.VideoCapture(0)

# Get the Default resolutions
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))

# Define the codec and filename.
out = cv2.VideoWriter('output.avi',cv2.VideoWriter_fourcc('M','J','P','G'), 10, (frame_width,frame_height))

while(cap.isOpened()):
    ret, frame = cap.read()
    if ret==True:

        # write the  frame
        out.write(frame)

        cv2.imshow('frame',frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break

# Release everything if job is finished
cap.release()
out.release()
cv2.destroyAllWindows()


这个确实可以工作,但是我的输入视频文件大小为83MB,但最终得到的output.avi文件大小接近1GB。您能否解释一下可能发生了什么以及如何纠正?另外,为什么需要使用相同的“height”和“width”? - aspiring1

3
其他答案适用于保存单个视频源。但是,如果您有多个视频源,例如多个IP摄像机源、RTSP流或网络摄像头,则可能希望同时记录它们。这里介绍一种使用多线程同时保存多个视频源的方法。其思想是为每个视频源设置两个线程:1)专门用于从流中读取帧;2)专门用于处理帧(显示和保存)。
由于cv2.VideoCapture.read()是一个阻塞操作,因此我们必须将读取帧与保存帧分开。我们可以在自己独立的线程中读取帧,通过减少I/O操作的延迟来提高性能。通过将帧捕获专用于其自己的线程,将始终有一帧准备好被处理,而不必等待I/O操作完成并返回新的帧。第二个线程专门用于处理和保存帧到输出文件。我们可以将所有这些封装到单个对象中,以便可以根据同时视频流的数量进行扩展。请确保更改视频src参数以适应您自己的视频源。以下是同时录制三个视频流的示例。
from threading import Thread
import cv2
import time

class VideoWriterWidget(object):
    def __init__(self, video_file_name, src=0):
        # Create a VideoCapture object
        self.frame_name = str(src)
        self.video_file = video_file_name
        self.video_file_name = video_file_name + '.avi'
        self.capture = cv2.VideoCapture(src)

        # Default resolutions of the frame are obtained (system dependent)
        self.frame_width = int(self.capture.get(3))
        self.frame_height = int(self.capture.get(4))

        # Set up codec and output video settings
        self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
        self.output_video = cv2.VideoWriter(self.video_file_name, self.codec, 30, (self.frame_width, self.frame_height))

        # Start the thread to read frames from the video stream
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()
        
        # Start another thread to show/save frames
        self.start_recording()
        print('initialized {}'.format(self.video_file))

    def update(self):
        # Read the next frame from the stream in a different thread
        while True:
            if self.capture.isOpened():
                (self.status, self.frame) = self.capture.read()

    def show_frame(self):
        # Display frames in main program
        if self.status:
            cv2.imshow(self.frame_name, self.frame)

        # Press Q on keyboard to stop recording
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            self.output_video.release()
            cv2.destroyAllWindows()
            exit(1)

    def save_frame(self):
        # Save obtained frame into video output file
        self.output_video.write(self.frame)
    
    def start_recording(self):
        # Create another thread to show/save frames
        def start_recording_thread():
            while True:
                try:
                    self.show_frame()
                    self.save_frame()
                except AttributeError:
                    pass
        self.recording_thread = Thread(target=start_recording_thread, args=())
        self.recording_thread.daemon = True
        self.recording_thread.start()

if __name__ == '__main__':
    src1 = 'Your link1'
    video_writer_widget1 = VideoWriterWidget('Camera 1', src1)
    src2 = 'Your link2'
    video_writer_widget2 = VideoWriterWidget('Camera 2', src2)
    src3 = 'Your link3'
    video_writer_widget3 = VideoWriterWidget('Camera 3', src3)
        
    # Since each video player is in its own thread, we need to keep the main thread alive.
    # Keep spinning using time.sleep() so the background threads keep running
    # Threads are set to daemon=True so they will automatically die 
    # when the main thread dies
    while True:
        time.sleep(5)

与相机/IP/RTSP/流媒体、FPS、视频、线程和多进程相关的帖子

  1. Python OpenCV从相机实时流 - 多线程,时间戳

  2. 使用OpenCV cv2.VideoCapture在Python中从IP相机进行视频流传输

  3. 如何使用OpenCV捕获多个相机流?

  4. OpenCV实时流视频捕获较慢。如何丢弃帧或与实时同步?

  5. 使用OpenCV VideoWriter将RTSP流存储为视频文件

  6. OpenCV视频保存

  7. Python OpenCV多进程cv2.VideoCapture mp4


你的解决方案提供了一种替代方法。感谢您发布这个想法。 - mnm

2

我曾经遇到过同样的问题,后来我尝试了以下方法:

frame = cv2.flip(frame,180) 

代替
frame= cv2.flip(frame,0) 

现在它正在运作。


1
如果您不想翻转图像,可以直接删除该行代码,这并非必需。 - aheigins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接