hand_landmarks_single_hand.py
# Ce travail est publié sous licence MIT.
# Copyright (c) 2013-2025 OpenMV LLC. Tous droits réservés.
# https://github.com/openmv/openmv/blob/master/LICENSE
#
# Cet exemple présente le modèle MediaPipe Hand Landmarks Detection de Google pour une seule main.
#
# REMARQUE : cet exemple nécessite une caméra OpenMV dotée d'un NPU, comme l'AE3 ou le N6, pour fonctionner en temps réel.
import csi
import time
import ml
from ml.preprocessing import Normalization
from ml.postprocessing.mediapipe import BlazePalm
from ml.postprocessing.mediapipe import HandLandmarks
# Initialiser le capteur.
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
# BlazePalm nécessite une image carrée pour obtenir les meilleurs résultats.
# HandLandmarks fonctionne avec des images non carrées issues des recadrages de BlazePalm.
csi0.window((400, 400))
# Charger le modèle intégré de détection de paume
palm_detection = ml.Model("/rom/palm_detection_full_192.tflite", postprocess=BlazePalm(threshold=0.4))
print(palm_detection)
# Charger le modèle intégré de points de repère de la main
hand_landmarks = ml.Model("/rom/hand_landmarks_full_224.tflite", postprocess=HandLandmarks(threshold=0.4))
print(hand_landmarks)
# Connexions linéaires entre les articulations de la main pour dessiner le squelette de la main.
hand_lines = ((0, 1), (1, 2), (2, 3), (3, 4), (0, 5), (5, 6), (6, 7), (7, 8),
(5, 9), (9, 10), (10, 11), (11, 12), (9, 13), (13, 14), (14, 15), (15, 16),
(13, 17), (17, 18), (18, 19), (19, 20), (0, 17))
# Variables de suivi.
n = None
clock = time.clock()
while True:
clock.tick()
img = csi0.snapshot()
if n is None:
# palms est une liste de tuples ((x, y, w, h), score, keypoints)
for r, score, keypoints in palm_detection.predict([img]):
# rect est (x, y, w, h) - agrandi par 3 pour le modèle de points de repère de la main
wider_rect = (r[0] - r[2], r[1] - r[3], r[2] * 3, r[3] * 3)
# Opérer uniquement sur la ROI de la paume détectée
n = Normalization(roi=wider_rect)
else:
# hands est une liste de tuples ((x, y, w, h), score, keypoints)
# l'index 0 (le cas échéant) correspond à la main gauche
# l'index 1 (le cas échéant) correspond à la main droite
hands = hand_landmarks.predict([n(img)])
# Aucune main détectée, réinitialiser le tracker.
if not hands:
n = None
continue
# Dessiner des boîtes englobantes autour des mains détectées et des points clés.
for i, detections in enumerate(hands):
for r, score, keypoints in detections:
ml.utils.draw_predictions(img, [r], ("right",) if i else ("left",), ((0, 0, 255),), format=None)
# keypoints : ndarray (21, 3) des articulations de la main (x, y, z)
# Les indices suivent la convention MediaPipe :
# 0 : poignet
# Pouce : 1 cmc, 2 mcp, 3 ip, 4 tip
# Index : 5 mcp, 6 pip, 7 dip, 8 tip
# Majeur : 9 mcp, 10 pip, 11 dip, 12 tip
# Annulaire : 13 mcp, 14 pip, 15 dip, 16 tip
# Auriculaire : 17 mcp, 18 pip, 19 dip, 20 tip
# (cmc=base, mcp=jointure, pip=milieu, dip=distale, ip=articulation du pouce, tip=bout du doigt)
ml.utils.draw_skeleton(img, keypoints, hand_lines, kp_color=(255, 0, 0), line_color=(0, 255, 0))
# Centrer new_wider_rect sur la main pour le suivi
new_wider_rect = (r[0] + (r[2] // 2) - (wider_rect[2] // 2),
r[1] + (r[3] // 2) - (wider_rect[3] // 2),
wider_rect[2],
wider_rect[3])
# Opérer uniquement sur la ROI de la main détectée
n = Normalization(roi=new_wider_rect)
print(clock.fps(), "fps")