5.15. Operacje morfologiczne

Operacje morfologiczne działają na obrazach binarnych – maskach, które wychodzą z progowania i wykrywania krawędzi. Każda operacja przechodzi przez taki sam rodzaj przesuwającego się sąsiedztwa, jakiego używają filtry wygładzające, ale pytanie, które zadaje w każdej pozycji, brzmi tak/nie: czy każdy piksel w sąsiedztwie jest włączony, czy którykolwiek piksel w sąsiedztwie jest włączony, jak wygląda wzór włączony/wyłączony? Odpowiedzi powiększają obszary, kurczą je i przekształcają ich granice w sposób, którego filtr uśredniający nie potrafi.

Morfologia jest tym, co znajduje się pomiędzy początkową maską binarną – wyjściem progowania, wykrywania krawędzi lub innego klasyfikatora – a czystą maską binarną, której może użyć reszta potoku. Surowe wyjście progowania zwykle ma odizolowane piksele szumu rozrzucone po obszarach prawdziwego pierwszego planu, małe dziury wybite w skądinąd litych obszarach oraz poszarpane granice tam, gdzie próg ciął blisko krawędzi obiektu. Morfologia usuwa te wady.

5.15.1. Cztery klasyczne operacje

Na zestaw narzędzi morfologicznych składają się dwie operacje pierwotne i dwie ich kompozycje:

dilate() powiększa każdy obszar pierwszego planu. Reguła brzmi: każdy piksel, który ma co najmniej jednego sąsiada pierwszoplanowego w swoim oknie (2 * size + 1), staje się pierwszoplanowy. Widocznym efektem jest to, że obszary pierwszego planu powiększają się o size pikseli w każdym kierunku, a dziury wewnątrz nich kurczą się (lub znikają) o tę samą wartość.

erode() działa odwrotnie. Każdy piksel, który nie ma każdego sąsiada w swoim oknie w pierwszym planie, staje się tłem. Obszary pierwszego planu kurczą się o size pikseli w każdym kierunku, odizolowane piksele pierwszego planu (które nie mają sąsiadów pierwszoplanowych) znikają całkowicie, a małe połączenia między większymi obszarami zostają przecięte.

Binarny obszar wejściowy w skrajnie lewym panelu: lity kwadrat z jednopikselową dziurą w środku i trzema odizolowanymi pikselami szumu wokół niego. Cztery panele po prawej pokazują wynik zastosowania dilate, erode, open i close do tego wejścia. Dilate powiększa obszar, wypełnia dziurę i rozrasta każdy piksel szumu w blok; erode kurczy obszar, powiększa dziurę i usuwa szum; open usuwa szum przy jednoczesnym wiernym odtworzeniu obszaru i jego dziury dokładnie; close wypełnia dziurę przy jednoczesnym wiernym odtworzeniu zarysu obszaru i pikseli szumu dokładnie.

Cztery klasyczne operacje morfologiczne zastosowane do zaszumionego obszaru binarnego. Erode kurczy; dilate powiększa; open to erode, a następnie dilate (usuwa szum); close to dilate, a następnie erode (wypełnia dziury).

open() to erode, po którym następuje dilate. Z obrazu poddanego erozji usunięto każdy odizolowany piksel szumu, ale został on także skurczony o size pikseli w każdym kierunku. Wykonanie po erode operacji dilate o tym samym rozmiarze przywraca prawdziwe obszary pierwszego planu mniej więcej do ich pierwotnych granic, pozostawiając szum usuniętym. Ta kompozycja sprawia, że open jest standardową operacją „usuwania szumu” w klasycznej morfologii: odizolowane piksele pierwszego planu znikają, a rzeczywiste obszary wracają nienaruszone.

close() to odbicie lustrzane – dilate, po którym następuje erode. Dilate wypełnia małe dziury wewnątrz obszarów pierwszego planu i łączy obszary rozdzielone małymi przerwami; erode kurczy wynik z powrotem do jego pierwotnej zewnętrznej granicy, pozostawiając wypełnione wnętrze litym. close jest standardową operacją „wypełniania małych przerw”.

binary_mask.open(1)       # remove single-pixel noise
binary_mask.close(2)      # fill small holes and gaps

Parametr size ma to samo znaczenie co w filtrach jasności: size=1 oznacza sąsiedztwo 3 na 3, size=2 oznacza 5 na 5 i tak dalej. Większe rozmiary oznaczają bardziej agresywne porządkowanie – i dłuższy koszt na piksel.

5.15.2. Top-hat i black-hat

Warto poznać dwie kolejne kompozycje, ponieważ wyodrębniają one dokładnie te cechy, które usuwają open i close:

top_hat() zwraca różnicę między obrazem oryginalnym a jego wersją po operacji open – piksele pierwszego planu, które open by usunął. To dosłownie maska pikseli szumu, odizolowanych małych obszarów pierwszego planu, cienkich struktur pierwszoplanowych, których operacja open nie zdołała zachować. Przydatne do wyodrębniania małych cech pierwszego planu, gdy to właśnie te cechy interesują aplikację, zamiast ich usuwania.

black_hat() zwraca różnicę między wersją obrazu po operacji close a oryginałem – piksele tła, które close by wypełnił. To maska małych dziur wewnątrz obszarów pierwszego planu oraz wąskich przerw między obszarami, które operacja close by zmostkowała.

Po obie sięga się rzadziej niż po cztery podstawowe operacje, ale ten wzorzec warto zapamiętać – gdy aplikacja potrzebuje wyodrębnić małe lub cienkie cechy, które standardowy przebieg porządkujący usuwa, top-hat i black-hat są naturalnym sposobem na ich odzyskanie.

5.15.3. Tryb progowania

Cztery podstawowe operacje morfologiczne przyjmują całkowitoliczbowy argument kluczowy threshold, który łagodzi test włączony/wyłączony w każdej pozycji. Bez niego operacje zachowują się tak, jak mówiły powyższe opisy: erode() wymaga, by każdy sąsiad był włączony, dilate() wymaga co najmniej jednego. Z ustawionym threshold każda operacja toleruje tylu sąsiadów głosujących inaczej. Dla erode threshold to liczba sąsiadów tła, jaką piksel może mieć i wciąż przetrwać: threshold=4 zachowuje każdy piksel mający co najmniej czterech z ośmiu sąsiadów włączonych (w oknie 3 na 3 piksel centralny ma ośmiu sąsiadów), więc erozja jest mniej agresywna. Dla dilate threshold to liczba sąsiadów pierwszego planu, jakich piksel tła musi mieć więcej, zanim się włączy: threshold=2 wymaga co najmniej trzech sąsiadów pierwszoplanowych zamiast jednego, więc powiększanie jest mniej agresywne.

Forma z progiem jest przydatna do strojenia agresywności przebiegu morfologicznego bez zmiany rozmiaru jego okna, co zmieniłoby także skalę cech, na które działa. Większość aplikacji pozostaje przy zachowaniu domyślnym; forma z progiem istnieje dla przypadków, w których wartość domyślna jest po prostu odrobinę za duża lub za mała.