KursyPoradnikiInspirujące DIYForum

OpenCV – #2 – Wykrywanie obiektów

OpenCV – #2 – Wykrywanie obiektów

Pora na wykrywaniem obiektów. OpenCV oferuje szereg narzędzi, które ułatwią nam to zadanie.

Program będzie pobierał klatki obrazu z kamery. Obraz będzie przetwarzany na bieżąco, zwracane będą współrzędne prostokąta, który otoczy największy obiekt o wybranym kolorze.

« Poprzedni artykuł z seriiNastępny artykuł z serii »

Przestrzenie barw

Najpierw postaram się przybliżyć zagadnienie przestrzeni barw. Większość z nas miała już pewnie styczność lub słyszała o modelu RGB, w którym każdy kolor jest reprezentowany przez trzy składowe (czerwoną, zieloną, niebieską) przy czym każda z nich przyjmuje wartość od 0 do 255.

Model RGB możemy traktować jako przestrzeń trójwymiarową, gdzie kolejnym osiom przypisujemy kolejne składowe kolory. Reprezentuje to poniższa ilustracja, na której zaznaczono umiejscowienie koloru (80, 200, 130) w przestrzeni RGB.

Przestrzenny model barw RGB

Przestrzenny model barw RGB

Model ten jest bardzo często używany w informatyce, przy zapisie obrazów i ich wyświetlaniu, również w technice cyfrowej: telewizory, monitory, aparaty itp. W OpenCV obrazy przechowywane są w obiektach typu cv:Mat, które budowane są na wzór macierzy, a kolor zapisany jest w formacie BGR, a więc trzeba pamiętać, że mamy tu do czynienia z zamianą miejscami koloru niebieskiego i czerwonego.

Przy wykrywaniu kolorowych obiektów, znacznie lepszy okazuje się model HSV, którego składowymi są ( hue – brawa, saturation – nasycenie, value – wartość ). Reprezentacje geometryczną tego modelu stanowi stożek lub cylinder. Choć na pierwszy rzut oka wydaje się to dziwne, jest jednak jak najbardziej naturalne gdyż model ten nawiązuje do sposobu, w jakim widzi ludzki narząd wzroku. Zgodnie z tym modelem wszystkie barwy wywodzą się ze światła białego (środek stożka ).

Reprezentacja modelu HSV

Reprezentacja modelu HSV

Jak widzimy na powyższym rysunku składowa H – czyli barwa (hue) jest określana jako kąt od 0 do 360 stopni i określa barwę jaką ma dany kolor. Składowa Snasycenie ( saturation), czyli odległość od środka na promieniu podstawy odkreśla nam nasycenie koloru, a więc im mniejsza wartość tym bledszy (bliższy białemu) nasz kolor. Natomiast składowa Vwartość ( value ) oznacza wysokość na stożku, a właściwie możemy traktować tą wartość jako jasność naszego koloru, im mniejsza jej wartość tym kolor ciemniejszy.

Co czyni model HSV lepszym od RGB w tym przypadku?

Jeśli popatrzymy chwilę na sposób w jakim grupowane są poszczególne barwy w model RGB i HSV szybko znajdziemy wyjaśnienie.

Otóż chodzi o to, że dla modelu HSV, aby określić dany kolor wystarczy jedna składowa H. Na przykład, kolor żółty na stożku wyżej jest zgrupowany w jednym miejscu, wystarczy wskazać zakres kątów odpowiadających żółtemu, przykładowo od 15 do 30 stopni, aby wyselekcjonować obiekty o danym kolorze z naszego obrazu. W programach graficznych takich jak np. GIMP przy wyborze kolorów możemy przejść do trybu HSV, zobaczymy wówczas następujący diagram.

HSV w dwóch wymiarach.

HSV w dwóch wymiarach

Jest to nic innego jak nasz stożek rozłożony na dwa wymiary. Obręcz stanowi reprezentację podstawy stożka, a trójkąt przekrój przez stożek wzdłuż jego wysokości. Widzimy, że odcienie koloru żółtego są zgrupowane w jednym miejscu i odpowiednio dobierając zakres kątów można określić obszar występowania koloru żółtego. Poniżej zostało to zobrazowane.

Kąt określający kolor żółty, model HSV 2D

Kąt określający kolor żółty, model HSV 2D

Gdybyśmy chcieli zrobić to samo przy użyciu modelu RGB mielibyśmy pewien trójwymiarowy rejon w którym zgrupowany jest kolor żółty. Musielibyśmy podać ograniczenia jako skomplikowane funkcje, aby nasz program działał zgodnie z oczekiwaniami, co utrudnia sprawę i obliczenia.

Aby zrozumieć sprawę jeszcze lepiej popatrzmy jak wygląda poniższy obrazek w wersji hsv:

Porównanie modelu RGB oraz HSV.

Porównanie modelu RGB oraz HSV.

Natomiast po rozbiciu obrazka na jego poszczególne składowe i wyświetleniu każdej z nich z osobna otrzymujemy co następuje:

Obraz rozłożony na składowe.

Obraz rozłożony na składowe.

Szczególnie pierwszy obrazek reprezentujący składową hue jest dla nas istotny, gdyż pokazuje, że różne odcienie żółtego na liściach słonecznika, na obrazku reprezentującym barwę hue stanowią praktyczne ten sam odcień.

Oczywiście model HSV nie jest też idealny. Ma także swoje wady, gdyż nie pozwala na pełne wyeliminowanie wpływu zmieniającego się oświetlenia na reprezentację kolorów. Jednak jego zalety są na tyle duże, że posłużymy się nim aby wykrywać obiekt o określonym kolorze.

Konwersja przestrzeni barw w OpenCV

Program ilustruje sposób przejścia z przestrzeni barw BGR do HSV w OpenCV.

Na początek do naszego programu musimy dołączyć odpowiednie moduły OpenCV.

W highgui znajdują się funkcje pomocne przy tworzeniu interfejsu: okien, trackbar'ów, obsługa myszki itp. W imgproc znajdują się funkcje wykorzystywane do przetwarzania obrazów.

Używamy przestrzeni nazw opencv.

Funkcja służy do konwersji między wybranymi modelami kolorów. Pierwszym argumentem jest obraz, z którego chcemy dokonać konwersji, drugi to obraz, który otrzymany w wyniku konwersji, ostatnim argumentem jest stała określająca pomiędzy jakimi przestrzeniami barw zostanie dokonana konwersja. CV_BGR2HSV oznacza konwersję z modelu BGR, domyślnego dla OpenCV na model HSV, jest jeszcze czwarty parametr oznaczający ilość kanałów w obrazku skonwertowanym, jeśli go nie podamy ilość kanałów będzie wyliczona automatycznie.

Inne stałe konwersji CV_RGB2GRAY ( RGB → Odcienie szarości ) więcej można znaleźć w dokumentacji OpenCV lub w podpowiedziach eclipse.

Powoduje podział obrazka, który podajemy jako pierwszy argument na poszczególne kanały. Czyli np. dla RGB otrzymamy trzy obrazki, każdy zawierający jedną składową R, G, B. Drugim argumentem powinien być wektor typu Mat lub tablica elementów typu Mat.

Wykrywanie kolorowych obiektów w OpenCV

Objaśnienia

Tworzy TrackBar u góry okna. W programie mamy dwa takie paski, za pomocą których określamy wartość dolnego i górnego progu (zakres danego koloru). Pierwszy parametr to nazwa pojawiająca się po lewej stronie paska. Drugi parametr to nazwa okna, do którego zostanie podpięty trackbar, trzeci parametr to zmienna, której odwzorowaniem będzie wartość ustawiona na pasku. Kolejny parametr określa maksymalną możliwą do ustalenia wartość na pasku. Ostatni parametr to wskaźnik na funkcję, która zostanie wywołana przy przesuwaniu suwaka na pasku, tutaj nie jest nam taka funkcja potrzebna więc podajemy wskaźnik pusty.

Metoda kopiująca jeden obrazek do drugiego.

Funkcja realizująca progowanie. Pierwszym parametrem jest obrazek, który poddamy progowaniu, u nas jest to obrazek reprezentujący składową hue, HSV. Kolejnymi argumentami są dolna i górna granica przedziału do którego musi należeć dany piksel aby w obrazie wyjściowym (binary) miał kolor biały, jeżeli piksel nie należy do tego zakresu przypisywany mu jest kolor czarny.

Funkcja ta powoduje przefiltrowanie naszego obrazka z zastosowaniem filtru erozji. Aby zobrazować działanie filtru wyobraźmy sobie pojedynczy piksel naszego obrazka, nazwijmy go kotwicą natomiast wszystkie piksele otaczające naszą kotwicę. Takie, że się z nią stykają, są jej sąsiedztwem mamy wówczas element 3x3 piksele, gdzie środkowy piksel to kotwice, a pozostałe 8 pikseli wokół niego to sąsiedztwo.

Erozja działa w ten sposób, że zastępuje wartość naszego piksela-kotwicy, minimalną wartością znalezioną wśród pikseli sąsiedztwa. Jeżeli nasz obraz jest binarny, tzn. piksele przyjmują wartości 0 lub 255 to możemy to sobie wyobrazić tak, że w momencie gdy wśród sąsiedztwa kotwicy jest piksel tła (0 – czarny), to kotwica przyjmie kolor czarny, stanie się tłem.

Powoduje to zredukowanie rozmiarów obiektu, ale też pozwala na zredukowanie lub usunięcie szumów z obrazka, które pojawiają się często w postaci białych pojedynczych pikseli lub ich małych grupek otoczonych przez piksele tła. W OpenCV domyślnie element filtru ma wartość 3x3 piksele (przy podaniu cv::Mat() jako trzeci argument), można jednak zwiększyć jego rozmiary np. do 7x7 w następujący sposób:

Mamy wówczas element 7x7 lub możemy zrobić coś takiego:

Gdzie ostatni parametr wskazuje, że erozja zostanie na obrazku dokonana 3-krotnie.

Operacją odwrotną do erozji jest dylacja. Jeżeli jeden z pikseli sąsiedztwa ma wartość białą, to kotwica też taką przyjmie.

Funkcja powoduje rozmycie obrazka podanego jako pierwszy argument, drugim argumentem jest obrazek, do którego zostanie wpisany obraz powstały w wyniku rozmycia. Trzecim parametrem jest pewien kwadratowy obszar, do kotwicy tego obszaru zostanie wpisana średnia wartość pikseli sąsiedztwa.

Wykrywanie konturu

Możemy teraz przystąpić do kolejnej rzeczy jaką będzie wykrycie kontur obiektu na otrzymanym przez progowanie obrazie binarnym.

Nowy kod obejmuje dodanie dodatkowej zmiennej cont do przechowywania kopii obrazu binarnego.

Tutaj będziemy przechowywać wszystkie kontury obiektów jakie znajdziemy w naszym obrazie binarnym. Kontury te są przechowywane jako punkty. Potrzebujemy więc wektora punktów, jednak z racji, że na obrazie może pojawić się więcej niż jeden obiekt, a więc możemy mieć kilka oddzielnych konturów, potrzebujemy wektora do ich przechowania a więc ostatecznie nasze kontury będziemy przechowywać w wektorze, wektorów punktów.

Naszym celem będzie stworzenie obrysu prostokątnego naszego konturu, aby tego dokonać będziemy kontur aproksymować za pomocą wielokąta, tutaj definiujemy wektor punktów, które będą reprezentować ten wielokąt.

Definiujemy obiekt, który posłuży do przechowania danych o obrysie prostokątnym naszego konturu.

Główna funkcja znajdująca kontury w naszym obrazku binarnym. Dane o konturach są umieszczane w zmiennej contours. Trzeci parametr CV_RETR_EXTERNAL oznacza że, interesują nas tylko kontury zewnętrzne obiektów, w dokumentacji można znaleźć inne stałe umożliwiające np. zwrócenie konturów wewnętrznych w postaci hierarchii.

Pozostałe parametry określają sposób aproksymacji konturów oraz przesunięcie każdego z punktów konturu. Więcej informacji jak zwykle można znaleźć w dokumentacji, albo zadając pytania.

Tworzymy pusty obrazek wypełniony zerami o rozmiarze obrazka cont. Będziemy w nim rysować kontury.

W pętli przechodzimy po wszystkich konturach, funkcja contourArea(Mat(contours[i]) zwraca nam obszar zajmowany przez dany kontur, wyszukujemy największy obszar a następnie zapamiętujemy dla niego indeks konturu.

Używamy wartości bezwzględnych w przypadku gdyby obszary konturów miały wartości ujemne, co zdarza się dla konturów wewnętrznych których tu nie mamy ale jak wiadomo licho nie śpi.

Testujemy czy w ogóle wykryto jakiś kontur, jeśli tak to kolejno przybliżamy go wielokątem.

Gdzie parametrami są: przybliżany kontur, zbiór punktów do których zostanie wpisany otrzymany w wyniku przybliżenia wielokąt, dokładność przybliżenia. Czwarty parametr określa czy mamy do czynienia z kształtem zamkniętym.

Następnie za pomocą poniższej funkcji zwracamy prostokąt otaczający nasz kontur.

Natomiast funkcja:

Powoduje wypełnienie środka wielokąta kolorem i narysowanie go na naszym oryginalnym obrazku. Kolejne funkcje odpowiedzialne są za narysowanie kwadratu i linii na obrazie z kamery. Następnie umieszczamy napis wyświetlający współrzędne środka kwadratu otaczającego kontur, a więc w pewnym sensie środka naszego obiektu. Na koniec rysujemy kontur naszego obiektu w stworzonym obrazku drawing.

Parametrami są obrazek na którym zostanie narysowany kontur, wektor z konturami, indeks konturu do narysowania, kolor konturu w formacie BGR oraz grubość linii.

Efekt działania programu

Program działa znacznie lepiej w przypadku światła naturalnego. Przy świetle sztucznym efekty nie są już tak zadowalające. Metoda tu opisana stanowi jedynie wprowadzenie do rozległego tematu segmentacji obrazów, w przyszłości postaram się opisać jeszcze jakieś inne, o większym stopniu złożoności, dające lepsze wyniki.

Jeżeli wystąpiły by jakieś problemu ze zrozumieniem artykułu lub z działaniem programów, proszę o informacje, postaram się wówczas niezwłocznie to naprawić.

Dokumentacja OpenCV 2.4.2 http://opencv.itseez.com/index.html

« Poprzedni artykuł z seriiNastępny artykuł z serii »

barwy, hsv, kolory, krawędzie, kurs, OpenCV, rgb

Trwa ładowanie komentarzy...