videocapture test open example camara python opencv tkinter video-capture

test - video capture python opencv



OpenCV(cv2 en Python) VideoCapture no libera la cámara después de la eliminación (2)

Soy relativamente nuevo en Python, después de haberlo aprendido durante el último mes y haberlo pirateado a partir de ejemplos y el código de otros que encontré en línea.

Obtuve una GUI de Tkinter para mostrar el feed de una cámara web como un bucle de imágenes continuamente actualizadas en un lienzo. Salir de la GUI y volver a ejecutar el script cada dos veces da como resultado este 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 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

Cuando ocurre el error, no se leen las imágenes y el videofeed no recibe imágenes para actualizar el lienzo. La secuencia de comandos se ejecuta normalmente sin errores la primera vez y cada dos veces. A partir de pruebas anteriores con la función VideoCapture en el módulo cv2, descubrí que tenía que eliminar el objeto de la cámara para liberarlo, de modo que las ejecuciones posteriores puedan capturar la secuencia de la cámara sin problemas. Comprueba el espacio de nombres escribiendo who en la consola no muestra la cam así sé que se borrará correctamente después de que se cierre la GUI. No entiendo por qué la función de lectura de cv2 está dando un error. Creo que solo ocurre cada dos veces porque cuando se produce el error, la recolección de basura o el manejo de errores eliminan o liberan algo que hacer con la cámara, pero no sé qué es esto ...

Aquí está mi código:

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

Refactorizando el código de esta manera:

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

no muestra el botón en la GUI y muestra este error después de cerrar la ventana:

RuntimeError: Too early to create image

Tengo 3 preguntas

1 - ¿Cómo puedo evitar cualquier excepción? ACTUALIZACIÓN: cambiando "root.after (0, update_video (cam, root, canvas))" a "root.after (0, lambda: update_video (cam, root, canvas))" y "update_video (cam, root, canvas) "to" update_video (cam, root, canvas, event = None) O pasando los argumentos a la devolución de llamada usando este formato: "root.after (time_to_wait, callback, arguments, master)" corrige el segundo error (y otros que hice) no publicar). También como Kobejohn señaló, agregando un try: except block también corrige el segundo error. Por favor, consulte su respuesta para más detalles.

2 - ¿Hay una función más rápida y más eficiente que .read () en cv2? Editar: ¿Hay alguna manera de refactorizar mi código para obtener mayores tasas de fotogramas? La función de lectura es la única que aparece en los documentos y acabo de leer en alguna parte que si no está en los documentos, entonces no está disponible. Este método solo me da unos 5 fps, donde 10-20 fps serían mucho más aceptables. ACTUALIZACIÓN: a partir de las discrepancias entre las pruebas de Kobejohn y las mías con diferentes cámaras, la baja tasa de cuadros es el resultado de cámaras web de baja calidad. Las cámaras web de mejor calidad producen tasas de fotogramas más altas.

3 - He estado leyendo que la actualización () debe evitarse tanto como sea posible, pero ¿cómo puedo obtener el lienzo para redibujar la imagen de otra manera (o implementar update_idletasks () con este código) ?. ¿Tengo que implementar algún tipo de subprocesamiento o puedo evitar eso? ACTUALIZACIÓN: He obtenido el código para trabajar sin utilizar el método update (), pero tengo que considerar la implementación de subprocesamiento de todos modos porque cuando comienzo a grabar el videofeed desde un botón de la GUI principal, se congela / deja de responder.

El programa terminado se usará en Ubuntu y Windows (posiblemente en Mac también). Estoy ejecutando Windows 7, IDE es Spyder 2.1.11 (Python 2.7.3).

Gracias de antemano, cualquier consejo y / o solución será muy apreciado.

Saludos,

S. Chia


¿Puedes probar este código y ver qué FPS obtienes? Incluí un cálculo de FPS para que podamos comparar notas. (editar: también qué errores. No obtuve los errores que obtuve en el código original y obtengo cero errores con el código a continuación)

Empecé desde cero solo para ver si se me ocurría algo diferente. Hay algunas diferencias:

  1. Hubo un error (¿menor?): Los canales de color predeterminados de opencv son BGR en lugar de RGB. Así que cambie su conversión de cv2.COLOR_RGB2GRAY de cv2.COLOR_RGB2GRAY -> cv2.COLOR_BGR2GRAY . Puede ver en el ejemplo de VideoCapture que hacen algo similar.
  2. Utilicé una etiqueta simple para mostrar la imagen en lugar de un lienzo. No he usado el lienzo antes, así que no estoy seguro de qué hacer con él. Con una etiqueta simple, debe mantener una referencia a la imagen que está mostrando para que no se recoja basura. Puedes ver eso en update_image ().
  3. Para las devoluciones de llamada, utilicé lambdas con argumentos (como mencionaste en tu comentario). De lo contrario, cuando realiza una llamada a función con argumentos, está ejecutando la devolución de llamada inmediatamente en lugar de registrarla. Termina pareciendo que está funcionando, pero no está haciendo lo que usted pensaría. Alternativamente, puede usar functools.partial si prefiere empaquetar sus argumentos y enviarlos como una función no llamada.
  4. También para la devolución de llamada, agregué una opción: excepto el bloque para el caso en que la devolución de llamada comience a ejecutarse después de que se haya destruido la raíz. No sé si esta es la forma "correcta" de hacerlo, pero funciona hasta donde yo sé.

Con este código, obtengo 15 FPS y no hay errores en 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()


Resuelto OpenCV 2.4.2 / cv2 en python

Por alguna extraña razón, no pude encontrar el método de ''liberación'' antes y otros foros, páginas específicamente mencionaban que los enlaces de python a opencv no incluían el método de publicación. Quizás esto solo se aplicaba al usar ''importar cv''. Hice mi prototipo inicial usando el último y por alguna razón me perdí el método de ''liberación'' en cv2 cuando estaba buscando un método ReleaseCapture.

Acabo de encontrarlo en los documentos: http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html

import cv2 cam=cv2.VideoCapture(0) cam.release