Skip to content

Latest commit

 

History

History
209 lines (164 loc) · 8.75 KB

README.md

File metadata and controls

209 lines (164 loc) · 8.75 KB

Speed-limit-detector

PL

Widzenie Maszynowe

Projekt

„Wykrywanie limitów prędkości z wykorzystaniem biblioteki OpenCV w języku Python oraz konwolucyjnej sieci neuronowonej zbudowanej przy pomocy biblioteki PyTorch”.

Autor: Sulej Patryk
Prowadzący: Duszak Piotr

  1. Opis projektu

Celem projektu było stworzenie algorytmu, który rozpoznawałby limity prędkości na drodze w czasie rzeczywistym. W projekcie wykorzystano materiał filmowy, w którym pojawiają się znaki 30, 40 oraz 70 km/h. Kod programu znajduje się pod adresem: https://github.com/Patric/Speed-limit-detector.git oraz zawiera stosowne komentarze będące częścia sprawozdania.

  1. Krótki opis programu

Program składa się z kilku klas oraz głównego pliku wykonawczego main.py. Klasa video odpowiada za wczytywanie video oraz zasobów. Klasa analyser odpowiada za analizę każdej klatki zasobu. Klasa analyser zawiera obiekt klasy suspectAnalyser, która analizuje elementy klatki, które są podejrzanie o bycie znakiem limitu prędkości. Oprócz tego istnieje klasa speedLNet, która jest architekturą sieci neuronowej, dzieci po nn.Module. Za pomocą klasy speedLNetManager można wczytywać gotowy model, trenować, testować tworzyć nowe modele architektury sieci zawartej w speedLNet. W klasie suspectAnalyser wykorzystywany jest speedLNetManager w celu sklasyfikowania wycinka podejrzanego o bycie cyfrą z wycinka podejrzanego o bycie znakiem limitu prędkości.

  1. Przetwarzanie obrazu:

a) Analyser.py

Rysunek . Klatka w przestrzeni barw BGR.

Na samym początku klatka poddawana jest preprocessingowi. W trakcie preprocessingu klatka jest skalowana przy współczynniku skali 0,5, następnie stosowane jest rozmycie Gaussa w celu pozbycia się szumów wysokiej częstotliwości. Następnie klatka konwertowania jest z przestrzeni barw BGR do przestrzeni bartw HSV, gdzie ustawiane są thresholdy, które wycinają z klatki tylko kolor czerwony. Przy pomocy bitwise_or sumowane są piksele z obu zakresów koloru czerwonego w przestrzeni barw HSV. Następnie zastosowane są operacje morfologiczne – otwarcie i zamknięcie celem pozbycia się niepotrzebnych blobów, które obciążałyby dalszą ekstrakcję cech obrazu. Na zbinaryzowanym obrazie stosowana jest transformata Hough celem wykrycia wszystkich okręgów. Współrzędne wykrytych obiektów przekazywane sądo metody analyseCircles, gdzie każdy okrąg jest wycinany i przekazywany do suspecyAnalysera.

Rysunek . Binarny obraz z czerwoną maską. Widać wykryte okręgi.

a) SuspectAnalyzer.py

SuspectAnalyser wycina z klatki BGR kwadrat o identycznej geometrii jak ten wycięty w analyserze. Kawałek ten zawiera wycięty cały limit znaku. Następnie wycięty znak limit prędkości poddawany jest operacji zmiany kontrastu, jasności oraz gammy, co ma pozwolić na uzyskanie bardziej jednolitej struktury obrazu. Następnie wycinek poddawamy jest konwersji do przestrzeni bartw hsv i nakładane są thresholdy odcinające większość kolorów innych niż czarny i biały. Następnie wycinek poddawany jest rozmyciu gaussa w celu pozbycia się szumów. Następnie wycinek poddawany jest adaptacyjnemu progowaniu metodą ADAPTIVE_THRESH_GAUSSIAN. Następnie wykonywane są operacje morfologiczne otwarcia i zamknięcia celem pozbycia się niepotrzebnych blobów.

Rysunek . Wycięty limit znaku po przetwarzaniu obrazu.

Następnie możemy przejść do wykrywania konturów. Na kontury nakładane jest górne i dolne ograniczenie dotyczące ich pola. Następnie zamieniane są w binarny obraz i wypełniane. Każdy kontur jest oddzielnie wycinany i przekazywany do sieci neuronowej, która dokonuje dokonuje predykcji, czy otrzymany wycięty kontur należy do jednej z kategorii: „3, 4, 7, 0, „unidentified”. Następnie z danego wycinka wybierane są wszystkie kontury różne od unidentified i z otrzymane cyfry układane są w gotową liczbę reprezentującą limit prędkości. (zera ustawiane są na końcu), oraz filtrowane pod względem bycia podzielnym przez 5 oraz bycia mniejszym od 140 i większym od 0. Następnie gotowa predykcja przekazywana jest do analysera. W analyserze predykcja konsultowna jest z predykcjami z całej klatki i wybierana jest najczęściej występująca predykcja.

Rysunek . Wykryty kontur sklasyfikowany jako "0".

Następnie ta najczęściej występująca predykcja w danej klatce dodawana jest do chwilwej listy predykcji z ostatnich 60 klatek, co odpowiada 1 sekundzie filmu. (zasób jest w 60fsps). Dzięki temu nawet jeśli sieć neuronowa się pomyli to uzyskujemy najmocniejszą predykcję z kilku klatek, a w ciągu 1 sekundy niemożliwe jest, aby kierowca spotkał dwa limity prędkości. Limit prędkości wyświetlany jest i zmienia się, gdy wykryty zostanie nowy limit prędkości.

Rysunek 6. Znak zakazu zatrzymywania się nie zustaje uznany za żadną z kategorii.

  1. Sieć neuronowa

Sieć neuronowa ma architekturę bardzo podobną do sieci przykładowej w dokumentacji PyTorch. Pierwsza warstwa konwolucyjna ma na wejściu 1 kanał, gdyż przekazujemy obrazek binarny. Następnie mamy 16 kanałów wyjściowych oraz 4x4. Obrazki wejściowe są w formacie 32x32. Batch size wynosi 1. Kolejna wartstwa na wejściu ma 16 kanałów i na wyjściu 16 kanałów. Następnie są 3 warstwy liniowe, które na wyjściu zwracają 1 z 5 klas cyfr. Jako funkcję straty wybrano funkcję CrossEntropyLoss oraz jako optimiser wybrano SGD. Zbiór uczący wybrano z obrazków pozyskanych z zasobów, zmieniając nieco parametry, aby nie były identycznie jak obrazki. Niestety zbiór uczący składał się z 13 do 30 obrazków w danej kategorii, więc był bardzo mały. W celu zwiększenia skuteczności sieci należałoby zwiększyć zbiór uczący.

class speedLNet(nn.Module):

   class speedLNet(nn.Module):

    def __init__(self):
         # calling super class constructor to use parent methods later
        super(speedLNet, self).__init__()
        # first convolution layer
        # in channels: 1
        # out channels: 16
        # kernel size: 4x4
        # object size 32x32, to flatten we do 32*32

     
        self.conv1 = nn.Conv2d(1, 16, 4)
        # 2nd out * number of calsses is the next input
        self.conv2 = nn.Conv2d(16, 32, 4)
        self.pool = nn.MaxPool2d(2, 2)

        #[m1 = kernel size * kernelsize * outchannels  in last conv x batchsize )
        #[m2 = insize from linear, out size from linear]

        # an affine operation y = Wx + b
        # 5*5 from image dimension
        # in_features - size of each features sample
        # out_features = sieze of an output feature - image
        self.fc1 = nn.Linear(5 * 5*32, 128)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, 5)
        
        #using cuda for acceleration
        #self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    
        #   max pooling over a (2,2) window - take a maximum  valuefrom each feature
        #   relu applies activation function to each element of the matrix
        #   rectifier - activation function defined as the positive part of its argument
        #   also called ramp function. 

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        # channels * width * height
        x = x.view(-1, 1*5*5*32)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

  

Rysunek . Wycinek obrazków poddawanych klasyfikacji przez sieć neuronową.

Rysunek . Zbiór uczący dla cyry "4".

  1. Wnioski

Nie udało się stworzyć zbioru walidacyjnego z powodu małej ilości zasobów i ograniczenia w postaci czasu na wykonanie projektu. W celu zwiększenia skuteczności sieci należy zebrać dodatkowe zasoby i douczyć się powiększając zbiory uczące, a następnie z całkiem innych zasobów stworzyć zbiór walidacyjny. Przedstawiona sieć działa w warunkach słonecznego dnia. Prawdopodobnie w innych warunkach pogodowych należałoby zmienić parametry przetwarzania obrazu. Można by rozważyć dopasowanie parametrów w zależności od histogramu obrazu, tworząc kolejne profile pogodowe lub nawet wykorzystać tu kolejną sieć neuronową, która wybierałaby optymalne parametry na podstawie histogramu. Lepszym wyjściem byłoby jednak stworzenie sieci, która nie przyjmowałaby na wejściu binarnego obrazka konturu cyfry lecz cały obrazek limitu prędkości. Wymagałoby to jednak dużo większego zbioru uczącego jednak byłoby bardziej niezawodne ze względu na to, że nie potrzebowałoby dostosowywania parametrów przetwarzania obrazu.