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

痴心易碎 提交于 2019-12-05 08:32:16

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 cv2

cam=cv2.VideoCapture(0)
cam.release

Can you try this code and see what FPS you get? I included an FPS calculation so we can compare notes. (edit: also what errors. I didn't get the errors you got in the original code and I get zero errors with the code below)

I started from scratch just to see if I came up with something different. There are a few differences:

  1. There was a (minor?) bug: the opencv default color channels are BGR rather than RGB. So change your grascale conversion from cv2.COLOR_RGB2GRAY --> cv2.COLOR_BGR2GRAY. You can see in the VideoCapture example they do something similar.
  2. I used a simple label to display the image instead of a canvas. I haven't used the canvas before, so I'm not sure what you need to do with it. With a simple label, you have to keep a reference to the image you are displaying so it doesn't get garbage collected. You can see that in update_image().
  3. For callbacks, I used lambdas with arguments (as you mentioned in your comment). Otherwise, when you make a function call with arguments, you are running the callback immediately instead of registering it. Ends up looking like it is working, but it's not doing quite what you would think. Alternately, you can use functools.partial if you prefer to package up your arguments and send that as an uncalled function.
  4. Also for the callback, I added a try: except block for the case that the callback starts running after root has been destroyed. I don't know if this is the 'right' way to do it, but it works as far as I know.

With this code, I get 15 FPS and no errors on windows 7:

from collections import deque
import cv2
import Image, ImageTk
import time
import Tkinter as tk

def quit_(root):
    root.destroy()

def update_image(image_label, cam):
    (readsuccessful, f) = cam.read()
    gray_im = cv2.cvtColor(f, cv2.COLOR_BGR2GRAY)
    a = Image.fromarray(gray_im)
    b = ImageTk.PhotoImage(image=a)
    image_label.configure(image=b)
    image_label._image_cache = b  # avoid garbage collection
    root.update()


def update_fps(fps_label):
    frame_times = fps_label._frame_times
    frame_times.rotate()
    frame_times[0] = time.time()
    sum_of_deltas = frame_times[0] - frame_times[-1]
    count_of_deltas = len(frame_times) - 1
    try:
        fps = int(float(count_of_deltas) / sum_of_deltas)
    except ZeroDivisionError:
        fps = 0
    fps_label.configure(text='FPS: {}'.format(fps))


def update_all(root, image_label, cam, fps_label):
    update_image(image_label, cam)
    update_fps(fps_label)
    root.after(20, func=lambda: update_all(root, image_label, cam, fps_label))


if __name__ == '__main__':
    root = tk.Tk()
    # label for the video frame
    image_label = tk.Label(master=root)
    image_label.pack()
    # camera
    cam = cv2.VideoCapture(0)
    # label for fps
    fps_label = tk.Label(master=root)
    fps_label._frame_times = deque([0]*5)  # arbitrary 5 frame average FPS
    fps_label.pack()
    # quit button
    quit_button = tk.Button(master=root, text='Quit',
                            command=lambda: quit_(root))
    quit_button.pack()
    # setup the update callback
    root.after(0, func=lambda: update_all(root, image_label, cam, fps_label))
    root.mainloop()
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!