final
1
.gitignore
vendored
@ -4,4 +4,3 @@ venv/
|
|||||||
.idea
|
.idea
|
||||||
__pycache__/
|
__pycache__/
|
||||||
/resources/smieci w kontenerach
|
/resources/smieci w kontenerach
|
||||||
|
|
118
adamORaport.md
Normal 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
1
game.py
@ -109,7 +109,6 @@ def game():
|
|||||||
rodzaj = kacper.przewidz(smiec)
|
rodzaj = kacper.przewidz(smiec)
|
||||||
elif osoba == 'adamB':
|
elif osoba == 'adamB':
|
||||||
rodzaj = adamB.predict(smiec)
|
rodzaj = adamB.predict(smiec)
|
||||||
# rodzaj = kacper.przewidz(smiec)
|
|
||||||
else:
|
else:
|
||||||
rodzaj = adamO.przewidz(smiec, rfc)
|
rodzaj = adamO.przewidz(smiec, rfc)
|
||||||
|
|
||||||
|
54
kacperRaport.md
Normal 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
|
4
main.py
@ -1,10 +1,10 @@
|
|||||||
import game
|
import game
|
||||||
import uczenie_adamO
|
import uczenie_adamO
|
||||||
import uczenie_adamB
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
game.game()
|
rfc = game.game()
|
||||||
|
uczenie_adamO.wyswietlZdjecia(rfc)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
BIN
parametry_zdjec.h5
Normal file
@ -99,3 +99,31 @@ def predict(img_path):
|
|||||||
- zainicjowano sieć, wczytano ścieżke, przetransformowano argument funkcji(zdjecie) do porządanego formatu
|
- zainicjowano sieć, wczytano ścieżke, przetransformowano argument funkcji(zdjecie) do porządanego formatu
|
||||||
- następnie przekazano tensor jako argument do instancji klasy sieci
|
- 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ę
|
- 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`)
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
pygame==1.9.6
|
pygame==1.9.6
|
||||||
numpy==1.18
|
numpy==1.18
|
||||||
|
Keras==2.3.1
|
||||||
|
tensorflow==2.2.0
|
||||||
|
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 8.5 KiB |
BIN
resources/screenShots/adamo1.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
resources/screenShots/adamo10.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
resources/screenShots/adamo11.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
resources/screenShots/adamo12.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
resources/screenShots/adamo13.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
resources/screenShots/adamo2.png
Normal file
After Width: | Height: | Size: 134 KiB |
BIN
resources/screenShots/adamo3.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
resources/screenShots/adamo4.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
resources/screenShots/adamo5.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
resources/screenShots/adamo6.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
resources/screenShots/adamo7.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
resources/screenShots/adamo8.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
resources/screenShots/adamo9.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
resources/smieci w kontenerach/glass/google-image(0113).jpeg
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
resources/smieci/plastic/google-image(0645).jpeg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
resources/zbior_uczacy/plastic/бутылка.jpg
Normal file
After Width: | Height: | Size: 90 KiB |
@ -10,6 +10,9 @@ import matplotlib.pyplot as plt
|
|||||||
import mahotas
|
import mahotas
|
||||||
import random
|
import random
|
||||||
from math import ceil
|
from math import ceil
|
||||||
|
from io import StringIO
|
||||||
|
from sklearn.tree import export_graphviz
|
||||||
|
import pydotplus
|
||||||
|
|
||||||
warnings.filterwarnings('ignore')
|
warnings.filterwarnings('ignore')
|
||||||
|
|
||||||
@ -29,7 +32,8 @@ def wyznaczHuMomenty(zdj):
|
|||||||
|
|
||||||
def wyznaczHistogram(zdj, mask=None):
|
def wyznaczHistogram(zdj, mask=None):
|
||||||
zdj = cv2.cvtColor(zdj, cv2.COLOR_BGR2HSV)
|
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)
|
cv2.normalize(hist, hist)
|
||||||
return hist.flatten()
|
return hist.flatten()
|
||||||
|
|
||||||
@ -57,12 +61,25 @@ def rozpocznijUczenie():
|
|||||||
h5f_etykiety.close()
|
h5f_etykiety.close()
|
||||||
|
|
||||||
(uczenieDane, testowanieDane, uczenieEtykiety, testowanieEtykiety) = train_test_split(np.array(dane),
|
(uczenieDane, testowanieDane, uczenieEtykiety, testowanieEtykiety) = train_test_split(np.array(dane),
|
||||||
np.array(etykiety),
|
np.array(
|
||||||
|
etykiety),
|
||||||
test_size=rozmiar_zbioru_testowego)
|
test_size=rozmiar_zbioru_testowego)
|
||||||
|
|
||||||
rfc = RandomForestClassifier(max_depth=15, n_jobs=4, random_state=1)
|
rfc = RandomForestClassifier(max_depth=15, n_jobs=4, random_state=1)
|
||||||
rfc.fit(uczenieDane, uczenieEtykiety)
|
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
|
return rfc
|
||||||
|
|
||||||
|
|
||||||
@ -77,7 +94,8 @@ def przewidz(zdjecie, rfc):
|
|||||||
haralick = wyznaczHaralick(zdj)
|
haralick = wyznaczHaralick(zdj)
|
||||||
histogram = wyznaczHistogram(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
|
wiersz = wiersz.reshape(1, -1) # zmniejsz wymiar z 2 do 1
|
||||||
przewidywany_typ = rfc.predict(wiersz)[0] # zwraca wartosc 0,1,2,3
|
przewidywany_typ = rfc.predict(wiersz)[0] # zwraca wartosc 0,1,2,3
|
||||||
return klasy[przewidywany_typ] # zwraca glass,metal,paper,plastic
|
return klasy[przewidywany_typ] # zwraca glass,metal,paper,plastic
|
||||||
@ -115,15 +133,19 @@ def wyswietlZdjecia(rfc):
|
|||||||
haralick = wyznaczHaralick(zdjecie)
|
haralick = wyznaczHaralick(zdjecie)
|
||||||
histogram = wyznaczHistogram(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
|
wiersz = wiersz.reshape(1, -1) # zmniejsz wymiar z 2 do 1
|
||||||
|
|
||||||
przewidywany = rfc.predict(wiersz)[0]
|
przewidywany = rfc.predict(wiersz)[0]
|
||||||
prawdopodobienstwo = rfc.predict_proba(wiersz)
|
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, klasy[przewidywany], (3, 30),
|
||||||
cv2.putText(zdjecie, str(prawdopodobienstwo), (3, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), thickness=1)
|
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), thickness=3)
|
||||||
cv2.putText(zdjecie, i, (3, 460), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), thickness=2)
|
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.imshow(cv2.cvtColor(zdjecie, cv2.COLOR_BGR2RGB))
|
||||||
plt.show()
|
plt.show()
|
||||||
|