一次从OpenCV中的两个摄像头捕获视频

时间:2015-04-16 02:35:54

标签: python opencv camera

如何使用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,我的代码中的两个调用都会失败。

9 个答案:

答案 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 板(只有在台式计算机上才有可能)。

多线程不会让您绕过带宽限制。