Diseño de Interfaces para Visión Artificial: Integración de YOLO con Tkinter

Al desarrollar proyectos de visión artificial con YOLO, la etapa final suele requerir una interfaz de usuario (GUI) que permita interactuar con el modelo de forma intuitiva. Aunque herramientas como Qt son populares, Tkinter (TK) sigue siendo una opción ligera y eficiente para prototipos rápidos y herramientas internas, especialmente cuando se complementa con librerías de estilos como ttkbootstrap o herramientas de diseño visual.

Para lograr una aplicación mantenible, es recomendable separar la lógica del negocio de la capa de presentación. A continuación, se presenta una arquitectura basada en el patrón controlador-vista para integrar la detección de objetos en una ventana funcional.

Estructura del Proyecto

El sistema se divide en tres componentes principales:

  • Vista (interfaz.py): Define la disposición de los elementos gráficos.
  • Controlador (logica.py): Gestiona la carga de imágenes, la ejecución del modelo YOLO y el procesamiento de resultados.
  • Punto de entrada (app.py): Inicializa y coordina los módulos.

Implementación de la Interfaz Gráfica

En este módulo se define la clase base para la ventana, utilizando coordenadas precisas para los lienzos (canvas) donde se mostrarán las imágenes.

import tkinter as tk
from tkinter import ttk

class VentanaBase(tk.Tk):
    def __init__(self):
        super().__init__()
        self.configurar_ventana()
        self.inicializar_componentes()

    def configurar_ventana(self):
        self.title("Sistema de Detección YOLO")
        ancho, alto = 650, 550
        x_dato = (self.winfo_screenwidth() // 2) - (ancho // 2)
        y_dato = (self.winfo_screenheight() // 2) - (alto // 2)
        self.geometry(f'{ancho}x{alto}+{x_dato}+{y_dato}')
        self.resizable(False, False)

    def inicializar_componentes(self):
        # Etiquetas
        ttk.Label(self, text="Vista Original").place(x=100, y=20)
        ttk.Label(self, text="Resultado YOLO").place(x=420, y=20)
        
        # Áreas de visualización
        self.canvas_previo = tk.Canvas(self, bg="#d1d1d1", width=280, height=220)
        self.canvas_previo.place(x=20, y=50)
        
        self.canvas_resultado = tk.Canvas(self, bg="#d1d1d1", width=280, height=220)
        self.canvas_resultado.place(x=340, y=50)

        # Entrada de datos y control
        ttk.Label(self, text="Ruta de imagen:").place(x=20, y=320)
        self.entrada_ruta = ttk.Entry(self)
        self.entrada_ruta.place(x=120, y=320, width=350)
        
        self.btn_detectar = ttk.Button(self, text="Ejecutar Inferencia")
        self.btn_detectar.place(x=480, y=318, width=140)

class AppFinal(VentanaBase):
    def __init__(self, controlador):
        self.ctrl = controlador
        super().__init__()
        self.conectar_eventos()
        self.ctrl.vincular_ui(self)

    def conectar_eventos(self):
        self.btn_detectar.config(command=self.ctrl.procesar_deteccion)

Lógica de Detección y Procesamiento

El controlador se encarga de invocar a la librería ultralytics para procesar la imagen y actualizar los elementos visuales de Tkinter mediante PIL (Pillow).

import os
from PIL import Image, ImageTk
from ultralytics import YOLO

class ProcesadorYOLO:
    def __init__(self, ruta_modelo):
        self.modelo = YOLO(ruta_modelo)
        self.ui = None
        self.ref_img1 = None
        self.ref_img2 = None

    def vincular_ui(self, interfaz):
        self.ui = interfaz

    def redimensionar_para_canvas(self, ruta, max_w=280, max_h=220):
        img = Image.open(ruta)
        img.thumbnail((max_w, max_h), Image.Resampling.LANCZOS)
        return ImageTk.PhotoImage(img)

    def procesar_deteccion(self):
        ruta_img = self.ui.entrada_ruta.get()
        if not os.path.exists(ruta_img):
            print("Archivo no encontrado")
            return

        # Ejecutar modelo
        inferencia = self.modelo(ruta_img)
        temp_path = "output_temp.jpg"
        
        for resultado in inferencia:
            resultado.save(filename=temp_path)

        # Actualizar UI
        self.ref_img1 = self.redimensionar_para_canvas(ruta_img)
        self.ref_img2 = self.redimensionar_para_canvas(temp_path)
        
        self.ui.canvas_previo.create_image(140, 110, image=self.ref_img1)
        self.ui.canvas_resultado.create_image(140, 110, image=self.ref_img2)

Ejecución del Sistema

Finalmente, el script principal instancia el motor de detección y arranca el bucle de la aplicación.

from interfaz import AppFinal
from logica import ProcesadorYOLO

if __name__ == "__main__":
    # Ajustar la ruta al peso (.pt) de tu modelo entrenado
    cerebro = ProcesadorYOLO("weights/yolov8n.pt")
    app = AppFinal(cerebro)
    app.mainloop()

Este flujo permite escalar la herramienta fácilmente. Por ejemplo, se pueden añadir selectores de archivos mediante filedialog o barras de progreso sin afectar la lógica de inferencia del modelo YOLO.

Etiquetas: YOLOv8 tkinter Python gui Computer Vision

Publicado el 11-2 17:30