Tuesday, October 27, 2026

Count Cars with YOLO: Why Track IDs Are Not Vehicle Counts

A box follows a car, a number appears above it, and a counter increases. It looks simple. But what exactly did the application count: detections, tracking identities, or vehicles passing a particular point? Those are different measurements.

In this Python example, YOLOv8n detects vehicles, ByteTrack associates observations across frames, and a separate rule accepts downward crossings of a horizontal line. Keeping those responsibilities separate makes the result easier to explain and test.

Traffic counter showing tracked vehicle boxes, temporary IDs and a horizontal counting segment
The demonstration uses a synthetic traffic scene. Boxes, IDs and crossing events come from the same recorded tracking run.

Three measurements that should stay separate

A detection describes an object in one frame. A track ID associates observations over time. A crossing event records an observation sequence that satisfies our counting rule.

One car can appear in many frames. A tracker may also lose a vehicle and assign a new ID when it is detected again. Counting every detection would repeatedly count visible objects; counting distinct IDs would measure the tracker's associations. Neither automatically tells us how many physical vehicles passed our chosen location.

In this particular 241-frame, 1280 × 720 clip, the recorded run contains 23 temporary IDs. Our selected rule accepts 7 crossing events. This is a result for one clip and configuration, not an accuracy claim for general traffic footage.

Define a crossing before calculating a total

Image coordinates start at the top left: y increases downward. We place the line at line_y = 413 and restrict its horizontal span to x_range = (80, 1000). A qualifying center must move from above the line to the line or below it. Its current x must fall within that span.

The complete rule below remembers the previous y for each ID. It also remembers IDs already counted during this function call.

# Turn tracked positions into one downward pass per ID.
def count_passes(frame_tracks, line_y, x_range):
    previous_y, counted_ids, events = {}, set(), []
    for frame in frame_tracks:
        for obj in frame["objects"]:
            track_id = obj["track_id"]
            x, y = obj["center"]
            crossed = previous_y.get(track_id, y) < line_y <= y
            if crossed and x_range[0] <= x <= x_range[1] and track_id not in counted_ids:
                counted_ids.add(track_id)
                events.append({"frame": frame["frame_idx"], "id": track_id, "class": obj["class_name"]})
            previous_y[track_id] = y
    return events

Code: Copyright © 2026 ARTSCI — Author of the AI/ML/CV course materials. Licensed under the GNU AGPL version 3, without warranty. This code notice does not automatically apply to the independent images, video or course presentation.

The decisive comparison is previous_y.get(track_id, y) < line_y <= y. For a first observation, the default previous value is the current y, so the comparison is false. An object first detected below the line therefore does not produce an invented crossing.

counted_ids prevents a second accepted event for the same ID in one call. The function updates the previous y after examining every observation, even when the object is outside the horizontal span. It does not interpolate where the center crossed the line: the span check uses the current x.

Explain one event with pictures and numbers

For ID 3, the recorded center moves from y = 411.236 in frame 36 to y = 414.239 in frame 37. The rule accepts frame 37 because 411.236 < 413 <= 414.239. Its current x is approximately 474.9, inside the permitted span. ID 3 has not yet been counted.

Before, crossing and after observations for track ID 3 in frames 36, 37 and 38
The same ID before, at and after its accepted event. Coordinates are rounded for display; the rule uses the original values.

This evidence is more informative than a final total alone. You can inspect the visible vehicle, the relevant center positions and the exact decision together.

Equal totals can hide different events

Move only the line upward by 30 pixels, from y = 413 to y = 383. Reuse the same recorded tracks and horizontal span. The result is again 7 events, but the accepted IDs differ:

LineEventsAccepted IDs
y = 41374, 3, 2, 5, 9, 7, 55
y = 38373, 2, 5, 9, 7, 55, 48

ID 4 is first observed at y ≈ 398.5, already below the shifted line. Its observations support a crossing of y = 413, but not y = 383. ID 48 supplies an event only at the shifted line. Comparing event membership exposes what the matching totals hide.

Check logic separately from tracking quality

Run these checks after defining count_passes above. Their deliberately chosen center positions isolate Python logic from model quality. The three assertions test repeated downward crossings by one ID, an ID first seen below the line, and upward movement. All x positions are inside the counting segment.

def sample(y_values):
    return [{"frame_idx": i, "objects": [{"track_id": 1,
             "class_name": "car", "center": (200, y)}]}
            for i, y in enumerate(y_values)]

assert len(count_passes(sample([411, 413, 410, 416]), 413, (80, 1000))) == 1
assert len(count_passes(sample([414, 420]), 413, (80, 1000))) == 0
assert len(count_passes(sample([420, 410]), 413, (80, 1000))) == 0
print("All three counting-rule checks passed.")

The expected counts are 1, 0 and 0. After these checks pass, inspect a short video interval manually and match individual passages to event rows.

This compact rule has limits. A changed ID can allow the same vehicle to count again. Missing observations may leave a long gap between the two positions used by the comparison. There is no gap limit or hysteresis band here, and motion upward is intentionally excluded. Real deployment needs representative footage and separate checks for missed passages and false counts.

For the tracking API and tracker configuration, see the official Ultralytics tracking documentation. This example explicitly selects bytetrack.yaml; it does not rely on whichever tracker a library version uses by default.

If you want to build the full project, including the notebook, local application and practical exercises, see my English course: Computer Vision: OpenCV, Machine Learning and YOLO.

New Udemy Course: Computer Vision with OpenCV, Machine Learning and YOLO

I have published my new English-language course on Udemy: Computer Vision: OpenCV, Machine Learning and YOLO.

The course takes you from working with image arrays to building Python applications that detect, track and count objects in images and video. It combines theory with separate practical lessons, so you can see the inputs, follow the code and inspect the results.

What you will learn

  • Python, NumPy and OpenCV: image representation, color channels, transformations, filtering and edge detection.
  • Machine learning: feature extraction, classification, model comparison and evaluation with scikit-learn.
  • Deep learning and CNNs: neural network foundations, convolution, pooling, image classification and transfer learning with TensorFlow and PyTorch.
  • YOLO: object detection, custom training, object tracking, segmentation and image classification.
  • Pose estimation and deployment: body keypoints, movement features, model export, CPU performance measurements and comparisons with alternative detectors.

Four projects that bring the ideas together

  • Parking Lot Monitor: work with parking-space regions and occupancy information.
  • Traffic Counter: detect and track vehicles, then count crossings of a defined line.
  • Fitness Tracker: use pose keypoints and movement states to count squat repetitions.
  • Document Scanner: find document boundaries and correct perspective.

These are learning projects that make the connection between model outputs and application logic visible.

Video lessons, notebooks and knowledge checks

The course includes more than 10 hours of English video, downloadable notebooks and lesson resources, and 12 section quizzes with 36 questions. Practical lessons include setup and dependency instructions to help you run the examples locally.

It is aimed at developers, students and engineers who want to move from computer vision concepts to working Python examples. Some programming experience is helpful; the opening sections review Python and NumPy foundations.

Launch offer: US$12.99

Coupon code: CBEE0F25D4870B5F9123

Open the course with the US$12.99 coupon

The coupon is valid until 27 November 2026 at 06:20 UTC (26 November, 22:20 PST). Check that the coupon is applied and review the final price shown by Udemy before purchasing.

You can also use my standard Udemy course link to view the course and its curriculum. This separate link does not include the coupon code.

I would be glad to hear which computer vision applications you would like to build.

Monday, July 27, 2020

Inteligentne miasto (Smart City - Polish)

Inteligentne miasto 

Wstęp

Chciałbym dzisiaj zaproponować przygotowanie projektu architektonicznego opisującego zbudowanie systemów dla  inteligentnego miasta. W celu wykonania transformacji cyfrowej miasta należy podejść do problemu całościowo tzn. najpierw należy wykonać inwentaryzację posiadanych zasobów i oferowanych usług biznesowych. Następnie należy zebrać wywiad z użytkownikami obecnych systemów oraz z  interesariuszami, a w tym z odbiorcami końcowymi usług. Tak pozyskane dane wraz z wykonaną inwentaryzacją pozwolą zdiagnozować problemy dotyczące obsługi sygnalizacji (brak zielonej fali), obsługa miejsc parkingowych (brak możliwości rezerwacji miejsc parkingowych) czy komunikacja miejsca (nieoptymalne połączenia - mieszkańcy tracą dużo czasu na dojazd do pracy). Dobra diagnoza pozwoli zaproponować architekturę TO-BE w podziale na domeny:

  • biznesową,
  • aplikacyjną,
  • danych,
  • techniczną (infrastrukturalną)

Dodatkowo należy zdefiniować wymagania niefunkcjonalne oraz pryncypia architektoniczne, które będą drogowskazem dla wszystkich dalszych działań.

Pryncypia architektoniczne

Pryncypia architektoniczne dla naszego miasta wraz z modelem motywacji zostały umieszczone na poniższym diagramie.

 

    

 

Stan obecny i architektura AS-IS

W celu przygotowania architektury AS-IS należy przeprowadzić inwentaryzację w podziale na domeny:

    •  architektura biznesowa (procesy biznesowe, usługi biznesowe, aktorzy, itp.)
    • architektura aplikacyjna (stworzone lub zakupione systemy i aplikacje, licencje, opisy integracji między komponentami, interfejsy)
    • architektura danych (należy zdefiniować encje danych, właścicieli danych biznesowych, zdefiniować dane referencyjne oraz przepływy danych oraz sposoby ich wykorzystania)
    • architektura techniczna (serwery fizyczne i wirtualne, systemy operacyjne, bazy danych, licencje, komponenty sieciowe fizyczne i wirtualne, systemy do backupu danych, ułożenie komponentów w podsieciach, pozostałe komponenty takie jak: firewall, WAF, IAM, HSM, SIEM, Gateway, itp.)

Z uwagi na możliwość wystąpienia drobnej przestępczości należy zweryfikować możliwość ataków na systemy. Należy wykonać testy penetracyjne i wykonać stosowne zabezpieczenia jeśli będą potrzebne.

Po pozyskaniu wiedzy dotyczącej architektury AS-IS należy przeprowadzić badania satysfakcji w obszarach:

1.       Użytkownicy wewnętrzni systemu w podziale na departamenty:

    • Zebranie informacji czy procesy biznesowe działają bez problemów
    • Czy są wąskie gardła w procesach
    • Jak działa i czy jest dostępna automatyzacja procesów
    • Propozycje usprawnień

2.       Interesariusze i odbiorcy biznesowi (grupa, która chce jak najszybciej dojechać (sterowanie ruchem) i móc zaparkować pojazd):

    • Zebranie opinii o jakości działania usługi
    • Pozyskanie informacji o problemach poruszania się i parkowania
    • Propozycje i uwagi dotyczące usprawnień (lista życzeń)

3.       Użytkownicy końcowi (użytkownicy indywidualni potrzebujący dojechać w określone miejsce)

    • Krótkie uwagi dotyczące usługi (co jest dobre, a co nie),
    • możliwości usprawnień

Architektura TO-BE

Warstwa biznesowa

Rozpoczynając od warstwy architektury biznesowej należy zdefiniować podstawowe procesy biznesowe oraz powiązane usługi biznesowe. Najważniejsze są oczywiście zewnętrzne usługi i procesy, które możemy podzielić na usługi dla biznesu i usługi dla odbiorcy indywidualnego. Same usługi zostały zdefiniowane na wysokim poziomie ogólności.

 


Część elementów powinna zostać rozbudowana i opisana dokładniej:

  • rozliczenia za korzystanie z usługi powinny być naliczane za faktyczne wykorzystanie usługi 
  •  powinna być weryfikacja, czy miejsce jest zajęte przez samochód, dla którego była wykonana rezerwacja. Miejsca powinny być monitorowane, a obraz powinien być przetwarzany i powinien być rejestrowany numer rejestracyjny
  • podstawowa usługa przejazdu i parkowania powinna być spójna i intuicyjna do przejścia

 

Procesu i usługi dedykowane dla klienta wewnętrznego zostały zwizualizowane poniżej


 

Warstwa aplikacyjna

Wymagania dotyczące warstwy aplikacyjnej i funkcjonalności jakie muszą zostać dostarczone wynikają bezpośrednio lub pośrednio z wyższej warstwy – warstwy biznesowej. Główne założenia dla warstwy aplikacyjnej:

1.       Wszystkie czujniki, komponenty kontrolujące światła drogowe i dostępność miejsc parkingowych są podłączone do jednego systemu zarządzającego tymi komponentami – mówimy tutaj o IoT

2.       Użytkownik końcowy ma dostęp do swojego konta i funkcjonalności poprzez portal WWW

3.       Funkcjonalności podstawowe pracują w oparciu o konteneryzację i orchiestrację np. k8s.

4.       Zostaną stworzone minimalnie 2 podsieci:

a.       Dla komponentów udostępniających usługi WWW

b.       Dla wewnętrznych komponentów, które nie udostępniają usług zewnętrznych

5.       Zdefiniowane procesy będą działały w oparciu o zdefiniowane workflow

6.       Procesy wyszukania najlepszej trasy, zarezerwowania miejsca parkingowego w okolicy, czy weryfikacji czy faktycznie oczekiwany samochód zaparkował na miejscu wyznaczanym będą odbywały się z wykorzystaniem ML (rozpoznawanie obrazów, wyznaczenie najlepszej ścieżki lub rezerwacja miejsca o określonej porze w okolicy)

7.       Wszystkie działania będą zbierane i agregowane w BigData – będziemy mogli weryfikować , czy klient zastanawiał się nad wyborem opcji doręczenia, ceną itp. Na podstawie tych danych będziemy mogli skierować do takich użytkowników pakiety promocyjne w przyszłości.  

8.       Tworzone API musi być udokumentowane. W tym celu należy wykorzystać OpenAPI.

9.       Wytwarzany kod aplikacji musi przechodzić automatyczną weryfikację np. Sonar oraz ręczną rewizję kodu

10.   Tworzone oprogramowanie powinno być zgodne z podejściem DevOps

11.   Tworzone oprogramowanie bazuje na wzorcach GOF, przy wykorzystaniu języka java należy bazować dodatkowo na wzorcach J2EE. Integracja między aplikacjami muszą korzystać z dobrych praktyk  integracyjnych wzorców architektonicznych

 

Założenia dla warstwy aplikacyjnej zostały rozpisane powyżej. Z uwagi, że mamy zbudować Smart Citi oraz możemy korzystać z najnowszych dostępnych rozwiązań to nasza architektura na najwyższym poziomie powinna wyglądać następująco (HLD):

 


Istniejące systemy do zarządzania majątkiem, obsługą zgłoszeń i roszczeń oraz systemami geolokalizacyjnymi powinny zostać dostosowane. Powinny zostać zbudowane nowe systemy w oparciu o chmurę AWS, które będą zapewniały HA, bezpieczeństwo dla podłączonych urządzeń IoT (Anazon IoT) oraz analizę danych w BigData (Athena), czy wykorzystanie ML dla lepszego zrozumienia działań klienta w portalu (Amazon Personalize) oraz wyszukanie najlepszej drogi lub wybranie najdogodniejszego miejsca parkingowego w punkcie docelowym podróży (Amazon SageMaker). Proces rezerwacji, płatności oraz wyboru parkingu będzie opierał się o kroki w procesie (Amazon StepFunction). Wszystkie potwierdzenia, zmiany itp. będą notyfikowane do klienta (SNS). Dla potrzeb całości zostaną wykorzystane kolejki SQS oraz komponenty, które będą zasilały systemy typu BigData np. Kinesis.

 

Portal Użytkownika zostanie zbudowany w oparciu konteneryzację, którą będzie zarządzał kubernetes (k8s). Całość zostanie uruchomiona w jednym regionie. VPC (Virtual Private Cloud) zostanie uruchomiona w dwóch strefach (nienależnych serwerowniach w ramach regionu).   Aplikacja zostanie podzielona na frontend i backend. Reguły zostaną tak zdefiniowane, że do warstwy backendowej nie będzie dostępu z zewnątrz. Jedynie będą mogły się tam dostać w komponenty warstwy frontendowej. Na maszynach EC2 zostaną postawione Master i Worker Nody K8s.

 

 

 

 

Integracje z urządzeniami peryferyjnymi wykonane będą za pomocą komponentu Amazon IoT. Do tego komponentu będą podłączone ścieżki:

1.       BigData -> na potrzeby procesowania wyników zestawień wraz z podłączonym narzędziem BI

2.       Workflow -> na potrzeby:

a.        rejestracji przejazdu i sterowania ruchem (wykorzystanie ML), weryfikacji zaparkowanego pojazdu (wykorzystanie ML) na potrzeby przeciwdziałania drobnej przestępczości,

b.       Informowania portalu klienta o dostępności i zwolnieniu miejsca parkingowego. Na tej podstawie będą wykonywane naliczenia


 

Bezpieczeństwo

Główne wymagania dla potrzeb zapewnienie bezpieczeństwa całego rozwiązania:

1.       Dla aplikacji jest wymagany cykliczny audyt bezpieczeństwa

2.       Przed uruchomieniem produkcyjnym wymagane jest uruchomienie testów penetracyjnych

3.       Wszystkie komponenty IoT komunikują się z systemem centralnym z wykorzystaniem przydzielonego certyfikatu X.509

4.       Portal WWW wymaga uwierzytelnienia i autoryzacji. Zabezpieczony jest protokołem TLS w wersji minimalnie 1.2

5.       Wewnętrza komunikacja między komponentami odbywa się z wykorzystaniem certyfikatów (Zero Trust Networking (ZTN))

6.       Wymagane jest użycie Firewalla i WAF

7.       Dostęp do podsieci jest zdefiniowany i określony. Otwarte są jedynie konieczne porty do obsługi funkcjonalności

8.       Wszystkie żądania API muszą być uwierzytelnione, a wewnętrzne wywołania muszą być zgodne ze zdefiniowanymi politykami bezpieczeństwa

9.       Zabezpieczenie API musi być wykonane za pośrednictwem OAuth2

10.   Uwierzytelnienie z systemów zewnętrznych (google, facebook, LinkedIn) musi się odbywać za pośrednictwem SAML 2.0

 

Wymagania niefunkcjonalne

Wymagania niefunkcjonalne są bardzo istotną częścią aplikacji. Wymagania te mogą decydować o kosztach całej aplikacji. Poniżej podstawowe wymagania:

1.       Aplikacje muszą być tak stworzone, aby dawały się skalować horyzontalnie

2.       Aplikacje muszą zapewniać HA więc muszą być rozmieszczone przynajmniej w 2 serwerowniach oddalonych od siebie. Musi być zapewnione zapasowe łącze

3.       Dostęp do danych wrażliwych oraz do systemów musi być audytowany

4.       Musi być zapewniony backup danych

5.       Dokumentacja projektu musi się zgadzać z wytworzonym oprogramowaniem

6.       Dokumentacja API powinna bazować na standardach OpenAPI (wraz ze swaggerem)

 

Analiza luki

Z uwagi, że mamy architekturą AS-IS i TO-BE musimy stworzyć proces dojścia ze stanu obecnego do stanu docelowego. W celu realizacji tego zadania konieczne będzie rozpisanie zadań koniecznych do wykonania z odpowiednio ustawionymi priorytetami. Należy uwzględnić takie elementy jak:

1.       stworzenie fasad do przykrycia wcześniejszych funkcjonalności – jeśli były już integracje z zewnętrznymi dostawcami

2.       przeprowadzenie migracji danych w koniecznych obszarach

3.       przepisanie lub zastąpienie komponentów oznaczonych do usunięcia

4.       stworzenie nowych komponentów i ich priorytetyzacja

 

 

Uwagi końcowe

Sprawny specjalista zauważy, że nie wszystkie domeny zostały pokryte. Brakuje domeny danych i można się pokusić o lepsze rozpisanie domeny technicznej. To jednak zostawiam dla specjalistów lubiących się pobawić :-) Powodzenia.