Search code examples
pythonmultithreadingopencvvideo-capture

How to access 1 webcam with 2 threads


I am using python 3.5 with opencv.

I want to use 2 threads:

  • Thread 1: Save the video to a file
  • Thread 2: Display the video to the user

To view/capture the video from webcam i am using snippets of code from the following website: opencv video docs

I can capture and save the video using the following code:

# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi',fourcc, 20.0, (640,480))

while(True):
    ret, frame = cap.read()
    if ret==True:
        frame = cv2.flip(frame,0)

        # write the flipped frame
        out.write(frame)
    else:
        break

out.release()
cv2.destroyAllWindows()

I can view the video using the following code:

while(True):
    # Capture frame-by-frame
    ret, frame = cap.read()
    cv2.imshow('frame',frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cv2.destroyAllWindows()

Each of these pieces of code are in their own functions called capture and display. I then call them in separate threads with pythons threading library as follows:

cap = cv2.VideoCapture(0)
Thread(target=capture).start()
Thread(target=display).start()
cap.release()

I get an error I assume is related to both threads wanting to access the video buffer at the same time.

I understand this can be done without threads but there are other things I would like to do further than can only be done in separate threads.

How can I access the cap video capture from both threads?


Solution

  • My flask/django experience is increadibly limited, so I am not sure how to do it for that exactly, but I will answer the question posted directly.

    First you need to create a thread-safe object to avoid calling at the same time the read function in different threads.

    import cv2
    import threading
    
    class VideoCamera(object):
      # filename can be 0 to access the webcam
      def __init__(self, filename):
        self.lock = threading.Lock()
        self.openVideo(filename)
    
      def openVideo(self, filename):
        self.lock.acquire()
        self.videoCap = cv2.VideoCapture(filename)
        self.lock.release()
    

    With this, you should be able to create an object with a lock and to open safely a video (in case that you want to open another video with the same object).

    Now you have 2 options, either you create a thread that updates the frame and stores the current one internally or update do in a thread safe manner the get next frame function. I will do the second one here to show you:

      def getNextFrame(self):
        self.lock.acquire()
        img = None
        # if no video opened return None
        if self.videoCap.isOpened():
          ret, img = self.videoCap.read()
        self.lock.release()
        return img
    

    This way you should be able to access the video cap with 2 frames... however, the frames will be different every time the function is called.

    I hope this helps you.