Mike Lawrence Mike Lawrence - 1 month ago 19
Python Question

OpenCV crash on OS X when reading USB cam in separate process

I'm running OpenCV 2.4.5 via the cv2 python bindings, using OS X (10.8.4). I'm trying to capture images from a USB webcam in a separate process via the multiprocessing module. Everything seems to work if I use my laptop's (2011 macbook air) internal webcam, but when I attempt to read from a usb webcam (Logitech C920), I get a crash (no crash when I use the USB cam without the multiprocessing encapsulation). The crash log is here. Code I'm using that will reliably reproduce the crash is below. Getting this working is pretty mission-critical for me, so any help would be greatly appreciated!

import multiprocessing
import cv2 #doesn't matter if you import here or in cam()

def cam():
vc = cv2.VideoCapture(0) #modify 0/1 to toggle between USB and internal camera
while True:
junk,image = vc.read()

camProcess = multiprocessing.Process( target=cam )
camProcess.start()

while True:
pass

Answer

Your problem stems from the way python spans its subprocess using os.fork. The OpenCV video backend on Mac uses QTKit which uses CoreFoundation these parts of MacOS are not save to run in a forked subprocess, sometimes they just complain, sometimes they crash.

You need to create the subprocess without using os.fork. This can be achieved with python 2.7.

You need to use billiard (https://github.com/celery/billiard/tree/master/billiard) It serves as a replacement for pythons multiprocessing and has some very useful improvements.

from billiard import Process, forking_enable
import cv2 #does matter where this happens when you don't use fork

def cam():
    vc = cv2.VideoCapture(0) #modify 0/1 to toggle between USB and internal camera
    while True:
        junk,image = vc.read()

forking_enable(0) # Is all you need!
camProcess = Process( target=cam )
camProcess.start()

while True:
    pass

alright, lets add a more complete example:

from billiard import Process, forking_enable

def cam(cam_id):
    import cv2 #doesn't matter if you import here or in cam()
    vc = cv2.VideoCapture(cam_id) #modify 0/1 to toggle between USB and internal camera
    while True:
        junk,image = vc.read()
        cv2.imshow("test",image)
        k = cv2.waitKey(33)
        if k==27:    # Esc key to stop
            break

def start():

    forking_enable(0) # Is all you need!
    camProcess = Process(target=cam, args=(0,))
    camProcess.start()


if __name__ == '__main__':
    start()
    cam(1)

You need two cameras attached for this:It should open a window and run each camera in a separate process (one on the main process one in a spawned one). I use this strategy to stream images form multiple cameras at once each in its own python process.