Ya habíamos hablado de como usar Mediapipe Hands, e incluso habíamos hecho una aplicación usando esta solución, en donde controlamos el mouse a distancia con una mano. En este tutorial veremos como contar los dedos extendidos de una mano y además identificaremos de qué dedo se trata. ¡Vamos a por ello!.
CONTENIDO
- Contando dedos con visión artificial con Mediapipe, OpenCV en Python
- Instalación de packages
- ¡Vamos con la programación!
Contando dedos con visión artificial con Mediapipe, OpenCV en Python
Como había mencionado, el objetivo en esta ocasión será contar el número de dedos extendidos de una mano y determinar el estado de cada uno de ellos, es decir, qué dedo se encuentra estirado y cuál no. Ya que anteriormente habíamos usado MediaPipe Hands, que nos devuelve varios puntos claves de las manos, podremos usarlo una vez más, para, junto con trigonometría y distancia entre puntos, resolver esta aplicación planteada.
Para resolver este problema haremos lo siguiente: leeremos el video de entrada junto con los landmarks de una mano, luego tomaremos ciertos puntos para determinar cuando el pulgar está extendido o no, para ello usaremos la ley de cosenos. A continuación identificaremos cuando alguno de los otros cuatro dedos estén extendidos o no, con ayuda de la distancia entre puntos, para finalmente visualizar los resultados.
Instalación de packages
En esta oportunidad necesitaremos instalar Mediapipe con ayuda de pip install mediapipe
. Este package a su vez instalará otros módulos como OpenCV o Numpy. Podemos listar los packages instalados usando pip freeze
.

Figura 1: Listando los packages instalados, con pip freeze.
¡Vamos con la programación!
Para una explicación más detallada del programa que veremos a continuación, por favor dirígete a los videos que he preparado en mi canal, constan de dos partes (parte 1, parte 2) en donde explico paso a pasito cada procedimiento efectuado. ¡Anímate a verlos ?!.
import cv2 import mediapipe as mp import numpy as np from math import acos, degrees def palm_centroid(coordinates_list): coordinates = np.array(coordinates_list) centroid = np.mean(coordinates, axis=0) centroid = int(centroid[0]), int(centroid[1]) return centroid mp_drawing = mp.solutions.drawing_utils mp_drawing_styles = mp.solutions.drawing_styles mp_hands = mp.solutions.hands cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) # Pulgar thumb_points = [1, 2, 4] # Índice, medio, anular y meñique palm_points = [0, 1, 2, 5, 9, 13, 17] fingertips_points = [8, 12, 16, 20] finger_base_points =[6, 10, 14, 18] # Colores GREEN = (48, 255, 48) BLUE = (192, 101, 21) YELLOW = (0, 204, 255) PURPLE = (128, 64, 128) PEACH = (180, 229, 255)
En esta primera sección del programa importamos los módulos que usaremos. Además, declaramos la función palm_centroid
que llamaremos luego para calcular el centroide de la palma de la mano, que nos ayudará a determinar cuando el dedo índice, medio, anular o meñique están estirados o no.
En esta sección también especificamos que vamos a leer un video streaming. Asimismo, especificamos los puntos de la mano que usaremos y los colores para cada dedo.
with mp_hands.Hands( model_complexity=1, max_num_hands=1, min_detection_confidence=0.5, min_tracking_confidence=0.5) as hands: while True: ret, frame = cap.read() if ret == False: break frame = cv2.flip(frame, 1) height, width, _ = frame.shape frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results = hands.process(frame_rgb) fingers_counter = "_" thickness = [2, 2, 2, 2, 2] if results.multi_hand_landmarks: coordinates_thumb = [] coordinates_palm = [] coordinates_ft = [] coordinates_fb = [] for hand_landmarks in results.multi_hand_landmarks: for index in thumb_points: x = int(hand_landmarks.landmark[index].x * width) y = int(hand_landmarks.landmark[index].y * height) coordinates_thumb.append([x, y]) for index in palm_points: x = int(hand_landmarks.landmark[index].x * width) y = int(hand_landmarks.landmark[index].y * height) coordinates_palm.append([x, y]) for index in fingertips_points: x = int(hand_landmarks.landmark[index].x * width) y = int(hand_landmarks.landmark[index].y * height) coordinates_ft.append([x, y]) for index in finger_base_points: x = int(hand_landmarks.landmark[index].x * width) y = int(hand_landmarks.landmark[index].y * height) coordinates_fb.append([x, y]) ########################## # Pulgar p1 = np.array(coordinates_thumb[0]) p2 = np.array(coordinates_thumb[1]) p3 = np.array(coordinates_thumb[2]) l1 = np.linalg.norm(p2 - p3) l2 = np.linalg.norm(p1 - p3) l3 = np.linalg.norm(p1 - p2) # Calcular el ángulo angle = degrees(acos((l1**2 + l3**2 - l2**2) / (2 * l1 * l3))) thumb_finger = np.array(False) if angle > 150: thumb_finger = np.array(True) ################################ # Índice, medio, anular y meñique nx, ny = palm_centroid(coordinates_palm) cv2.circle(frame, (nx, ny), 3, (0, 255, 0), 2) coordinates_centroid = np.array([nx, ny]) coordinates_ft = np.array(coordinates_ft) coordinates_fb = np.array(coordinates_fb) # Distancias d_centrid_ft = np.linalg.norm(coordinates_centroid - coordinates_ft, axis=1) d_centrid_fb = np.linalg.norm(coordinates_centroid - coordinates_fb, axis=1) dif = d_centrid_ft - d_centrid_fb fingers = dif > 0 fingers = np.append(thumb_finger, fingers) fingers_counter = str(np.count_nonzero(fingers==True))
De la línea 33 a la 105 llevamos a cabo la extracción de los puntos claves de la mano. En las líneas 76 a 89 determinamos cuando el pulgar está extendido o no, con base en la ley de cosenos.
En las líneas 93 a 103 nos encargamos de los otros dedos usando la distancia entre ciertos puntos.
Unimos toda esta información en un vector booleano en donde True querrá decir que un dedo está extendido, mientras que False lo contrario, esto lo podemos apreciar en la línea 105.
for (i, finger) in enumerate(fingers): if finger == True: thickness[i] = -1 mp_drawing.draw_landmarks( frame, hand_landmarks, mp_hands.HAND_CONNECTIONS, mp_drawing_styles.get_default_hand_landmarks_style(), mp_drawing_styles.get_default_hand_connections_style()) ################################ # Visualización cv2.rectangle(frame, (0, 0), (80, 80), (125, 220, 0), -1) cv2.putText(frame, fingers_counter,(15, 65), 1, 5, (255, 255, 255), 2) # Pulgar cv2.rectangle(frame, (100, 10), (150, 60), PEACH, thickness[0]) cv2.putText(frame, "Pulgar", (100, 80), 1, 1, (255, 255, 255), 2) # Índice cv2.rectangle(frame, (160, 10), (210, 60), PURPLE, thickness[1]) cv2.putText(frame, "Indice", (160, 80), 1, 1, (255, 255, 255), 2) # Medio cv2.rectangle(frame, (220, 10), (270, 60), YELLOW, thickness[2]) cv2.putText(frame, "Medio", (220, 80), 1, 1, (255, 255, 255), 2) # Anular cv2.rectangle(frame, (280, 10), (330, 60), GREEN, thickness[3]) cv2.putText(frame, "Anular", (280, 80), 1, 1, (255, 255, 255), 2) # Menique cv2.rectangle(frame, (340, 10), (390, 60), BLUE, thickness[4]) cv2.putText(frame, "Menique", (340, 80), 1, 1, (255, 255, 255), 2) cv2.imshow("Frame", frame) if cv2.waitKey(1) & 0xFF == 27: break cap.release() cv2.destroyAllWindows()
Por último tenemos el apartado de visualización, en la que ubicamos recuadros en la sección superior izquierda de la imagen. Aquí cada color estará asociado a un dedo.
Podemos ver los resultados de esta aplicación a continuación:
Figura 2: Visualización de resultados.
Y bien, esto ha sido todo por el tutorial de hoy. ¡Espero que te haya gustado! ? Nos vemos en el siguiente… ¡Qué te vaya súper bien!.