YOLO People Counting Using Python: Build a Real-Time People Counting System

People counting is a popular computer vision application that uses artificial intelligence to automatically detect and count people in images and videos.

It can be used in shopping malls, retail stores, offices, airports, schools, events, public spaces, and security systems.

A simple object detection model can identify people in each video frame. However, there is an important problem: the same person appears in many consecutive frames.

If we simply count every detection, one person walking across a camera could be counted hundreds of times.

To solve this problem, we can combine YOLO object detection with object tracking. The tracker assigns a unique ID to each detected person and keeps track of them as they move through the video.

In this tutorial, we will build a real-time People Counting System using Python, YOLO, OpenCV, and object tracking.


What Is People Counting?

People counting is the process of automatically determining how many people are present in a camera view or how many people pass through a specific area.

For example, a camera may detect:

Person 1
Person 2
Person 3
Person 4

The application can then display:

Current People: 4

A more advanced system can count people entering and leaving an area:

Entered: 25
Exited: 18
Currently Inside: 7

This makes people counting useful for real-world monitoring systems.


Why Use YOLO?

YOLO is a real-time object detection model that can identify objects in images and videos.

For people counting, we are mainly interested in the person class.

For every detected person, YOLO provides:

  • Bounding box

  • Class

  • Confidence score

For example:

Person
Confidence: 0.94

Bounding Box:
x1 = 150
y1 = 100
x2 = 300
y2 = 450

YOLO is particularly useful because it can process video frames quickly.


Why Detection Alone Is Not Enough

Consider a video containing one person.

The camera might capture:

Frame 1 → Person detected
Frame 2 → Person detected
Frame 3 → Person detected
Frame 4 → Person detected
Frame 5 → Person detected

If we simply increase the counter for every detection:

People = 5

But there is actually only:

People = 1

This is why object tracking is important.

A tracker can assign an ID:

Frame 1 → Person ID 1
Frame 2 → Person ID 1
Frame 3 → Person ID 1
Frame 4 → Person ID 1
Frame 5 → Person ID 1

Now the application understands that all five detections belong to the same person.


Technologies Used

This project uses:

  • Python

  • YOLO

  • Ultralytics

  • OpenCV

  • Object Tracking

Install the required packages:

pip install ultralytics opencv-python

Create a requirements.txt file:

ultralytics
opencv-python

Install the dependencies:

pip install -r requirements.txt

Project Structure

Create a project directory:

yolo-people-counting/
│
├── videos/
│   └── people.mp4
│
├── people_count.py
├── webcam_count.py
└── requirements.txt

1. Load the YOLO Model

Create:

people_count.py

Then:

from ultralytics import YOLO

model = YOLO("yolo11n.pt")

print("YOLO model loaded successfully")

The model will be downloaded automatically if it is not already available locally.


2. Detect People in an Image

Before working with video, let's start with a single image.

from ultralytics import YOLO

model = YOLO("yolo11n.pt")

results = model("people.jpg")

for result in results:
    result.show()

Run:

python people_count.py

The model will detect objects in the image.

If people are present, YOLO can identify them using the person class.


3. Detect Only People

YOLO models can detect multiple object categories.

For a people-counting system, we only need the person class.

We can filter the results:

from ultralytics import YOLO

model = YOLO("yolo11n.pt")

results = model("people.jpg")

for result in results:

    for box in result.boxes:

        class_id = int(box.cls[0])
        confidence = float(box.conf[0])

        class_name = result.names[class_id]

        if class_name == "person":

            print(
                f"Person detected | "
                f"Confidence: {confidence:.2f}"
            )

Example output:

Person detected | Confidence: 0.94
Person detected | Confidence: 0.91
Person detected | Confidence: 0.87

4. Count People in an Image

If we only need to count people in a single image, we can count the number of person detections.

from ultralytics import YOLO

model = YOLO("yolo11n.pt")

results = model("people.jpg")

people_count = 0

for result in results:

    for box in result.boxes:

        class_id = int(box.cls[0])
        class_name = result.names[class_id]

        if class_name == "person":
            people_count += 1

print("People:", people_count)

Example:

People: 7

This works well for a single image.

However, it is not sufficient for counting people in a video.


5. Simple Real-Time People Detection

Let's create a webcam application.

import cv2
from ultralytics import YOLO

model = YOLO("yolo11n.pt")

camera = cv2.VideoCapture(0)

while True:

    success, frame = camera.read()

    if not success:
        break

    results = model(frame)

    annotated_frame = results[0].plot()

    cv2.imshow(
        "People Detection",
        annotated_frame
    )

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

camera.release()
cv2.destroyAllWindows()

Run:

python people_count.py

This will open the webcam and detect people in real time.


6. Display the Current Number of People

We can count the people detected in each frame.

import cv2
from ultralytics import YOLO

model = YOLO("yolo11n.pt")

camera = cv2.VideoCapture(0)

while True:

    success, frame = camera.read()

    if not success:
        break

    results = model(frame)

    people_count = 0

    for result in results:

        for box in result.boxes:

            class_id = int(box.cls[0])
            class_name = result.names[class_id]

            if class_name == "person":
                people_count += 1

    cv2.putText(
        frame,
        f"People: {people_count}",
        (30, 50),
        cv2.FONT_HERSHEY_SIMPLEX,
        1,
        (0, 255, 0),
        2
    )

    cv2.imshow(
        "People Counting",
        frame
    )

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

camera.release()
cv2.destroyAllWindows()

This shows the number of people detected in the current frame.

For example:

People: 6

However, this is still not a true entry/exit counting system.


7. Why Tracking Is Important

Imagine five people walking through a camera.

The first frame might detect:

Person A
Person B
Person C
Person D
Person E

The next frame detects the same people:

Person A
Person B
Person C
Person D
Person E

If we count every frame, the counter increases continuously.

Instead, we need persistent IDs:

ID 1 → Person A
ID 2 → Person B
ID 3 → Person C
ID 4 → Person D
ID 5 → Person E

The tracker attempts to keep these IDs associated with the same people across frames.


8. YOLO Tracking

Ultralytics provides tracking functionality that can be used with YOLO.

A simple tracking example:

from ultralytics import YOLO

model = YOLO("yolo11n.pt")

results = model.track(
    source="people.mp4",
    show=True,
    tracker="bytetrack.yaml"
)

Here we use:

ByteTrack

as the tracking algorithm.

The exact tracking configuration can be adjusted depending on the application.


9. Real-Time People Tracking

Let's build a webcam tracker.

Create:

webcam_count.py

Add:

import cv2
from ultralytics import YOLO

model = YOLO("yolo11n.pt")

camera = cv2.VideoCapture(0)

while True:

    success, frame = camera.read()

    if not success:
        break

    results = model.track(
        frame,
        persist=True,
        classes=[0],
        tracker="bytetrack.yaml"
    )

    annotated_frame = results[0].plot()

    cv2.imshow(
        "People Tracking",
        annotated_frame
    )

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

camera.release()
cv2.destroyAllWindows()

The important parameters are:

persist=True

and:

tracker="bytetrack.yaml"

persist=True tells the tracker to maintain tracking information across video frames.

classes=[0] limits detection to the person class for standard COCO-trained models.


10. Count Unique People

Now we can maintain a set of tracking IDs.

import cv2
from ultralytics import YOLO

model = YOLO("yolo11n.pt")

camera = cv2.VideoCapture(0)

unique_people = set()

while True:

    success, frame = camera.read()

    if not success:
        break

    results = model.track(
        frame,
        persist=True,
        classes=[0],
        tracker="bytetrack.yaml"
    )

    result = results[0]

    if result.boxes.id is not None:

        track_ids = result.boxes.id.int().cpu().tolist()

        for track_id in track_ids:
            unique_people.add(track_id)

    annotated_frame = result.plot()

    cv2.putText(
        annotated_frame,
        f"Unique People: {len(unique_people)}",
        (30, 50),
        cv2.FONT_HERSHEY_SIMPLEX,
        1,
        (0, 255, 0),
        2
    )

    cv2.imshow(
        "People Counting",
        annotated_frame
    )

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

camera.release()
cv2.destroyAllWindows()

Now the system keeps track of the IDs it has seen.

For example:

ID 1
ID 2
ID 3
ID 4

The counter becomes:

Unique People: 4

This is useful when you want to estimate how many unique people appeared during a camera session.


11. Entry and Exit Counting

A more advanced system can count people crossing a virtual line.

Imagine a camera pointed at a store entrance.

We can draw a line:

--------------------------------
          COUNTING LINE
--------------------------------

When a person crosses the line from one side to another:

Outside → Inside

we increment:

Entered += 1

If someone crosses in the opposite direction:

Inside → Outside

we increment:

Exited += 1

Then:

Current People = Entered - Exited

12. Define a Counting Line

For example:

LINE_Y = 300

We can draw the line using OpenCV:

cv2.line(
    frame,
    (0, LINE_Y),
    (frame.shape[1], LINE_Y),
    (255, 0, 0),
    2
)

This creates a horizontal line across the camera frame.


13. Detect When a Person Crosses the Line

We can calculate the center of a person's bounding box.

x1, y1, x2, y2 = map(
    int,
    box.xyxy[0]
)

center_x = (x1 + x2) // 2
center_y = (y1 + y2) // 2

Now we know the person's approximate center position.

If:

Previous Y < LINE_Y

and:

Current Y >= LINE_Y

the person crossed the line downward.


14. Complete Entry Counting Example

Here is a simple implementation:

import cv2
from ultralytics import YOLO

model = YOLO("yolo11n.pt")

camera = cv2.VideoCapture(0)

LINE_Y = 300

entered = 0

previous_positions = {}

counted_ids = set()

while True:

    success, frame = camera.read()

    if not success:
        break

    results = model.track(
        frame,
        persist=True,
        classes=[0],
        tracker="bytetrack.yaml"
    )

    result = results[0]

    if result.boxes.id is not None:

        boxes = result.boxes.xyxy.cpu().tolist()
        track_ids = result.boxes.id.int().cpu().tolist()

        for box, track_id in zip(
            boxes,
            track_ids
        ):

            x1, y1, x2, y2 = map(
                int,
                box
            )

            center_x = (x1 + x2) // 2
            center_y = (y1 + y2) // 2

            previous_y = previous_positions.get(
                track_id
            )

            if (
                previous_y is not None
                and previous_y < LINE_Y
                and center_y >= LINE_Y
                and track_id not in counted_ids
            ):

                entered += 1

                counted_ids.add(track_id)

            previous_positions[track_id] = center_y

            cv2.rectangle(
                frame,
                (x1, y1),
                (x2, y2),
                (0, 255, 0),
                2
            )

            cv2.putText(
                frame,
                f"ID: {track_id}",
                (x1, y1 - 10),
                cv2.FONT_HERSHEY_SIMPLEX,
                0.6,
                (0, 255, 0),
                2
            )

    cv2.line(
        frame,
        (0, LINE_Y),
        (frame.shape[1], LINE_Y),
        (255, 0, 0),
        2
    )

    cv2.putText(
        frame,
        f"Entered: {entered}",
        (30, 50),
        cv2.FONT_HERSHEY_SIMPLEX,
        1,
        (0, 255, 0),
        2
    )

    cv2.imshow(
        "People Counting",
        frame
    )

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

camera.release()
cv2.destroyAllWindows()

This example tracks people and increments the counter when their tracked center crosses the configured line.


15. Entry and Exit Counting

We can extend the previous example to support both directions.

Maintain two counters:

entered = 0
exited = 0

Then determine the direction based on the previous and current position.

Conceptually:

Previous Position
       ↓
Current Position
       ↓
Did the person cross the line?
       ↓
Yes
       ↓
Which direction?
       ↓
Entered / Exited

A simplified condition might look like:

if previous_y < LINE_Y and center_y >= LINE_Y:
    entered += 1

elif previous_y > LINE_Y and center_y <= LINE_Y:
    exited += 1

Then calculate:

current_people = entered - exited

Display:

cv2.putText(
    frame,
    f"Entered: {entered}",
    (30, 50),
    cv2.FONT_HERSHEY_SIMPLEX,
    0.8,
    (0, 255, 0),
    2
)

cv2.putText(
    frame,
    f"Exited: {exited}",
    (30, 85),
    cv2.FONT_HERSHEY_SIMPLEX,
    0.8,
    (0, 255, 0),
    2
)

cv2.putText(
    frame,
    f"Inside: {current_people}",
    (30, 120),
    cv2.FONT_HERSHEY_SIMPLEX,
    0.8,
    (0, 255, 0),
    2
)

The dashboard could show:

Entered: 120
Exited: 93
Inside: 27

16. People Counting in a Video

The same tracking approach can be used with a recorded video.

import cv2
from ultralytics import YOLO

model = YOLO("yolo11n.pt")

video = cv2.VideoCapture(
    "videos/people.mp4"
)

while True:

    success, frame = video.read()

    if not success:
        break

    results = model.track(
        frame,
        persist=True,
        classes=[0],
        tracker="bytetrack.yaml"
    )

    frame = results[0].plot()

    cv2.imshow(
        "People Tracking",
        frame
    )

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

video.release()
cv2.destroyAllWindows()

This allows you to analyze recorded surveillance or event footage.


17. People Counting in a Store

One practical application is retail analytics.

A camera can be installed near the entrance of a store.

The system can calculate:

People Entered
People Exited
Current Visitors
Peak Visitors

For example:

-----------------------------
       STORE ANALYTICS
-----------------------------

Entered:       1,245
Exited:        1,108
Currently In:    137

Peak Visitors:  192
-----------------------------

This data can help businesses understand customer traffic.


18. People Counting in Offices

The same system can be used in offices.

For example, a camera can monitor an entrance and estimate occupancy.

The system can provide:

Current Employees: 34
Maximum Capacity: 50
Available Capacity: 16

This can be useful for smart-building systems.


19. People Counting for Events

At events, conferences, exhibitions, and public venues, people counting can help estimate attendance.

For example:

Total Visitors: 4,820
Current Visitors: 1,240

The data can be stored in a database and displayed through a dashboard.


20. Improving People Counting Accuracy

Real-world people counting is more difficult than counting people in a simple image.

Common challenges include:

  • People overlapping

  • Crowded environments

  • Poor lighting

  • Camera movement

  • People partially hidden

  • Low-resolution cameras

  • Fast movement

  • Similar-looking people

To improve performance, consider the following techniques.

Use a Better Camera

Higher-resolution video can help the detector identify people more accurately.

Position the Camera Correctly

A camera positioned above the entrance can sometimes provide a better view than a camera at ground level.

Use a Suitable YOLO Model

A larger model may provide better detection accuracy but generally requires more computational resources.

Adjust the Confidence Threshold

For example:

results = model.track(
    frame,
    persist=True,
    classes=[0],
    conf=0.50,
    tracker="bytetrack.yaml"
)

Use Tracking

Tracking helps maintain identity across frames and reduces repeated counting.


21. People Counting With a Database

A production application can store the results in a database.

For example:

people_count
-------------------------
timestamp
entered
exited
current_people

Python could then send the data to a backend API.

For example:

import requests

data = {
    "entered": entered,
    "exited": exited,
    "current_people": current_people
}

requests.post(
    "https://example.com/api/people-count",
    json=data
)

This allows a web dashboard to display real-time statistics.


22. Creating a Dashboard

The detection system can be connected to a dashboard.

A dashboard could display:

=================================
       PEOPLE ANALYTICS
=================================

Current People
      137

Entered Today
     1,245

Exited Today
     1,108

Peak Occupancy
      192

=================================

Historical data can also be displayed using charts:

Visitors
  |
  |              █
  |        █     █
  |    █   █     █
  |____█___█_____█________
       9AM 12PM  3PM  6PM

This turns a basic computer vision project into a complete analytics system.


23. Important Limitations

People counting systems are not perfect.

Tracking algorithms can sometimes lose a person's ID when:

  • The person becomes temporarily hidden.

  • Multiple people overlap.

  • The person leaves and re-enters the camera view.

  • The video quality is poor.

  • The camera angle changes.

Therefore, production systems should be tested using the actual camera position, lighting conditions, and expected crowd density.

The counting line should also be positioned carefully to minimize accidental crossings.


24. Complete People Counting Workflow

The complete system can be summarized as:

Camera
   ↓
Video Frame
   ↓
YOLO Detection
   ↓
Detect Person
   ↓
Object Tracker
   ↓
Assign Tracking ID
   ↓
Track Movement
   ↓
Detect Line Crossing
   ↓
Entry / Exit Counter
   ↓
Database
   ↓
Analytics Dashboard

This architecture can be expanded into a complete real-world monitoring system.


25. Complete Simple Tracking Example

Here is a compact version of the main detection and tracking functionality:

import cv2
from ultralytics import YOLO

model = YOLO("yolo11n.pt")

camera = cv2.VideoCapture(0)

while True:

    success, frame = camera.read()

    if not success:
        break

    results = model.track(
        frame,
        persist=True,
        classes=[0],
        conf=0.50,
        tracker="bytetrack.yaml"
    )

    result = results[0]

    if result.boxes.id is not None:

        track_ids = (
            result.boxes.id
            .int()
            .cpu()
            .tolist()
        )

        for track_id in track_ids:

            print(
                f"Person detected: ID {track_id}"
            )

    annotated_frame = result.plot()

    cv2.imshow(
        "YOLO People Counting",
        annotated_frame
    )

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

camera.release()
cv2.destroyAllWindows()

This is a good starting point for building more advanced people-counting functionality.


Conclusion

YOLO-based people counting is a practical example of how object detection and object tracking can be combined to solve real-world computer vision problems.

A simple YOLO model can detect people in individual images or video frames. However, reliable counting requires tracking so that the same person is not counted repeatedly.

The basic architecture is:

YOLO
  ↓
Person Detection
  ↓
Object Tracking
  ↓
Unique Person ID
  ↓
Movement Tracking
  ↓
Line Crossing
  ↓
Entry / Exit Count

With Python, YOLO, OpenCV, and a tracking algorithm such as ByteTrack, developers can build systems for retail analytics, office occupancy, event monitoring, smart buildings, security systems, and many other applications.

The next step for a production system is to connect the computer vision pipeline to a backend API and database, allowing the collected data to be visualized through a real-time analytics dashboard.