如何使用 opencv 和多线程 (logitech c920) 在 python 中捕获视频

Posted

技术标签:

【中文标题】如何使用 opencv 和多线程 (logitech c920) 在 python 中捕获视频【英文标题】:How to capture video in python with opencv and multithread (logitech c920) 【发布时间】:2020-06-01 09:46:13 【问题描述】:

我正在尝试使用 Manjaro XFCE 中的 python3.8 和 opencv(4.2.0) 每 Y 小时捕获一段持续时间为 X 秒的视频。我想以 720p 或 1080p 的分辨率获得至少 20-30fps。我使用的是罗技 c920,但分辨率很高(例如 1920x1080),我的堆栈速度为 5fps。我可以将 fps 设置为 30,但视频会缩短大约 6 倍(python 以约 5fps 的速度拍摄视频,但将其记录为 30fps)。因此,为了提高 fps,我应该使用多线程,尽管我无法在我的代码中实现我见过的任何示例。知道怎么做吗?

这是我的代码:

import numpy as np
import cv2
import time

#choose resolution and fps:

x=1920
y=1080
fps=30

# The duration in seconds of the video captured (s)
capture_duration = 5

# Lapse of time between videos (s), from beginning to beginning
vids_lapse=10

# Name of videos
data_string= time.strftime("%m-%d-%H-%M-%S")

for i in range (0,2): #Let's say I only want to do this process twice, to simplify
    cap = cv2.VideoCapture(2) #my camera is 2, but could be 0
    fourcc = cv2.VideoWriter_fourcc('M','J','P','G')

#set resolution and fps
    cap.set(3,int(x))
    cap.set(4,int(y))
    cap.set(cv2.CAP_PROP_FPS, int(fps))
#set name of video
    out = cv2.VideoWriter(data_string+"_vid"+str(i).zfill(2)+".avi",fourcc, fps, (x,y))
    start_time = time.time()
#start recording for a determined duration
    while( int(time.time() - start_time) < capture_duration+1 ):
        ret, frame = cap.read()
        if ret==True:
            #frame = cv2.flip(frame,0)
            out.write(frame)
            cv2.imshow('frame',frame)
        else:
            break

    cap.release()
    out.release()
    cv2.destroyAllWindows()
#Here I added time.sleep to wait for the next capture
    time.sleep(vids_lapse-capture_duration)

使用上面的这段代码,我的 30fps 视频比预期的要短,因为它不能超过 ~5fps。

我已经看到我可以使用多线程,并且我能够使用以下代码以 30fps 录制视频(至少 ffprobe 是这么说的,尽管我不确定是这样):

from threading import Thread
import cv2, time

#again define resolution and fps
x=1920
y=1080
fps=30

class VideoStreamWidget(object):
    def __init__(self, src=2):
        self.capture = cv2.VideoCapture(src)
        self.capture.set(3,int(x))
        self.capture.set(4,int(y))
        self.capture.set(cv2.CAP_PROP_FPS, int(fps))
        # Start the thread to read frames from the video stream
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()

    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()
            time.sleep(.03)

    def show_frame(self):
        out.write(self.frame)
        # Display frames in main program
        cv2.imshow('frame', self.frame)
        key = cv2.waitKey(1)
        if key == ord('q'):
            self.capture.release()
            out.release()
            cv2.destroyAllWindows()
            exit(1)

if __name__ == '__main__':
    fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
    out = cv2.VideoWriter("output.avi",fourcc, fps, (x,y))
    video_stream_widget = VideoStreamWidget()
    while True:
        try:
            video_stream_widget.show_frame()
        except AttributeError:
            pass

第二个代码的问题是我真的不知道如何引入我的参数(在 5 秒内记录,等待 10 秒,等等)或者我正在使用多少个线程(我可以指定数量使用 V4L2 工具从我的相机中提取的缓冲区,不知道它有多相关)。

所以我的问题是:知道我该怎么做吗?是否可以合并两个代码?还是有更好的办法?

以下是一些我检查过但无法使用我的代码的指南: https://www.pyimagesearch.com/2015/12/21/increasing-webcam-fps-with-python-and-opencv/ https://github.com/gilbertfrancois/video-capture-async

提前非常感谢!

干杯!

编辑

我终于有工作要做了。尽管如此,即使我的视频是 30fps,它仍然看起来像一个 5fps 的视频...似乎所有捕获的图片都几乎相同...知道如何解决这个问题吗?

代码如下:

rom threading import Thread, Lock
import cv2, time

#choose resolution
x=1920
y=1080
fps=60

# The duration in seconds of the video captured (s)
capture_duration = 5

# Days
Days=1

# Lapse of time between videos (s), from beginning to beginning
vids_lapse=10

data_string= time.strftime("%m-%d-%H-%M-%S")

class CameraStream(object):
    def __init__(self, src=0):
        self.stream = cv2.VideoCapture(src)
        self.stream.set(3,int(x))
        self.stream.set(4,int(y))
        self.stream.set(cv2.CAP_PROP_FPS,int(fps))
        self.video_file_name = data_string+"_vid"+str(i).zfill(2)+".avi"
        (self.grabbed, self.frame) = self.stream.read()
        self.started = False
        self.read_lock = Lock()
        
        # Set up codec and output video settings
        self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
        self.out = cv2.VideoWriter(self.video_file_name, self.codec, fps, (x, y))

    def start(self):
        if self.started:
            print("already started!!")
            return None
        self.started = True
        self.thread = Thread(target=self.update, args=())
        self.thread.start()
        return self

    def update(self):
        while self.started:
            (grabbed, frame) = self.stream.read()
            self.read_lock.acquire()
            self.grabbed, self.frame = grabbed, frame
            self.read_lock.release()
            # ~ time.sleep(1/fps)

    def read(self):
        self.read_lock.acquire()
        frame = self.frame.copy()
        self.read_lock.release()
        self.save_frame()
        return frame
    
    def save_frame(self):
        # Save obtained frame into video output file
        self.out.write(self.frame)

    def stop(self):
        self.started = False
        self.thread.join()
        self.stream.release()
        self.out.release()

    def __exit__(self, exc_type, exc_value, traceback):
        self.stream.release()


if __name__ == "__main__" :
    for i in range (0,2):
        start_time = time.time()
        cap = CameraStream(0).start()
        while (int(time.time() - start_time) < capture_duration):
            frame = cap.read()
            cv2.imshow('webcam', frame)
            # ~ if cv2.waitKey(1) == 27 :
        cap.stop()
        cv2.destroyAllWindows()
        time.sleep(vids_lapse-capture_duration)

【问题讨论】:

您是否使用 guvcview 之类的工具获得了良好的性能?另外,你的机器是什么样的? 我没试过guvcview,但我相信我可以用这个cam得到很好的表现。我有一台戴尔 XPS13。 【参考方案1】:

问题在于FOURCC,你应该设置cv2.CAP_PROP_FOURCC,试试这个例子:

import cv2

HIGH_VALUE = 10000
WIDTH = HIGH_VALUE
HEIGHT = HIGH_VALUE

capture = cv2.VideoCapture(-1)

capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
capture.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)

width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = capture.get(cv2.CAP_PROP_FPS)

print(width, height, fps)

while True:
    ret, frame = capture.read()
    if ret:
        cv2.imshow('frame', frame)

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

capture.release()
cv2.destroyAllWindows()

【讨论】:

谢谢,现在它可以捕获图像,但如果我能以 30 fps 的速度录制它,我会稍后尝试。只是一个问题……为什么要设置 High_value?是不是因为 cv2 会使用最大可能值(低于 10000)? 是的,这将设置最大可能值,如果您愿意,可以使用 1080p (1920x1080)。

以上是关于如何使用 opencv 和多线程 (logitech c920) 在 python 中捕获视频的主要内容,如果未能解决你的问题,请参考以下文章

教程 | 如何使用DockerTensorFlow目标检测API和OpenCV实现实时目标检测和视频处理

多线程和多处理时 fprintf 的行为如何?

图文|Android 使用Thread 和多线程使用互斥锁

Java线程教程:使用Java创建线程和多线程

Java线程教程:使用Java创建线程和多线程

异步和多线程