Thursday, October 29, 2026

Object Detection in Python with YOLO: Boxes, Classes and Confidence

An object detector gives you more than a name. It estimates where objects are, assigns class labels and reports scores. The useful first project is to connect those predictions to a visible image, then change one setting and inspect the result.

In this example, YOLOv8n processes a synthetic 1024 × 1024 harbor image on a Windows CPU. With the same image and model, it returns 9 boxes at confidence 0.15 and 4 boxes at 0.70. Those are retained predictions, not independently verified counts of physical boats.

Watch the Python walkthrough

Watch the full tutorial on YouTube · Watch the confidence-threshold Short

What does object detection return?

Image classification answers a question about an image's category. Object detection adds estimated locations. In this run, every returned class label is boat, but each prediction has its own score and rectangle. The official detection documentation describes these outputs.

A bounding box does not trace an object's exact outline. It gives four edges around an estimated region. A confidence score helps filter predictions; it is not a certificate that a box is correct. Neither a convincing overlay nor a large number of boxes establishes model accuracy.

Nine actual YOLOv8n prediction boxes on the synthetic harbor image at confidence 0.15; labels identify prediction rows
Actual low-threshold result. Numbered labels identify returned prediction rows, not persistent object IDs. Overlapping boxes and a questionable region near the seawall are reasons to inspect the output.

Prepare two local files and a small script

Use a local yolov8n.pt checkpoint and an image named harbor_scene.jpg, placed beside detect.py. The demonstrated input and checkpoint are included in lesson 28 of the course. For your own experiment, use an image you have permission to process and update the image path in the script. Your results will depend on that image; the numbers below belong to the demonstrated input.

The checkpoint in this run matches the official Ultralytics YOLOv8n release asset, SHA-256 f59b3d833e2ff32e194b5bb8e08d211dc7c5bdf144b90d2c8412c47ccfc83b36. Downloading the model is a separate setup step; the example deliberately stops if a local file is missing. Review the model license and Ultralytics licensing information before integrating it into another project.

For Windows with Python 3.13 installed, create an isolated environment and install the demonstrated Ultralytics version. Save the following script and put the two assets beside it before running the final command:

py -3.13 -m venv .venv
.venv\Scripts\python.exe -m pip install ultralytics==8.4.24
.venv\Scripts\python.exe detect.py

The measured run used Python 3.13.9, Ultralytics 8.4.24, PyTorch 2.10.0, NumPy 2.3.5 and OpenCV 4.13.0.92. The short installation command pins Ultralytics, not every transitive dependency. It is a starting setup, not a claim that a fresh installation on every machine reproduces the complete tested environment. Installing packages or obtaining assets may need internet access; prediction can run offline once they are present. No GPU or model training is required for this example.

Run the complete Python example

# SPDX-License-Identifier: AGPL-3.0-only
# Copyright (c) 2026 ARTSCI - Author of the AI/ML/CV course materials.
"""Run detection on two local files; do not download missing assets."""
from pathlib import Path
from ultralytics import YOLO

ROOT = Path(__file__).resolve().parent
weights = ROOT / "yolov8n.pt"
image = ROOT / "harbor_scene.jpg"
for path in (weights, image):
    if not path.is_file():
        raise FileNotFoundError(f"Required local file: {path}")

model = YOLO(str(weights))
for confidence in (0.15, 0.70):
    result = model.predict(
        source=str(image), conf=confidence, iou=0.70,
        imgsz=640, device="cpu", max_det=300,
        classes=None, rect=True, half=False,
        augment=False, agnostic_nms=False, verbose=False,
    )[0]
    print(f"conf={confidence:.2f}: {len(result.boxes)} detections")
    for box in result.boxes:
        label = result.names[int(box.cls.item())]
        score = float(box.conf.item())
        xyxy = [round(float(v), 1) for v in box.xyxy[0].tolist()]
        print(f"  {label}: score={score:.3f}, xyxy={xyxy}")
    result.save(filename=str(ROOT / f"detections_{confidence:.2f}.jpg"))

Complete sample source above: copyright © 2026 ARTSCI — Author of the AI/ML/CV course materials. Licensed under GNU AGPL version 3, without warranty. Preserve applicable notices when sharing modified code. Ultralytics software and weights retain their own terms. This code notice does not automatically license independent images, narration or course videos.

Path(__file__).resolve().parent makes the asset paths relative to the saved script. Use it in a Python file; an interactive notebook needs paths appropriate to its working directory instead. The existence checks prevent an accidental missing-model download. YOLO(str(weights)) loads the local checkpoint, and model.predict(...)[0] selects the result for this single image.

The loop runs twice, changing only conf. It keeps IoU at 0.70, processing size at 640, the device on CPU and the other shown options fixed. For this YOLOv8 model, IoU controls overlap suppression. It is a different setting from confidence, so changing both at once would make this comparison harder to interpret. See the prediction argument documentation for their roles.

Read one prediction all the way back to the image

result.boxes contains the retained boxes. The class identifier indexes result.names to obtain a readable label. box.conf supplies the score. box.xyxy supplies left, top, right and bottom coordinates in the original input image's pixel coordinate system.

One prediction over the far-left red boat has label boat, score approximately 0.621 and coordinates [60.3, 517.6, 339.8, 852.8]. The rounded rectangle begins near x = 60 and y = 518, ending near x = 340 and y = 853. Resizing an illustration for display does not turn these into display-pixel coordinates; a custom overlay must apply the image's display transform once.

Crop of the unchanged harbor input with the actual selected prediction box over the far-left red boat, score about 0.621
A crop used to inspect one actual prediction. Its coordinates still refer to the original 1024 × 1024 input.

Here is an abbreviated excerpt from the printed output. The ellipses omit other rows; the script prints every returned row:

conf=0.15: 9 detections
  ...
  boat: score=0.621, xyxy=[60.3, 517.6, 339.8, 852.8]
  ...
conf=0.70: 4 detections

Why does a visible boat lose its box?

The selected score, about 0.621, passes 0.15 but falls below 0.70. In the second run that prediction is absent. The underlying image has not changed, and the visible boat has not disappeared. The acceptance threshold has changed.

Four actual YOLOv8n prediction boxes on the same harbor input at confidence 0.70, with the selected far-left red boat box absent
Same input, checkpoint, IoU and processing size; higher confidence threshold. Four boxes remain in this execution.

Keeping fewer predictions can remove weak duplicates or mistakes, but it can also remove useful detections. Keeping more can reveal weak candidates while introducing extra errors. We have not annotated every true object in this synthetic scene, so this is a controlled output comparison, not an accuracy benchmark. A score of 0.621 should not be read as a demonstrated 62.1% probability of correctness.

The two saved images make the result inspectable. For another experiment, retain the original input, record the model and versions, and change one parameter at a time. Test representative images and decide which missed or false detections matter for your application.

For the next step, see how detection becomes tracking and crossing events in Traffic Counter. The full notebooks, supporting files and complete applications are developed in my English course, Computer Vision: OpenCV, Machine Learning and YOLO.

Wednesday, October 28, 2026

A Knee Angle Is Not a Repetition Counter

A knee angle is a measurement. A repetition count is a decision about a sequence. Connect them without defining that decision and a plausible skeleton can give a misleading impression of what the camera observed.

This synthetic demonstration contains 241 frames at 24 frames per second. Our pose pipeline records two bottom-to-standing returns, at about 1.79 and 8.17 seconds. Yet the first frame already shows the person down. Only the second event has a complete standing–down–standing cycle visible within the recording.

Watch the pose-counter breakdown

Watch the full breakdown on YouTube · Watch the short demonstration

Actual pose-model output on the first synthetic video frame, with the person already at the bottom of the movement
The first frame, with predicted landmarks. The recording starts at the bottom, so it cannot show the descent that preceded the first return.

Follow the input all the way to an event

The input is a 1280 × 720 video, analysed offline with YOLOv8n-pose on a CPU. Pose estimation supplies predicted landmark coordinates and scores. The official Ultralytics pose documentation describes these keypoint outputs. The model does not decide what our application should call a repetition.

This example accepts exactly one detected pose per frame. For each leg, it examines the hip, knee and ankle scores, then chooses the leg with the higher minimum score. That minimum must reach 0.15, and all three coordinates must be finite. This filter selects observations; passing it does not establish landmark correctness.

The hip–knee and ankle–knee vectors meet at the knee. Their normalized dot product and arccosine produce an estimated image-space angle. It depends on the camera view and predicted points. An angle alone contains no memory of whether the person was previously standing or down.

Before counting, the implementation smooths the angles with a centered nine-frame moving average. It pads segment edges and processes each continuous valid segment separately, leaving missing observations as gaps. Because a centered filter uses future frames, this is an offline example. A live version needs a deliberate causal filter and new checks for delay and changed event timing.

Remember a bottom, then accept a return

The counter starts unarmed. A finite angle at or below 70° arms it. A later armed observation at or above 150° records the current frame and disarms it. Another standing frame adds nothing. A missing angle clears the remembered state.

The separated thresholds provide hysteresis: movement around the standing threshold cannot repeatedly count without another bottom observation. These are demonstration settings for this signal, not a definition of correct exercise technique. Here is the complete counting rule; it expects angles that have already been measured and smoothed. Represent a missing numeric angle as float("nan"), rather than passing None or a pose record:

import math


def count_returns(angles, down=70.0, up=150.0):
    if not 0 < down < up <= 180:
        raise ValueError("Require 0 < down < up <= 180.")
    armed, events = False, []
    for frame, angle in enumerate(angles):
        if not math.isfinite(angle):
            armed = False
        elif angle <= down:
            armed = True
        elif armed and angle >= up:
            events.append(frame)
            armed = False
    return events

Code adapted from the course practical source. Copyright © 2026 ARTSCI — Author of the AI/ML/CV course materials. Licensed under GNU AGPL version 3, without warranty. This code notice does not automatically license independent images, audio, video or course presentation. Ultralytics software and model terms remain applicable.

Inspect the frame that changes the count

All 241 frames in this run provide usable leg observations. At frame 42, the smoothed angle is about 148.5°. At frame 43, it is about 150.9°. The counter was armed by an earlier bottom observation, so frame 43 satisfies the upper threshold and records the first return.

Actual frame 43 with inferred hip, knee and ankle points and a smoothed knee-angle label of 150.9 degrees
Frame 43: the estimated leg points come from the recorded pose output; the displayed 150.9° is the smoothed angle used by the rule.
Accepted returnFrame indexTime
First431.79 s
Second1968.17 s

Frame indices start at zero; the times are frame index divided by 24. These are two detections under this rule for this particular clip. They are not an accuracy score against independently labelled movement.

Test the meaning, not just the final total

Run these small examples after the function. They separate state-machine behavior from pose-model quality and make the recording-boundary issue explicit:

# Standing -> bottom -> standing: one accepted return.
assert count_returns([160, 60, 100, 150, 170]) == [3]
# Starting down still counts; it does not prove a full observed cycle.
assert count_returns([60, 150, 160]) == [1]
# A shallow bend never arms the rule.
assert count_returns([160, 145, 160]) == []
# Missing data clears the remembered bottom.
assert count_returns([60, float("nan"), 150]) == []
# Both thresholds are inclusive; remaining high does not count again.
assert count_returns([70, 149.9, 150, 170]) == [2]

The second test matters most here. A clip that begins down immediately arms this rule. Its next return counts even though no preceding descent was recorded. If your requirement is a fully observed cycle, begin with an observed standing state, then require a bottom and a return. That is a different state machine; it should reject the initial partial cycle.

Thresholds also affect timing. Keeping this clip and smoothed signal fixed but raising the upper threshold to 160° moves the events to frames 48 and 200. That change does not by itself establish better accuracy. Inspect which observations satisfy the rule before interpreting a changed total or timestamp.

The same distinction appears in the traffic-counter example: track IDs and accepted crossings answer different questions. For pose data, define the event, inspect recording boundaries and missing observations, then explain each accepted transition. The full notebook, local application and broader practical workflow are developed in my English course, Computer Vision: OpenCV, Machine Learning and YOLO.

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.

Watch the tutorial

Watch the full tutorial on YouTube · Watch the short demonstration

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.