ELIGIENDO VIDEO DE ENTRADA ? + DETECCIÓN FACIAL ? | GUI con Tkinter y OpenCV en Python

Por Administrador

Y empezamos un nuevo post, en el que también estaremos hablando sobre la creación de una GUI con Tkinter, ayudándonos de OpenCV. En esta ocasión vamos a fusionar los dos programas que realizamos en el post anterior: GUI con Tkinter y OpenCV en Python | Videos ?. De tal modo que crearemos una GUI en la cual podamos elegir entre leer un video ya almacenado en nuestro ordenador o leer uno en vivo desde la webcam.

¡Vamos a empezar!.

CONTENIDO

  • Construyendo la Interfaz Gráfica de Usuario con Tkinter para elegir un video de entrada leído con OpenCV, en donde se aplicará detección de rostros
    • Importando los paquetes necesarios
    • Leer el clasificador de rostros con OpenCV
    • Creando una función para elegir el video de entrada o video en directo mediante la selección de los radiobuttons
    • Función para visualizar un video en la GUI con OpenCV y Tkinter
    • Función para la detección de rostros con OpenCV
    • Función para finalizar y limpiar la visualización del video, se asignará al botón “Finalizar visualización y limpiar”
    • Construcción de la GUI con Tkinter
    • Probando la GUI creada con Tkinter

Construyendo la Interfaz Gráfica de Usuario con Tkinter para elegir un video de entrada leído con OpenCV, en donde se aplicará detección de rostros

Antes de describir la GUI que vamos a realizar, tengo que aclarar que este programa está basado en los programas desarrollados en el post anterior, por lo que si quieres profundizar un poquito más sobre ello, te recomiendo hecharle un vistazo a ese post o a su videotutorial.

Ahora sí, vamos a describir la GUI que construiremos.

Figura 1: Visualización de la GUI que tendremos que obtener al final del programa.

Como lo podemos apreciar en la figura 1, la GUI tendrá:

  • Label «VIDEO DE ENTRADA», que se ubicará en la sección superior de la GUI.
  • 2 radio buttons: «Elegir video» y «Video en directo». Estos nos permitirán determinar si se escoge un video ya almacenado en el computador o se va a realizar la captura desde una webcam.
  • Label informativo, que se encontrará debajo del radiobutton correspondiente a «Elegir video». Si dicho radiobutton es elegido, entonces se visualizarán los últimos 20 caracteres del path de dicho video.
  • Label para el video, que nos permitirá ubicar el video en la GUI.
  • Botón «Finalizar visualización y limpiar», que nos permitirá terminar con la visualización.

Importando los paquetes necesarios

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

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

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

Línea 2: filedialog, nos permitirá más adelante abrir un cuadro de diálogo para elegir el video de entrada.

Línea 3 y 4: Al igual que en el post anterior, usaremos PIL (Python Imaging Library) que nos permitirá relacionar el video 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 6 a 6: Importamos OpenCV e imutils.

Leer el clasificador de rostros con OpenCV

faceClassif = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")

Línea 8: Vamos a leer el clasificador de rostros entrenado con haar cascades, como lo hemos visto en otros tutoriales. La lectura la realizo en esta línea y no dentro de una función, ya que solo será necesario leer una vez dicho clasificador.

Creando una función para elegir el video de entrada o video en directo mediante la selección de los radiobuttons

Procederemos a darle funcionalidad a los radiobuttons, de tal forma que se pueda elegir si se lee un video almacenado ya en nuestro computador o capturar un videostreaming desde la webcam. A esta función la llamaremos en las líneas 77 y 78.

def video_de_entrada():
    global cap
    if selected.get() == 1:
        path_video = filedialog.askopenfilename(filetypes = [
            ("all video format", ".mp4"),
            ("all video format", ".avi")])
        if len(path_video) > 0:
            btnEnd.configure(state="active")
            rad1.configure(state="disabled")
            rad2.configure(state="disabled")

            pathInputVideo = "..." + path_video[-20:]
            lblInfoVideoPath.configure(text=pathInputVideo)
            cap = cv2.VideoCapture(path_video)
            visualizar()
    if selected.get() == 2:
        btnEnd.configure(state="active")
        rad1.configure(state="disabled")
        rad2.configure(state="disabled")
        lblInfoVideoPath.configure(text="")
        cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
        visualizar()

Línea 10: Declaramos la función video_de_entrada.

Línea 11: Declaramos global cap ya que vamos a usar dicha variable en distintas funciones dentro del programa.

Línea 12 a 24: En la línea 12 tenemos que si se ha elegido el primer radiobutton es decir si selected.get() == 1 entonces se abrirá un diálogo de archivos para escoger archivos .mp4 y .avi. Si se ha elegido un video, es decir si len(path_video) > 0, entonces el botón «Finalizar visualización y limpiar» se activará, mientras que los radiobuttos se desactivarán para no interferir con el video que se esté reproduciendo en la GUI en este momento.

En cuanto al label de información relacionado al path del video de entrada, este mostrará tres puntos suspensivos y los últimos 20 caracteres del path del video elegido. Esto se hará únicamente cuando se elija un video, no cuando se realice un el video en directo con la webcam (que corresponde a escoger el radiobutton 2).

Luego en la línea 23 establecemos path_video como argumento de cv2.VideoCapture para indicar que ese será el video que se leerá. Mientras que en la línea 24 se llamará a la función visualizar que veremos más adelante.

Línea 25 a 31: Si se ha elegido el segundo radiobutton es decir si selected.get() == 2, entonces quiere decir que el usuario desea realizar un video en directo con ayuda de la cámara. De este modo en la GUI, se realizará una acción similar a la del otro radiobutton, es decir que el botón «Finalizar visualización y limpiar» se activa, mientras que los radiobuttos se desactivan. El label de información sobre el path del video se limpiará y establecemos cv2.VideoCapture(0, cv2.CAP_DSHOW) para indicar que el video se realizará mediante el uso de la cámara del computador.

Mientras que en la línea 31, al igual que en el anterior radiobutton, llamará a la función visualizar.

Función para visualizar un video en la GUI con OpenCV y Tkinter

Vamos a crear la función visualizar, que nos permitirá visualizar el video almacenado en el computador o de la webcam, veamos:

def visualizar():
    global cap
    ret, frame = cap.read()
    if ret == True:
        frame = imutils.resize(frame, width=640)
        frame = deteccion_facilal(frame)
        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 = ""
        lblInfoVideoPath.configure(text="")
        rad1.configure(state="active")
        rad2.configure(state="active")
        selected.set(0)
        btnEnd.configure(state="disabled")
        cap.release()

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

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

Línea 35 a 45: Procedemos a leer los fotogramas del video, luego los redimensionaremos a un ancho de 640 pixeles y en la línea 38 estaremos aplicando la función deteccion_facial, la cual crearemos más adelante en la línea 55.

Para que se visualice el video con los colores correctos, transformaremos los fotogramas de BGR a RGB con cv2.cvtColor. Y luego convertimos las imágenes a fomato ImageTk, para en las líneas 43 y 44 insertarlas en la GUI mediante lblVideo.

Debido a que necesitamos leer constantemente múltiples fotogramas, requerimos un ciclo. Esto lo lograremos con after  que nos permitirá llamar a la función visualizar, cada 10 milisegundos.

Línea 46 a 53: Si por el contrario no hubieran fotogramas a leer, entonces se limpia lblVideo al igual que lblInfoVideoPath. Los radiobuttons vuelven a activarse y se los setea en 0 con selected.set(0), para que se limpie la selección previa.

Mientras que el botón «Finalizar visualización y limpiar» se desactiva y la captura se libera.

Función para la detección de rostros con OpenCV

En la función visualizar, en la línea 38 llamamos a la función deteccion_facial. Es ahora que vamos a construir esta función.

def deteccion_facilal(frame):
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = faceClassif.detectMultiScale(gray, 1.3, 5)
    for (x, y, w, h) in faces:
        frame = cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
    return frame

Línea 55: Declaramos la función deteccion_facilal, la cual pedirá como argumento a frame, para sobre este encontrar los rostros presentes.

Línea 56 y 57: Para este programa voy a transformar frame, de BGR a escala de grises. En la siguiente línea se procede a detectar los rostros presentes en gray que a su vez de almacenarán en faces .

NOTA: Para más información sobre como OpenCV realiza la detección facial con haar cascades te recomiendo ir a este tutorial: ? DETECCIÓN DE ROSTROS ? con Haar Cascades Python – OpenCV

Línea 58 y 59: Vamos a desempaquetar las coordenadas, ancho y alto de todos los rostros detectados, y en la línea 59 procedemos a dibujar un rectángulo de tal modo que rodee a cada rostro.

Línea 60: Esta función retornará frame.

Función para finalizar y limpiar la visualización del video, se asignará al botón «Finalizar visualización y limpiar»

Continuamos con la última función que crearemos, que es finalizar_limpiar. Esta permitirá acabar con la visualización y limpiar la GUI una vez que sea presionado el botón «Finalizar visualización y limpiar».

def finalizar_limpiar():
    lblVideo.image = ""
    lblInfoVideoPath.configure(text="")
    rad1.configure(state="active")
    rad2.configure(state="active")
    selected.set(0)
    cap.release()

Línea 62: Declaramos la función finalizar_limpiar.

Línea 63 y 64: Limpiamos los labels lblVideo y lblInfoVideoPath.

Línea 65 a 67: Activamos los radiobuttons y usamos selected.set(0), para limpiar cualquier selección que haya existido.

Línea 68: Liberamos la captura del video.

Construcción de la GUI con Tkinter

Lo único que nos queda por hacer es crear la interfaz gráfica de usuario, de donde estaremos llamando a las funciones que hemos creado hasta ahora.

cap = None
root = Tk()

lblInfo1 = Label(root, text="VIDEO DE ENTRADA", font="bold")
lblInfo1.grid(column=0, row=0, columnspan=2)

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

Línea 71: Creamos la venta principal.

Línea 73 y 74: Creamos un label llamado «VIDEO DE ENTRADA» en negrita, para que se ubique en la parte superior de la GUI. Usamos  columnspan=2 para que ocupe 2 columnas.

selected = IntVar()
rad1 = Radiobutton(root, text="Elegir video", width=20, value=1, variable=selected, command=video_de_entrada)
rad2 = Radiobutton(root, text="Video en directo", width=20, value=2, variable=selected, command=video_de_entrada)
rad1.grid(column=0, row=1)
rad2.grid(column=1, row=1)

Línea 76: Digitamos selected = IntVar(), esto nos permitirá manejar diferentes valores enteros para los radio buttons.

Línea 77: Vamos a crear el primer radio button. Entonces digitamos Radiobutton y especificaremos la ventana en donde se va a visualizar, y el texto será «Elegir video», en este caso tendrá un ancho de 30 y asignamos value=1 y variable=selected. Cada vez que se seleccione este radio button, selected tomará el valor de value, en este caso 1.  También tendremos que añadir command= video_de_entrada para que se llame a la función video_de_entrada, cada vez que se presione dicho radio button.

Línea 78: Similar a la línea 77, con la diferencia que se mostrará «Video en directo» y value será igual a 2.

Línea 79 y 80: Con ayuda de grid ubicamos estos radiobuttos en la fila 1 y en la columna 0 y 1 respectivamente.

lblInfoVideoPath = Label(root, text="", width=20)
lblInfoVideoPath.grid(column=0, row=2)

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

btnEnd = Button(root, text="Finalizar visualización y limpiar", state="disabled", command=finalizar_limpiar)
btnEnd.grid(column=0, row=4, columnspan=2, pady=10)

root.mainloop()

Línea 82 y 83: Creamos el label lblInfoVideoPath que será en donde se visualizará el path del video de entrada. Este en un principio estará vacío y tendrá un ancho de 20. Este label se ubicará en la columna 0 fila 2.

Línea 85 y 86: Este es el label en donde se visualizará el video de entrada, ya sea pre grabado o un video en directo. Se ubicará en la columna 0, fila 3 y ocupará 2 columnas.

Línea 88 y 89: Finalmente tenemos el botón «Finalizar visualización y limpiar». Este en un principio estará desactivado (solo se activará cuando se esté visualizando o reproduciendo un video), pero al cambiar de estado llamará a la función finalizar_limpiar.

Este botón se ubicará en la columna 0, fila 4, ocupará 2 columnas y se ha especificado pady=10 para que no se ubique tan cerca de la parte inferior de la GUI.

Línea 91: 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.

Probándo la GUI creada con Tkinter

Ahora que ya hemos construido el programa, ¡vamos a probarlo!. 

Figura 2: Visualización de la ventana al ejecutarse el programa.

Una vez en la ventana de la figura 2, elegiremos el primer radiobutton «Elegir video» y se nos desplegará un diálogo de archivos.

Figura 3: Diálogo de archivos que se nos despliega al haber elegido el radio button «Elegir video».

Como podemos apreciar en la figura 3, en el diálogo de archivos nos aparecen los videos en .mp4 y en .avi para escogerlos. Podremos escoger cualquier video con estas extensiones.

Figura 4: Lectura de un video pre grabado al cual también se le está aplicando detección de rostros.

En la figura 4 en la GUI podemos ver que al momento que se está visualizando el video, los radiobuttons se desactivan, se visualiza parte del path del video elegido y el botón se activa.

Si en este caso presionamos el botón «Finalizar visualización y limpiar», obtendríamos lo siguiente:

Figura 5: Ventana al momento de presionar el botón «Finalizar visualización y limpiar» o una vez que se ha terminado de reproducir el video de entrada.

En caso de que se presione el botón «Finalizar visualización y limpiar» o que ya haya terminado la visualización del video, entonces los radiobuttons se vuelven a activar, además de que se limpia la selección realizada anteriormente sobre ellos. El label correspondiente al path del video elegido se limpia, y el botón antes presionado se vuelve a desactivar.

Ahora procederemos a probar el radiobutton con el «Video en directo».

Figura 6: Lectura de un video en directo, usando la webcam en el cual también se está aplicando detección de rostros.

Similar a la figura 4, ya que una vez que hemos elegido «Video en directo», los radiobuttons se desactivan, se limpia el label del path del video ya que en este caso no tendríamos ningún path y se activa el botón «Finalizar visualización y limpiar».

Y esto ha sido todo por el tutorial de hoy, espero que les haya gustado mucho. ¡Tengan un grandioso día!, nos vemos en un siguiente tutorial/videotutorial. 🙂