如何使用 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 中捕获视频的主要内容,如果未能解决你的问题,请参考以下文章