Widzenie maszynowe cz. 1
Przetwarzanie obrazów w Pythonie
Widzenie maszynowe to fascynująca i błyskawicznie się rozwijająca dziedzina techniki. Pozwala wyposażyć systemy techniczne w takie możliwości jak automatyczna analiza tekstu, generowanie rozszerzonej rzeczywistości czy rozpoznawanie twarzy.
Takimi właśnie tematami będziemy się zajmować w tej serii wpisów. Obecnie najwygodniejszym narzędziem do rozwiązywania wymienionych problemów wydaje się być Pythona. Dlatego zaczniemy od krótkiej demonstracji jak wykorzystać go do prostych operacji na obrazach.
Skąd wziąć Pythona?
Siłą Pythona w budowaniu systemów widzenia maszynowego jest dostęp do mnóstwa przydatnych pakietów. Żeby nie instalować każdego z nich osobno można zainstalować gotową dystrybucję Pythona o nazwie Anaconda. Jak to zrobić opisałem w innym wpisie.
Wszystkie przykłady, które będziemy analizować można ściągnąć w formie notatnika Jupytera korzystając z tego linku. Możesz pracować w tym notatniku lub utworzyć nowy (tworzenie notatników również znajdziesz we wpisie poświęconym instalacji Anacondy). Możesz też utworzyć skrypt Pythona w dowolnym edytorze tekstu i uruchomić go z konsoli systemu operacyjnego. Pamiętaj, że inaczej będzie się wtedy zachowywać biblioteka Matplotlib, której będziemy używać do wyświetlania obrazów. W szczególności nic nie zostanie wyświetlone bez wywołania funkcji plt.show().
Import niezbędnych bibliotek
Zaczynamy od importu bibliotek potrzebnych nam do wykonania wszystkich ćwiczeń:
import numpy as np
import PIL
import matplotlib.pyplot as plt
import scipy.signal
Biblioteki, z których korzystamy to:
- NumPy – jest to bardzo przydatna i bardzo popularna biblioteka do obliczeń numerycznych. Bazuje na niej też dużo bibliotek do analizy danych, uczenia maszynowego, sztucznej inteligencji czy właśnie przetwarzania obrazów.
- PIL (Python Image Library) – biblioteka zawierające różne funkcje do przetwarzania obrazów. Jest mniej popularna od np. OpenCV, ale instaluje się razem z Anacondą, więc na razie z niej skorzystamy. Będzie nam potrzebna tylko do wczytania obrazu z dysku.
- Matplotlib – biblioteka zawierająca różne przydatne funkcje do rysowania obrazów i wykresów. W swojej składni stara się bazować na funkcjach znanych z MATLABa.
- SciPy signal processing – moduł wchodzący w skład biblioteki SciPy. Zawiera funkcję do cyfrowego przetwarzania sygnałów. My użyjemy jej do rozmycia obrazu za pomocą filtru splotowego.
Ładowanie i wyświetlania obrazu
Po zaimportowaniu bibliotek możemy wczytać z dysku obraz i wyświetlić go za pomocą biblioteki Matplotlib:
im_pil = PIL.Image.open('city.jpg')
im = np.array(im_pil)
plt.imshow(im)
plt.show()
W pierwszej linii wczytujemy obraz za pomocą biblioteki PIL. Jako argument do funkcji open musisz podać oczywiście swoją ścieżkę do obrazu.
W drugiej linii formatujemy obraz wczytany przez PIL tworząc obiekt klasy Ndarray używanej przez NumPy.
W trzeciej linii wyświetlamy obraz za pomocą biblioteki Matplotlib.
Wywołaniu funkcji show() w ostatniej linii wyświetli obraz. Nie trzeba tego robić jeżeli korzystamy z notatnika Jupytera.
Jeżeli wszystko zrobiliśmy poprawnie to zobaczymy następujący obraz:
Konwersja do skali szarości
W wielu zastosowaniach operuje się na obrazach w skali szarości. Obraz kolorowy jest reprezentowany w naszym przykładzie jako trójwymiarowa tablica, w której pierwszy wymiar oznacza numer wiersza, drugi numer kolumny, a trzeci numer jednego z trzech kanałów koloru (czerwony, zielony lub niebieski). Żeby wygenerować obraz w skali szarości możemy np. uśrednić wszystkie składowe koloru:
im_gray = np.mean(im, axis=2)
plt.imshow(im_gray, cmap='gray')
W pierwszej linii wykorzystujemy funkcję mean to uśrednienia obrazu. Argument axis=2 oznacza, że uśrednianie przeprowadzamy po trzecim wymiarze (pierwszy wymiar ma numer 0), który jak już wiemy oznacza kanał koloru.
Argument cmap=’gray’ spowoduje, że Matplotlib nie zastosuje tzw. pseudokolorów podczas wizualizacji obrazu bez trzech kanałów koloru.
W efekcie działania kody zobaczymy przekonwertowany obraz:
Progowanie obrazu
Innym przykładem prostej operacji na obrazie jest jego progowanie. Zamieniamy nasz obraz, którego piksele mogą przyjmować wartości od 0 do 255 na obraz, którego piksele przyjmują tylko jedną z dwóch wartości (tzw. obraz binarny). Możemy przyjąć, że wszystkie piksele o wartościach powyżej pewnej liczby przyjmą w naszym obrazie binarnym wartość 1, a pozostałe wartość 0. Za pomocą biblioteki NumPy można to zrobić bardzo prosto:
thresh = 160
im_thresh = im_gray > thresh
plt.imshow(im_thresh, cmap='gray')
Jako próg przyjęliśmy tutaj wartość 160. W naszym przykładzie pozwala ona mniej więcej oddzielić niebo od reszty obrazu. Matplotlib prawidłowo wyświetli obraz binarny przedstawiając piksele o wartości 0 jako czarne, a 1 jako białe:
Filtracja splotowa
Na koniec wykonamy filtrację splotową obrazu. Sama filtracja splotowa ma bardzo wiele zastosowań i stanowi jedną z podstawowych technik przetwarzania obrazu. Jest to jednak temat bardzo szeroki. Nie będziemy się w niego wgłębiać. Wykonamy tylko prosty przykład wykorzystania biblioteki Scipy do rozmycia obrazu. Odpowiedni kod wygląda następująco:
#Liczba pikseli do lokalnego uśredniania
filter_size = 3
#Maska filtru
mask = np.ones((filter_size, filter_size))
#Zwizualizujmy sobie maskę
print('Maska filtru: '+str(mask))
#Filtracja obrazu
im_filtered = scipy.signal.convolve2d(im_gray, mask)
#Narysujmy obraz oryginalny i po filtracji
plt.imshow(im_gray, cmap='gray')
plt.title('Obraz oryginalny')
plt.figure() #Potrzebujemy utworzyć nowe okno, żeby nie nadpisać poprzednio wyświetlonego obrazu
plt.imshow(im_filtered, cmap='gray')
plt.title('Obraz rozmyty')
Ponieważ o filtracji splotowej można się rozpisać bardzo szeroko to nie będę wyjaśniał kodu powyżej. Dodałem do niego tylko trochę komentarzy. W wyniku jego działania powinniśmy uzyskać rozmyty obraz:
Możesz poeksperymentować i sprawdzić jak na wygląd obrazu wpłynie zmiana wartości zmiennej filter_size.
Podsumowanie
Zobaczyliśmy jak wykorzystać możliwości Pythona do realizacji prostych operacji przetwarzających obrazy i jak wizualizować wyniki takich operacji. W kolejnej części spróbujemy za pomocą analizy koloru zaimplementować bardzo prostą aplikację widzenia maszynowego.
Do zobaczenia!