This commit is contained in:
Bowske 2020-05-19 21:14:55 +02:00
commit c44c16e836
31 changed files with 236 additions and 14 deletions

1
.gitignore vendored
View File

@ -4,4 +4,3 @@ venv/
.idea
__pycache__/
/resources/smieci w kontenerach

118
adamORaport.md Normal file
View File

@ -0,0 +1,118 @@
# Sztuczna Inteligencja
**Temat projektu:** Inteligenta Śmieciarka
**Zespół:** Kacper Borkowski, Adam Borowski, Adam Osiowy
**Podprojekt:** Adam Osiowy - *segregator śmieci*
---
## Opis podprojektu:
- w projekcie wykorzystane zostały drzewa decyzyjne jako metoda uczenia
- projekt podzielony jest na 4 pliki
- plik tworzenie_danych_AO.py jest odpowiedzialny za wydobycie z każdego zdjęcia własności i zapis ich do pliku
![4](resources/screenShots/adamo4.png)
- w pliku uczenie_adamO.py znajdują się funkcje odpowiedzialne za uczenie i testowanie modelu
![5](resources/screenShots/adamo5.png)
- plik parametry_zdjec.h5 zawiera własności wszystkich zdjęć wykorzystanych w projekcie
- plik etykiety.h5 zawiera odpowiedni typ każdego ze zdjęć (glass,paper,plastic,metal)
---
## Ogólne działanie:
- na początku zbierane są informacje o każdym zdjęciu
```
momenty = wyznaczHuMomenty(zdj)
haralick = wyznaczHaralick(zdj)
histogram = wyznaczHistogram(zdj)
```
- wybrane własności to:
1. Histogram kolorów okreslający rozkład jasności pixeli w każdej komórce na zdjęciu w skali szarości
![6](resources/screenShots/adamo6.png)
zdjęcie jest przekształcane do przestrzeni barw hsv
po czym wyliczany jest histogram podając do funkcji zdjęcie, kanały (hsv), maskę, podział zdjęcia na 512 przedziałów (8x8x8), zakres każdego kanału
2. Momenty obrazu (Hu Moments) określające kształt obiektu na zdjęciu
![8](resources/screenShots/adamo8.png)
są średnią ważoną intensywności pikseli obrazu.
Są liczone ze wzoru:
![10](resources/screenShots/adamo10.png)
gdzie I(x,y) to intensywność pixela w danym punkcie
*Momenty surowe* - informują o intensywności pikseli i ich położeniu na obrazie
*Momenty centralne* - otrzymujemy po odjęciu od momentów surowych środka ciężkości danego kształtu
![11](resources/screenShots/adamo11.png)
momenty te są niezmienne w wyliczaniu to znaczy że jeśli kształt jest ten sam to nie ważne jest jego położenie na zdjęciu
*Momenty Hu* - to zbiór 7 liczb obliczonych na podstawie momentów centralnych.
Pierwsze 6 momentów są niezmienne dla translacji, skali i rotacji.
Podczas gdy znak siódmej liczby zmienia się wraz z odbiciem kształu (względem osi).
![12](resources/screenShots/adamo12.png)
3. Tekstura Haralicka określająca nasycenie ilości pixeli w skali szarości
![7](resources/screenShots/adamo7.png)
"Haralick zasugerował zastosowanie macierzy współwystępowania poziomu szarości (GLCM).
Ta metoda opiera się na połączonych rozkładach prawdopodobieństwa par pikseli.
GLCM pokazuje, jak często każdy poziom szarości występuje w pikselach umieszczonych w ustalonym położeniu
geometrycznym względem siebie, w zależności od poziomu szarości."
![13](resources/screenShots/adamo13.png)
- własności sa zapisywane jako macierze, ustawiane w szereg jako wiersz i zapisywane do pliku z danymi .h5
```
wiersz = np.hstack([momenty, histogram, haralick])
```
- dane dzielone są losowo na 2 pary, jedna testowa druga treningowa
```
(uczenieDane, testowanieDane, uczenieEtykiety, testowanieEtykiety) =
train_test_split(np.array(dane), np.array(etykiety), test_size=rozmiar_zbioru_testowego)
```
gdzie rozmiar zbioru testowego określony wcześniej na 20%
- tworzony jest estymator
```
rfc = RandomForestClassifier(max_depth=15, n_jobs=4, random_state=1)
```
gdzie n_jobs to ilość wątków, random_state pilnuje aby zbiór był zawsze dzielony tak samo,
a max_depth to maksymalna głebokość każdego drzewa
estymator domyślnie korzysta ze strategii opierającej się o indeks Giniego
```
'indeks Giniego jest to miara która określa jak często losowo wybrany element zostanie błędnie zidentyfikowany'
```
indeks jest obliczany ze wzoru:
![9](resources/screenShots/adamo9.png)
[przykład](https://www.geeksforgeeks.org/decision-tree-introduction-example/)
- estymator rozpoczyna uczenie korzystając ze zbiorów treningowych
```
rfc.fit(uczenieDane, uczenieEtykiety)
```
- następnie wyliczana jest skuteczność na zbiorach testowych
```
rfc.score(testowanieDane, testowanieEtykiety)
```
---
## Integracja z projektem zespołowym:
- Przy starcie programu estymator rozpoczyna nauke
```
rfc = adamO.rozpocznijUczenie()
```
- Śmieciarka porusza się po domach zbierając z nich śmieci
- Po zebraniu wszystkich śmieci kieruje się na wysypisko
- Każde zdjęcie śmieci jest segregowane z wykorzystaniem funkcji przewidującej typ
```
rodzaj = adamO.przewidz(smiec, rfc)
```
![3](resources/screenShots/adamo3.png)
- Zdjęcia posegregowanych śmieci umieszczane są w odpowiednich folderach:
![1](resources/screenShots/adamo1.png)
- Na koniec wyświetlane są losowo wybrane zdjęcia śmieci z kontenerów wraz z informacją o typie ustalonym przez estymator
![2](resources/screenShots/adamo2.png)
1. górny napis to typ zwrócony przez estymator
2. drugi napis to wartości prawpopodobieństwa z jakim estymator ocenił typ
3. trzeci napis to nazwa zdjęcia
---
## Efekt działania programu w postaci drzewa decyzyjnego:
![10](graph.png)

BIN
etykiety.h5 Normal file

Binary file not shown.

View File

@ -109,7 +109,6 @@ def game():
rodzaj = kacper.przewidz(smiec)
elif osoba == 'adamB':
rodzaj = adamB.predict(smiec)
# rodzaj = kacper.przewidz(smiec)
else:
rodzaj = adamO.przewidz(smiec, rfc)

BIN
graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

54
kacperRaport.md Normal file
View File

@ -0,0 +1,54 @@
# Sztuczna Inteligencja
**Temat projektu:** Inteligenta Śmieciarka
**Zespół:** Kacper Borkowski, Adam Borowski, Adam Osiowy
**Podprojekt:** Kacper Borkowski
---
## 1. Model:
![model](resources/screenShots/kacper1.png)
- Powyższa funkcja tworzy sekwencyjny model sieci neuronowej
- Składa się on z warstw
- Warstwa Conv2D jest to warstwa splotu, stosuje ona filtr na obrazku
- Warstwa Activation jest to warstwa aktywacji wykorzystująca funkcję aktywacji, relu jest to funkcja zwracająca 0 dla x < 0 oraz x dla pozostałych argumentów; softmax to funkcja pozwalająca na poznanie rozkładu prawdopodobieństwa na kategorie
- Warstwa MaxPooling wyciąga największą wartość z wycinka obrazka, w tym przypadku z kawałka 2x2 piksele
- Warstwa Flatten spłaszcza macierz do wektorów
- Warstwa Dense to połączone ze sobą neurony
- Warstwa Dropout przepuszcza część danych, w tym przypadku 50% w celu uniknięcia przeuczenia sieci
---
## 2. Uczenie modelu:
![uczenie](resources/screenShots/kacper2.png)
- Model uczy się na 1599 zdjęciach śmieci podzielonych na 4 kategorie
- Wszystkie zdjęcia mają rozmiar 299x299 pikseli
- Podczas uczenia zbiór dzielony jest na paczki po 16 elementów
- Zastosowana funkcja straty to categorical_crossentropy ponieważ mamy więcej niż dwie klasy śmieci
---
## 2. Przewidywanie:
![przewidywanie](resources/screenShots/kacper3.png)
- Obrazki są zamieniane na macierze
- Prediction zawiera rozkład prawdopodobieństwa obrazka na kategorie
- Funkcja zwraca konkretny typ śmiecia w zależności od przewidzianego prawdopodobieństwa
---
## 2. Integracja w projekcie:
![integracja](resources/screenShots/kacper4.png)
- Podczas wizyty śmieciarki w domu wykonywana jest funkcja przewidzenia kategorii na każdym ze śmieci w danym domu
- Zależnie od wyniku przewidywania śmieć jest umieszczany na odpowiedniej liście śmieci w śmieciarce
- Śmieci z wszystkich list są wyładowywane na wysypisku do kontenerów odpowiadających listom
- Zdjęcia śmieci znajdują się finalnie w posortowanych folderach

View File

@ -1,10 +1,10 @@
import game
import uczenie_adamO
import uczenie_adamB
def main():
game.game()
rfc = game.game()
uczenie_adamO.wyswietlZdjecia(rfc)
if __name__ == '__main__':

BIN
parametry_zdjec.h5 Normal file

Binary file not shown.

View File

@ -99,3 +99,31 @@ def predict(img_path):
- zainicjowano sieć, wczytano ścieżke, przetransformowano argument funkcji(zdjecie) do porządanego formatu
- następnie przekazano tensor jako argument do instancji klasy sieci
- w ostatnim kroku za pomocą funkcji `max` wyciągnięto największą wagę i na jej podstawie rozpoznano klasę
## 4. Integracja w projekcie:
```
for dom in obiekty["domy"]:
if dom.x == pozX and dom.y == pozY:
while dom.smieci:
smiec = dom.smieci.pop(0)
rodzaj = ""
if osoba == 'kacper':
rodzaj = kacper.przewidz(smiec)
elif osoba == 'adamB':
rodzaj = adamB.predict(smiec)
else:
rodzaj = adamO.przewidz(smiec, rfc)
if rodzaj == "paper":
obiekty["smieciarka"].dodajPapier(smiec)
elif rodzaj == "glass":
obiekty["smieciarka"].dodajSzklo(smiec)
elif rodzaj == "metal":
obiekty["smieciarka"].dodajMetal(smiec)
elif rodzaj == "plastic":
obiekty["smieciarka"].dodajPlastik(smiec)
```
- zgodnie z wybraną osobą na starcie wykonywana jest odpowiednia funkcja przewidywania na śmieciach w poszczególnych domach
- finalnie zdjęcia posortowanych śmieci znajdują się w kontenerach(folder `smieci w kontenerach`)

View File

@ -1,2 +1,4 @@
pygame==1.9.6
numpy==1.18
Keras==2.3.1
tensorflow==2.2.0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@ -10,6 +10,9 @@ import matplotlib.pyplot as plt
import mahotas
import random
from math import ceil
from io import StringIO
from sklearn.tree import export_graphviz
import pydotplus
warnings.filterwarnings('ignore')
@ -29,7 +32,8 @@ def wyznaczHuMomenty(zdj):
def wyznaczHistogram(zdj, mask=None):
zdj = cv2.cvtColor(zdj, cv2.COLOR_BGR2HSV)
hist = cv2.calcHist([zdj], [0, 1, 2], mask, [8, 8, 8], [0, 256, 0, 256, 0, 256])
hist = cv2.calcHist([zdj], [0, 1, 2], mask, [8, 8, 8],
[0, 256, 0, 256, 0, 256])
cv2.normalize(hist, hist)
return hist.flatten()
@ -57,12 +61,25 @@ def rozpocznijUczenie():
h5f_etykiety.close()
(uczenieDane, testowanieDane, uczenieEtykiety, testowanieEtykiety) = train_test_split(np.array(dane),
np.array(etykiety),
np.array(
etykiety),
test_size=rozmiar_zbioru_testowego)
rfc = RandomForestClassifier(max_depth=15, n_jobs=4, random_state=1)
rfc.fit(uczenieDane, uczenieEtykiety)
print("uzyskana skutecznosc: ", rfc.score(testowanieDane, testowanieEtykiety))
print("uzyskana skutecznosc: ", rfc.score(
testowanieDane, testowanieEtykiety))
# tworzenie grafu
# dot_data = StringIO()
# print(rfc.estimators_)
# estimator = rfc.estimators_[5]
# export_graphviz(estimator, out_file=dot_data,
# feature_names=dane[1],
# rounded=True, proportion=False,
# precision=2, filled=True,
# special_characters=True, class_names=['glass', 'metal', 'paper', 'plastic'])
# graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
# graph.write_png('graph.png')
return rfc
@ -77,7 +94,8 @@ def przewidz(zdjecie, rfc):
haralick = wyznaczHaralick(zdj)
histogram = wyznaczHistogram(zdj)
wiersz = np.hstack([momenty, histogram, haralick]) # ustaw poziomo, jeden za drugim
# ustaw poziomo, jeden za drugim
wiersz = np.hstack([momenty, histogram, haralick])
wiersz = wiersz.reshape(1, -1) # zmniejsz wymiar z 2 do 1
przewidywany_typ = rfc.predict(wiersz)[0] # zwraca wartosc 0,1,2,3
return klasy[przewidywany_typ] # zwraca glass,metal,paper,plastic
@ -104,7 +122,7 @@ def wyswietlZdjecia(rfc):
wszystkie_pliki = sum(wszystkie_pliki, [])
rozmiar = ceil(0.25 * len(wszystkie_pliki))
wybrane_zdjecia = random.sample(wszystkie_pliki, k=rozmiar)
print("ilosc wybranych zdjec: ",len(wybrane_zdjecia))
print("ilosc wybranych zdjec: ", len(wybrane_zdjecia))
for i in wybrane_zdjecia:
zdjecie = cv2.imread(path + "\\" + katalog_testujacy + "\\" + i)
@ -115,15 +133,19 @@ def wyswietlZdjecia(rfc):
haralick = wyznaczHaralick(zdjecie)
histogram = wyznaczHistogram(zdjecie)
wiersz = np.hstack([momenty, histogram, haralick]) # ustaw poziomo, jeden za drugim
# ustaw poziomo, jeden za drugim
wiersz = np.hstack([momenty, histogram, haralick])
wiersz = wiersz.reshape(1, -1) # zmniejsz wymiar z 2 do 1
przewidywany = rfc.predict(wiersz)[0]
prawdopodobienstwo = rfc.predict_proba(wiersz)
cv2.putText(zdjecie, klasy[przewidywany], (3, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), thickness=3)
cv2.putText(zdjecie, str(prawdopodobienstwo), (3, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), thickness=1)
cv2.putText(zdjecie, i, (3, 460), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), thickness=2)
cv2.putText(zdjecie, klasy[przewidywany], (3, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), thickness=3)
cv2.putText(zdjecie, str(prawdopodobienstwo), (3, 60),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), thickness=1)
cv2.putText(zdjecie, i, (3, 460), cv2.FONT_HERSHEY_SIMPLEX,
1, (255, 0, 0), thickness=2)
plt.imshow(cv2.cvtColor(zdjecie, cv2.COLOR_BGR2RGB))
plt.show()