Filtracja Sygnału w Pythonie z Wykorzystaniem Bibliotek

W tym artykule szczegółowo omówimy działanie funkcji splotu, operacji matematycznej, która jest niezwykle przydatna w przetwarzaniu obrazów. Ponadto, jest to jeden z głównych elementów składowych konwolucyjnych sieci neuronowych. Konwolucyjne Sieci Neuronowe (Convolutional Neural Networks - #ConvNets) stanowią jeden z fundamentów metod klasyfikacji i rozpoznawania obrazów, a swoją moc zawdzięczają warstwom dokonującym operacji splotu pomiędzy obrazem (lub warstwami dolnymi) a macierzą wag w danej warstwie. Zanim jednak przejdziemy do analizy architektury sieci konwolucyjnych, warto zdobyć intuicję na temat tego, czym jest ta operacja i jaką rolę pełni w przetwarzaniu obrazów.

W niniejszym wpisie przeczytasz o:

  • Konwolucja, czyli połączenie dwóch funkcji - wyjaśnienie operacji splotu. Przedstawienie formalnej matematycznej definicji oraz związanych z nią intuicji.
  • Operacja splotu w analizie obrazów - obraz jako dyskretny sygnał dwuwymiarowy wraz z przykładem.
  • Implementacja splotu w Pythonie i NumPy - napisanie skryptu w Pythonie i SciPy, dokonującego operacji konwolucji z kilkoma popularnymi filtrami.

Konwolucja (Splot) Czyli Połączenie Dwóch Funkcji

Operacja splotu po raz pierwszy została wprowadzona do sieci neuronowych w pracy LeCunn at al [bibcite key=LeCun1989], lecz jest to operacja matematyczna, której pierwsze wzmianki przypisuje się D’Alebert’owi w 1754 roku. 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)\). 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}

Przeczytaj także: Definicja i pomiar filtracji kłębuszkowej

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\)

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)\). Na funkcję \(g(t)\) można patrzeć jak na funkcję określającą wagi dla funkcji \(f(t)\) (jak przy średniej ważonej). Całkowanie można rozumieć jako zsumowanie wartości poszczególnych iloczynów z pewnej okolicy (przedziału).

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:

Przeczytaj także: Webber AP8400 - wymiana filtrów

\((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}

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.

Przeczytaj także: Optymalne rozcieńczenie bimbru

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.

Implementacja Operacji Splotu w Pythonie

Operacja konwolucji jest na tyle standardową operacją, że nie musimy jej sami implementować. Dwie popularne biblioteki numeryczne Numpy i Scipy mają tą operację zaimplementowaną.

Poniżej przykład kodu:

im =im/255.plt.show()

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:

Poniżej kolejny przykład:

plt.show()

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.

tags: #filtracja #sygnału #python #biblioteki

Popularne posty: