← Back to list

Stateful Video Analytics: Building a Custom Line-Crossing Object Counter with YOLO11 and Python

Getting Started with YOLO11 Object Counting Python for Real-World Analytics

Eran Feit in Object Detection tutorials · 2026-05-17 16:27 · 0 claps · 14.0 min read paywalled
#yolov11 #object-counting #object-tracking #multi-object-tracking #python
Open on Medium ↗
Wiki topics: GRW · Growth & Analytics

Stateful Video Analytics: Building a Custom Line-Crossing Object Counter with YOLO11 and Python

Getting Started with YOLO11 Object Counting Python for Real-World Analytics

Implementing a yolo11 object counting python pipeline represents a massive leap forward from traditional frame-by-frame object detection. Standard detection algorithms analyze an isolated image and tell you what is present, but they completely lack memory. In dynamic environments — such as monitoring foot traffic in a storefront or managing vehicle flow on a highway — knowing an object exists is only half the battle. You need a system that remembers unique objects across frames, assigning persistent tracking IDs so that individual entities are logged exactly once as they pass through your target area.

At its core, this architecture pairs the cutting-edge feature extraction of the YOLO11 model with a tracking algorithm that calculates spatial continuity. When a video frame is processed, the model predicts bounding boxes and confidence scores, while the tracking module matches these detections with historical coordinates from preceding frames. By writing a Python script that targets specific class indices and calculates center points, you can create pixel-perfect trigger boundaries. When a tracked ID’s center point changes coordinates relative to a user-defined line, the software registers a crossing event, permanently incrementing the count for that specific class.

The true beauty of utilizing yolo11 object counting python frameworks lies in its extreme customizability and resource efficiency. Developers can filter out background noise by restricting inference to specific objects, ensuring the processor doesn’t waste cycles tracking irrelevant elements. Whether your target is counting livestock on a ranch, identifying anomalies on a factory conveyor belt, or monitoring urban traffic patterns, this lightweight approach eliminates the need for massive cloud compute infrastructure, allowing for robust edge deployment and real-time visualization.

If you want the source video file to duplicate my test workflow?

If you want to achieve the exact same tracking results and test the python script using the identical source video featured in this tutorial, I am happy to share it with you. Simply drop me an email mentioning the name of this guide, and I will send over a direct download link so you can get started right away.

Email: feitgemel@gmail.com

To build a high-performance yolo11 object counting python pipeline, everything begins with establishing a robust, hardware-accelerated development environment. By creating a dedicated Conda workspace running Python 3.12 and installing PyTorch 2.9.1 paired with CUDA 12.8, the underlying execution engine can offload intensive matrix multiplications straight to your NVIDIA graphics hardware. This preparation ensures that when the script initializes the large-scale yolo11l.pt architecture via the Ultralytics framework, the system is primed to handle high-resolution video arrays without bottlenecking your CPU cycles or dropping critical frames.

Once the environment is active, the script establishes a structural bridge between video stream ingestion and deep learning inference. Using OpenCV’s cv2.VideoCapture, raw frames are systematically read from your local media file and passed directly into the tracking engine using the model.track() method. A key element here is setting persist=True, which instructs the model to retain visual memory of previously identified targets across sequential frames. Furthermore, by passing the argument classes=[17], we explicitly instruct the neural network to ignore all background noise and irrelevant entities, focusing its predictive power solely on tracking horses with a strict confidence threshold above 60%.

The true geometric magic happens inside the execution loop, where spatial tracking coordinates are translated into actionable telemetry. For every frame processed, the script extracts the boundary box coordinates (xyxy) and calculates the exact mathematical midpoint, or centroid, of the detected object using basic pixel averaging ($cx = (x1 + x2) // 2$). This dynamic center point is evaluated against a fixed horizontal boundary set at line_y_red = 600. Simultaneously, the script utilizes OpenCV drawing functions to render clear green bounding boxes, unique identifier text labels, and a bright red tracking dot on the center of each target, making the internal mechanics instantly visible to the user.

To prevent the algorithm from counting the same entity multiple times as it lingers over the line, the script introduces a highly efficient state management system. By using a native Python set() named crossed_ids, the system cross-references the unique tracking ID assigned to an object against a historical log of elements that have already breached the boundary. If a new ID crosses the threshold ($cy > line_y_red$), it is permanently added to the set, and the corresponding class counter inside a defaultdict is incremented. These accumulated metrics are then drawn directly onto the top-left corner of the video stream in real-time, providing an elegant, automated visual dashboard of your analytics.

[embed]

Link to the video tutorial here

Download the code for the tutorial **here or [here ](https://ko-fi.com/s/b0bb4f82c9)**.

Best AI Photo Tools (Backgrounds, Objects, Headshots)

✅ Phot-AI packs more than 30 AI‑powered tools into one place — covering background and object removal/replacement, image extension and a suite of creative generators for art, icons and logos.

follow the link and start creating : https://phot.ai?ref=eran33

✅ Create and remix stunning AI art and photos with community-driven creativity. tap the link and start creating today! : https://www.remixai.io/?ref=eran

✅ PhotoGPT AI acts as your personal photographer — just describe what you need and the platform generates high‑quality headshots or casual images within minutes.

Its built‑in photo editor lets you remove objects, replace backgrounds and make studio‑quality corrections with a single click.

You can even train your own AI model using a few selfies, receive context‑aware prompt suggestions and upscale images for print‑ready results.

Dive into this all‑in‑one AI photo studio : https://www.photogptai.com/?ref=eran

My Blog

You can follow my blog here .

Link to the full post and code here

Want to get started with Computer Vision or take your skills to the next level ?

Great Interactive Course : “Deep Learning for Images with PyTorch” here

If you’re just beginning, I recommend this step-by-step course designed to introduce you to the foundations of Computer Vision — Complete Computer Vision Bootcamp With PyTorch & TensorFlow

If you’re already experienced and looking for more advanced techniques, check out this deep-dive course — Modern Computer Vision GPT, PyTorch, Keras, OpenCV4

Constructing the Ultimate Computational Sandbox via Isolated Environments and Hardware Rails

Setting up a dedicated computing environment serves as the vital infrastructure layer for any advanced vision deployment. By isolating software dependencies inside a closed package container, you insulate your engineering workspace from version mismatch issues. This preventative strategy ensures that tracking arrays perform consistently when shifting projects between distinct testing devices.

Verifying the active graphic runtime compiler version establishes the framework configuration needed for intense mathematical acceleration. Offloading continuous matrix multiplications from standard central processors to hardware-accelerated graphic kernels maximizes system pipeline throughput. This operational tuning is absolutely mandatory if your ultimate design target requires steady, high-frequency execution streams.

Installing specialized computer vision modules bridges your application workspace with next-generation network architectures. These state-of-the-art framework libraries manage complex coordinate tracking arrays cleanly behind your execution thread loops. This approach frees up valuable runtime resources, allowing you to focus your code on mining strategic business telemetry.

### Create a clean, isolated Conda development workspace specifically running Python 3.12 to avoid version conflicts.
conda create -n YoloV11-312 python=3.12
### Activate your newly configured Conda development workspace to prepare it for subsequent library dependencies.
conda activate YoloV11-312

### Query the system's terminal to find your local NVIDIA CUDA compiler driver version for hardware compatibility checks.
nvcc --version

### Use pip to install PyTorch version 2.9.1 pre-configured with native CUDA 12.8 GPU acceleration paths.
pip install torch==2.9.1 torchvision==0.24.1 torchaudio==2.9.1 --index-url https://download.pytorch.org/whl/cu128

### Install the official Ultralytics framework version 8.4.21 to access the cutting-edge YOLO11 computer vision models.
pip install ultralytics==8.4.21

This foundational preparation phase organizes your graphic hardware channels and library dependencies systematically. It establishes the massive data-handling capabilities required for advanced neural networks to process sequential frames cleanly.

Activating the Architecture by Instantiating Model Weights and Opening File Handles

Importing core matrix libraries introduces native file streaming and computer vision operations directly to your main script. These structured dependencies look after frame array memory management and video channel operations efficiently behind the scenes. This approach enables you to feed external media files straight into your program logic with highly compressed code layouts.

Loading pre-trained deep learning parameters provides your computational workspace with instant predictive performance capabilities. This established parameter matrix has spent thousands of processing hours mapping out edge configurations and semantic profiles across extensive visual training pools. Integrating this compiled neural knowledge gives your tracking program stable shape detection instincts from the very first run.

Connecting file-capture stream variables links your target digital video files straight to the neural network engine. This framework loop ingests individual frame arrays sequentially and passes the pixel metrics down for algorithmic valuation. This data highway serves as the mandatory transport mechanism for all downstream tracking operations and telemetry evaluations.

Original frame vs. Result :

Original frame vs. Result

Original frame vs. Result

What makes pre-trained convolutional parameters highly effective for custom vision development?

Pre-trained model parameters provide an optimized visual shortcut because they already understand how to identify lines, shapes, and complex object outlines. Leveraging this pre-built network intelligence bypasses the need for massive dataset labeling campaigns, giving you instant real-time tracking performance.

### Import OpenCV to handle video file reading, frame manipulations, and graphical visual overlays.
import cv2 
### Import the core YOLO model class from the official Ultralytics framework for deep learning inference.
from ultralytics import YOLO 
### Import defaultdict from the collections module to cleanly initialize and manage our running class counter tallies.
from collections import defaultdict 

### Instantiate and load the heavy-duty pre-trained YOLO11 large neural network tracking architecture into memory.
model = YOLO("yolo11l.pt")

### Extract the complete list of target class names recognized natively by the pre-trained model.
class_list = model.names
### Print out the full list of available class labels to the system console for tracking verification.
print(class_list)

### Establish a video capture streaming pipeline using OpenCV to read the target asset file frame by frame.
cap = cv2.VideoCapture("Best-Object-Detection-models/Yolo-V11/Real-Time objects-Counting-and-Tracking/Horses.mp4")

This structural loading step prepares your application memory pools with advanced network configurations. It ensures that your active application pipeline can extract and translate streaming image arrays reliably.

Configuring Spatial Boundaries and Isolating Target Visual Categories

Defining fixed coordinate metrics maps out the precise mathematical boundary layout across your image canvas. This step establishes a specific pixel coordinate threshold that splits your processing field into two distinct data regions. This boundary serves as the structural metric required to detect movement patterns and check travel vectors accurately over time.

Initializing lightweight hash sets sets up a memory-efficient storage architecture to log passing object identities. These high-speed lookups allow your logic loop to evaluate historical registry states without hitting CPU processing limits. This code block keeps your software highly responsive when multiple items move across the frame simultaneously.

Evaluating frames continuously tracks object trajectories while maintaining absolute identity continuity across sequential states. Activating exclusive class parameter adjustments strips away unnecessary data noise, forcing the model to focus on your specific category. This custom tracking filter dramatically reduces processing waste, ensuring your system runs fast on edge hardware devices.

Why is the tracking persistence argument critical for sequential frame analysis?

Activating the persistence property forces the network to correlate tracking arrays between successive frame matrices using predictive motion mathematics. This feature keeps the assigned identity tracking values consistent even when targets briefly pass behind visual obstacles or cross paths.

### Define the absolute vertical Y-coordinate line threshold position in pixels where crossing triggers will activate.
line_y_red = 600 

### Initialize a dynamic default dictionary to automatically manage and track numerical count states by class name.
class_counts = defaultdict(int)

### Initialize an empty hash set to keep track of persistent individual object IDs that have already crossed the threshold.
crossed_ids = set() 

### Open a continuous loop to ingest and analyze each frame of the video file as long as the capture pipeline remains open.
while cap.isOpened():
    ### Read the current video frame along with a boolean validation flag indicating successful ingestion.
    ret , frame = cap.read()
    ### Evaluate the boolean validation flag and break out of the processing loop if the video stream finishes.
    if not ret:
        break

    ### Execute live YOLO tracking optimization on the current frame using persistent states and exclusive class filtering parameters.
    results = model.track(frame, persist=True, classes=[17], conf=0.6) # Only horeses with confidence above 60%

This logic architecture establishes your spatial trigger limits and fine-tunes your object filters cleanly. It manages tracking state continuity throughout the running video playback loop.

Computing Midpoint Vectors to Log Crossings and Projects Real-Time Analytics

Checking the structural integrity of your incoming tracking tensors protects your system loop from encountering null pointer crashes. Unpacking the results array extracts bounding vectors, identification values, and class tags directly into system memory. This structured data layout gives you the raw metrics needed to calculate coordinate intersections and generate custom visuals.

Deriving exact center coordinate points converts wide bounding areas into distinct, one-pixel reference markers. Checking these calculated spatial vectors against your fixed horizontal limit identifies the exact frame where a crossing occurs. This code block handles the vital translation step where raw movement variables turn into locked database records.

Overlaying bright bounding boxes and dynamic statistical counts builds a highly responsive video dashboard right on the stream. Stepping down text positioning rows systematically stops newly rendered lines from overwriting historical data metrics. This graphic rendering phase completes the application loop, giving human operators a clear visual report of the running software metrics.

How does calculating bounding box centers enhance tracking precision across lines?

Computing the box midpoint translates complex edge vectors into a single, unambiguous coordinate point. Evaluating this exact pixel marker against your line limit creates a clean, binary trigger mechanism that eliminates positional errors.

### Ensure the returned object tracking results data object contains valid bounding box information before processing pixel geometry.
    if results[0].boxes.data is not None:
        ### Pull down the spatial bounding box top-left and bottom-right corner tensors to local host CPU memory.
        boxes = results[0].boxes.xyxy.cpu()
        ### Extract the persistent historical identification numbers assigned by the tracking algorithm and convert them to a list.
        track_ids = results[0].boxes.id.int().cpu().tolist()
        ### Retrieve the specific class index integers for each detected item and format them into an iterable list.
        class_indices = results[0].boxes.cls.int().cpu().tolist()
        ### Fetch the statistical confidence scores for each prediction out of the tensor array.
        confidences = results[0].boxes.conf.cpu()

        ### Draw a solid horizontal red marker line onto the video canvas matrix at the predefined boundary coordinate.
        cv2.line(frame, (200, line_y_red),(1500, line_y_red), (0,0,255) , 3)

        ### Iterate over every detected object in the frame to break out individual bounding vectors, identities, and confidence states.
        for box, track_id , class_idx , conf in zip(boxes, track_ids, class_indices, confidences):

            ### Unpack the bounding box edge array and explicitly convert the values into standard integer coordinates.
            x1, y1 , x2, y2 = map(int, box)

            ### Calculate the exact horizontal midpoint coordinate of the target box using pixel center-point averaging.
            cx = (x1 + x2) // 2
            ### Calculate the exact vertical midpoint coordinate of the target box using pixel center-point averaging.
            cy = (y1 + y2) // 2

            ### Lookup the human-readable class text name corresponding to the numerical tracking index from the model array.
            class_name = class_list[class_idx]

            ### Render a solid red tracking indicator dot on the canvas matrix at the exact center coordinate of the target item.
            cv2.circle(frame, (cx, cy), 4, (0, 0, 255), -1 ) 

            ### Draw custom tracking status text labels above the object's upper boundary displaying its persistent ID number and name.
            cv2.putText(frame, f"ID: {track_id} {class_name}", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2) 

            ### Render a bright green bounding rectangle around the outer limits of the object to frame it visually.
            cv2.rectangle(frame, (x1, y1), (x2, y2), (0,255,0), 2)

            ### Evaluate if the center coordinate has bypassed the vertical threshold and confirm it has not been logged previously.
            if cy > line_y_red and track_id not in crossed_ids:
                ### Insert the unique tracking identification number into the crossed set to prevent any future duplicate registry.
                crossed_ids.add(track_id)
                ### Log a crossing event and increment the dynamic tally tracking dictionary for that unique class type.
                class_counts[class_name] += 1

        ### Initialize a vertical spacing offset variable to position the printed dashboard metrics neatly down the frame canvas.
        y_offset = 150 
        ### Loop through each monitored class name and corresponding count to print current statistics on the screen.
        for class_name, count in class_counts.items():
            ### Draw the human-readable count statistics text directly onto the video matrix background frame using high-visibility sizing.
            cv2.putText(frame, f"{class_name}: {count}", (50, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 3, (255,0,0), 3)
            ### Step down the vertical coordinate spacing to ensure next class print logs do not overwrite the previous data row.
            y_offset += 30

    ### Display the fully annotated video frame matrix instantly on the system screen inside a named graphical rendering window.
    cv2.imshow("YoloV11 object tracking & counting", frame)

    ### Monitor system keyboard interrupts continuously and halt execution instantly if the operator strikes the lower-case 'q' key.
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

### Release the video file handle allocation safely back to the operating system after termination of the loop.
cap.release()
### Safely tear down and close all active graphical rendering windows opened by OpenCV during video visualization playback.
cv2.destroyAllWindows()

This mathematical endpoint logic evaluates coordinate intersections reliably. It locks tracking states securely, aggregates numerical metrics, and projects your active dataset findings onto the live dashboard screen.

FAQ

Q: How do I configure this script to run on a standard CPU instead of a GPU?

A: If your workspace doesn’t have an active GPU, modify the PyTorch workspace dependency installation to match standard CPU wheels, and the tracking pipeline will run on the CPU automatically.

Q: What does the class filter value [17] stand for inside the track command?

A: The numerical index 17 points directly to the pre-loaded “horse” class matrix inside the foundational COCO dataset profile used by default.

Q: Can I track and count multiple different object classes at the exact same time?

A: Yes, you can pass an array of target class digits into the selection parameter, allowing your script to scan for cars, buses, or people concurrently.

Q: Why is using a set container better than a standard list for storing tracking IDs?

A: A set collection uses strict uniqueness restrictions and features ultra-fast constant time check cycles, keeping your framework fast as more objects pass the line.

Q: How can I shift the vertical line placement to adjust my counting trigger zone?

A: Simply change the pixel coordinate integer assigned to your horizontal boundary threshold variable to shift the trigger layout higher or lower on the frame.

Q: What should I do if my video capture stream fails to load or open properly?

A: Check that your local data paths point accurately to the media file location and confirm that your file extension string matches the source exactly.

Q: Why does the script execute .cpu() transformations before converting data arrays?

A: You must copy tensor information down from isolated graphic memory locations to standard system RAM arrays before running basic Python lists or OpenCV operations.

Q: How does the persist parameter help manage brief target occlusions?

A: The persistence option forces the computer vision model to utilize historical motion estimations, keeping identity metrics constant when targets are temporarily blocked from view.

Q: What is the function of the defaultdict container from the collections library?

A: The defaultdict structure sets uninitialized keys to a baseline value of zero automatically, protecting your mathematical counters from key tracking errors.

Q: Can this setup handle live security camera feeds or RTSP video streams?

A: Yes, swap out the source media text string for your camera’s hardware network link or local index stream handle to unlock real-time security tracking.

Conclusion

Developing an optimized real-time visual telemetry script transforms standard image detections into persistent analytical data networks. By deploying persistent model tracking arguments alongside custom pixel boundary math, you build an efficient pipeline capable of isolated class tracking. This custom code method sidesteps dependency inflation, ensuring that your logic runs natively and fast on modern developer workstations or edge devices. Applying these state-management configurations yields a robust computer vision blueprint ready to serve professional field monitoring setups.

Connect

☕ Buy me a coffee — https://ko-fi.com/eranfeit

🖥️ Email : feitgemel@gmail.com

🌐 https://eranfeit.net

🤝 Fiverr : https://www.fiverr.com/s/mB3Pbb

Enjoy,

Eran


메타데이터
post_id
8ea57e837480
slug
stateful-video-analytics-building-a-custom-line-crossing-object-counter-with-yolo11-and-python-8ea57e837480
url
https://medium.com/object-detection-tutorials/stateful-video-analytics-building-a-custom-line-crossing-object-counter-with-yolo11-and-python-8ea57e837480
canonical_url
https://medium.com/object-detection-tutorials/stateful-video-analytics-building-a-custom-line-crossing-object-counter-with-yolo11-and-python-8ea57e837480
author_url
https://medium.com/@feitgemel
status
ok
fetched_at
2026-06-15 20:49:13