Captura de video de dos cámaras en OpenCV a la vez

¿Cómo capturar video de dos o más cámaras a la vez (o casi) con OpenCV, usando la API de Python?

Tengo tres cámaras web, todas capaces de transmisión de video, ubicadas en / dev / video0, / dev / video1 y / dev / video2.

Usando el tutorial como ejemplo, capturar imágenes de una sola cámara es simplemente:

import cv2 cap0 = cv2.VideoCapture(0) ret0, frame0 = cap0.read() cv2.imshow('frame', frame0) cv2.waitKey() 

Y esto funciona bien.

Sin embargo, si bash inicializar una segunda cámara, intentar read() en ella devuelve Ninguna:

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

Solo para asegurarme de que no le estaba dando a OpenCV un índice de cámara incorrecto, probé cada índice de cámara individualmente y todos funcionan por sí mismos. p.ej

 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?! 

¿Qué estoy haciendo mal?

Edición: Mi hardware es un Macbook Pro que ejecuta Ubuntu. Al investigar el problema específicamente en Macbooks, he encontrado otros que también se han encontrado con este problema, tanto en OSX como con diferentes tipos de cámaras. Si accedo a la iSight, las dos llamadas en mi código fallarán.

Sí, definitivamente estás limitado por el ancho de banda USB. Al intentar leer desde ambos dispositivos a plena rez, probablemente obtuviste un error:

 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  assert ret1 # fails?! AssertionError 

Y luego cuando reduzcas la resolución a 160×120:

 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?! 

Ahora parece que funciona! Apuesto a que tienes ambas cámaras conectadas en la misma tarjeta USB. Puede ejecutar el comando lsusb para asegurarse, y debería indicar algo como:

 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 

(Tenga en cuenta las dos cámaras en el mismo bus). Si es posible, puede agregar otra tarjeta USB a su máquina para ganar más ancho de banda. He hecho esto antes para ejecutar varias cámaras a resolución completa en una sola máquina. A pesar de que era una estación de trabajo de torre con ranuras de placa base disponibles, y desafortunadamente, es posible que no tenga esa opción en una computadora portátil MacBook.

Usando OPENCV y dos cámaras USB estándar, pude hacer esto usando multihilo. Esencialmente, defina una función que abra una ventana abierta y un elemento VideoCapture. Luego, cree dos hilos con la identificación de la cámara y el nombre de la ventana como entradas.

 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() 

Gran recurso para aprender a crear hilos en python: https://www.tutorialspoint.com/python/python_multithreading.htm

He usado “imutils” y leí el progtwig de webcam en la imagen.

importar imutils

capturar marcos de vedio

— 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)

creo la función read_frame () enviar parámetro sobre Image.fromarray y mostrar

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

y función final mostrar video en el “imageFrame”

def webCameShow (N, Display): _, frameXX = N cv2imageXX = cv2.cvtColor (frameXX, cv2.COLOR_BGR2RGBA) imgXX = Image.fromarray (cv2imageXX) #imgtkXX = ImageTk.PhotoImage (image = imgXX) ThumbSimport.Acceso en un chisme. .configure (imagen = imgtkXX)

ejemplo. 4-webcam

Youtube: Youtube

Esto ha sido un dolor para mí durante mucho tiempo, así que hice una biblioteca sobre OpenCV para manejar múltiples cámaras y visores. Me encontré con un montón de problemas, como videos que no se comprimen de forma predeterminada o ventanas que solo se muestran en el hilo principal. Soy capaz de mostrar dos cámaras web de 720p en tiempo real en Windows hasta ahora.

Tratar:

 pip install CVPubSubs 

Luego, en 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() 

Aunque es relativamente nuevo, así que cuéntame sobre cualquier error o código no optimizado.

intente usar este código … funcionó como se esperaba … esto es para dos cámaras, si desea más cámaras, simplemente cree los objetos “VideoCapture ()” … por ejemplo, la tercera cámara tendrá: cv2.VideoCapture ( 3) y el código correspondiente en el bucle 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() 

TODO LO MEJOR !

 frame0 = cv2.VideoCapture(1) frame1 = cv2.VideoCapture(2) 

debe ser:

 frame0 = cv2.VideoCapture(0) # index 0 frame1 = cv2.VideoCapture(1) # index 1 

Asi que corre

Agregando un poco a lo que @TheoreticallyNick publicó anteriormente:

 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()) 

Esto abrirá un nuevo hilo para cada webcam que tengas. En mi caso, quería abrir tres fonts diferentes. Probado en Python 3.6. Avíseme si tiene algún problema, también gracias a TheoreticallyNick por el código legible / en funcionamiento.