title: How to capture video in python with opencv and multithread (logitech c920)
body: <p>I'm trying to capture a video of a duration of X seconds every Y hours with python3.8 and opencv(4.2.0) in Manjaro XFCE. I'd like to get at least 20-30fps with a resolution of 720p or 1080p. I'm using a Logitech c920, but with a good resolution (eg 1920x1080) I'm stack at 5fps. I can set the fps at 30 but then the video is about 6 times shorter (python takes video at ~5fps but records it as it was 30fps). Thus, to improve the fps I should use multithreading, although I can't get to implement in my code any of the examples I've seen. Any idea how to do it?</p>
  <p>Here is my code:</p>
  <pre><code>import numpy as np
  import cv2
  import time
  
  #choose resolution and fps:
  
  x=1920
  y=1080
  fps=30
  
  # The duration in seconds of the video captured (s)
  capture_duration = 5
  
  # Lapse of time between videos (s), from beginning to beginning
  vids_lapse=10
  
  # Name of videos
  data_string= time.strftime(&quot;%m-%d-%H-%M-%S&quot;)
  
  for i in range (0,2): #Let's say I only want to do this process twice, to simplify
      cap = cv2.VideoCapture(2) #my camera is 2, but could be 0
      fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
  
  #set resolution and fps
      cap.set(3,int(x))
      cap.set(4,int(y))
      cap.set(cv2.CAP_PROP_FPS, int(fps))
  #set name of video
      out = cv2.VideoWriter(data_string+&quot;_vid&quot;+str(i).zfill(2)+&quot;.avi&quot;,fourcc, fps, (x,y))
      start_time = time.time()
  #start recording for a determined duration
      while( int(time.time() - start_time) &lt; capture_duration+1 ):
          ret, frame = cap.read()
          if ret==True:
              #frame = cv2.flip(frame,0)
              out.write(frame)
              cv2.imshow('frame',frame)
          else:
              break
  
      cap.release()
      out.release()
      cv2.destroyAllWindows()
  #Here I added time.sleep to wait for the next capture
      time.sleep(vids_lapse-capture_duration)
  </code></pre>
  <p>With this code above, my video of 30fps is shorter than expected, as it cannot take more than ~5fps.</p>
  <p>I've seen I can use multithread, and I'm able to record a video with 30fps (at least that's what ffprobe says, although I'm not sure it is) with the following code:</p>
  <pre><code>from threading import Thread
  import cv2, time
  
  #again define resolution and fps
  x=1920
  y=1080
  fps=30
  
  class VideoStreamWidget(object):
      def __init__(self, src=2):
          self.capture = cv2.VideoCapture(src)
          self.capture.set(3,int(x))
          self.capture.set(4,int(y))
          self.capture.set(cv2.CAP_PROP_FPS, int(fps))
          # Start the thread to read frames from the video stream
          self.thread = Thread(target=self.update, args=())
          self.thread.daemon = True
          self.thread.start()
  
      def update(self):
          # Read the next frame from the stream in a different thread
          while True:
              if self.capture.isOpened():
                  (self.status, self.frame) = self.capture.read()
              time.sleep(.03)
  
      def show_frame(self):
          out.write(self.frame)
          # Display frames in main program
          cv2.imshow('frame', self.frame)
          key = cv2.waitKey(1)
          if key == ord('q'):
              self.capture.release()
              out.release()
              cv2.destroyAllWindows()
              exit(1)
  
  if __name__ == '__main__':
      fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
      out = cv2.VideoWriter(&quot;output.avi&quot;,fourcc, fps, (x,y))
      video_stream_widget = VideoStreamWidget()
      while True:
          try:
              video_stream_widget.show_frame()
          except AttributeError:
              pass
  </code></pre>
  <p>The problem of this second code is that I don't really know how to introduce my arguments of (recording during 5 seconds, wait 10 seconds, and so on) or how many threads I'm using (I can specify number of buffers from my camera using V4L2 tools, don't know how relevant is it).</p>
  <p>So my question is: Any idea how could I do it? Is it possible to merge both codes? Or is there a better way?</p>
  <p>Here are some guides I've checked but couldn't get it working with my code:
  <a href="https://www.pyimagesearch.com/2015/12/21/increasing-webcam-fps-with-python-and-opencv/" rel="nofollow noreferrer">https://www.pyimagesearch.com/2015/12/21/increasing-webcam-fps-with-python-and-opencv/</a>
  <a href="https://github.com/gilbertfrancois/video-capture-async" rel="nofollow noreferrer">https://github.com/gilbertfrancois/video-capture-async</a></p>
  <p>Thanks a lot in advance!</p>
  <p>cheers!</p>
  <h1>EDIT</h1>
  <p>I finally got something to work. Nevertheless, even if my video is 30fps, it still looks like a 5fps video... Seems like all the pictures captured are almost the same... Any idea how to solve this?</p>
  <p>Here the code:</p>
  <pre><code>rom threading import Thread, Lock
  import cv2, time
  
  #choose resolution
  x=1920
  y=1080
  fps=60
  
  # The duration in seconds of the video captured (s)
  capture_duration = 5
  
  # Days
  Days=1
  
  # Lapse of time between videos (s), from beginning to beginning
  vids_lapse=10
  
  data_string= time.strftime(&quot;%m-%d-%H-%M-%S&quot;)
  
  class CameraStream(object):
      def __init__(self, src=0):
          self.stream = cv2.VideoCapture(src)
          self.stream.set(3,int(x))
          self.stream.set(4,int(y))
          self.stream.set(cv2.CAP_PROP_FPS,int(fps))
          self.video_file_name = data_string+&quot;_vid&quot;+str(i).zfill(2)+&quot;.avi&quot;
          (self.grabbed, self.frame) = self.stream.read()
          self.started = False
          self.read_lock = Lock()
          
          # Set up codec and output video settings
          self.codec = cv2.VideoWriter_fourcc('M','J','P','G')
          self.out = cv2.VideoWriter(self.video_file_name, self.codec, fps, (x, y))
  
      def start(self):
          if self.started:
              print(&quot;already started!!&quot;)
              return None
          self.started = True
          self.thread = Thread(target=self.update, args=())
          self.thread.start()
          return self
  
      def update(self):
          while self.started:
              (grabbed, frame) = self.stream.read()
              self.read_lock.acquire()
              self.grabbed, self.frame = grabbed, frame
              self.read_lock.release()
              # ~ time.sleep(1/fps)
  
      def read(self):
          self.read_lock.acquire()
          frame = self.frame.copy()
          self.read_lock.release()
          self.save_frame()
          return frame
      
      def save_frame(self):
          # Save obtained frame into video output file
          self.out.write(self.frame)
  
      def stop(self):
          self.started = False
          self.thread.join()
          self.stream.release()
          self.out.release()
  
      def __exit__(self, exc_type, exc_value, traceback):
          self.stream.release()
  
  
  if __name__ == &quot;__main__&quot; :
      for i in range (0,2):
          start_time = time.time()
          cap = CameraStream(0).start()
          while (int(time.time() - start_time) &lt; capture_duration):
              frame = cap.read()
              cv2.imshow('webcam', frame)
              # ~ if cv2.waitKey(1) == 27 :
          cap.stop()
          cv2.destroyAllWindows()
          time.sleep(vids_lapse-capture_duration)
  </code></pre>