Jakie techniki przetwarzania obrazu można zastosować do wdrożenia aplikacji, która wykrywa choinki wyświetlane na poniższych obrazach?
Szukam rozwiązań, które będą działać na wszystkich tych obrazach. Dlatego podejścia wymagające szkolenia klasyfikatorów kaskadowych haar lub dopasowywania szablonów nie są bardzo interesujące.
Szukam czegoś, co można napisać w dowolnym języku programowania, pod warunkiem, że używa tylko technologii Open Source . Rozwiązanie należy przetestować z obrazami udostępnionymi na to pytanie. Istnieje 6 obrazów wejściowych, a odpowiedź powinna zawierać wyniki przetwarzania każdego z nich. Wreszcie, dla każdego obrazu wyjściowego muszą zostać narysowane czerwone linie otaczające wykrywane drzewo.
Jak poszedłbyś programowo wykrywając drzewa na tych obrazach?
źródło
Odpowiedzi:
Mam podejście, które moim zdaniem jest interesujące i nieco inne niż reszta. Główną różnicą w moim podejściu, w porównaniu do niektórych innych, jest sposób przeprowadzania etapu segmentacji obrazu - użyłem algorytmu klastrowania DBSCAN z scikit-learn Pythona; jest zoptymalizowany do znajdowania nieco amorficznych kształtów, które niekoniecznie muszą mieć jeden wyraźny środek ciężkości.
Na najwyższym poziomie moje podejście jest dość proste i można je podzielić na około 3 kroki. Najpierw stosuję próg (a właściwie logiczne „lub” dwóch oddzielnych i odrębnych progów). Podobnie jak w przypadku wielu innych odpowiedzi, założyłem, że choinka będzie jednym z jaśniejszych obiektów na scenie, więc pierwszy próg to po prostu prosty test jasności monochromatycznej; wszelkie piksele o wartościach powyżej 220 w skali 0-255 (gdzie czerń to 0, a biel to 255) są zapisywane w binarnym czarno-białym obrazie. Drugi próg próbuje szukać czerwonych i żółtych świateł, które są szczególnie widoczne na drzewach w lewym górnym i prawym dolnym rogu sześciu zdjęć, i wyróżniają się dobrze na niebiesko-zielonym tle, które dominuje na większości zdjęć. Konwertuję obraz rgb na przestrzeń hsv, i wymagam, aby odcień był mniejszy niż 0,2 w skali 0,0-1,0 (co odpowiada z grubsza granicy między żółtym a zielonym) lub większy niż 0,95 (odpowiadający granicy między fioletowym a czerwonym), a dodatkowo wymagam jasnych, nasyconych kolorów: nasycenie i wartość muszą być powyżej 0,7. Wyniki dwóch procedur progowych są logicznie „lub” razem, a wynikowa macierz czarno-białych obrazów binarnych pokazano poniżej:
Możesz wyraźnie zobaczyć, że każdy obraz ma jedną dużą grupę pikseli z grubsza odpowiadającą lokalizacji każdego drzewa, a kilka zdjęć ma również inne małe skupiska odpowiadające albo światłom w oknach niektórych budynków, albo scena tła na horyzoncie. Następnym krokiem jest sprawienie, aby komputer rozpoznał, że są to oddzielne klastry i odpowiednio oznaczył każdy piksel numerem identyfikacyjnym członkostwa w klastrze.
Do tego zadania wybrałem DBSCAN . Istnieje całkiem dobre wizualne porównanie tego, jak zwykle zachowuje się DBSCAN, w porównaniu z innymi algorytmami klastrowania, dostępnymi tutaj . Jak powiedziałem wcześniej, dobrze sobie radzi z amorficznymi kształtami. Dane wyjściowe DBSCAN, z każdym klastrem wykreślonym w innym kolorze, pokazano tutaj:
Patrząc na ten wynik, należy pamiętać o kilku kwestiach. Po pierwsze, DBSCAN wymaga od użytkownika ustawienia parametru „bliskości” w celu regulacji jego zachowania, które skutecznie kontroluje, jak oddzielona musi być para punktów, aby algorytm mógł zadeklarować nowy oddzielny klaster, zamiast skupiać punkt testowy na już istniejący klaster. Ustawiam tę wartość na 0,04 razy rozmiar wzdłuż przekątnej każdego obrazu. Ponieważ obrazy różnią się wielkością, od z grubsza VGA do około HD 1080, ten typ definicji względem skali ma kluczowe znaczenie.
Inną kwestią wartą odnotowania jest to, że algorytm DBSCAN, który jest zaimplementowany w scikit-learn, ma limity pamięci, które są dość trudne dla niektórych większych obrazów w tej próbce. Dlatego w przypadku kilku większych obrazów musiałem „zdziesiątkować” (tzn. Zachować tylko co 3 lub 4 piksele i upuścić pozostałe) każdy klaster, aby pozostać w tym limicie. W wyniku tego procesu wygładzania trudno zobaczyć pozostałe pojedyncze rzadkie piksele na niektórych większych obrazach. Dlatego też, wyłącznie w celach wyświetlania, piksele oznaczone kolorami na powyższych obrazach zostały nieznacznie „rozszerzone”, aby lepiej się wyróżniały. To czysto kosmetyczna operacja dla samej narracji; chociaż w moim kodzie są komentarze dotyczące tej dylatacji,
Po zidentyfikowaniu i oznaczeniu klastrów trzeci i ostatni krok jest łatwy: po prostu biorę największy klaster na każdym obrazie (w tym przypadku zdecydowałem się zmierzyć „rozmiar” pod względem całkowitej liczby pikseli członka, chociaż można zamiast tego równie łatwo zastosował jakiś rodzaj miernika, który mierzy zasięg fizyczny) i obliczył wypukły kadłub dla tego gromady. Wypukły kadłub staje się wówczas granicą drzewa. Sześć wypukłych kadłubów obliczonych tą metodą pokazano poniżej na czerwono:
Kod źródłowy jest napisany dla Pythona 2.7.6 i zależy od numpy , scipy , matplotlib i scikit-learn . Podzieliłem to na dwie części. Pierwsza część odpowiada za faktyczne przetwarzanie obrazu:
a druga część to skrypt na poziomie użytkownika, który wywołuje pierwszy plik i generuje wszystkie powyższe wykresy:
źródło
scipy.ndimage.filters.maximum_filter()
w tym samym miejscu, w którym użyłem progu.EDYTUJ UWAGĘ: Zredagowałem ten post, aby (i) przetworzyć każdy obraz drzewa indywidualnie, zgodnie z wymaganiami, (ii) w celu uwzględnienia zarówno jasności obiektu, jak i kształtu w celu poprawy jakości wyniku.
Poniżej przedstawiono podejście uwzględniające jasność i kształt obiektu. Innymi słowy, szuka obiektów o kształcie trójkąta i znacznej jasności. Został zaimplementowany w Javie, przy użyciu frameworka przetwarzania obrazu Marvin .
Pierwszym krokiem jest próg koloru. Celem jest skupienie analizy na obiektach o znacznej jasności.
obrazy wyjściowe:
kod źródłowy:
W drugim kroku najjaśniejsze punkty na obrazie są rozszerzane w celu utworzenia kształtów. Wynikiem tego procesu jest prawdopodobny kształt obiektów o znacznej jasności. Po zastosowaniu segmentacji wypełnienia powodziowego wykrywane są odłączone kształty.
obrazy wyjściowe:
kod źródłowy:
Jak pokazano na obrazie wyjściowym, wykryto wiele kształtów. W tym problemie jest tylko kilka jasnych punktów na obrazach. Jednak to podejście zostało wdrożone, aby poradzić sobie z bardziej złożonymi scenariuszami.
W następnym kroku analizowany jest każdy kształt. Prosty algorytm wykrywa kształty o wzorze podobnym do trójkąta. Algorytm analizuje kształt obiektu linia po linii. Jeśli środek masy każdej linii kształtu jest prawie taki sam (podany próg), a masa rośnie wraz ze wzrostem y, obiekt ma kształt trójkąta. Masa linii kształtu to liczba pikseli w tej linii, która należy do kształtu. Wyobraź sobie, że wycinasz obiekt poziomo i analizujesz każdy segment poziomy. Jeśli są one scentralizowane względem siebie, a długość zwiększa się od pierwszego segmentu do ostatniego w układzie liniowym, prawdopodobnie masz obiekt podobny do trójkąta.
kod źródłowy:
Na koniec pozycja każdego kształtu podobna do trójkąta i ze znaczną jasnością, w tym przypadku choinką, jest podświetlona na oryginalnym obrazie, jak pokazano poniżej.
końcowe zdjęcia wyjściowe:
ostateczny kod źródłowy:
Zaletą tego podejścia jest fakt, że prawdopodobnie będzie on działać z obrazami zawierającymi inne świecące obiekty, ponieważ analizuje kształt obiektu.
Wesołych Świąt!
EDYCJA UWAGA 2
Trwa dyskusja na temat podobieństwa obrazów wyjściowych tego rozwiązania i niektórych innych. W rzeczywistości są bardzo podobne. Ale to podejście nie tylko segmentuje obiekty. W pewnym sensie analizuje również kształty obiektów. Może obsłużyć wiele świecących obiektów w tej samej scenie. W rzeczywistości choinka nie musi być najjaśniejsza. Po prostu zgadzam się, aby wzbogacić dyskusję. Próbki zawierają błąd, który po prostu szukając najjaśniejszego obiektu, znajdziesz drzewa. Ale czy naprawdę chcemy na tym etapie zakończyć dyskusję? W tym momencie, jak daleko komputer naprawdę rozpoznaje obiekt przypominający choinkę? Spróbujmy wypełnić tę lukę.
Poniżej przedstawiono wynik, aby wyjaśnić ten punkt:
obraz wejściowy
wynik
źródło
Oto moje proste i głupie rozwiązanie. Opiera się na założeniu, że drzewo będzie najjaśniejszą i największą rzeczą na zdjęciu.
Pierwszym krokiem jest wykrycie najjaśniejszych pikseli na obrazie, ale musimy wprowadzić rozróżnienie między samym drzewem a śniegiem odbijającym jego światło. Tutaj staramy się wykluczyć śnieg, stosując naprawdę prosty filtr kodów kolorów:
Następnie znajdujemy każdy „jasny” piksel:
Wreszcie łączymy dwa wyniki:
Teraz szukamy największego jasnego obiektu:
Teraz już prawie zrobiliśmy, ale wciąż istnieje pewna niedoskonałość z powodu śniegu. Aby je odciąć, zbudujemy maskę za pomocą koła i prostokąta w celu przybliżenia kształtu drzewa w celu usunięcia niepożądanych elementów:
Ostatnim krokiem jest znalezienie konturu naszego drzewa i narysowanie go na oryginalnym zdjęciu.
Przepraszam, ale w tej chwili mam złe połączenie, więc nie mogę przesyłać zdjęć. Spróbuję to zrobić później.
Wesołych Świąt.
EDYTOWAĆ:
Oto kilka zdjęć końcowego wyniku:
źródło
./christmas_tree ./*.png
. Może być ich tyle, ile chcesz, wyniki będą wyświetlane jeden po drugim po naciśnięciu dowolnego klawisza. Czy to źle?<img src="http://i.stack.imgur.com/nmzwj.png" width="210" height="150">
Wystarczy zmienić link do obrazka;)Napisałem kod w Matlab R2007a. Użyłem k-średnich, aby z grubsza wydobyć choinkę. Pokażę mój wynik pośredni tylko z jednym obrazem, a wyniki końcowe ze wszystkimi sześcioma.
Najpierw zmapowałem przestrzeń RGB na przestrzeń Lab, co może zwiększyć kontrast czerwieni w kanale b:
Oprócz funkcji w przestrzeni kolorów, użyłem również funkcji tekstury, która jest odpowiednia dla sąsiedztwa, a nie dla każdego piksela. Tutaj liniowo połączyłem intensywność z 3 oryginalnych kanałów (R, G, B). Powodem, dla którego sformatowałem w ten sposób, jest to, że wszystkie choinki na zdjęciu mają czerwone światła, a czasem także zielone / czasami niebieskie podświetlenie.
Zastosowałem lokalny wzór binarny 3X3
I0
, użyłem środkowego piksela jako wartości progowej i uzyskałem kontrast, obliczając różnicę między średnią wartością intensywności pikseli powyżej wartości progowej a średnią wartością poniżej niej.Ponieważ mam w sumie 4 funkcje, wybrałbym K = 5 w mojej metodzie klastrowania. Kod k-średnich pokazano poniżej (pochodzi on z kursu uczenia maszynowego Dr. Andrew Nga. Wziąłem ten kurs wcześniej i sam napisałem kod w jego zadaniu programistycznym).
Ponieważ program działa bardzo wolno na moim komputerze, właśnie uruchomiłem 3 iteracje. Zwykle kryterium zatrzymania jest (i) czas iteracji co najmniej 10 lub (ii) brak zmian na centroidach. Według mojego testu zwiększenie iteracji może różnicować tło (niebo i drzewo, niebo i budynek, ...) dokładniej, ale nie wykazało drastycznych zmian w pozyskiwaniu choinki. Zauważ też, że k-średnie nie jest odporne na losową inicjalizację centroidu, dlatego zalecane jest kilkukrotne uruchomienie programu w celu porównania.
Po k-średnich
I0
wybrano znakowany region o maksymalnej intensywności . Do wyodrębnienia granic zastosowano śledzenie granic. Dla mnie ostatnia choinka jest najtrudniejsza do wyodrębnienia, ponieważ kontrast na tym zdjęciu nie jest wystarczająco wysoki, jak w pierwszych pięciu. Kolejną kwestią w mojej metodzie jest to, że użyłembwboundaries
funkcji w Matlabie do śledzenia granicy, ale czasami wewnętrzne granice są również uwzględnione, jak można zaobserwować w wynikach 3, 5, 6. Ciemna strona w choinkach nie tylko nie jest zgrupowana z oświetloną stroną, ale także prowadzi do tak wielu śladów wewnętrznych granic (imfill
niewiele się poprawia). W sumie mój algorytm wciąż ma dużo miejsca na ulepszenia.Niektóre publikacje wskazują, że przesunięcie średnie może być bardziej niezawodne niż średnie k, a wiele algorytmów opartych na cięciu wykresu jest również bardzo konkurencyjnych w przypadku skomplikowanej segmentacji granic. Sam napisałem algorytm zmiany średniej, wydaje się, że lepiej wyodrębnia regiony bez wystarczającej ilości światła. Jednak zmiana średniej jest nieco nadmiernie podzielona na segmenty i konieczna jest pewna strategia łączenia. Na moim komputerze działało nawet znacznie wolniej niż k-średnie, obawiam się, że muszę się poddać. Z niecierpliwością oczekuję, że inni przedstawią tutaj doskonałe wyniki dzięki tym nowoczesnym algorytmom wspomnianym powyżej.
Jednak zawsze uważam, że wybór funkcji jest kluczowym elementem w segmentacji obrazu. Przy odpowiednim wyborze funkcji, który może zmaksymalizować margines między obiektem a tłem, wiele algorytmów segmentacji na pewno będzie działać. Różne algorytmy mogą poprawić wynik od 1 do 10, ale wybór funkcji może poprawić go od 0 do 1.
Wesołych Świąt !
źródło
To mój ostatni post wykorzystujący tradycyjne metody przetwarzania obrazu ...
Tutaj w jakiś sposób łączę moje dwie inne propozycje, osiągając jeszcze lepsze wyniki . W rzeczywistości nie widzę, w jaki sposób te wyniki mogą być lepsze (szczególnie, gdy spojrzysz na zamaskowane obrazy wytwarzane przez tę metodę).
U podstaw tego podejścia leży połączenie trzech kluczowych założeń :
Mając na uwadze te założenia, metoda działa w następujący sposób:
Oto kod w MATLAB (ponownie, skrypt ładuje wszystkie obrazy jpg do bieżącego folderu i, znowu, nie jest to zoptymalizowany fragment kodu):
Wyniki
Wyniki w wysokiej rozdzielczości są nadal dostępne tutaj!
Jeszcze więcej eksperymentów z dodatkowymi obrazami można znaleźć tutaj.
źródło
Moje kroki rozwiązania:
Pobierz kanał R (z RGB) - wszystkie operacje, które wykonujemy na tym kanale:
Utwórz region zainteresowania (ROI)
Kanał progowy R o wartości minimalnej 149 (prawy górny obraz)
Obszar wyniku rozszerzenia (środkowy lewy obraz)
Wykryj ery w obliczonych roi. Drzewo ma dużo krawędzi (środkowy prawy obraz)
Rozwiń wynik
Erozja z większym promieniem (lewy dolny obraz)
Wybierz największy obiekt (według obszaru) - jest to region wynikowy
ConvexHull (drzewo jest wypukłym wielokątem) (prawy dolny obraz)
Ramka ograniczająca (prawy dolny obraz - skrzynka Grren)
Krok po kroku:
Pierwszy wynik - najprostszy, ale nie w oprogramowaniu open source - „Adaptive Vision Studio + Adaptive Vision Library”: To nie jest open source, ale bardzo szybko prototypuje:
Cały algorytm do wykrywania choinki (11 bloków):
Następny krok. Chcemy rozwiązania typu open source. Zmień filtry AVL na filtry OpenCV: Tutaj zrobiłem małe zmiany, np. Wykrywanie krawędzi korzystam z filtra cvCanny, aby uszanować roi pomnożyłem obraz regionu z obrazem krawędzi, aby wybrać największy element, którego użyłem findContours + contourArea, ale pomysł jest taki sam.
https://www.youtube.com/watch?v=sfjB3MigLH0&index=1&list=UUpSRrkMHNHiLDXgylwhWNQQ
Nie mogę teraz wyświetlać zdjęć z pośrednimi krokami, ponieważ mogę umieścić tylko 2 linki.
Ok, teraz używamy filtrów openSource, ale wciąż nie jest to cały open source. Ostatni krok - port do kodu c ++. Użyłem OpenCV w wersji 2.4.4
Wynik końcowego kodu c ++ to:
Kod c ++ jest również dość krótki:
źródło
std::max_element()
połączenie? Chciałbym również wynagrodzić twoją odpowiedź. Myślę, że mam gcc 4.2.... inne staromodne rozwiązanie - oparte wyłącznie na przetwarzaniu HSV :
Słowo o heurystyce w przetwarzaniu HSV:
Oczywiście można eksperymentować z wieloma innymi możliwościami dostrojenia tego podejścia ...
Oto kod MATLAB, aby załatwić sprawę (ostrzeżenie: kod jest daleki od optymalizacji !!! Użyłem technik niezalecanych do programowania MATLAB tylko po to, aby móc śledzić cokolwiek w procesie - można to znacznie zoptymalizować):
Wyniki:
W wynikach pokazuję zamaskowany obraz i obwiednię.
źródło
Niektóre staromodne podejście do przetwarzania obrazu ...
Pomysł opiera się na założeniu, że obrazy przedstawiają oświetlone drzewa na zazwyczaj ciemniejszym i gładszym tle (lub w niektórych przypadkach na pierwszym planie). Oświetlony obszar drzewo jest bardziej „energiczny” i ma większą intensywność .
Proces przebiega następująco:
Otrzymasz maskę binarną i obwiednię dla każdego obrazu.
Oto wyniki przy użyciu tej naiwnej techniki:
Kod w MATLAB wygląda następująco: Kod działa na folderze z obrazami JPG. Ładuje wszystkie obrazy i zwraca wykryte wyniki.
źródło
Używając zupełnie innego podejścia niż to, co widziałem, stworzyłem phpskrypt wykrywający choinki na podstawie ich świateł. Rezultatem jest zawsze symetryczny trójkąt, a w razie potrzeby wartości liczbowe, takie jak kąt („grubość”) drzewa.
Największym zagrożeniem dla tego algorytmu są oczywiście światła obok (w dużych liczbach) lub przed drzewem (większy problem do dalszej optymalizacji). Edycja (dodano): Czego nie można zrobić: Dowiedz się, czy jest tam choinka, czy nie, znajdź wiele choinek na jednym obrazie, poprawnie wykryj choinkę w środku Las Vegas, wykrywaj mocno wygięte choinki, do góry nogami lub posiekany ...;)
Różne etapy to:
Objaśnienie oznaczeń:
Kod źródłowy:
Obrazy:
Bonus: niemiecki Weihnachtsbaum, z Wikipedii http://commons.wikimedia.org/wiki/File:Weihnachtsbaum_R%C3%B6merberg.jpg
źródło
Użyłem Pythona z OpenCV.
Mój algorytm wygląda następująco:
Kod:
Jeśli zmienię jądro z (25,5) na (10,5), otrzymam ładniejsze wyniki na wszystkich drzewach, z wyjątkiem lewego dolnego rogu,
mój algorytm zakłada, że drzewo ma na sobie światła, aw lewym dolnym drzewie góra ma mniej światła niż inne.
źródło