✌️ Contando dedos con visión artificial | Mediapipe – OpenCV – Python
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!.




Hola, me marca este error IndexError: list index out of range en la parte de coordinates_thumb[1], podrías ayudarme?
Buen trabajo, me sirvió
Muy bien!!!! gracias por el video!!!!