testsAndMisc-archive/python_pkg/praca_magisterska_video/answers/pytanie_23.md

35 KiB
Raw Permalink Blame History

PYTANIE 23: Segmentacja obrazu

Problem, strategie klasyczne i sieci neuronowe.


Tło pojęciowe — słowniczek

Obraz cyfrowy (digital image) — macierz pikseli. Obraz 1920×1080 = ~2 mln pikseli. Każdy piksel ma wartość (grayscale: 0-255) lub kanały RGB (3 × 0-255). Segmentacja operuje na tej macierzy.

Piksel (pixel) — najmniejsza jednostka obrazu. „Picture element." Segmentacja = przypisanie etykiety KAŻDEMU pikselowi.

Segmentacja obrazu (image segmentation) — podział obrazu na regiony, gdzie każdy piksel dostaje etykietę klasy (np. „samochód", „droga", „niebo"). Różni się od klasyfikacji (cały obraz → 1 etykieta) i detekcji (bounding box + etykieta).

Czy naprawdę KAŻDY piksel? Tak, w semantic segmentation wynik to mapa o IDENTYCZNYM rozmiarze jak obraz wejściowy. Obraz 640×480 → mapa 640×480, w której KAŻDY z 307 200 pikseli ma etykietę klasy. Żaden piksel nie jest pominięty. Nawet piksele „tła" dostają etykietę (np. klasa „background" lub „void"). W instance segmentation dodatkowo piksele tego samego obiektu dostają ten sam ID instancji.

Obraz wejściowy:        640 × 480 pikseli (RGB, 3 kanały)
Mapa segmentacji:       640 × 480 pikseli (1 kanał — numer klasy)
Piksel (100, 200):      RGB=(134, 178, 210) → klasa 3 ("niebo")
Piksel (320, 400):      RGB=(82, 79, 73)    → klasa 7 ("droga")
KAŻDY piksel ma etykietę — nawet ten "nudny" fragment tła.

Over-segmentation (nad-segmentacja) — sytuacja, gdy algorytm segmentacji generuje ZBYT WIELE regionów — więcej niż jest obiektów/klas na obrazie. Jeden obiekt zostaje podzielony na kilka-kilkadziesiąt fragmentów. Problem typowy dla metod klasycznych (watershed, region growing).

Obraz: jeden kubek na stole
Idealna segmentacja: 2 regiony (kubek, tło)
Over-segmentation:  47 regionów! (kubek podzielony na 12 kawałków,
                    stół na 20, tło na 15)

Dlaczego to się dzieje?
- Watershed: każde lokalne minimum jasności → osobny region → setki regionów
- Region Growing: drobne różnice w intensywności → osobne regiony
- Szum (noise) w obrazie → fałszywe granice

Jak sobie z tym radzić?
- **Markers/seeds:** zamiast automatycznych minimów → podaj ręczne punkty startowe
- **Superpixels:** celowa nad-segmentacja na ~100-500 jednorodnych "superpikseli"
  (np. SLIC), potem GRUPOWANIE superpikseli w klasy → szybsze i stabilniejsze
- **Hierarchiczne:** wielopoziomowa segmentacja → scalanie regionów bottom-up
- **Deep learning:** sieci neuronowe uczą się "co jest obiektem" z danych → nie mają
  problemu z over-segmentation (bo wiedzą, że kubek to jeden obiekt)

Under-segmentation (pod-segmentacja) — przeciwieństwo: zbyt mało regionów, różne obiekty zlane w jeden region. Mniej typowy problem.


Typy segmentacji:

Semantic segmentation — każdy piksel → klasa, ale NIE rozróżnia instancji. Wszystkie samochody = jedna klasa „samochód".

[samochód][samochód][droga][droga][pieszo][niebo]
Dwa samochody = ta sama etykieta "samochód"

Instance segmentation — rozróżnia instancje tego samego obiektu. Samochód#1 i Samochód#2 mają różne etykiety.

Panoptic segmentation — łączy semantic + instance. Obiekty „things" (samochody, ludzie) mają instancje; „stuff" (niebo, droga) — tylko klasy.


Pojęcia kluczowe dla progowania i Otsu

Wariancja (variance, σ²) — miara tego, jak bardzo wartości RÓŻNIĄ SIĘ od swojej średniej. Im większa wariancja, tym bardziej „rozrzucone" są dane. Wzór: σ² = Σ(xᵢ - μ)² / n, gdzie μ to średnia.

Przykład 1 — MAŁA wariancja (dane skupione):
wartości: [48, 50, 52, 49, 51]     średnia μ = 50
σ² = ((48-50)² + (50-50)² + (52-50)² + (49-50)² + (51-50)²) / 5
   = (4 + 0 + 4 + 1 + 1) / 5 = 2.0

Przykład 2 — DUŻA wariancja (dane rozrzucone):
wartości: [10, 90, 30, 80, 50]     średnia μ = 52
σ² = ((10-52)² + (90-52)² + (30-52)² + (80-52)² + (50-52)²) / 5
   = (1764 + 1444 + 484 + 784 + 4) / 5 = 896.0

Mała σ² = punkty blisko średniej = dane JEDNORODNE
Duża σ² = punkty daleko od średniej = dane RÓŻNORODNE

Wewnątrzklasowa (within-class) — „wewnątrz klasy" oznacza, że mierzymy wariancję OSOBNO dla każdej grupy (klasy), a potem ważymy wynik proporcją pikseli w grupie. Jeśli klasa 0 ma piksele [30, 50, 45] a klasa 1 ma piksele [180, 200, 190], to σ²_wewnątrz = (udział_kl0 × σ²_kl0) + (udział_kl1 × σ²_kl1).

Wariancja wewnątrzklasowa (within-class variance) — obliczasz wariancję KAŻDEJ klasy osobno, ważysz przez udział pikseli w tej klasie, sumujesz. Jeśli σ²_wewnątrz jest MAŁA → klasy są „jednorodne" (piksele w klasie 0 mają podobne jasności, piksele w klasie 1 też).

Co to znaczy „klasy jednorodne"? — jednorodna klasa to taka, w której WSZYSTKIE piksele mają podobne wartości. Np. klasa „tło" ma jasności [195, 200, 198, 205] → jednorodna (σ² mała). Klasa mieszająca tło i obiekt [30, 200, 50, 190] → niejednorodna (σ² duża). Otsu szuka progu T, który daje NAJBARDZIEJ jednorodne klasy.

Histogram bimodalny (bimodal histogram) — histogram z DWOMA wyraźnymi „garbami" (pikami). „Bi" = dwa, „modal" = moda (najczęstsza wartość). Typowy dla obrazów z jednym obiektem na tle — garb 1 odpowiada ciemnym pikselom (obiekt), garb 2 jasnym (tło). Otsu działa TYLKO gdy histogram jest bimodalny — bo szuka progu MIĘDZY garbami.

Garb 1 (ciemne~60): piksele obiektu
Garb 2 (jasne~190): piksele tła
Dolina między garbami → tu Otsu stawia próg T!

Gdyby histogram miał JEDEN garb (unimodalny) → brak naturalnego
podziału → Otsu wybierze losowy próg → słaby wynik.

Histogram bimodalny, wariancja wewnątrzklasowa i jednorodność klas — Otsu


Thresholding (progowanie) — najprostsza metoda segmentacji. Pomysł: każdy piksel ma wartość jasności (0=czarny, 255=biały). Wybierz PRÓG T: piksel > T → klasa 1 (obiekt), piksel ≤ T → klasa 0 (tło). Działa lepiej niż się wydaje na prostych obrazach (tekst na kartce, RTG, dokumenty).

Obraz (jasność pikseli): [50][200][180][30][220][190]
Próg T=128:
50 ≤ 128 → 0 (tło)
200 > 128 → 1 (obiekt)
180 > 128 → 1
30 ≤ 128 → 0
Wynik:                   [ 0 ][ 1 ][ 1 ][ 0][ 1 ][ 1 ]

Problem: JAK wybrać T? Ręcznie → subiektywne. Rozwiązanie → Otsu.

Mnemonik: „PRÓG na bramce" — jak bramkarz, przepuszcza piksele jaśniejsze od T,
blokuje ciemniejsze.

Otsu — automatyczny dobór progu. Algorytm: przetestuj WSZYSTKIE progi T=0..255, dla każdego oblicz wariancję wewnątrzklasową (jak „różnorodne" są piksele w klasie 0 i klasie 1). Wybierz T minimalizujące tę wariancję = klasy jak najbardziej jednorodne. Złożoność: O(n·L) gdzie n=piksele, L=poziomy jasności (256). Ograniczenie: działa TYLKO dla 2 klas i zakłada bimodalny histogram jasności (dwa „garby"). Patrz diagram powyżej.

Pseudokod Otsu:
best_T = 0
min_var = ∞
for T in 0..255:
    c0 = piksele z jasność ≤ T
    c1 = piksele z jasność > T
    w0 = len(c0) / len(all_pixels)
    w1 = len(c1) / len(all_pixels)
    var = w0 * variance(c0) + w1 * variance(c1)
    if var < min_var:
        min_var = var
        best_T = T
return best_T

Mnemonik: „AUTO-bramkarz Otsu" — sam sprawdza 256 progów i wybiera najlepszy.

Pojęcia kluczowe dla Region Growing

Region Growing (rozrastanie regionu) — zaczynasz od jednego piksela „ziarna" (seed) wybranego ręcznie lub automatycznie. Sprawdzasz sąsiadów: jeśli sąsiad jest PODOBNY (np. |jasność_sąsiada - jasność_regionu| < próg), dodaj go do regionu. Powtarzaj aż nie ma więcej podobnych sąsiadów. Następnie nowy seed → nowy region.

Dlaczego seed „ręcznie LUB automatycznie"? — to dwa różne scenariusze użycia:

RĘCZNY seed:
- Użytkownik klika myszką na obraz: „tu jest obiekt"
- Użycie: segmentacja interaktywna (Photoshop „magic wand",
  narzędzia medyczne do zaznaczania guzów na RTG)
- Zaleta: precyzyjny, użytkownik wie co chce segmentować
- Wada: wymaga człowieka → nie skaluje się do 10 000 obrazów

AUTOMATYCZNY seed — metody:
1. Siatka (grid): seed co N pikseli (np. co 50 px na obrazie 500×500 → 100 seedów)
2. Lokalne ekstrema histogramu: znajdź najczęstszą jasność → seed tam
3. Losowanie: wylosuj K punktów jako seedy
4. Analiza gradientu: piksele w „płaskich" regionach (brak krawędzi) → dobre seedy

Dlaczego OR a nie AND?
Bo to ALTERNATYWNE podejścia — albo człowiek wybiera (mało i precyzyjnie),
albo algorytm wybiera (dużo i szybko, ale mniej precyzyjnie).

Region Growing: seed ręczny vs automatyczny, krok po kroku, fale BFS

Pseudokod Region Growing:
region = {seed}
queue = [seed]
while queue not empty:
    pixel = queue.pop()
    for neighbor in pixel.neighbors():  # 4 lub 8 sąsiadów
        if neighbor not visited AND similar(neighbor, region):
            region.add(neighbor)
            queue.append(neighbor)

Mnemonik: „PLAMA atramentu" — seed to kropla atramentu na papierze,
rozlewa się na podobne (jasne) miejsca, zatrzymuje się na granicach.

Pojęcia kluczowe dla Watershed

Watershed (metoda zlewiska) — traktuje obraz jak mapę topograficzną: wartość jasności piksela = wysokość terenu. Ciemne piksele = doliny, jasne = szczyty. Algorytm „zalewa" mapę wodą od najniższych punktów (minimów). Gdy woda z dwóch dolin się spotyka — tam jest GRANICA segmentu (grań).

Watershed: obraz jako mapa topograficzna, zalewanie, over-segmentation i marker-controlled watershed

Algorytm:
1. Zamień obraz na „mapę wysokości" (jasność = wysokość)
2. Znajdź wszystkie lokalne minima (najciemniejsze punkty)
3. „Zalewaj" od minimów — woda rośnie równomiernie
4. Gdy woda z dwóch dolin się spotyka → postaw TAMĘ (granicę segmentu)
5. Kontynuuj aż cały obraz zalany

Problem: MASYWNA over-segmentation — każde lokalne minimum (nawet szum!) → osobna dolina
Rozwiązanie: marker-controlled watershed — użytkownik podaje markery (seedy),
zalewamy TYLKO od tych markerów

Mnemonik: „ZALEWANIE terenu" — wyobraź sobie model terenu z plasteliny w wannie.
Powoli nalewasz wodę → doliny się wypełniają → granie gór = granice segmentów.

Pojęcia kluczowe dla Mean Shift

Okno (window) / jądro (kernel) — w kontekście Mean Shift to koło (lub kula w wielowymiarowej przestrzeni) o ustalonej szerokości (bandwidth = promień h) wokół aktualnego punktu. Wewnątrz okna algorytm oblicza „średnią ważoną" pozycji pikseli. Okno = jądro — to synonim. Nazwa „jądro" pochodzi od estymacji jądrowej gęstości (kernel density estimation, KDE).

Okno o promieniu h = 30 wokół punktu (100, 150):
Bierze WSZYSTKIE piksele, których cechy (jasność, x, y)
są w odległości ≤ 30 od (100, 150).
Oblicza ich średnią → przesuwa okno NA TĘ ŚREDNIĄ.
Powtarza aż okno się „zatrzyma" (przesunięcie < ε).

Najwyższa gęstość (density peak) — punkt w przestrzeni cech, gdzie jest NAJWIĘKSZE skupisko pikseli. Jak najwyższy szczyt góry w 3D. Mean Shift = „przesuń w kierunku średniej" → iteracyjnie zbliża się do szczytu gęstości.

Przestrzeń cech (feature space) — każdy piksel jest opisany nie tylko pozycją (x, y) ale też cechami koloru (jasność, R, G, B). Przestrzeń cech to przestrzeń wielowymiarowa, np. (R, G, B, x, y) = 5 wymiarów. Piksele o podobnych kolorach i blisko siebie będą blisko w przestrzeni cech → tworzą klastry (skupiska).

Piksel A: (x=100, y=200, R=30, G=25, B=35)  → punkt w 5D
Piksel B: (x=102, y=201, R=32, G=27, B=33)  → BLISKO A w 5D
Piksel C: (x=105, y=198, R=200, G=210, B=220)  → DALEKO od A w 5D (inny kolor!)
→ A i B w jednym segmencie, C w innym

Dlaczego Mean Shift NIE wymaga podania liczby segmentów? — W K-means musisz podać K=3 (trzy klastry) ZANIM uruchomisz algorytm. Mean Shift działa inaczej: każdy piksel startuje i „toczy się" do najbliższego szczytu gęstości. Ile jest szczytów = tyle segmentów. Algorytm sam ODKRYWA liczbę klastrów. Parametrem jest tylko bandwidth (szerokość okna h): duże h → mało szczytów → mało segmentów; małe h → dużo szczytów → dużo segmentów.

Mean Shift: przestrzeń cech, jądro przesuwane do max gęstości, dlaczego bez K

Pseudokod Mean Shift:
for each pixel p:
    x = p.features  # np. (R, G, B, pos_x, pos_y)
    repeat:
        window = all pixels within distance h from x
        x_new = weighted_mean(window)
        if |x_new - x| < epsilon:
            break
        x = x_new
    p.cluster = x  # zbieżny punkt = ID klastra

Mnemonik: „KULKI toczą się do dołków" — rozsyp kulki na nierównym stole,
każda toczy się do najbliższego zagłębienia. Ile dołków = tyle segmentów.

Pojęcia kluczowe dla Normalized Cuts

Cięcie grafu (graph cut) — graf to zbiór węzłów (pikseli) połączonych krawędziami (z wagami = podobieństwo). „Ciąć graf" to znaleźć LINIĘ dzielącą węzły na grupy, tak aby krawędzie „przecięte" tą linią miały niską wagę (= łączyły niepodobne piksele), a krawędzie wewnątrz grup miały wysoką wagę (= łączyły podobne piksele).

Jak szukamy cięcia? — Naiwnie: sprawdź WSZYSTKIE możliwe podziały → wykładnicza złożoność. Normalized Cuts zamienia problem na rozwiązanie „problemu wartości własnych" (eigenvalue problem) macierzy Laplacianu grafu. Drugi najmniejszy wektor własny wskazuje, które piksele należą do grupy A (wartości dodatnie) a które do B (wartości ujemne).

Dlaczego „znormalizowane" (normalized)? — Zwykłe cięcie (min-cut) ma wadę: preferuje odcinanie MALUTKICH grup (1 piksel odcięty = małe cięcie). Normalizowanie dzieli koszt cięcia przez rozmiar grup → duże, zrównoważone segmenty.

Normalized Cuts: obraz jako graf, cięcie, algorytm krok po kroku

Pseudokod Normalized Cuts (uproszczony):
# 1. Zbuduj macierz podobieństwa W
for each pair of pixels (i, j):
    W[i,j] = exp(-|color_i - color_j|^2 / sigma^2)  # jeśli sąsiedzi
    W[i,j] = 0                                        # jeśli odlegli

# 2. Macierz stopni D
D = diag(sum(W, axis=1))  # D[i,i] = suma wiersza i

# 3. Rozwiąż problem wartości własnych
(D - W) * y = lambda * D * y
# Weź DRUGI najm. wektor własny y (pierwszy = trywialny)

# 4. Podziel piksele
segment_A = {i : y[i] > 0}
segment_B = {i : y[i] <= 0}

Mnemonik: „CIĘCIE sznurków" — piksele połączone sznurkami (mocne = podobne).
Tnij SŁABE sznurki → dwie grupy. Normalizacja = nie odcinaj samotnych pikseli.

Pojęcia kluczowe dla sieci neuronowych

ReLU (Rectified Linear Unit) — najpopularniejsza funkcja aktywacji w sieciach neuronowych. Wzór: ReLU(x) = max(0, x). Jeśli wejście jest ujemne → wynik = 0 (neuron „milczy"). Jeśli wejście jest dodatnie → wynik = x (neuron „przepuszcza" sygnał bez zmiany). Prosta, ale bardzo skuteczna — szybsza od starszych funkcji (sigmoid, tanh), bo nie wymaga obliczania exp().

ReLU(-3) = max(0, -3) = 0    ← neuron „wyłączony"
ReLU(0)  = max(0, 0)  = 0    ← na granicy
ReLU(2.5) = max(0, 2.5) = 2.5 ← neuron „włączony", przekazuje 2.5

Dlaczego nie po prostu f(x) = x (bez progu)?
Bo liniowość → cała sieć = jedna warstwa liniowa (tracisz głębokość).
ReLU jest NIELINIOWA (ma „zakręt" w 0) → pozwala sieci uczyć się
skomplikowanych wzorców.

ReLU: wykres funkcji, dlaczego ReLU, przykład numeryczny

Iloczyn skalarny (dot product) — operacja na dwóch wektorach (listach liczb) dająca JEDNĄ liczbę. Mnożysz odpowiednie elementy parami i sumujesz wyniki. W CNN konwolucja = iloczyn skalarny filtra × fragment obrazu. Duży wynik = wektory „podobne" (filtr pasuje do fragmentu).

a = [1, 3, -2]     b = [4, -1, 5]
a · b = 1·4 + 3·(-1) + (-2)·5 = 4 - 3 - 10 = -9

W konwolucji:
filtr = [-1, 0, 1, -1, 0, 1, -1, 0, 1]  (spłaszczony 3×3)
fragment = [50, 50, 200, 50, 50, 200, 50, 50, 200]
dot = (-1)·50 + 0·50 + 1·200 + ... = 450 → duży = krawędź!

Iloczyn skalarny: definicja, geometryczna interpretacja, użycie w konwolucji


Warstwa Fully Connected (FC, gęsta, dense) — warstwa, w której KAŻDY neuron jest połączony z KAŻDYM wejściem. Obraz 7×7×512 (po konwolucjach) = 25 088 wartości. FC z 4096 neuronami = 25 088 × 4 096 = ~103 miliony wag. Wady: (1) wymaga STAŁEGO rozmiaru wejścia (zawsze 7×7×512), (2) traci informację GDZIE coś jest (spłaszcza przestrzeń na wektor 1D).

Konwolucja (convolution) — operacja przesuwania małego filtra (np. 3×3) po obrazie. W każdej pozycji oblicza iloczyn skalarny filtra × fragment obrazu → jedną liczbę. TE SAME wagi filtra użyte w KAŻDEJ pozycji → dzielenie parametrów. Zachowuje informację przestrzenną (GDZIE coś jest).

Conv 1×1 (konwolucja punktowa) — filtr o rozmiarze 1×1 pikseli. „Patrzy" na JEDEN piksel, ale WSZYSTKIE kanały (np. 512). Działa jak FC, ale OSOBNO dla KAŻDEGO piksela → zachowuje mapę H×W. FCN zamienia FC na Conv 1×1: zamiast spłaszczyć 7×7×512 → 25 088 → FC, robi Conv1×1 na KAŻDYM z 7×7 pikseli × 512 kanałów → mapa 7×7×C (C = liczba klas).

Jak FCN zamienia FC na Conv 1×1? — Klasyczny CNN: ostatnia mapa cech 7×7×512 → FLATTEN → wektor 25 088 → FC → 1000 klas → „to jest kot". FCN: ostatnia mapa cech H×W×512 → Conv1×1(512→C) → mapa H×W×C → upsample do pełnej rozdzielczości. Kluczowa różnica: NIE spłaszczamy → możemy przetwarzać obraz o DOWOLNYM rozmiarze.

Skip connections z encodera — w encoder-decoder encoder zmniejsza obraz (pooling): 224→112→56→28→14. W tym procesie traci DETALE przestrzenne (dokładne krawędzie). Skip connections = „drogi na skróty" — cechy z wczesnych warstw encodera (pełne detali) są przekazywane WPROST do odpowiednich warstw decodera. Decoder wie CO i GDZIE.

FCN: warstwa FC vs Conv 1×1, konwolucja, skip connections


U-Net — dlaczego kształt „U"? — Narysuj architekturę: encoder zmniejsza rozdzielczość (bloki idą w DÓŁ po lewej stronie), bottleneck jest na dole, decoder zwiększa rozdzielczość (bloki idą W GÓRĘ po prawej stronie). Wizualnie tworzy literę „U". „Encoder schodzi w dół" = każda warstwa encodera ma MNIEJSZĄ rozdzielczość (224→112→56→28), wizualizowane jako bloki o malejącym rozmiarze ułożone jeden pod drugim.

Concatenation (konkatenacja, złączenie) — operacja „sklejania" dwóch tensorów wzdłuż osi kanałów. Jeśli encoder na poziomie 2 daje mapę 128×128×64 kanałów, a decoder na poziomie 2 daje mapę 128×128×64 kanałów, to concatenation = 128×128×128 kanałów (64+64). Różni się od DODAWANIA (addition), które daje 128×128×64 (element-wise sum). Concatenation zachowuje WIĘCEJ informacji — sieć sama wybiera, które kanały wykorzystać.

Dodawanie (ResNet-style):
encoder [a, b, c] + decoder [x, y, z] = [a+x, b+y, c+z]  → 3 kanały

Concatenation (U-Net-style):
encoder [a, b, c] ++ decoder [x, y, z] = [a, b, c, x, y, z]  → 6 kanałów!
→ więcej informacji, sieć sama zdecyduje co ważne

U-Net: architektura w kształcie U, skip connections z concatenation, encoder ↓ decoder ↑

Mnemonik U-Net: „Litera U — w dół i w górę" — encoder schodzi ↓ (zmniejsza),
decoder wraca ↑ (zwiększa), między nimi mosty (skip = concat).

Receptive field (pole widzenia, pole recepcyjne) — ile pikseli WEJŚCIOWYCH wpływa na JEDEN piksel wyjściowy. Konwolucja 3×3 → RF = 3×3. Dwie konwolucje 3×3 pod rząd → RF = 5×5 (druga widzi 3×3 fragmenty, z których każdy widział 3×3 → efektywnie 5×5). Większe RF = neuron widzi większy kontekst = lepiej rozumie co to za piksel.

Dlaczego większe RF jest lepsze? — Pojedynczy piksel o jasności 150 może być fragmentem nieba LUB samochodu. Patrząc na otoczenie 3×3 → nadal nie wiesz. Patrząc na otoczenie 50×50 → widzisz budynki obok → „to droga!". Segmentacja wymaga KONTEKSTU globalnego.

Rate (współczynnik dylatacji) — parametr atrous (dilated) convolution. Rate=1 = zwykła konwolucja (filtr dotyka sąsiadów). Rate=2 = filtr próbkuje co DRUGI piksel → RF rośnie z 3×3 do 5×5 przy TYCH SAMYCH 9 wagach. Rate=3 → RF = 7×7. Większy kontekst za darmo (bez dodatkowych parametrów).

Global Average Pooling (GAP) — operacja redukcji: mapa cech H×W×C → 1×1×C. Dla KAŻDEGO kanału oblicza ŚREDNIĄ ze wszystkich H×W pikseli. Wynik: jeden wektor o wymiarze C, reprezentujący „średnią informację" z całego obrazu. RF = nieskończone (cały obraz). Używane w ASPP DeepLab jako jedna z równoległych gałęzi.

Mapa cech 7×7×512:
Kanał 0: macierz 7×7 wartości → średnia → jedna liczba
Kanał 1: macierz 7×7 wartości → średnia → jedna liczba
...
Kanał 511: macierz 7×7 wartości → średnia → jedna liczba
Wynik: wektor [avg₀, avg₁, ..., avg₅₁₁] → 1×1×512

Receptive field: zwykła vs dilated konwolucja, rate, global average pooling


Transformer — architektura sieci neuronowej zaproponowana w 2017 (Vaswani et al., „Attention Is All You Need"). Oryginalnie dla NLP (tłumaczenie), od 2020 (ViT — Vision Transformer) stosowana w wizji komputerowej. Kluczowy mechanizm: self-attention — każdy element (piksel/token) „pyta" WSZYSTKIE inne elementy: „jak bardzo jesteś ze mną powiązany?". Każdy element tworzy trzy wektory: Q (Query — czego szukam?), K (Key — co oferuję), V (Value — moja wartość). Attention = softmax(Q·Kᵀ / √d) · V. Koszt: O(n²) pamięci (n = liczba elementów).

SOTA (State Of The Art) — najlepszy znany wynik na danym benchmarku (zbiorze testowym) w danym momencie. Np. „Mask2Former osiąga mIoU 57.8% na ADE20K — to aktualny SOTA". SOTA ciągle się zmienia — każdy nowy paper może pobić poprzedni rekord.

Transformer: CNN lokalny vs Transformer globalny, self-attention Q/K/V, SOTA


mIoU (mean Intersection over Union) — standardowa metryka segmentacji. Dla każdej klasy: IoU = (piksele poprawne ∩ ground truth) / (piksele poprawne ground truth). Potem średnia z klas.

Klasa "samochód": predykcja=100 pikseli, GT=120, wspólne=80
IoU = 80 / (100+120-80) = 80/140 = 0.571 = 57.1%

Dice Loss — funkcja kosztu powiązana z IoU: 2·|A∩B| / (|A|+|B|). Popularna w segmentacji medycznej (dobrze radzi sobie z class imbalance).

Focal Loss — modyfikacja cross-entropy redukująca wpływ łatwych przykładów, skupiająca uczenie na trudnych. Kluczowa przy class imbalance (np. 99% tła, 1% obiekt).


Problem: czym jest segmentacja obrazu?

Segmentacja obrazu to przypisanie etykiety klasy KAŻDEMU pikselowi obrazu. Wynik: mapa segmentacji o tym samym rozmiarze co obraz wejściowy, gdzie każdy piksel ma etykietę (np. „samochód", „droga", „niebo").

Wejście:   obraz 640×480 (RGB)                    = 307 200 pikseli
Wynik:     mapa 640×480, każdy piksel → etykieta   = 307 200 etykiet

Obraz:           [niebo niebo niebo niebo]
                 [niebo drzewo drzewo niebo]
                 [droga droga samochód droga]
                 [droga droga droga   droga]

Czym segmentacja NIE jest:

Zadanie              Wynik                          Granulacja
──────────────────────────────────────────────────────────────
Klasyfikacja         1 etykieta na cały obraz       obraz
Detekcja             bounding box + klasa            prostokąt
Segmentacja          etykieta per piksel             piksel

3 warianty segmentacji:

Typy segmentacji obrazu

Wariant Co robi Przykład
Semantic klasa per piksel, bez rozróżniania instancji wszystkie samochody = „samochód"
Instance rozróżnia instancje tego samego obiektu samochód#1, samochód#2
Panoptic semantic + instance razem „stuff" (niebo) + „things" (samochód#1, #2)

Strategie klasyczne

Metody niewymagające uczenia maszynowego — oparte na ręcznie zdefiniowanych regułach (próg, podobieństwo, struktura grafu).

Metoda Idea Wada Złożoność Mnemonik
Thresholding piksel > T → klasa 1, else → klasa 0 tylko 2 klasy, proste sceny O(n) „PRÓG na bramce"
Otsu automatyczny próg (min wariancja wewnątrzklasowa) j.w. ale dobiera T sam O(n·L) „AUTO-bramkarz"
Region Growing dodawaj sąsiednie piksele o podobnej wartości over-segmentation, zależy od seeda O(n) „PLAMA atramentu"
Watershed obraz = mapa wysokości, granice = granie gór over-segmentation O(n log n) „ZALEWANIE terenu"
Mean Shift iteracyjnie przesuwaj jądro do max gęstości wolny O(n²) „KULKI toczą się"
Normalized Cuts piksele = węzły grafu, minimalizuj znormalizowane cięcie bardzo wolny O(n³) „CIĘCIE sznurków"

DIY Przykład — Thresholding (Otsu) krok po kroku

Poniższy diagram pokazuje CAŁY pipeline progowania Otsu od obrazu wejściowego do wyniku. Obraz syntetyczny 64×64 z ciemnym kołem na jasnym tle — typowy przypadek bimodalny.

DIY Thresholding + Otsu: obraz → histogram bimodalny → progowanie → szukanie min σ² → pseudokod → wynik

Pseudokod Otsu (Python-style):
best_T, min_var = 0, float('inf')
for T in range(256):
    c0 = pixels[pixels <= T]      # piksele ciemne
    c1 = pixels[pixels > T]       # piksele jasne
    if len(c0) == 0 or len(c1) == 0:
        continue
    w0 = len(c0) / len(pixels)    # udział klasy 0
    w1 = len(c1) / len(pixels)    # udział klasy 1
    var = w0 * variance(c0) + w1 * variance(c1)  # σ² wewnątrzklasowa
    if var < min_var:
        min_var = var
        best_T = T
# best_T = optymalny próg (np. 128)
result = (pixels > best_T).astype(int)  # binaryzacja

Wspólna wada klasycznych metod: wymagają ręcznego doboru parametrów (próg, seed, kernel), nie uczą się cech z danych, słabe na złożonych obrazach naturalnych.


Sieci neuronowe (deep learning)

Metody uczące się automatycznie rozpoznawać cechy z danych treningowych. Wszystkie oparte na architekturze encoder-decoder z wariacjami.

Wspólna idea encoder-decoder:

Encoder: obraz [224×224] → [112] → [56] → [28] → [14]   (wyciąga CECHY)
Decoder: cechy [14] → [28] → [56] → [112] → [224×224]   (odtwarza MAPĘ)
                               bottleneck
Sieć Rok Kluczowa innowacja Use case Mnemonik
FCN 2015 w pełni konwolucyjna + skip connections pierwsza end-to-end „FC → Conv 1×1"
U-Net 2015 U-shape + skip concat + data augmentation segmentacja medyczna „Litera U + mosty"
DeepLab v3+ 2018 atrous (dilated) conv + ASPP general-purpose „DZIURY w filtrze"
SegFormer 2021 transformer encoder (self-attention) SOTA lightweight „WSZYSCY ze WSZYSTKIMI"
Mask2Former 2022 masked attention + unified architecture SOTA universal „WSZYSCY ze WSZYSTKIMI"

FCN (Fully Convolutional Network):

Mnemonik: „FC → Conv 1×1 = otwieramy bramkę dla DOWOLNEGO rozmiaru"
Zwykły CNN:  Conv → Conv → Pool → ... → FC → FC → "kot"
FCN:         Conv → Conv → Pool → ... → Conv1×1 → Upsample → mapa pikseli
Innowacja: zamiana FC na Conv1×1 → wejście dowolnego rozmiaru
Skip connections: łączą cechy z encodera → zachowują detale przestrzenne

U-Net:

Mnemonik: „Litera U + mosty" — schodzisz w dół, wracasz w górę,
po drodze mosty (skip connections z concat) przenoszą detale.
Encoder (↓)         Decoder (↑)
[64]────skip────→[64]        ← skip connections = concatenation
[128]───skip───→[128]           (przenosi detale z encodera do decodera)
[256]──skip──→[256]
[512]─skip─→[512]
     [1024]                  ← bottleneck
Dlaczego medycyna? Działa dobrze z MAŁYMI zbiorami danych (data augmentation)

DeepLab v3+:

Mnemonik: „DZIURY w filtrze" — filtr dosłownie ma dziury (à trous),
przez co widzi dalej bez dodatkowych parametrów.
Zwykła konwolucja 3×3:   [x][x][x]         receptive field = 3
Dilated (rate=2):        [x][ ][x][ ][x]   receptive field = 5, te same parametry!
ASPP: równolegle rate=6,12,18 → multi-scale features → łączenie
Efekt: widzi kontekst globalny BEZ zwiększania parametrów

Transformery (SegFormer, Mask2Former):

Mnemonik: „WSZYSCY ze WSZYSTKIMI" — każdy piksel rozmawia z KAŻDYM innym.
CNN: filtr 3×3 widzi LOKALNY kontekst (sąsiadów)
Transformer: self-attention widzi CAŁY obraz naraz
Cena: O(n²) pamięci (n = piksele), ale lepsze wyniki

DIY Przykład — U-Net krok po kroku

Poniższy diagram pokazuje CAŁY pipeline U-Net od obrazu wejściowego do mapy segmentacji. Obraz syntetyczny 64×64 z dwoma obiektami (koła) na jasnym tle.

DIY U-Net: obraz → encoder zmniejsza → bottleneck → decoder zwiększa + skip → mapa segmentacji → pseudokod

Pseudokod U-Net (PyTorch-style):
# ENCODER — zmniejsza rozdzielczość, wyciąga cechy
e1 = conv_block(input, filters=64)      # [64×64×64]
e2 = conv_block(maxpool(e1), filters=128)  # [32×32×128]
e3 = conv_block(maxpool(e2), filters=256)  # [16×16×256]

# BOTTLENECK — najgłębsza warstwa
b = conv_block(maxpool(e3), filters=512)   # [8×8×512]

# DECODER — zwiększa rozdzielczość + skip connections (concat!)
d3 = conv_block(concat(upconv(b), e3), filters=256)    # [16×16×256]
d2 = conv_block(concat(upconv(d3), e2), filters=128)   # [32×32×128]
d1 = conv_block(concat(upconv(d2), e1), filters=64)    # [64×64×64]

# WYNIK — Conv 1×1 → mapa klas
output = conv_1x1(d1, n_classes=3)  # [64×64×3] → argmax → [64×64] etykiety

Metryki i funkcje kosztu

Metryka/Loss Wzór Kiedy użyć
mIoU mean(IoU per klasa) standardowy benchmark
Pixel Accuracy poprawne / wszystkie prosta, ale zła przy class imbalance
Dice Loss 1 - 2·|A∩B| / (|A|+|B|) segmentacja medyczna
Focal Loss -α(1-p)^γ · log(p) class imbalance (99% tła)

Etymologia

Segmentacja — łac. „segmentum" = odcięty kawałek; podział obrazu na regiony. Otsu — Nobuyuki Otsu (1979); automatyczny dobór progu. Watershed — metafora: woda spływająca z grani do dolin (z geografii). U-Net — Ronneberger et al. (Freiburg, 2015); „U" od kształtu architektury. FCN — Fully Convolutional Network (Long, Shelhamer, Darrell, 2015). DeepLab — Google (20152018); „Atrous" z fr. „à trous" = „z dziurami" (dilated convolutions). mIoU — mean Intersection over Union.

Jak zapamiętać

Super-mnemonik na kolejność algorytmów:

„Turyści Oglądają Rzekę, Wodospad, Morze, Nurt — Fotografują Uroczy Dwór Tajemnic"

Klasyczne: Thresholding → Otsu → Region growing → Watershed → Mean shift → Normalized cuts
Neuronowe: FCN → U-Net → DeepLab → Transformer

Mnemoniki: karty z algorytmami segmentacji i ich skojarzeniami

Mnemoniki per algorytm — STRATEGIE KLASYCZNE:

Algorytm Mnemonik Skojarzenie
Thresholding „PRÓG na bramce" Bramkarz przepuszcza piksele > T, blokuje ≤ T
Otsu „AUTO-bramkarz" Sam sprawdza 256 progów, wybiera najlepszy (min σ²)
Region Growing „PLAMA atramentu" Kropla atramentu rozlewa się na podobne piksele (BFS)
Watershed „ZALEWANIE terenu" Woda zalewa doliny, granie gór = granice segmentów
Mean Shift „KULKI toczą się do dołków" Każda kulka → max gęstości, ile dołków = tyle segmentów
Normalized Cuts „CIĘCIE sznurków" Tnij słabe sznurki (krawędzie grafu), zachowaj silne

Mnemoniki per algorytm — SIECI NEURONOWE:

Sieć Mnemonik Skojarzenie
FCN „FC → Conv 1×1" Otwiera bramkę dla dowolnego rozmiaru wejścia
U-Net „Litera U + mosty" Schodzisz ↓, wracasz ↑, mosty (skip concat) przenoszą detale
DeepLab „DZIURY w filtrze" Filtr ma dziury (à trous) → widzi dalej bez dodatkowych wag
Transformer „WSZYSCY ze WSZYSTKIMI" Każdy piksel pyta każdy inny (self-attention, O(n²))

Mnemoniki per metrykę:

  • mIoU = „Nakładka / Suma" → intersection / union, uśrednione per klasa
  • Dice = „Dwie nakładki / Razem" → 2·|A∩B| / (|A|+|B|)
  • Focal = „Fokus na TRUDNYCH" → trudne piksele ważą więcej