GUI con Tkinter y OpenCV en Python | Videos ?

Por Administrador

En el post anterior vimos como crear una interfaz gráfica de usuario y en ella insertamos imágenes, para ello estuvimos usando OpenCV y Tkinter para desarrollar la GUI.

En este post construiremos también una GUI, pero esta vez leeremos un video de entrada. Para ello crearemos dos programas, el primero para leer un videostreaming en la GUI y otro para leer un video de entrada pregrabado.

CONTENIDO

  • ¿Cómo leer un video streaming usando OpenCV y Tkinter?
    • Importando los paquetes necesarios
    • Desarrollando las funciones para visualizar el video streaming en la GUI
    • Creando la interfaz gráfica de usuario con Tkinter
  • ¿Cómo buscar y leer un video pre-grabado usando OpenCV y Tkinter?
    • Importando los paquetes necesarios
    • Desarrollando las funciones para buscar y leer el video que entrada
    • Creando la interfaz gráfica de usuario con Tkinter

¿Cómo leer un video streaming usando OpenCV y Tkinter?

Figura 1: Visualización del videostreaming en la GUI que además contiene dos botones.

La GUI que estaremos construyendo en esta sección será bastante sencilla. Esta tendrá:

  • Botón «Iniciar», el cual nos permitirá iniciar con la visualización del videostreaming.
  • Botón «Finalizar», que nos permitirá acabar con la visualización.
  • Label para el video, que nos permitirá ubicar el video en directo en la GUI.

Importando los paquetes necesarios

Vamos a empezar creando un script de python llamado gui_webcam.py.

from tkinter import *
from PIL import Image
from PIL import ImageTk
import cv2
import imutils

Línea 1: Importamos Tkinter que nos servirá para construir la GUI con distintos widgets. 

Línea 2 y 3: Al igual que en el post anterior, usaremos PIL (Python Imaging Library) que nos permitirá relacionar el video en directo leído con OpenCV, con la GUI. Para ello necesitamos Image e ImageTk.

Si no tienes instalada la librería puedes hacerlo con: pip install pillow. 

Línea 4 a 5: Importamos OpenCV e imutils.

Desarrollando las funciones para poder visualizar el video streaming en la GUI

Ahora desarrollaremos 3 funciones que nos servirán para visualizar el video streaming y también detenerlo, veamos:

def iniciar():
    global cap
    cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
    visualizar()

Línea 7: Creamos la función iniciar, que nos permitirá iniciar con el videostremaming.

Línea 8: Declaramos global cap, que nos permitirá emplear dicha variable en diferentes funciones.

Línea 9: Como vamos a leer un videostreaming, empleamos cv2.VideoCapture y en esta especificamos 0, para leer el video dado por la webcam. Puedes revisar este post, si quieres profundizar un poquito más sobre la lectura de videos con OpenCV.

Línea 10: Ahora procederemos a llamar a la función visualizar (que crearemos a continuación). Esta nos ayudará a leer cada fotograma, similar a como lo hacíamos con OpenCV.

def visualizar():
    global cap
    if cap is not None:
        ret, frame = cap.read()
        if ret == True:
            frame = imutils.resize(frame, width=640)
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

            im = Image.fromarray(frame)
            img = ImageTk.PhotoImage(image=im)

            lblVideo.configure(image=img)
            lblVideo.image = img
            lblVideo.after(10, visualizar)
        else:
            lblVideo.image = ""
            cap.release()

Línea 12: Creamos la función visualizar.

Línea 13: Al igual que con la función anterior, declaramos global cap.

Línea 14: Si cap no es None, es decir, si se ha iniciado la captura del video, procederemos a leerlo y visualizarlo.

Línea 15 a 18: Procedemos a leer cada fotograma, lo redimensionamos a 640 pixeles de ancho y con cv2.cvtColor transformamos de BGR a RGB. Esto debido a que OpenCV lee las imágenes/fotogramas por defecto en BGR mientras que PIL trabaja en RGB.

Línea 20 y 21: Para incorporar frame a la GUI vamos a usar Image.fromarray y luego ImageTk.PhotoImage, de este modo obtendremos los fotogramas en formato ImageTk.

Línea 23 y 24: Ahora vamos a ubicar img en la GUI mendiante lblVideo (este label lo estaremos declarando en la línea 43). Luego tendremos que agregar la línea 24 para que los fotogramas puedan visualizarse en la GUI y no sean borrados.

Línea 25: Ya que se deben leer constantemente múltiples fotogramas, necesitamos un ciclo. Esto lo lograremos con after  que nos permitirá llamar a la función visualizar, cada 10 milisegundos. Para más información puedes darle clic aquí.

Línea 26 a 28: Cuando la visualización haya terminado o ret toma el valor de False, lblVideo se vacía y se libera la captura.

def finalizar():
    global cap
    cap.release()

Línea 30: Creamos la función finalizar, que nos ayudará a finalizar la visualización del video streaming.

Línea 31 y 32: Declaramos global cap, luego se libera la captura.

Creando un la interfaz gráfica de usuario con Tkinter

cap = None
root = Tk()

btnIniciar = Button(root, text="Iniciar", width=45, command=iniciar)
btnIniciar.grid(column=0, row=0, padx=5, pady=5)

btnFinalizar = Button(root, text="Finalizar", width=45, command=finalizar)
btnFinalizar.grid(column=1, row=0, padx=5, pady=5)

lblVideo = Label(root)
lblVideo.grid(column=0, row=1, columnspan=2)

root.mainloop()

Línea 34: Declaramos la variable cap = None, que es la que usamos en las funciones anteriores.

Línea 35: Creamos la venta principal.

Línea 37: Usando Button crearemos el botón Iniciar que nos permitirá empezar con el video en directo en la GUI. En él especificamos el nombre de la ventana a visualizar, el texto que tendrá el botón y le especificamos un ancho de 45. Añadiremos command= iniciar para que cada vez que se presione este botón, se llame a la función iniciar.

Línea 38: Vamos a especificar en donde se va a ubicar el botón, para ello usamos grid. Como argumentos especificamos la columna y fila donde estará ubicado. Además con padx y pady añadimos cierta separación entre widgets o componentes. 

Línea 40 y 41: Similar a las líneas 37 y 38 seguimos el mismo procedimiento. En lo que va a diferir de dichas líneas es que el botón Finalizar, llamará a la función finalizar. Mientras que en la siguiente línea ubicaremos este botón en la columna 1 y fila 0.

Línea 43 y 44: Creamos un label llamado lblVideo que es en donde se visualizará el videostraming. Este estará presente en la ventana root en la columna 0, fila 1 y ocupará 2 columnas.

Línea 46: Con root.mainloop() creamos un ciclo sin fin para la ventana creada. De este modo la ventana estará disponible  hasta que el usuario la cierre.

Una vez que hemos terminado con el programa, podremos probarlo, para ello lo primero que obtendremos es la siguiente ventana:

Figura 2: Ventana de inicio del programa, que presenta las botones Iniciar y Finalizar.

Luego procederemos a pulsar el botón Iniciar y empezará nuestro videostreaming.

Figura 3: Visualización del video en directo en la GUI creada con Tkinter.

Ahora que ya tenemos la visualización del video, presionaremos el botón Finalizar, y se obtendremos lo siguiete:

Figura 4: Visualización de la GUI al momento que se ha presionado el botón Finalizar, luego de haberse realizado un videstreaming.

Entonces al presionar Finalizar se limpia el label del video y estamos listos para iniciar con el videostreaming en caso de ser necesario.

¿Cómo buscar y leer un video pre-grabado usando OpenCV y Tkinter?

Figura 5: Visualización del video de entrada en la GUI.

Ahora vamos a continuar con el segundo programa del post. En este veremos como elegir un video de entrada, para que este se visualice en la GUI desarrollada con Tkinter.

Cabe descatar que he preparado videos en .mp4 y .avi, ya que especificaremos más adelante que solo leeremos este tipo de archivos. Toma esto solo como un ejemplo, ya que podrías añadir otros formatos.

La GUI que desarrollaremos tendrá:

  • Botón «Elegir y visualizar video», el cual nos permitirá elegir el video de entrada y por consiguiente poder visualizarlo.
  • Label «Video de entrada:», este nos servirá como información.
  • Label con el path del video de entrada elegido, que se ubicará a la derecha del label anterior..
  • Label para el video, que nos permitirá ubicar el video en la GUI.

Importando los paquetes necesarios

Debido a que el programa que realizaremos tiene similitudes con el anterior, describiré brevemente las funciones antes usadas. Ahora si, vamos a empezar creando un script de python llamado gui_leer_video.py. 

from tkinter import *
from tkinter import filedialog
from PIL import Image
from PIL import ImageTk
import cv2
import imutils

Línea 1 a 6: Importamos todos los paquete necesarios que usaremos. Como podrás ver he añadido la línea dos con filedialog, que nos permitirá más adelante abrir un cuadro de diálogo para elegir el video de entrada.

Desarrollando las funciones para buscar y leer el video que entrada

A continuación crearemos la función elegir_visualizar_video que será la que nos permitirá elegir el video de entrada y visualizarlo.

def elegir_visualizar_video():
    global cap

    if cap is not None:
        lblVideo.image = ""
        cap.release()
        cap = None

    video_path = filedialog.askopenfilename(filetypes = [
        ("all video format", ".mp4"),
        ("all video format", ".avi")])
    if len(video_path) > 0:
        lblInfoVideoPath.configure(text=video_path)
        cap = cv2.VideoCapture(video_path)
        visualizar()
    else:
        lblInfoVideoPath.configure(text="Aún no se ha seleccionado un video")

Línea 8: Creamos la función elegir_visualizar_video.

Línea 9: Declaramos global cap, ya que al igual que en el programa anterior, estaremos usando esta variable en la función (visualizar) que veremos luego.

Línea 11 a 14: Cada vez que se ingrese a esta función, si es que hay un video visualizándose en la GUI entrará a esta condición. Por lo tanto se limpiara el label con el video, se libera la captura y además establecemos cap = None.

Línea 16 a 18: Mediante filedialog.askopenfilename abriremos un diálogo de archivos en el cual podremos especificar los tipos de archivos que este nos pueda mostrar. En filetypes especificaremos los tipos de archivos, que en este caso serán mp4 y avi (aunque podrías especificar otros tipos de archivos).

Línea 19: Si se ha elegido algún archivo entonces ingresamos a esta condición.

Línea 20 y 21: Como un archivo de video ha sido elegido, entonces vamos a configurar lblInfoVideoPath (que estaremos declarando en la línea 54), para que en este aparezca el path del video seleccionado.

Línea 22: Vamos a llamar la función visualizar, esta es similar a la que vimos en el anterior programa salvo por una línea que veremos más adelante.

Línea 23 y 24: Si no se ha elegido ningún archivo entonces vamos a configurar lblInfoVideoPath para que se muestre el mensaje de «Aún no se ha seleccionado un video». 

Ahora continuamos con la construcción de la función visualizar. Ya que esta es prácticamente igual a la función visualizar que vimos temprano en este post, solo me centraré en la línea que difiere.

def visualizar():
    global cap
    if cap is not None:
        ret, frame = cap.read()
        if ret == True:
            frame = imutils.resize(frame, width=640)
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

            im = Image.fromarray(frame)
            img = ImageTk.PhotoImage(image=im)

            lblVideo.configure(image=img)
            lblVideo.image = img
            lblVideo.after(10, visualizar)
        else:
            lblInfoVideoPath.configure(text="Aún no se ha seleccionado un video")
            lblVideo.image = ""
            cap.release()

Línea 41: Si la visualización del video de entrada termina y por lo tanto ret es igual a False. Entonces el label lblInfoVideoPath se modifica al texto: «Aún no se ha seleccionado un video». Para que el usuario pueda elegir otro video a visualizar.

Creando la interfaz gráfica de usuario con Tkinter

cap = None
root = Tk()

btnVisualizar = Button(root, text="Elegir y visualizar video", command=elegir_visualizar_video)
btnVisualizar.grid(column=0, row=0, padx=5, pady=5, columnspan=2)

lblInfo1 = Label(root, text="Video de entrada:")
lblInfo1.grid(column=0, row=1)

lblInfoVideoPath = Label(root, text="Aún no se ha seleccionado un video")
lblInfoVideoPath.grid(column=1, row=1)

lblVideo = Label(root)
lblVideo.grid(column=0, row=2, columnspan=2)

root.mainloop()

Línea 45: Declaramos la variable cap = None.

Línea 46: Creamos la venta principal.

Línea 48: Usando Button crearemos el botón Elegir y visualizar video que nos permitirá elegir y visualizar el video de entrada en la GUI. En él especificamos el nombre de la ventana a visualizar, el texto que tendrá el botón. Añadiremos command= elegir_visualizar_video para que cada vez que se presione este botón, se llame a la función elegir_visualizar_video.

Línea 49: Especificamos en donde se va a ubicar el botón, para ello usamos grid. Como argumentos especificamos la columna y fila donde estará ubicado. Además con padx y pady añadimos cierta separación entre widgets o componentes, finalmente especificamos columnspan=2, para indicar que el botón va a usar 2 columnas. 

Línea 51 y 52: Creamos un label llamado lblInfo1, que se va a visualizar en root, y cuyo texto dirá: Video de entrada. Este label se ubicará en la columna 0, fila 1.

Línea 54 y 55: Creamos un label llamado lblInfoVideoPath, que se va a visualizar en root, y cuyo texto dirá: Aún no se ha seleccionado un video. Al contenido de este label irá cambiando conforme elijamos un video de entrada. Este label se ubicará en la columna 1, fila 1.

Línea 57 y 58: Creamos un label llamado lblVideo que es en donde se visualizará el video de entrada Este estará presente en la ventana root en la columna 0, fila 2 y ocupará 2 columnas.

Línea 60: Con root.mainloop() creamos un ciclo sin fin para la ventana creada. De este modo la ventana estará disponible  hasta que el usuario la cierre.

Una vez que hayamos terminado. ¡Vamos a probar el programa!.

Figura 6: Primera ventana de GUI.

Ahora que tenemos la ventana de inicio de la GUI, procederemos a darle clic al botón Elegir y visualizar video y nos aparecerá un cuadro de diálogo de archivos para elegir el video de entrada.

Figura 7: Elegir el video de entrada que deseemos reproducir.

Ahora que hemos elegido el video de entrada, podremos visualizar lo siguiente:

Figura 8: Reproducción del video de entrada elegido.

Como podemos visualizar en la figura 8, a más de la reproducción del video podemos ver que el label de la derecha de «Video de entrada», se encuentra el path del video que se está leyendo actualmente.

Finalmente si el video que se está reproduciendo se termina, entonces podremos visualizar lo siguiente:

Figura 9: Visualización de la GUI al momento que se haya terminado de reproducir el video.

Y hemos llegado al final de este post, espero que te haya gustado mucho. Nos vemos en un siguiente post o videotutorial. 🙂