Skip to content

Finger Counter

Shows how many fingers are raised, from 0 to 5, on either hand. Hold your hand upright with your palm toward the camera.

Run it

python examples/finger_counter.py

Code

examples/finger_counter.py
"""Count raised fingers on one hand using landmark positions from locate().

Press q to quit.
"""

import math

import cv2 as cv
# from mediapipe.tasks.python.vision.hand_landmarker import HandLandmark

from detectors_world import create_detector

FINGER_TIPS = [8, 12, 16, 20]
THUMB_TIP = 4
THUMB_JOINT = 3
PINKY_BASE = 17

# To use landmark names instead of numbers, uncomment the import above and these
# lines, then use the second "raised" line below in place of the first.
# THUMB_TIP = HandLandmark.THUMB_TIP
# THUMB_JOINT = HandLandmark.THUMB_IP
# PINKY_BASE = HandLandmark.PINKY_MCP
# FINGERS = [
#     (HandLandmark.INDEX_FINGER_TIP, HandLandmark.INDEX_FINGER_PIP),
#     (HandLandmark.MIDDLE_FINGER_TIP, HandLandmark.MIDDLE_FINGER_PIP),
#     (HandLandmark.RING_FINGER_TIP, HandLandmark.RING_FINGER_PIP),
#     (HandLandmark.PINKY_TIP, HandLandmark.PINKY_PIP),
# ]


def distance_to_pinky(landmarks, point):
    return math.dist(landmarks[point][1:], landmarks[PINKY_BASE][1:])


cap = cv.VideoCapture(0)
with create_detector("hand", max_hands=1) as hand:
    while cap.isOpened():
        ok, frame = cap.read()
        if not ok:
            break
        frame = cv.flip(frame, 1)
        hand.detect(frame)
        landmarks = hand.locate(frame, draw=False)
        if landmarks:
            # A finger is raised when its tip is above the joint two points below it (smaller y).
            raised = sum(landmarks[tip][2] < landmarks[tip - 2][2] for tip in FINGER_TIPS)
            # raised = sum(landmarks[tip][2] < landmarks[joint][2] for tip, joint in FINGERS)
            # The thumb moves sideways: it is out when its tip is farther from the pinky than its joint.
            raised += distance_to_pinky(landmarks, THUMB_TIP) > distance_to_pinky(landmarks, THUMB_JOINT)
            cv.putText(frame, str(raised), (10, 70), cv.FONT_HERSHEY_SIMPLEX, 2.5, (0, 255, 0), 4)
        cv.imshow("Finger Counter", frame)
        if cv.waitKey(1) & 0xFF == ord("q"):
            break
cap.release()
cv.destroyAllWindows()

How it works

Fingertips are landmarks 8, 12, 16 and 20, and each middle joint is two numbers below its tip, as the hand landmark map shows.

In images, y grows downward, so a raised fingertip has a smaller y than its joint. Each comparison gives True or False, and sum() counts the True results.

The thumb moves sideways, so height doesn't help. When it's out, its tip (4) is farther from the base of the pinky (17) than its joint (3) is. When it's folded across the palm, the tip is closer. This works for left and right hands, and on mirrored images.

Landmark names

The numbers can be replaced with names. The comments in the code show how: uncomment the import, the three thumb and pinky names and the FINGERS list, then use the second raised line in place of the first.

Try next

  • Count both hands with max_hands=2, using hand.locate(frame, hand=1) for the second one.