Lab 09 - Filtr Kalmana

Lab. 09 - Filtr Kalmana

Przykład wyszukiwania cech podczas działania ORB-SLAM3

1. Cel zajęć

Celem zajęć jest poznanie podstaw zastosowania filtra Kalmana na przykładzie użycia biblioteki MediaPipe.

2. Przygotowanie środowiska

Do zajęć nie będzie konieczny Docker. Niezbędna będzie jednak kamera internetowa lub nagranie oraz aktualna instalacja Pythona.

Rekomendowane jest użycie środowiska wirtualnego:

python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate

Następnie zainstaluj biblioteki MediaPipe oraz OpenCV:

pip install mediapipe opencv-python

Pobierz także plik modelu hand_landmarker.task i umieść go w katalogu projektu, aby móc zainicjalizować moduł Hand Landmarker offline:

curl -L -o hand_landmarker.task https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/latest/hand_landmarker.task

3. Filtr Kalmana

Jest to algorytm rekurencyjnego wyznaczania stanu obiektu na podstawie obserwacji i predykcji (wejścia i wyjścia układu). Do działania filtra konieczny jest znany model obiektu (np. dla toczącej się piłki są to wzory na położenie, prędkość i przyspieszenie w zależności od czasu). Warto pamiętać, że każda iteracja filtra składa się z dwóch kroków: predykcji (propagacja stanu i macierzy kowariancji) oraz korekty (uwzględnienie nowego pomiaru).

Schemat korzystania z Filtru Kalmana. źródło

4. Mediapipe

Biblioteka od Google, która oferuje wiele rozwiązań z dziedziny Machine Learningu możliwych do uruchomienia na powszechnie dostępnych urządzeniach.

Na zajęciach będziemy używać modułu Hand Landmarker. Przykładowe działanie przedstawiono poniżej.

Hand Landmarker

Jak widać, czasami ręka przestaje być wykrywana. Dzieje się tak, ponieważ moduł hands działa jako One-Shot Detector, używa jednej klatki nagrania/jednego zdjęcia (co prawda ma możliwość trackingu, ale ważne jest, żeby co najmniej część dłoni była widoczna).

5. Definicja problemu

Należy uruchomić detektor dłoni z biblioteki MediaPipe oraz dodać do niego filtr Kalmana z biblioteki OpenCV tak, aby była możliwa predykcja położenia dłoni nawet, jeśli ta zniknie z pola widzenia kamery. Dodatkowo, predykcja punktów dłoni powinna być stabilniejsza.

Bez filtru Kalmana Z filtrem Kalmana

6. Filtr Kalmana w OpenCV

Aby przygotować filtr Kalmana z OpenCV sugerowane jest obudowanie go dodatkową klasą (np. poprzez dziedziczenie), która przechowuje konfigurację oraz wygodnie udostępnia kroki predykcji i korekty.

Konieczne będzie utworzenie filtru, który ma 4 parametry dynamiki (x, y, Vx, Vy) oraz 2 pomiary (x, y). Macierz dynamiki F powinna uwzględniać aktualne dt (np. x' = x + dt * Vx), a macierz obserwacji H mapuje stan na pozycję. Macierze szumu procesu (Q) oraz pomiaru (R) dobieramy eksperymentalnie i możemy je zwiększać po utracie detekcji.

Przykładową implementację filtru można znaleźć tutaj.

Proszę pamiętać, że dla każdego landmarka (wykrywanego fragmentu dłoni) konieczne jest utworzenie osobnej instancji filtra.

Proszę pamiętać, że w momencie, kiedy nie mamy żadnej informacji (dłoń jest poza obrazem) z mediapipe, powinniśmy pominąć krok: self.kalman.correct(input_points) lub równoznaczny i korzystać wyłącznie z predykcji.

7. MediaPipe w Pythonie

Przykładowe użycie MediaPipe w Pythonie można znaleźć tutaj, a wskazówki pracy tutaj.

Proszę nie zmieniać opcji running_mode na inną niż IMAGE – w ten sposób moduł będzie działał jako one-shot detektor, a ciągłość ruchu zostanie utrzymana wyłącznie dzięki samodzielnie zaimplementowanemu filtrowi Kalmana.

Aktualne API MediaPipe Tasks pozwala zainicjalizować moduł następująco:

import mediapipe as mp
from mediapipe.tasks import python as mp_python
from mediapipe.tasks.python import vision

base_options = mp_python.BaseOptions(model_asset_path="hand_landmarker.task")
options = vision.HandLandmarkerOptions(
    base_options=base_options,
    num_hands=1,
    running_mode=vision.RunningMode.IMAGE,
)
detector = vision.HandLandmarker.create_from_options(options)

Obraz z kamery na mp.Image można zamienić następująco:

img = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame)
result = detector.detect(img)

Do współrzędnej x pierwszego wykrytego landmarku możemy się dostać następująco:

result.hand_landmarks[0][0].x

8. Zadanie do samodzielnej realizacji

Na eKursy proszę wgrać: - skrypt Pythona realizujący zadanie - nagranie z działania (max. 100MB)

Drawing with MediaPipe

Autor: Kamil Młodzikowski