title: OpenCV (cv2 in Python) VideoCapture not releasing camera after deletion
body: <p>I am relatively new to Python, just having learnt it over the past month or so and have hacked this together based off examples and others' code I found online.</p>
  
  <p>I have gotten a Tkinter GUI to display the feed from a webcam as a loop of continuously updated images on a canvas. Quitting the GUI and re-running the script every other time results in this error:</p>
  
  <pre><code>Exception in Tkinter callback
  Traceback (most recent call last):
      File "C:\Python27\lib\lib-tk\Tkinter.py", line 1410, in __call__
          return self.func(*args)
      File "C:\Python27\lib\lib-tk\Tkinter.py", line 495, in callit
          func(*args)
     File "C:\...\cv2_cam_v8.py", line 20, in update_video
          (self.readsuccessful,self.f) = self.cam.read()
  SystemError: NULL object passed to Py_BuildValue
  </code></pre>
  
  <p>When the error happens no images get read and the videofeed recieves no images to update the canvas. The script runs normally with no errors the first time and every second time. From previous tests with the VideoCapture function in the cv2 module, I found that I had to delete the camera object to release it so that subsequent runs are able to capture the camera stream with no issue. Checks on the namespace by typing <code>who</code> in the console do not show <code>cam</code> so I know it is being deleted properly after the GUI is closed. I do not understand why cv2's read function is giving an error. I think it is only happening every second time because when the error occurs, some garbage collection or error handling deletes or frees up something to do with the camera but I do not know what this is...</p>
  
  <p>Here is my code:</p>
  
  <pre><code>import cv2
  import Tkinter as tk
  from PIL import Image, ImageTk
  
  
  class vid():      
      def __init__(self,cam,root,canvas):
          self.cam = cam
          self.root = root
          self.canvas = canvas
  
      def update_video(self):
          (self.readsuccessful,self.f) = self.cam.read()
          self.gray_im = cv2.cvtColor(self.f, cv2.COLOR_RGB2GRAY)
          self.a = Image.fromarray(self.gray_im)
          self.b = ImageTk.PhotoImage(image=self.a)
          self.canvas.create_image(0,0,image=self.b,anchor=tk.NW)
          self.root.update()
          self.root.after(33,self.update_video)
  
  
  if __name__ == '__main__':
      root = tk.Tk()
      videoframe = tk.LabelFrame(root,text='Captured video')
      videoframe.grid(column=0,row=0,columnspan=1,rowspan=1,padx=5, pady=5, ipadx=5, ipady=5)
      canvas = tk.Canvas(videoframe, width=640,height=480)
      canvas.grid(column=0,row=0)
      cam = cv2.VideoCapture(2)
      x = vid(cam,root,canvas)
      root.after(0,x.update_video)
      button = tk.Button(text='Quit',master=videoframe,command=root.destroy)
      button.grid(column=0,row=1)
      root.mainloop()
      del cam
  </code></pre>
  
  <p>Refactoring the code like this:</p>
  
  <pre><code>def update_video(cam,root,canvas):
      (readsuccessful,f) = cam.read()
      gray_im = cv2.cvtColor(f, cv2.COLOR_RGB2GRAY)
      a = Image.fromarray(gray_im)
      b = ImageTk.PhotoImage(image=a)
      canvas.create_image(0,0,image=b,anchor=tk.NW)
      root.update()
      root.after(33,update_video(cam,root,canvas))
  
  if __name__ == '__main__':
      root = tk.Tk()
      videoframe = tk.LabelFrame(root,text='Captured video')
      videoframe.grid(column=0,row=0,columnspan=1,rowspan=1,padx=5, pady=5, ipadx=5, ipady=5)
      canvas = tk.Canvas(videoframe, width=640,height=480)
      canvas.grid(column=0,row=0)
      cam = cv2.VideoCapture(2)
      root.after(0,update_video(cam,root,canvas))
      button = tk.Button(text='Quit',master=videoframe,command=root.destroy)
      button.grid(column=0,row=1)
      root.mainloop()
      del cam
  </code></pre>
  
  <p>does not display the button in the GUI and gives this error after closing the window:</p>
  
  <pre><code>RuntimeError: Too early to create image
  </code></pre>
  
  <p>I have 3 questions</p>
  
  <p>1 - <strike>How can I prevent either exception?</strike>
  UPDATE: changing "root.after(0,update_video(cam,root,canvas))" to "root.after(0,lambda: update_video(cam,root,canvas))" and "update_video(cam,root,canvas)" to "update_video(cam,root,canvas,event=None)" OR passing the arguments to the callback using this format: "root.after(time_to_wait, callback, arguments, master)" fixes the second error (and others I did not post). Also as kobejohn pointed out, adding a try: except block also fixes the second error. Please see his answer for more details.</p>
  
  <p>2 - <strike>Is there a faster, more efficient function than .read() in cv2? Edit: Is there a way to refactor my code to get higher framerates? The read function is the only one listed in the docs and I just read somewhere that if it is not in the docs, then it is not available. This method only gives me about 5fps, where 10-20fps would be much more acceptable.</strike>
  UPDATE: From the discrepancies between kobejohn's tests and mine with different cameras, the low framerate is a result of poor quality webcams. Better quality webcams yield higher framerates.</p>
  
  <p>3 - <strike>I have been reading that update() should be avoided as much as possible but how do I get the canvas to redraw the image otherwise (or implement update_idletasks() with this code)?. Do I have to implement some sort of threading or can I avoid that?</strike>
  UPDATE: I have gotten the code to work without using the update() method but have to look at implementing threading anyway because when I start recording the videofeed from a button the main GUI, it freezes/ becomes unresponsive.</p>
  
  <p>The finished program will be used in Ubuntu and windows (possibly on macs as well). I am running Windows 7, IDE is Spyder 2.1.11 (Python 2.7.3).</p>
  
  <p>Thank you in advance, any advice and/or solutions will be much appreciated!</p>
  
  <p>Regards,</p>
  
  <p>S. Chia</p>