OpenCV (cv2 in Python) VideoCapture not releasing camera after deletion

2024/10/14 11:17:47

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.

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:

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 callitfunc(*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

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 who in the console do not show cam 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...

Here is my code:

import cv2
import Tkinter as tk
from PIL import Image, ImageTkclass vid():      def __init__(self,cam,root,canvas):self.cam = camself.root = rootself.canvas = canvasdef 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

Refactoring the code like this:

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

does not display the button in the GUI and gives this error after closing the window:

RuntimeError: Too early to create image

I have 3 questions

1 - How can I prevent either exception? 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.

2 - 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. 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.

3 - 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? 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.

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

Thank you in advance, any advice and/or solutions will be much appreciated!

Regards,

S. Chia

Answer

Solved! OpenCV 2.4.2/ cv2 in python

For some strange reason, I could not find the 'release' method before and other forums, pages specifically mentioned that the python bindings to opencv did not include the release method. Perhaps this only applied when using 'import cv'. I did my initial prototyping using the latter and for some reason missed the 'release' method in cv2 when I was looking for a ReleaseCapture method.

Just found it in the docs: http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html

import cv2cam=cv2.VideoCapture(0)
cam.release
https://en.xdnf.cn/q/69422.html

Related Q&A

Paho MQTT Python Client: No exceptions thrown, just stops

I try to setup a mqtt client in python3. This is not the first time im doing this, however i came across a rather odd behaviour. When trying to call a function, which contains a bug, from one of the c…

SSH Key-Forwarding using python paramiko

We currently run a script on our desktop that uses paramiko to ssh to a remote linux host. Once we are on the remote linux host we execute another command to log into another remote machine. What we wa…

Is it possible to ignore Matplotlib first default color for plotting?

Matplotlib plots each column of my matrix a with 4 columns by blue, yellow, green, red.Then, I plot only the second, third, fourth columns from matrix a[:,1:4]. Is it possible to make Matplotlib ignore…

`sock.recv()` returns empty string when connection is dead on non-blocking socket

I have a non-blocking socket in Python called sock. According to my understanding the recv() method should raise an exception if the connection has been closed by the peer, but it returns an empty stri…

Iteration order with pandas groupby on a pre-sorted DataFrame

The SituationIm classifying the rows in a DataFrame using a certain classifier based on the values in a particular column. My goal is to append the results to one new column or another depending on cer…

How do I pass an exception between threads in python

I need to pass exceptions across a thread boundary.Im using python embedded in a non thread safe app which has one thread safe call, post_event(callable), which calls callable from its main thread.I am…

How to check if a docker instance is running?

I am using Python to start docker instances.How can I identify if they are running? I can pretty easily use docker ps from terminal like:docker ps | grep myimagenameand if this returns anything, the i…

Retrieving facets and point from VTK file in python

I have a vtk file containing a 3d model,I would like to extract the point coordinates and the facets.Here is a minimal working example:import vtk import numpy from vtk.util.numpy_support import vtk_to_…

Tensorflow: feed dict error: You must feed a value for placeholder tensor

I have one bug I cannot find out the reason. Here is the code:with tf.Graph().as_default():global_step = tf.Variable(0, trainable=False)images = tf.placeholder(tf.float32, shape = [FLAGS.batch_size,33,…

Pass many pieces of data from Python to C program

I have a Python script and a C program and I need to pass large quantities of data from Python script that call many times the C program. Right now I let the user choose between passing them with an AS…