如何使用Python API一次(或几乎)使用OpenCV从两个或多个摄像头捕获视频?
我有三个网络摄像头,都支持视频流,位于/ dev / video0,/ dev / video1和/ dev / video2。
以tutorial为例,从单个摄像头捕捉图像只是:
import cv2
cap0 = cv2.VideoCapture(0)
ret0, frame0 = cap0.read()
cv2.imshow('frame', frame0)
cv2.waitKey()
这很好用。
但是,如果我尝试初始化第二台摄像机,尝试从read()
返回无:
import cv2
cap0 = cv2.VideoCapture(0)
cap1 = cv2.VideoCapture(1)
ret0, frame0 = cap0.read()
assert ret0 # succeeds
ret1, frame1 = cap1.read()
assert ret1 # fails?!
为了确保我不会意外地给OpenCV一个坏的相机索引,我单独测试了每个相机索引,它们都是自己工作的。 e.g。
import cv2
#cap0 = cv2.VideoCapture(0)
cap1 = cv2.VideoCapture(1)
#ret0, frame0 = cap0.read()
#assert ret0
ret1, frame1 = cap1.read()
assert ret1 # now it works?!
我做错了什么?
编辑:我的硬件是运行Ubuntu的Macbook Pro。在Macbook上专门研究这个问题,我发现其他人也遇到过这个问题,无论是在OSX上还是在不同类型的相机上。如果我访问iSight,我的代码中的两个调用都会失败。
答案 0 :(得分:6)
是的,你肯定受到USB带宽的限制。尝试在全速读取这两个设备时,您可能会收到错误:
libv4l2: error turning on stream: No space left on device
VIDIOC_STREAMON: No space left on device
Traceback (most recent call last):
File "p.py", line 7, in <module>
assert ret1 # fails?!
AssertionError
然后当你将res减少到160x120时:
import cv2
cap0 = cv2.VideoCapture(0)
cap0.set(3,160)
cap0.set(4,120)
cap1 = cv2.VideoCapture(1)
cap1.set(3,160)
cap1.set(4,120)
ret0, frame0 = cap0.read()
assert ret0 # succeeds
ret1, frame1 = cap1.read()
assert ret1 # fails?!
现在它似乎工作了!我打赌你在同一个USB卡上连接了两个摄像头。您可以运行lsusb
命令来确保它,并且它应该表示如下:
Bus 001 Device 006: ID 046d:081b Logitech, Inc. Webcam C310
Bus 001 Device 004: ID 0409:005a NEC Corp. HighSpeed Hub
Bus 001 Device 007: ID 046d:0990 Logitech, Inc. QuickCam Pro 9000
Bus 001 Device 005: ID 0409:005a NEC Corp. HighSpeed Hub
Bus 001 Device 003: ID 0409:005a NEC Corp. HighSpeed Hub
Bus 001 Device 002: ID 1058:0401 Western Digital Technologies, Inc.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
(请注意同一总线上的两台摄像机。)如果可能,您可以在机器上添加另一块USB卡以获得更多带宽。之前我已经完成了这项工作,以便在一台机器上以全分辨率运行多个凸轮。虽然这是一个带有可用主板插槽的塔式工作站,但不幸的是,MacBook笔记本电脑上可能没有这个选项。
答案 1 :(得分:4)
使用OPENCV和两个标准USB摄像头,我能够使用多线程完成此操作。本质上,定义一个打开opencv窗口和VideoCapture元素的函数。然后,使用摄像机ID和窗口名称作为输入创建两个线程。
import cv2
import threading
class camThread(threading.Thread):
def __init__(self, previewName, camID):
threading.Thread.__init__(self)
self.previewName = previewName
self.camID = camID
def run(self):
print "Starting " + self.previewName
camPreview(self.previewName, self.camID)
def camPreview(previewName, camID):
cv2.namedWindow(previewName)
cam = cv2.VideoCapture(camID)
if cam.isOpened(): # try to get the first frame
rval, frame = cam.read()
else:
rval = False
while rval:
cv2.imshow(previewName, frame)
rval, frame = cam.read()
key = cv2.waitKey(20)
if key == 27: # exit on ESC
break
cv2.destroyWindow(previewName)
# Create two threads as follows
thread1 = camThread("Camera 1", 1)
thread2 = camThread("Camera 2", 2)
thread1.start()
thread2.start()
学习如何在python中进行线程化的很好的资源:https://www.tutorialspoint.com/python/python_multithreading.htm
答案 2 :(得分:2)
我使用“imutils”并在图像上阅读网络摄像头。
import imutils
捕捉视频帧
--- WebCam1
cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH,300) cap.set(cv2.CAP_PROP_FRAME_HEIGHT,300)
--- WebCam2
cap1 = cv2.VideoCapture(1) cap1.set(cv2.CAP_PROP_FRAME_WIDTH,300) cap1.set(cv2.CAP_PROP_FRAME_HEIGHT,300)
--- WebCam3
cap2 = cv2.VideoCapture(2) cap2.set(cv2.CAP_PROP_FRAME_WIDTH,300) cap2.set(cv2.CAP_PROP_FRAME_HEIGHT,300)
--- WebCame4
cap3 = cv2.VideoCapture(3) cap3.set(cv2.CAP_PROP_FRAME_WIDTH,300) cap3.set(cv2.CAP_PROP_FRAME_HEIGHT,300)
我创建函数read_frame()发送有关Image.fromarray的参数并显示
def read_frame(): webCameShow(cap.read(),display1) webCameShow(cap1.read(),显示2) webCameShow(cap2.read(),display6) webCameShow(cap3.read(),display7)
window.after(10,read_frame)
和最终功能在“imageFrame”上显示视频
def webCameShow(N,Display): _,frameXX = N. cv2imageXX = cv2.cvtColor(frameXX,cv2.COLOR_BGR2RGBA) imgXX = Image.fromarray(cv2imageXX) #imgtkXX = ImageTk.PhotoImage(image = imgXX) Display.imgtk = imgtkXX Display.configure(图像= imgtkXX)
例子。 4-webcam
的Youtube: Youtube
答案 3 :(得分:0)
很长一段时间以来,这一直让我很痛苦,因此我在OpenCV上建立了一个库来处理多个摄像机和视口。我遇到了很多问题,例如视频默认情况下不压缩,或者Windows仅在主线程中显示。到目前为止,我可以在Windows上实时显示两个720p网络摄像头。
尝试:
pip install CVPubSubs
然后,在python中:
import cvpubsubs.webcam_pub as w
from cvpubsubs.window_sub import SubscriberWindows
t1 = w.VideoHandlerThread(0)
t2 = w.VideoHandlerThread(1)
t1.start()
t2.start()
SubscriberWindows(window_names=['cammy', 'cammy2'],
video_sources=[0,1]
).loop()
t1.join()
t1.join()
尽管它相对较新,所以请告诉我任何错误或未优化的代码。
答案 4 :(得分:0)
尝试使用此代码... 它按预期工作... 这是两个凸轮,如果要更多凸轮,只需创建“ VideoCapture()”对象...例如,第三个凸轮将具有:cv2.VideoCapture(3)和while循环中的相应代码
import cv2
frame0 = cv2.VideoCapture(1)
frame1 = cv2.VideoCapture(2)
while 1:
ret0, img0 = frame0.read()
ret1, img00 = frame1.read()
img1 = cv2.resize(img0,(360,240))
img2 = cv2.resize(img00,(360,240))
if (frame0):
cv2.imshow('img1',img1)
if (frame1):
cv2.imshow('img2',img2)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
frame0.release()
frame1.release()
cv2.destroyAllWindows()
一切顺利!
答案 5 :(得分:0)
frame0 = cv2.VideoCapture(1)
frame1 = cv2.VideoCapture(2)
必须是:
frame0 = cv2.VideoCapture(0) # index 0
frame1 = cv2.VideoCapture(1) # index 1
因此它可以运行
答案 6 :(得分:0)
在@TheoreticallyNick之前发布的内容中添加一些内容:
import cv2
import threading
class camThread(threading.Thread):
def __init__(self, previewName, camID):
threading.Thread.__init__(self)
self.previewName = previewName
self.camID = camID
def run(self):
print("Starting " + self.previewName)
camPreview(self.previewName, self.camID)
def camPreview(previewName, camID):
cv2.namedWindow(previewName)
cam = cv2.VideoCapture(camID)
if cam.isOpened():
rval, frame = cam.read()
else:
rval = False
while rval:
cv2.imshow(previewName, frame)
rval, frame = cam.read()
key = cv2.waitKey(20)
if key == 27: # exit on ESC
break
cv2.destroyWindow(previewName)
# Create threads as follows
thread1 = camThread("Camera 1", 0)
thread2 = camThread("Camera 2", 1)
thread3 = camThread("Camera 3", 2)
thread1.start()
thread2.start()
thread3.start()
print()
print("Active threads", threading.activeCount())
这将为您拥有的每个摄像头打开一个新线程。就我而言,我想打开三个不同的提要。在Python 3.6上测试。让我知道您是否有任何问题,也要感谢TheoreticallyNick的可读/功能代码!
答案 7 :(得分:0)
实际上,您不需要@TheoreticallyNick 之前发布的冗长代码。只需使用我强大的 VideGear 库的 CamGear API,它可继承地提供多线程,并且您可以编写相同的代码以更少的行。此外,所有相机流都将完全同步。
deleted
可以找到更多使用示例here
答案 8 :(得分:0)
绕过 USB 带宽限制的一个选项是在开始使用第二个之前释放第一个摄像头,如
import cv2
cap0 = cv2.VideoCapture(0)
ret0, frame0 = cap0.read()
assert ret0 # succeeds
cap0.release()
cap1 = cv2.VideoCapture(1)
ret1, frame1 = cap1.read()
assert ret1 # succeeds as well
释放相机并打开新相机对我来说需要 0.5-1 秒,这是否是可接受的时间延迟将取决于您的用例。
除此之外并降低相机的输出分辨率(如果相机允许...),唯一的选择似乎是为每个相机添加一个 PCI USB 板(只有在台式计算机上才有可能)。
多线程不会让您绕过带宽限制。