Przetwarzanie obrazu z wykorzystaniem splotu funkcji

Osobom interesującym się sieciami neuronowymi pewnie nie jest obce pojęcie Convolutional Neural Network – konwolucyjnej sieci neuronowej (tak wiem po Polsku nie brzmi to tak ładnie). Głównym elementem tych sieci jest warstwa wykorzystująca matematyczną operację zwaną konwolucją lub splotem.  W tym poście przedstawiam matematyczne podstawy oraz prezentuję mój sposób rozumienia tej operacji.

Convolutional Neural Networks (#ConvNets) obecnie dominują wśród metod klasyfikacji i rozpoznawania obrazów, swoją siłę zawdzięczają właśnie wykorzystaniu warstw dokonujących operacji splotu pomiędzy obrazem (lub warstwami dolnymi) a macierzą wag w danej warstwie. Lecz zanim zaczniemy analizować architekturę sieci konwolucyjnych warto nabrać intuicji czym ta operacja jest oraz jaką rolę pełni w przetwarzaniu obrazów.

W niniejszym wpisie przeczytasz o:

Konwolucja czyli połączenie dwóch funkcji

Operacja splotu po raz pierwszy do sieci neuronowych została wprowadzona w pracy LeCunn at al  [1], lecz jest to operacja matematyczna, której pierwsze wzmianki przypisuje się D’Alebert’owi w 1754. Formalnie zdefiniowana jest w dość zawiły sposób z wykorzystaniem całek, lecz spróbujmy rozłożyć to na czynniki pierwsze.

Po pierwsze jest to operacja wykonywana na dwóch funkcjach np. \(f(t), g(t)\), w wyniku której otrzymamy nową funkcję  \(h(t)\). Hej, hej, stop. Jak to otrzymujemy nową funkcję? To na funkcjach można wykonywać działania? A no można, już pewnie wcześniej takie operacje wykonywałeś np. dodawanie funkcji lub mnożenie, np. niech \(f(t)=t^2,  g(t)=\sin(t)\), możemy określić działania:

\begin{align*}
h(t)=&(f+g)(t)= f(t)+g(t)=t^2+\sin(t) \\
h(t)=&(f \cdot g)(t)= f(t)\cdot g(t)=t^2\cdot\sin(t)
\end{align*}

Analogicznie możemy określić działanie splotu funkcji wykorzystując szereg złożonych operacji: mnożenie funkcji, odbicie funkcji, translację oraz operacje całkowania:

$$h(t)=(f*g)(t)=\int\limits_{0}^t f(x)g(t-x) dx$$

Co to za poczwarka, skąd nagle wzięły się dwie literki \(t, x\) i jak mam rozumieć tę całkę?

  1. Po pierwsze, zauważ że główną zmienną jest cały czas u nas \(t\), zmienna \(x\) służy tylko jako zmienna do całkowania, ostatecznie zniknie ona w wyniku obliczenia całki. Wyobraź sobie ze chcemy policzyć \(h(5)\), czyli wszędzie w wzorach za t podstawiamy wartość 5.
  2. Pod całką obliczamy zwykły iloczyn dwóch funkcji \(f, g\) z tym, że funkcja \(g\) jest odbita względem osi OY, \(g(-x)\) oraz przesunięta o t \(g(t-x)\).
  3. Na funkcję \(g(t)\) można patrzeć jak na funkcję określającą wagi dla funkcji \(f(t)\) (jak przy średniej ważonej).
  4. Całkowanie można rozumieć jako zsumowanie wartości poszczególnych iloczynów z pewnej okolicy (przedzału).

Ja osobiście tłumaczę to sobie następująco, wybieram wartość \(t=t_1\), następnie wiem, że będę dokonywał sumowania wartości dla z pewnej okolicy \(t_1\), w naszym przykładzie \(x \in [0,t_1]\), dla każdej wartości z przedziału obliczam iloczyn pomiędzy \(f(x)\cdot g(t_1-x)\) oraz sumuje je. Sumowanie w tym przypadku jest określone przy pomocy całki, gdyż zmienna \(x\) jest ciągła.

Zobaczcie jak można policzyć konkretny przykład:

Bardzo pomocnym w zrozumieniu tej operacji jest przypadek dyskretny, w którym zmienne przyjmują wartości naturalne.

Splot funkcji z wartościami dyskretnymi

W tym przypadku nasze funkcje są ciągami o wyrazach \(f=\{ f[0],f[1],f[2], \dots \}\) oraz \( g=\{ g[0],g[1], g[2],\dots \}\), operację konwolucji dyskretnej możemy zdefiniować następująco:

$$(f*g)[n] =\sum _{{m }}^{{n }}f[m]\,g[n-m]$$

Tak na dobrą sprawę wzór jest taki sam, z tym że znak całki został zamieniony na znak sumy. Zakres zmiennej indeksującej \(m\) kolejno zmienia się w zależności od długości ciągu \(g\) oraz na której pozycji dla której chcemy obliczyć splot. Zobaczmy to na przykładzie, zwróćcie uwagę na różną długość ciągów oraz zmienną \(m\)

Policzmy przykład. Mamy dwa ciągi skończone \(f=\{1,0,1,1,1,0\}, g=\{1,1,0\}\), w których wyrazy numerujemy od zera, obliczmy kolejno \(h[0],h[1],…\)

\begin{align*}
n=0, m=&0 \\
h[0]=& f[0] \cdot g[0]=1 \cdot 1=1 \\
n=1, m =&0,1 \\
h[1]=& f[0] \cdot g[1-0]+f[1] \cdot g[1-1]=1 \cdot 1+0 \cdot 1=1 \\
n=2, m =&0,1,2 \\
h[2]=& f[0] \cdot g[2-0]+f[1] \cdot g[2-1]+f[2] \cdot g[2-2]=1 \cdot 0+0 \cdot 1+1 \cdot1=1 \\
\end{align*}

A teraz uwaga, jak policzyć \(h[3]\)? Zwróćmy uwagę że jeden z ciągów jest krótszy, więc możemy do obliczeń wziąć tylko 3 elementy ciągu

\begin{align*}
n=3, m =&1,2,3 \\
h[3]=&f[1]\cdot g[3-1]+f[2]\cdot g[3-2]+f[3]\cdot g[3-3]=0\cdot 0+1\cdot 1+1\cdot 1=2
\end{align*}

Pomocną techniką jest zapisanie dwóch dyskretnych sygnałów jeden nad drugim, z tym że jeden odbijamy lustrzanie, elementy nakładające się mnożymy i dodajemy do sąsiednich iloczynów.

 

Powyższe przykłady, zarówno ciągły jak i dyskretny były jednowymiarowe, tzn. funkcje \(f, g\) były funkcjami jednej zmiennej. Zobaczmy jak to wygląda dla sygnału dwuwymiarowego, którego dobrym przykładem jest właśnie obraz.

Operacja splotu w analizie obrazów

Cała idea konwolucji, w głównej mierze polega na przesuwaniu okna z wartościami z \(g\) (nazwijmy tę funkcję filtrem) wzdłuż sygnału \(f\), przemnażaniu odpowiadających wartości oraz dodawaniu tych iloczynów do siebie. W przypadku dwuwymiarowym, przesuwanie to będzie odbywało się z lewej do prawej, a następnie z góry na dół, formalnie prezentuje się to następująco:

$$h[m,n]=(f*g)[m,n]=\sum _{j}\sum _{k}{f[j,k]g[m-j,n-k]} $$

W kontekście przetwarzania obrazów funkcja \(f\) jest dwuwymiarową macierzą zawierającą wartości pikseli obrazu, zazwyczaj ma ona duże wymiary np. 600x400px, natomiast funkcja \(g\), nasz filtr, jest zdecydowanie mniejszą macierzą np. 3x3px, 5x5px itp. W wyniku konwolucji obrazu z filtrem, otrzymamy nowy obraz, w którym każdy piksel \(h[m,n]\) został utworzony na podstawie jego sąsiedztwa. W zależności do wyboru filtra możemy otrzymać obraz rozmyty, wyostrzony lub z uwypuklonymi krawędziami.

Image convolution example

 

Implementacja operacji splotu w Python’ie

Operacja konwolucji jest na tyle standardową operacją, że nie musimy jej sami implementować. Dwie popularne biblioteki numeryczne Numpy i Scipy mają tą operację zaimplementowaną. My na nasze potrzeby zastosujemy funkcję scipy.signal.convolve.

Wszystkie przykłady znajdują się w repo na githubie oraz jako gotowy do uruchomienia projekt w plon’ie:

Rozmycie obrazu kolorowego

Rozmycie obrazu możemy zrealizować uśredniając wartości z sąsiedztwa, stąd filtr wygląda następująco:

$$
g = \frac{1}{9}\left[\begin{array}{ccc}
1 & 1 & 1 \\
1 & 1 & 1 \\
1 & 1 & 1 \\
\end{array}
\right]
$$

Wartości w macierzy zostały podzielone przez 9, tak aby sumowały się do 1. A oto skrypt realizujący tę operację:

W liniach 1-4 importujemy niezbędne biblioteki, następnie odczytujemy obraz i dzieląc przez 255 normalizujemy wartości pikseli do przedziału [0,1]. W liniach 12-14 definiujemy filtr o wymiarach 3×3, na początku definiujemy macierz składającą się z samych jedynek o później dzielimy przez ilość elementów w macierzy.
Operację konwolucji stosujemy do każdego kanału RGB oddzielnie (linie 17-19) podając do funkcji convolve2d kolejno: obraz, filtr (kernel) oraz sposób w jaki mają być obsłużone wartości na krawędziach. Następnie (linie 22-23) składamy z powrotem poszczególne kanały wyniku w obraz,  denormalizujemy wartości z [0,1] na [0,255] i rzutujemy na int.
Ostatnie linie służą wyświetleniu wyniku:

Rozmyty obraz w wyniku zastosowania operacji konwolucji z filtrem 5x5

Rozmyty obraz w wyniku zastosowania operacji konwolucji z filtrem 5×5

Wydobycie głębi w obrazie w odcieniach szarości

Używając odpowiednio wybranych macierzy, jesteśmy w stanie otrzymać wiele ciekawych efektów na naszym obrazie. Chcąc wyostrzyć głębię oraz krawędzie na obrazie możemy zastosować operację Emboss (wybaczcie, nie wiem jak to przetłumaczyć), wystarczy użyć następującego filtra:

$$
g = \left[\begin{array}{ccc}
-2 & -1 & 0 \\
-1 & 1 & 1 \\
0 & 1 & 2 \\
\end{array}\right]
$$

Poniżej kod, który wykorzystuje powyższy filtr.

Cały kod wygląda podobnie do poprzedniego przykładu, z tym że tutaj operujemy na obrazie w odcieniach szarości, kolorowy obraz (linia 16) konwertujemy na obraz w odcieniach szarości (linia 17) przy pomocy prostej zdefiniowanej przez nas funkcji (linie 6-12). W lini 23 zdefiniowane jest nasze jądro, które następnie użyte w operacji konwolucji (linia 25). Na koniec wyświetlamy trzy obrazy obok siebie, kolorowy, szary oraz wynikowy.

emboss_convolution

Wydobycie głębi z obrazu w odcieniach szarości

Podsumowanie

Wpis ten, ma na celu zapoznanie was z pojęciem konwolucji oraz praktycznym jej wykorzystaniem w przetwarzaniu obrazów. Zachęcam do zabawy z tworzeniem własnych filtrów oraz odsyłam do kilku wartościowych  materiałów w sieci, w których znajdziecie inne standardowe filtry

Idee oraz intuicje przedstawione w tym poście mają stanowić fundament do zrozumienia konwolucyjnych sieci neuronowych, w których poszczególne filtry są wyuczane w trakcie treningu.

[1] [doi] Y. LeCun, B. Boser, J. S. Denker, D. Henderson, R. E. Howard, W. Hubbard, and L. D. Jackel, „Backpropagation applied to handwritten zip code recognition,” Neural comput., vol. 1, iss. 4, pp. 541-551, 1989.
[Bibtex]
@article{LeCun1989,
author = {LeCun, Y. and Boser, B. and Denker, J. S. and Henderson, D. and Howard, R. E. and Hubbard, W. and Jackel, L. D.},
title = {Backpropagation Applied to Handwritten Zip Code Recognition},
journal = {Neural Comput.},
issue_date = {Winter 1989},
volume = {1},
number = {4},
month = dec,
year = {1989},
issn = {0899-7667},
pages = {541--551},
numpages = {11},
url = {http://dx.doi.org/10.1162/neco.1989.1.4.541},
doi = {10.1162/neco.1989.1.4.541},
acmid = {1351090},
publisher = {MIT Press},
address = {Cambridge, MA, USA},
}

4 Comments Przetwarzanie obrazu z wykorzystaniem splotu funkcji

  1. Kamil B

    Profilowane reklamy na FB bardzo ładnie podsunęły mi tego bloga 😉 Będę starał się zaglądać i zaczerpnąć trochę informacji. Dzięki, czekam na kolejne wpisy!

    Reply
    1. ksopyla

      Dzięki Kamil. Tak, reklama na FB jak widzę skuteczna :), pewnie za jakiś czas także podzielę się doświadczeniami z nimi związanymi.
      Lepiej się pisze jak trafia się do zainteresowanych odbiorców.

      Reply
    1. ksopyla

      Niektóre zwroty ciężko przetłumaczyć, po Polsku nie oddają sedna i są mniej znane, stąd odpowiedniki anglojęzyczne.

      Reply

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *