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

Cer*_*rin 18 python camera opencv

如何使用Python API一次(或几乎)使用OpenCV从两个或多个摄像头捕获视频?

我有三个网络摄像头,都具有视频流功能,位于/ dev/video0,/ dev/video1和/ dev/video2.

教程为例,从单个摄像头捕获图像就是:

import cv2
cap0 = cv2.VideoCapture(0)
ret0, frame0 = cap0.read()
cv2.imshow('frame', frame0)
cv2.waitKey()
Run Code Online (Sandbox Code Playgroud)

这很好用.

但是,如果我尝试初始化第二个摄像头,尝试read()从它返回None:

import cv2
cap0 = cv2.VideoCapture(0)
cap1 = cv2.VideoCapture(1)
ret0, frame0 = cap0.read()
assert ret0 # succeeds
ret1, frame1 = cap1.read()
assert ret1 # fails?!
Run Code Online (Sandbox Code Playgroud)

为了确保我不会意外地给OpenCV一个坏的相机索引,我单独测试了每个相机索引,它们都是自己工作的.例如

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?!
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

编辑:我的硬件是运行Ubuntu的Macbook Pro.在Macbook上专门研究这个问题,我发现其他人也遇到过这个问题,无论是在OSX上还是在不同类型的摄像头上.如果我访问iSight,我的代码中的两个调用都会失败.

Vel*_*ker 9

是的,你肯定受到USB带宽的限制.尝试从full-rez读取这两个设备可能会出错:

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
Run Code Online (Sandbox Code Playgroud)

然后当你将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?!
Run Code Online (Sandbox Code Playgroud)

现在它似乎工作!我打赌你在同一个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
Run Code Online (Sandbox Code Playgroud)

(请注意同一总线上的两台摄像机.)如果可能,您可以在机器上添加另一块USB卡以获得更多带宽.之前我已经完成了这项工作,以便在一台机器上以全分辨率运行多个凸轮.虽然这是一个带有可用主板插槽的塔式工作站,但不幸的是,你可能没有在MacBook笔记本电脑上使用该选项.


The*_*ick 9

使用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()
Run Code Online (Sandbox Code Playgroud)

学习如何在python中进行线程学习的绝佳资源:https//www.tutorialspoint.com/python/python_multithreading.htm


Jac*_*las 6

在@TheoreticalNick 之前发布的内容中添加一点:

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())
Run Code Online (Sandbox Code Playgroud)

这将为您拥有的每个网络摄像头打开一个新线程。就我而言,我想打开三个不同的提要。在 Python 3.6 上测试。如果您有任何问题,请告诉我,还要感谢 TheoreticallyNick 提供可读/可运行的代码!


小智 5

我使用了“imutils”并阅读了图像上的网络摄像头显示。

import imutils
Run Code Online (Sandbox Code Playgroud)

捕捉视频帧

#--- 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)
Run Code Online (Sandbox Code Playgroud)

我创建函数read_frame发送关于 Image.fromarray 的参数并显示

def read_frame():
    webCameShow(cap.read(),display1)
    webCameShow(cap1.read(),display2)
    webCameShow(cap2.read(),display6)
    webCameShow(cap3.read(),display7)   
    window.after(10, read_frame)
Run Code Online (Sandbox Code Playgroud)

和最终功能在“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(image=imgtkXX)
Run Code Online (Sandbox Code Playgroud)

例子。 4-网络摄像头

优酷:优


abh*_*nix 5

有点晚了,但您可以使用我的 VideGear 库的CamGear API,它继承地提供多线程,并且您可以用更少的行数编写相同的代码。另外,从好的方面来说,所有摄像机流都将完全同步。

以下是两个相机流的示例代码:

# import required libraries
from vidgear.gears import VideoGear
import cv2
import time

# define and start the stream on first source ( For e.g #0 index device)
stream1 = VideoGear(source=0, logging=True).start() 

# define and start the stream on second source ( For e.g #1 index device)
stream2 = VideoGear(source=1, logging=True).start() 

# infinite loop
while True:
    
    frameA = stream1.read()
    # read frames from stream1

    frameB = stream2.read()
    # read frames from stream2

    # check if any of two frame is None
    if frameA is None or frameB is None:
        #if True break the infinite loop
        break
    
    # do something with both frameA and frameB here
    cv2.imshow("Output Frame1", frameA)
    cv2.imshow("Output Frame2", frameB)
    # Show output window of stream1 and stream 2 seperately

    key = cv2.waitKey(1) & 0xFF
    # check for 'q' key-press
    if key == ord("q"):
        #if 'q' key-pressed break out
        break

    if key == ord("w"):
        #if 'w' key-pressed save both frameA and frameB at same time
        cv2.imwrite("Image-1.jpg", frameA)
        cv2.imwrite("Image-2.jpg", frameB)
        #break   #uncomment this line to break out after taking images

cv2.destroyAllWindows()
# close output window

# safely close both video streams
stream1.stop()
stream2.stop()
Run Code Online (Sandbox Code Playgroud)

更多使用示例可以在这里找到