---
title: "Count fingers with MediaPipe in Python"
description: "Hold up three fingers and Python says three. Learn the landmark logic behind finger counting, including the thumb rule that trips everyone up."
url: https://articles.sythra.ai/articles/count-fingers-mediapipe-python
slug: count-fingers-mediapipe-python
author: "vaibhavkothari"
author_url: https://articles.sythra.ai/writers/vaibhavkothari
date_published: 2026-08-26T08:30:17.365Z
date_modified: 2026-08-29T10:29:25.265Z
topics: ["Python", "Mediapipe", "Opencv", "Computer Vision"]
reading_time_minutes: 6
publisher: "Sythra"
publisher_url: https://sythra.ai
access: free
language: en
---

# Count fingers with MediaPipe in Python

> Hold up three fingers and Python says three. Learn the landmark logic behind finger counting, including the thumb rule that trips everyone up.

Source: https://articles.sythra.ai/articles/count-fingers-mediapipe-python · Author: vaibhavkothari · Published: 2026-08-26 · Reading time: 6 min · Topics: Python, Mediapipe, Opencv, Computer Vision

Finger counting is the "hello world" of hand tracking. It takes about forty lines, it runs on any laptop webcam, and once you understand the trick behind it you can build gesture controls for anything.

The idea is small: MediaPipe gives you 21 points on the hand. For each finger, compare the **tip** to the joint below it. If the tip is higher up the screen, the finger is extended.

The thumb is the exception, and that exception is why most tutorials give wrong counts.

## What you need

```bash
python -m pip install opencv-python mediapipe
```

Python 3.8–3.12. MediaPipe usually does not support the newest release for a few months.

## Step 1 — Understand the landmark numbers

MediaPipe numbers 21 landmarks per hand. You need eight of them:

| Finger | Tip | Joint below (PIP) |
|---|---|---|
| Thumb | 4 | 2 |
| Index | 8 | 6 |
| Middle | 12 | 10 |
| Ring | 16 | 14 |
| Pinky | 20 | 18 |

Landmark 0 is the wrist. The pattern is regular: each finger's tip is the last of its four points, counting outward from the wrist.

## Step 2 — Get landmarks on screen

```python
import cv2
import mediapipe as mp

mp_hands = mp.solutions.hands
mp_draw = mp.solutions.drawing_utils

hands = mp_hands.Hands(
    max_num_hands=2,
    min_detection_confidence=0.7,
    min_tracking_confidence=0.6,
)

cap = cv2.VideoCapture(0)

while True:
    ok, frame = cap.read()
    if not ok:
        break

    frame = cv2.flip(frame, 1)
    result = hands.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))

    if result.multi_hand_landmarks:
        for hand in result.multi_hand_landmarks:
            mp_draw.draw_landmarks(frame, hand, mp_hands.HAND_CONNECTIONS)

    cv2.imshow("Finger Counter", frame)
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()
```

## Step 3 — Count the four straight fingers

Screen coordinates run **downwards**: y = 0 is the top of the frame. So a raised fingertip has a *smaller* y than the joint beneath it.

```python
FINGER_TIPS = [8, 12, 16, 20]     # index, middle, ring, pinky

def count_straight_fingers(hand):
    count = 0
    for tip in FINGER_TIPS:
        tip_y = hand.landmark[tip].y
        pip_y = hand.landmark[tip - 2].y      # the joint two points below
        if tip_y < pip_y:                     # tip is higher on screen
            count += 1
    return count
```

`tip - 2` works for all four because the landmarks are evenly spaced: tip 8 → joint 6, tip 12 → joint 10, and so on.

This assumes the hand is upright. Point your fingers sideways and it breaks — handled in Step 6.

## Step 4 — Handle the thumb correctly

The thumb does not fold down; it folds **sideways across the palm**. Comparing y values gives nonsense. Compare x instead — and the direction depends on which hand you are looking at.

MediaPipe tells you the handedness, so use it:

```python
def thumb_is_out(hand, handedness_label):
    tip_x = hand.landmark[4].x
    joint_x = hand.landmark[2].x

    if handedness_label == "Right":
        return tip_x > joint_x
    return tip_x < joint_x
```

One important gotcha: because you mirrored the frame with `cv2.flip`, MediaPipe's "Right" is your **left** hand on screen. Either flip the label or accept the mirrored naming — just be consistent, or your thumb count silently inverts for one hand.

## Step 5 — Put the counter together

```python
import cv2
import mediapipe as mp

mp_hands = mp.solutions.hands
mp_draw = mp.solutions.drawing_utils
hands = mp_hands.Hands(max_num_hands=2, min_detection_confidence=0.7)

FINGER_TIPS = [8, 12, 16, 20]

def count_fingers(hand, label):
    total = 0

    tip_x, joint_x = hand.landmark[4].x, hand.landmark[2].x
    if (label == "Right" and tip_x > joint_x) or (label == "Left" and tip_x < joint_x):
        total += 1

    for tip in FINGER_TIPS:
        if hand.landmark[tip].y < hand.landmark[tip - 2].y:
            total += 1

    return total

cap = cv2.VideoCapture(0)

while True:
    ok, frame = cap.read()
    if not ok:
        break

    frame = cv2.flip(frame, 1)
    result = hands.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))

    total = 0
    if result.multi_hand_landmarks and result.multi_handedness:
        pairs = zip(result.multi_hand_landmarks, result.multi_handedness)
        for hand, handedness in pairs:
            label = handedness.classification[0].label
            total += count_fingers(hand, label)
            mp_draw.draw_landmarks(frame, hand, mp_hands.HAND_CONNECTIONS)

    cv2.rectangle(frame, (20, 20), (170, 140), (255, 120, 40), cv2.FILLED)
    cv2.putText(frame, str(total), (55, 115),
                cv2.FONT_HERSHEY_SIMPLEX, 3, (255, 255, 255), 5)

    cv2.imshow("Finger Counter", frame)
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()
```

Hold up both hands and it counts up to ten.

## Step 6 — Make it robust

**Stop the flicker.** A count that wobbles between 2 and 3 is annoying. Keep the last few readings and report the most common one:

```python
from collections import deque, Counter

history = deque(maxlen=7)

def stable_count(current):
    history.append(current)
    return Counter(history).most_common(1)[0][0]
```

Seven frames is about a quarter of a second — long enough to smooth noise, short enough to feel instant.

**Handle rotated hands.** Instead of comparing to the joint, compare each tip's distance from the wrist against the joint's distance from the wrist. This works at any angle:

```python
import math

def finger_extended(hand, tip):
    wrist = hand.landmark[0]
    tip_pt = hand.landmark[tip]
    pip_pt = hand.landmark[tip - 2]

    d_tip = math.dist((tip_pt.x, tip_pt.y), (wrist.x, wrist.y))
    d_pip = math.dist((pip_pt.x, pip_pt.y), (wrist.x, wrist.y))
    return d_tip > d_pip
```

## Step 7 — Turn counts into commands

Once counting is stable, gestures are trivial:

```python
ACTIONS = {
    0: "pause",
    1: "play",
    2: "next track",
    3: "previous track",
    5: "stop",
}

action = ACTIONS.get(stable_count(total))
if action:
    print("Action:", action)
```

Add a short cooldown so one gesture does not fire thirty times per second, and you have a working gesture remote. The same landmark data drives [the gesture volume controller](https://articles.sythra.ai/articles/hand-gesture-volume-control-python) and [the virtual mouse](https://articles.sythra.ai/articles/virtual-mouse-opencv-mediapipe).

## Common problems (and fixes)

| Problem | Fix |
|---|---|
| Thumb always counted | You are using the y comparison; use x with handedness |
| Count flickers | Use the `deque` + `Counter` majority vote |
| Wrong when hand is sideways | Use the wrist-distance method from Step 6 |
| Left/right swapped | `cv2.flip` mirrors the frame; flip the label too |
| Nothing detected | Better lighting, hand 40–60 cm from the camera |
| `mediapipe` will not install | Use Python 3.8–3.12 |

```remember
# Remember this
Four fingers -> tip `y` above the joint `y` means extended
The thumb -> compare `x`, not `y`, and mind the handedness
Landmark 0 -> the wrist, your reference point for everything else
---
Image `y` grows **downward**. Higher on screen is a smaller number.
```


## FAQ

### How do I count fingers with MediaPipe in Python?

Compare each fingertip landmark to the joint two positions below it. If the tip's y value is smaller, the finger is up. Handle the thumb separately by comparing x values, using the reported handedness.

### Why does the thumb break finger counting?

The thumb folds sideways across the palm rather than downwards, so a vertical comparison gives the wrong answer. Compare the thumb tip's x coordinate to the joint below it and pick the direction based on which hand it is.

### Which MediaPipe landmarks are the fingertips?

Landmarks 4, 8, 12, 16 and 20 are the thumb, index, middle, ring and pinky tips. The wrist is landmark 0.

### How do I stop the finger count from flickering?

Store the last seven counts in a `deque` and report the most common value with `collections.Counter`. This majority vote removes single-frame noise without noticeable lag.

### Can MediaPipe count fingers on both hands?

Yes. Set `max_num_hands=2`, loop over `multi_hand_landmarks`, and add the counts together for a total up to ten.

## Next reading on Sythra Articles

- [Control your volume with hand gestures](https://articles.sythra.ai/articles/hand-gesture-volume-control-python)
- [Build a virtual mouse with OpenCV and MediaPipe](https://articles.sythra.ai/articles/virtual-mouse-opencv-mediapipe)
- [Build an AI push-up counter with pose estimation](https://articles.sythra.ai/articles/pushup-counter-pose-estimation-python)

## Glossary (terms defined in this article)
- **landmarks** (medium) — Key points on the hand — the wrist is 0, fingertips are 4, 8, 12, 16, 20
