środa, 6 kwietnia 2011

Detekcja ruchu w OpenCV - porównanie klatek

Pełny kod źródłowy
OpenCV zostało stworzone i jest głównie używane tam, gdzie jest potrzebna zaawansowana analiza sekwencji wideo. Jest to narzędzie, które zawiera nie tylko funkcje obsługujące pobieranie obrazu z kamery, ale również funkcje, które potrafią ten obraz przetwarzać i wydobywać z niego ważne dla nas informacje. W przypadku analizy sekwencji wideo jednym z ważniejszych zadań jest detekcja miejsc, gdzie wystąpił ruch. Sama detekcja nie musi być szczególnie problematyczna, jeśli chodzi nam jedynie o proste wskazanie obszarów, na których coś się rusza. Wystarczy jedynie sprawdzić, które piksele na kolejnych klatkach obrazu zmieniają swoją wartość i podjąć decyzję na ile ta zmiana powinna być istotna, aby wykryć rzeczywisty ruch obiektu, a nie np. zaburzenia oświetlenia, bądź też ruch cienia obiektu. Oczywiście istnieje wiele problemów, które wymagają bardziej zaawansowanych algorytmów, aniżeli proste porównywanie obrazów. Możemy do nich mi.in zaliczyć rozpoznawanie i śledzenie wybranych obiektów.

W dzisiejszym wpisie chciałem pokazać proste rozwiązanie problemu detekcji ruchu z wykorzystaniem biblioteki OpenCV. Skorzystam tutaj z opisanej powyżej koncepcji sprawdzania, które punkty obrazu zmieniają swoją wartość na kolejnych obrazach w sekwencji wideo. Najłatwiej w tym celu skorzystać z operacji odejmowania obrazów, a następnie binaryzacji obrazu wynikowego w celu określenia, jaka zmiana wartości pikseli powinna zostać uznana za ruch obiektu, a jaka za brak ruchu. Najczęściej stosuje się tutaj jedną z dwoch strategii odejmowania obrazów:
  • odejmowanie dwóch sąsiednich obrazów, tzn. odejmowanie od bieżącej klatki, klatki poprzedniej,
  • odejmowanie od bieżącej klatki, pierwszej klatki, która została zarejestrowana i która jest uznawana jako tło (zakładamy oczywiście, że na pierwszej klatce nie ma obiektów, które później będą zmieniać swoje położenie).
Oba powyższe podejścia pomimo tego, że są bardzo zbliżone do siebie dają całkiem różne rezultaty. Pierwszy sposób wskazuje jedynie, na fragmenty obiektu, które zmieniły swoją lokalizację w sekwencji ruchu, podczas gdy drugi powinien wskazać cały obszar poruszającego się obiektu.

Implementacja opisanych metod w OpenCV nie nastręcza wiele problemów. Obydwa sposoby mogą być zaimplementowane z pomocą poniższego fragmentu kodu:

//TESTED WITH OpenCV 2.2

int diffType = atoi( argv[1] );
int thresval = atoi( argv[2] );

Mat cam_frame, img_gray, img_prev, img_diff, img_bin;

bool first_frame = true;

// LOOP FOR GRABBING IMAGE FROM WEBCAM 

cap >> cam_frame;
cvtColor(cam_frame, img_gray, CV_BGR2GRAY);

if (first_frame) {
    img_prev=img_gray.clone();
    first_frame = false;
    continue;
}

absdiff(img_gray, img_prev, img_diff);
threshold(img_diff, img_bin, thresval, 255, THRESH_BINARY);
erode(img_bin,  img_bin, Mat(), Point(-1,-1), 3);
dilate(img_bin, img_bin, Mat(), Point(-1,-1), 1);

imshow(win_cam,  cam_frame);
imshow(win_gray, img_gray);
imshow(win_diff, img_bin);

if (diffType == 1) img_prev=img_gray.clone();

// END LOOP

Analizując powyższy kod łatwo zauważyć, że jego działaniem możemy sterować za pomocą dwóch zmiennych:
  • diffType - określa, którą klatkę należy odejmować od klatki bieżącej, czy poprzednią (wartość = 1), czy też pierwszą (wartość = 2). To sprawdzanie odbywa się w ostatniej linijce powyższego kodu, gdzie jest ustawiana klatka, która będzie odejmowana.
  • thresval - zmienna ta będzie służyć do określania, co uznajemy za ruch obiektu, a co nie. Polecam ustawiać ją w przedziale od 30 do 100 (60 powinno być w miarę bezpieczną, a zarazem skuteczną wartością progu).
Aby nie wchodzić w szczegółową analizę działania powyższego kodu, opiszę jedynie w skrócie jego najważniejsze fragmenty:
  • W pierwszym przebiegu pętli zapamiętujemy jedynie pierwszą klatkę obrazu. Dzięki temu w drugiej iteracji będziemy mieli co odejmować od klatki bieżącej.
  • absdiff - bezwzględne odjęcie (pomijamy znak) dwóch klatek od siebie.
  • threshold - operacja progowania wykonywana na obrazie różnicowym. Pomijamy punkty, które nieznacznie zmieniły swoją wartość.
  • erode - usunięcie drobnych elementów, oraz wygładzenie krawędzi tych większych. Skutkiem ubocznym tej operacji jest zmniejszenie rozmiaru wszystkich obiektów.
  • dilate - dylatację stosujemy po etapie erozji, aby delikatnie powiększyć obiekty, tak aby miały podobny rozmiar jak to było przed erozją. 

4 komentarze:

Marcin Berdys pisze...

Czy są dostępne materiały w języku polskim do biblioteki opneCV? Chętnie bym się tym zainteresował.

Rafał Petryniak pisze...

Po polsku kojarzę jedynie książkę "Algorytmy przetwarzania obrazów i wstęp do pracy z biblioteka OpenCV". Jednak porusza ona jedynie bardzo elementarne zagadnienia.

Polecam za to dwie książki anglojęzyczne:
1. Learning OpenCV
2. OpenCV 2 Computer Vision Application Programming Cookbook

No i oczywiście dokumentację biblioteki. A do tego dużo własnej pracy i testów :-)

PeaceMeal pisze...

11:46am pst 11/20 copied from your YT channel. Help?
Rafal, if you might be so kind as to direct me to this software with a GUI wrapper that takes video FILES as input, I'd be forever grateful! (I'm having a HELL of a time locating same and I'm a C/C++/ASM programmer! ;D) Also, heavily censored, by our Jewish communist 'commisars'; they're on both sides! (Family name here is Zuroski, btw, and from the great Poland, before fall.)

PeaceMeal pisze...

ps: Beware of anything j00gle, as they're censoring this communist takeover of Americans and then the WORLD!

Prześlij komentarz