?️ EVENTOS DEL MOUSE | OpenCV con Python

Por Administrador

Te doy la bienvenida a un nuevo post. Hace unos días, un lector de este blog me había consultado como usar el ratón para con OpenCV rotar una imagen cierto ángulo en sentido horario o antihorario, dependiendo claro del botón del mouse que se esté presionando. Esto me recordó que no había hablado aún sobre los eventos del mouse, es decir el como podemos tomar acciones hechas por el mouse y añadirles algún proceso usando OpenCV. Por ello en el post de hoy desarrollaremos este tema y al final también veremos como resolver el problema planteado por el lector.

CONTENIDO

  • Eventos del Mouse con OpenCV y Python
    • cv2.setMouseCallback
    • Ejemplo para obtener los eventos del mouse con OpenCV
      • Explorando cada uno de los eventos del mouse
        • cv2.EVENT_MOUSEMOVE
        • cv2.EVENT_LBUTTONDOWN
        • cv2.EVENT_RBUTTONDOWN
        • cv2.EVENT_MBUTTONDOWN
        • cv2.EVENT_LBUTTONUP
        • cv2.EVENT_RBUTTONUP
        • cv2.EVENT_MBUTTONUP
        • cv2.EVENT_LBUTTONDBLCLK
        • cv2.EVENT_RBUTTONDBLCLK
        • cv2.EVENT_MBUTTONDBLCLK
        • cv2.EVENT_MOUSEWHEEL
        • cv2.EVENT_MOUSEHWHEEL
      • Resultados obtenidos cada vez que se presionan los botones del mouse (línea 13 a 29)
    • Resolviendo a la pregunta: ¿Cómo  usar el ratón con OpenCV para rotar una imagen cierto ángulo en sentido horario o antihorario?

Eventos del Mouse con OpenCV y Python

OpenCV nos permite detectar acciones que realiza el ratón o mouse sobre una imagen, tales como: movimiento del puntero en la pantalla, clic, doble clic, desplazamientos (scrolling), entre otros. Permitiéndonos añadir procesos cuando estas acciones se realicen.

cv2.setMouseCallback

En la programación para detectar las acciones realizadas por el ratón necesitamos de la función cv2.setMouseCallbacken ella en primer lugar debemos especificar el nombre de la ventana de donde se van a obtener los eventos y como segundo argumento la función de devolución de llamada para eventos. Esta última debe contar con los siguientes parámetros:

  • event, una de las constantes MouseEventTypes:
    • cv2.EVENT_MOUSEMOVE = 0, indica que el puntero del mouse  se ha movido por la ventana.
    • cv2.EVENT_LBUTTONDOWN = 1, indica que el botón izquierdo del mouse es presionado.
    • cv2.EVENT_RBUTTONDOWN = 2, indica que el botón derecho del mouse es presionado.
    • cv2.EVENT_MBUTTONDOWN = 3, indica que el botón central del mouse es presionado.
    • cv2.EVENT_LBUTTONUP = 4, indica que se suelta el botón izquierdo del ratón.
    • cv2.EVENT_RBUTTONUP = 5, indica que se suelta el botón derecho del ratón.
    • cv2.EVENT_MBUTTONUP = 6, indica que se suelta el botón central del ratón.
    • cv2.EVENT_LBUTTONDBLCLK = 7, indica que se hace doble clic en el botón izquierdo del ratón.
    • cv2.EVENT_RBUTTONDBLCLK = 8, indica que se hace doble clic en el botón derecho del ratón.
    • cv2.EVENT_MBUTTONDBLCLK = 9, indica que se hace doble clic en el botón central del ratón.
    • cv2.EVENT_MOUSEWHEEL = 10, desplazamiento (scrolling) del ratón adelante y atrás.
    • cv2.EVENT_MOUSEHWHEEL = 11, desplazamiento (scrolling) del ratón izquierda y derecha.
  • x, la coordenada x del evento del mouse.
  • y, la coordenada y del evento del mouse.
  • flags, una de las constantes MouseEventFlags:
    • cv2.EVENT_FLAG_LBUTTON = 1, indica que el botón izquierdo del mouse está presionado.
    • cv2.EVENT_FLAG_RBUTTON = 2, indica que el botón derecho del mouse está presionado.
    • cv2.EVENT_FLAG_MBUTTON = 4, indica que el botón central del mouse está presionado.
    • cv2.EVENT_FLAG_CTRLKEY = 8, indica que la tecla CTRL está presionada.
    • cv2.EVENT_FLAG_SHIFTKEY = 16, indica que la tecla SHIFT está presionada.
    • EVENT_FLAG_ALTKEY = 32, indica que la tecla ALT está presionada.
  • userdata, parámetro opcional.

Cabe destacar que podrías usar combinaciones entre MouseEventTypes y MouseEventFlags.

Ejemplo para obtener los Eventos del mouse con OpenCV

A continuación te mostraré un programa, en el cual se están empleando los eventos del mouse para dibujar círculos, circunferencias y texto sobre una imagen. En esta ocasión usaremos 6 eventos, pero no te preocupes que exploraremos los demás imprimiendo los valores de event, x, y y flags. Lo importante es principalmente entender como se emplean.

Vamos a crear el programa llamado: dibujando_con_clics.py

import cv2
import numpy as np

def dibujando(event,x,y,flags,param):
    # Imprimimos la información sobre los eventos que se estén realizando
    print('-----------------------------')
    print('event=',event)
    print('x=',x)
    print('y=',y)
    print('flags=',flags)

    # Ejemplos de acciones con algunos eventos del mouse
    if event == cv2.EVENT_LBUTTONDOWN:
        cv2.circle(imagen,(x,y),20,(255,255,255),2)

    if event == cv2.EVENT_RBUTTONDOWN:
        cv2.circle(imagen,(x,y),20,(0,0,255),2)

    if event == cv2.EVENT_LBUTTONDBLCLK:
        cv2.circle(imagen,(x,y),10,(255,0,0),-1)

    if event == cv2.EVENT_RBUTTONDBLCLK:
        cv2.circle(imagen,(x,y),10,(0,255,0),-1)

    if event == cv2.EVENT_LBUTTONUP:
        cv2.putText(imagen,'Ha dejado de presionar (Izquierdo)',(x,y),2,0.4,(255,255,0),1,cv2.LINE_AA)

    if event == cv2.EVENT_RBUTTONUP:
        cv2.putText(imagen,'Ha dejado de presionar (Derecho)',(x,y),2,0.4,(0,255,255),1,cv2.LINE_AA)

imagen = np.zeros((480,640,3),np.uint8)
cv2.namedWindow('Imagen')
cv2.setMouseCallback('Imagen',dibujando)

while True:
    cv2.imshow('Imagen',imagen)
    
    k = cv2.waitKey(1) & 0xFF
    if k == ord('l'): # Limpiar el contenido de la imagen
        imagen = np.zeros((480,640,3),np.uint8)
    elif k == 27:
        break

cv2.destroyAllWindows()

Línea 1 y 2: Importamos OpenCV y numpy.

Línea 4 a 29: Creamos una función llamada dibujando, que nos servirá para detectar cada uno de los eventos del mouse, veamos más a profundidad esta función:

  • Línea 7 a 10: Imprimimos los valores de event, x, y y flags, con esto conseguiremos explorar cada uno de los eventos y banderas, al momento de realizar acciones con el mouse sobre la imagen.
  • Línea 13 y 14: Si event es igual a cv2.EVENT_LBUTTONDOWN , es decir si se ha dado un clic izquierdo, entonces se dibujará una circunferencia de color blanco con radio 20 en la ubicación (x,y), que corresponde a la ubicación del puntero sobre la imagen al momento de realizar el clic.
  • Línea 16 y 17: Si event es igual a cv2.EVENT_RBUTTONDOWN , es decir si se ha dado un clic derecho, entonces se dibujará una circunferencia de color rojo con radio 20 en la ubicación (x,y), que corresponde a la ubicación del puntero sobre la imagen al momento de realizar el clic.
  • Línea 19 y 20: Si event es igual a cv2.EVENT_LBUTTONDBLCLK , es decir si se ha dado doble clic izquierdo, entonces se dibujará un círculo de color azul con radio 10 en la ubicación (x,y), que corresponde a la ubicación del puntero sobre la imagen al momento de realizar el doble clic.
  • Línea 22 y 23: Si event es igual a cv2.EVENT_RBUTTONDBLCLK , es decir si se ha dado doble clic derecho, entonces se dibujará un círculo de color verde con radio 10 en la ubicación (x,y), que corresponde a la ubicación del puntero sobre la imagen al momento de realizar el doble clic.
  • Línea 25 y 26: Si event es igual a cv2.EVENT_LBUTTONUP , es decir si se ha soltado el botón izquierdo del ratón, entonces se visualizará 'Ha dejado de presionar (Izquierdo)' en color celeste, en la ubicación (x,y), que corresponde a la ubicación en donde se ha dejado de presionar el botón izquierdo.
  • Línea 28 y 29: Si event es igual a cv2.EVENT_RBUTTONUP , es decir si se ha soltado el botón derecho del ratón, entonces se visualizará 'Ha dejado de presionar (Derecho)' en color amarillo, en la ubicación (x,y), que corresponde a la ubicación en donde se ha dejado de presionar el botón derecho.

Línea 31: Creamos una imagen de ceros con 480 pixeles de alto, 640 de ancho y 3 canales. La imagen se verá así:

Figura 1: Visualización de la imagen de entrada.

Línea 32: Asignamos un nombre a la ventana de la cual extraeremos los eventos del mouse.

Línea 33: Dentro de cv2.setMouseCallback especificamos como primer argumento el nombre de la ventana que habíamos realizado en la línea 32, y como segundo argumento la función dibujando que está entre las líneas 4 y 29.

Línea 35 a 36: Visualizamos la imagen contenida en la variable image, toma en cuenta que el nombre de la ventana  es el mismo usado en las líneas 32 y 33.

Línea 39 y 40: Si se presiona la tecla l, se limpiará todo el contenido dibujado por los eventos del mouse.

Línea 41 y 42: Si se presiona ESC, el ciclo se romperá y el programa terminará.

Explorando cada uno de los eventos del mouse

Antes de pasar con los resultados obtenidos cada vez que se presionan los botones del mouse (línea 13 a 29) veamos los valores obtenidos con cada uno de los tipos de eventos realizados por el ratón, es decir estaremos examinando los valores obtenidos de las líneas 7 a 10.

Para estas pruebas estaremos ejecutando el script dibujando_con_clics.py

cv2.EVENT_MOUSEMOVE

Este evento se presenta al momento que el mouse se mueve en la imagen (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 0 # Corresponde a cv2.EVENT_MOUSEMOVE
x= 211   # Ubicación del puntero en la coordenada x
y= 192   # Ubicación del puntero en la coordenada y
flags= 0 

cv2.EVENT_LBUTTONDOWN

Se da cuando el botón izquierdo es presionado (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 1 # Corresponde a cv2.EVENT_LBUTTONDOWN
x= 204   # Ubicación del puntero en la coordenada x
y= 233   # Ubicación del puntero en la coordenada y
flags= 1 # Corresponde a cv2.EVENT_FLAG_LBUTTON

cv2.EVENT_RBUTTONDOWN

Indica que el botón derecho es presionado (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 2 # Corresponde a cv2.EVENT_RBUTTONDOWN 
x= 108 # Ubicación del puntero en la coordenada x 
y= 355 # Ubicación del puntero en la coordenada y 
flags= 2 # Corresponde a cv2.EVENT_FLAG_RBUTTON

cv2.EVENT_MBUTTONDOWN

Indica que el botón central es presionado (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 3 # Corresponde a cv2.EVENT_MBUTTONDOWN 
x= 108 # Ubicación del puntero en la coordenada x 
y= 355 # Ubicación del puntero en la coordenada y 
flags= 4 # Corresponde a cv2.EVENT_FLAG_MBUTTON

cv2.EVENT_LBUTTONUP

Indica que se suelta el botón izquierdo del ratón (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 4 # Corresponde a cv2.EVENT_LBUTTONUP 
x= 208 # Ubicación del puntero en la coordenada x 
y= 304 # Ubicación del puntero en la coordenada y 
flags= 0

cv2.EVENT_RBUTTONUP

Indica que se suelta el botón derecho del ratón (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 5 # Corresponde a cv2.EVENT_RBUTTONUP 
x= 208 # Ubicación del puntero en la coordenada x 
y= 304 # Ubicación del puntero en la coordenada y 
flags= 0

cv2.EVENT_MBUTTONUP

Indica que se suelta el botón central del ratón (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 6 # Corresponde a cv2.EVENT_MBUTTONUP 
x= 108 # Ubicación del puntero en la coordenada x 
y= 355 # Ubicación del puntero en la coordenada y 
flags= 0

cv2.EVENT_LBUTTONDBLCLK

Indica que se hace un doble clic en el botón izquierdo del ratón (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 7 # Corresponde a cv2.EVENT_LBUTTONDBLCLK 
x= 282 # Ubicación del puntero en la coordenada x 
y= 215 # Ubicación del puntero en la coordenada y 
flags= 1 # Corresponde a cv2.EVENT_FLAG_LBUTTON

cv2.EVENT_RBUTTONDBLCLK

Indica que se hace un doble clic en el botón derecho del ratón (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 8 # Corresponde a cv2.EVENT_RBUTTONDBCLK 
x= 282 # Ubicación del puntero en la coordenada x 
y= 215 # Ubicación del puntero en la coordenada y 
flags= 2 # Corresponde a cv2.EVENT_FLAG_RBUTTON

cv2.EVENT_MBUTTONDBLCLK

Indica que se hace un doble clic en el botón central del ratón (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 9 # Corresponde a cv2.EVENT_MBUTTONDBLCLK 
x= 108 # Ubicación del puntero en la coordenada x 
y= 355 # Ubicación del puntero en la coordenada y 
flags= 4 # Corresponde a cv2.EVENT_FLAG_MBUTTON

cv2.EVENT_MOUSEWHEEL

Desplazamiento (scrolling) del ratón adelante y atrás (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 10 # Corresponde a cv2.EVENT_MOUSEWHEEL  
x= 154 # Ubicación del puntero en la coordenada x  
y= 230 # Ubicación del puntero en la coordenada y  
flags= -131072 # Valores positivos o negativos de acuerdo al movimiento

cv2.EVENT_MOUSEHWHEEL

Desplazamiento (scrolling) del ratón izquierda y derecha (los valores de x e y pueden cambiar dependiendo de la ubicación del puntero).

event= 11 # Corresponde a cv2.EVENT_MOUSEWHEEL 
x = 420 # Ubicación del puntero en la coordenada x 
y= 239 # Ubicación del puntero en la coordenada y 
flags= 131072 # Valores positivos o negativos de acuerdo al movimiento

Resultados obtenidos cada vez que se presionan los botones del mouse (línea 13 a 29)

Ahora que hemos explorado cada uno de los eventos del mouse, pasemos a los resultados de las acciones de los eventos que programamos. Para ello daré clics derechos e izquierdos sobre la imagen.

Figura 2: Visualización de los resultados obtenidos de dibujando_con_clics.py

Como vemos, en la parte izquierda de la imagen, tenemos una circunferencia blanca que indica que el botón izquierdo del ratón ha sido presionado, mientras que más abajo tenemos el mensaje Ha dejado de presionar (Izquierdo), que es la ubicación en donde se ha dejado de presionar el botón. Finalmente tenemos un círculo azul que indica que se ha realizado un doble clic en el botón izquierdo, como consecuencia se ha dibujado también la circunferencia y el mensaje.

En la parte derecha de la imagen, tenemos una circunferencia roja que indica que el botón derecho del ratón ha sido presionado, mientras que debajo se muestra el mensaje Ha dejado de presionar (Derecho), que es la ubicación en donde se ha dejado de presionar el botón. Finalmente tenemos un círculo verde que indica que se ha realizado un doble clic en el botón derecho, como consecuencia se ha dibujado también la circunferencia y el mensaje.

Resolviendo a la pregunta: ¿Cómo usar el ratón con OpenCV para rotar una imagen cierto ángulo en sentido horario o antihorario?

Ya vimos como trabajan los eventos del mouse con OpenCV. Por lo que voy a responder a la duda que el lector de este blog me había planteado. Para ello, dada una imagen, esta rotará 15° grados en sentido horario o en sentido antihorario dependiendo si se presiona el botón izquiero o derecho del ratón respectivamente. Usaremos la siguiente imagen:

Figura 3: Imagen que se va a rotar según clics izquierdos o clics derechos

Para este programa crearemos un programa llamado: rotando_con_clics.py

import cv2

def rotar(event,x,y,flags,param):
    global angulo, imagen_a_rotar

    if event == cv2.EVENT_LBUTTONDOWN:
        angulo = angulo + 15
        M = cv2.getRotationMatrix2D((ancho//2,alto//2), angulo, 1)
        imagen_a_rotar = cv2.warpAffine(imagen, M, (ancho,alto))

    if event == cv2.EVENT_RBUTTONDOWN:
        angulo = angulo - 15
        M = cv2.getRotationMatrix2D((ancho//2,alto//2), angulo, 1)
        imagen_a_rotar = cv2.warpAffine(imagen, M, (ancho,alto))

imagen = cv2.imread('ave.jpg')
imagen_a_rotar = imagen.copy() 
ancho = imagen.shape[1] # columnas
alto = imagen.shape[0]  # filas
angulo = 0
cv2.namedWindow('imagen_a_rotar')
cv2.setMouseCallback('imagen_a_rotar',rotar)

while True:
    #cv2.imshow('Imagen',imagen)
    cv2.imshow('imagen_a_rotar',imagen_a_rotar)
    print('Ángulo=',angulo)
    if cv2.waitKey(1) & 0xFF == 27:
        break

cv2.destroyAllWindows()

Línea 1: Importamos OpenCV.

Línea 3 a 14: Creamos la función rotar que nos servirá para detectar los eventos del mouse.

  • Línea 4: Tomamos las variables angulo e imagen_a_rotar.
  • Línea 6 a 9: Si se presiona el botón izquierdo del mouse, entonces la variable angulo incrementará su valor y gracias a cv2.getRotationMatrix2D y cv2.warpAffine la imagen rotará en sentido horario. 
  • Línea 11 a 14: Si se presiona el botón derecho del mouse, entonces la variable angulo disminuirá su valor, luego aplicamos las funciones cv2.getRotationMatrix2D y cv2.warpAffine de tal modo que la imagen rotará en sentido antihorario. 

NOTA: Recuerda que las funcionescv2.getRotationMatrix2D y cv2.warpAffine las habíamos tratado en el post: ?️ Como trasladar, rotar, escalar y recortar una imagen | Python – OpenCV

Línea 16 a 20: Leemos la imagen de entrada, realizamos una copia de ella que se almacenará en imagen_a_rotar, luego procedemos a obtener el ancho y alto de la imagen de entrada. En la línea 20 declaramos la variable angulo que se modificará conforme se presione el botón derecho o izquierdo del mouse.

Línea 21 y 22: Asignamos un nombre a la ventana de donde se tomarán los eventos del mouse, y luego aplicamos la función cv2.setMouseCallback que llamará a la función rotar, previamente creada.

Línea 24 a 31: Visualizamos imagen_a_rotar, imprimimos el ángulo que tengamos en ese momento en consola y si presionamos ESC, el proceso terminará.

Veamos los resultados:

Figura 4: Rotación de la imagen cuando se presiona el botón izquierdo del mouse.

 

Figura 5: Rotación de la imagen cuando se presiona el botón derecho del mouse.

Como podemos apreciar en las figuras 4 y 5, hemos cumplido con el objetivo de rotar una imagen en sentido horario o antihorario dependiendo de los clics derechos o izquierdos.

Y hasta aquí llega el tutorial de hoy, espero que te haya gustado ?. ¡Nos vemos en un siguiente video/post tutorial!.

Referencias