Lesson 15.29 Intermediate 30–40 mins read

Image Segmentation: Semantic Segmentation, Instance Segmentation & U-Net

Transition from coarse rectangular bounding boxes to dense pixel-level visual understanding. Master semantic, instance, and panoptic segmentation, formulate pixel-wise loss landscapes (Cross-Entropy, Soft Dice, and Focal Loss), dissect the U-Net architecture and skip connection mechanics, and implement an end-to-end segmentation pipeline in PyTorch.

1 Introduction: What Is Image Segmentation?

In the previous lesson, we explored Object Detection, where models detect multiple instances in an image and draw rectangular bounding boxes around them. While bounding boxes tell us approximately where an object is located, they fail to describe its exact geometric shape, irregular contours, or fine boundaries.

Consider an autonomous vehicle navigating an urban intersection. Knowing that a pedestrian or vehicle exists inside a bounding box $[x_1, y_1, x_2, y_2]$ is insufficient for path planning: the box includes extraneous pixels of the background street, sidewalk, or neighboring trees. The vehicle must know with surgical precision:

  • Which exact pixels belong to the drivable road surface?
  • Which pixels delineate the curved boundary of the sidewalk and curbs?
  • What is the precise morphological outline of pedestrians and cyclists?

The Formal Definition of Image Segmentation

Image Segmentation is a dense computer vision task in which a model partitions an input image into meaningful visual regions by assigning a discrete class label or object instance identifier to every individual pixel:

f: \mathbf{X} \in \mathbb{R}^{3 \times H \times W} \longrightarrow \mathbf{M} \in \{0, 1, \dots, K-1\}^{H \times W}

Unlike image classification which outputs a single global vector $\hat{y} \in \mathbb{R}^K$, image segmentation produces a full spatial prediction tensor with matching height $H$ and width $W$.

Medical Imaging Tumor & organ boundary resection
Autonomous Driving Drivable lane & curb demarcation
Smart Agriculture Crop vs weed foliage separation
Satellite Imagery Deforestation & flood mapping

2 Learning Objectives

Formulate the taxonomy of segmentation: Semantic, Instance, and Panoptic Segmentation.
Master segmentation mask data structures (Binary masks, Multiclass categorical masks, and Instance ID maps).
Understand the U-Net architecture: Contracting path, Bottleneck, Expanding path, and Skip Connections.
Derive segmentation loss functions: Pixel-wise Cross-Entropy, Soft Dice Loss, and Combined Loss.
Differentiate evaluation metrics: Pixel Accuracy vs Mean Intersection over Union (mIoU) vs Dice Score.
Avoid critical preprocessing bugs: Nearest-neighbor interpolation for masks vs bilinear for images.
Build a full U-Net model from scratch in PyTorch with DoubleConv blocks and skip concatenation.
Compute per-class IoU and confusion matrices using vectorized GPU operations.

3 Semantic, Instance and Panoptic Segmentation

Depending on how pixel identities and object instances are treated, computer vision recognizes three distinct segmentation tasks:

1. Semantic Segmentation

Class-Only

Assigns a semantic category label to every pixel without distinguishing between separate individual objects. If an image contains three cars, all car pixels receive the identical class ID 1.

Target: M[i, j] ∈ {0, …, K-1}

2. Instance Segmentation

Object-Aware

Detects and segments individual countable objects ("things"). If an image contains three cars, each car is assigned an independent mask and instance ID (Car 1, Car 2, Car 3). Background pixels ("stuff") are ignored.

Target: {(Mask_k, Class_k, ID_k)}

3. Panoptic Segmentation

Unified Vision

The holy grail: combines semantic and instance segmentation. It classifies amorphous background regions ("stuff" like road, sky, grass) while simultaneously distinguishing unique instance IDs for countable objects ("things").

Target: (Class_Map, Instance_ID_Map)
Feature / Property Semantic Segmentation Instance Segmentation Panoptic Segmentation
Pixel-Level Prediction ✓ Yes (Full Coverage) ✓ Yes (Detected Objects) ✓ Yes (100% Coverage)
Differentiates Individual Instances × No (All instances share label) ✓ Yes (Unique mask per object) ✓ Yes (Unique ID per "thing")
Handles Amorphous "Stuff" (Sky, Road) ✓ Yes × No (Ignores background) ✓ Yes (Continuous regions)
Primary Canonical Architectures U-Net, DeepLabv3+, SegFormer Mask R-CNN, YOLOV8-Seg Panoptic-DeepLab, Mask2Former

4 Understanding Segmentation Masks & Data Representations

A segmentation mask is a 2D matrix $\mathbf{M} \in \mathbb{Z}^{H \times W}$ where each entry $M_{i, j}$ stores an integer identifier. Beginners often mistake segmentation masks for colored images; colors in visualizations are merely human-readable palettes mapping integer IDs to RGB values.

Binary Segmentation Masks

Used when isolating a single target entity from the background (e.g., tumor vs healthy tissue, polyp detection in endoscopy).

M_{i, j} \in \{0, 1\} \quad (0 = \text{Background}, 1 = \text{Foreground})

Stored as 1-channel 8-bit unsigned integers (uint8).

Multiclass Categorical Masks

Used in multi-class semantic segmentation (e.g., Cityscapes dataset with Road, Sidewalk, Vehicle, Sky, Building).

M_{i, j} \in \{0, 1, \dots, K-1\} \quad (K = \text{Total Classes})

Fed directly to PyTorch CrossEntropyLoss as torch.long tensor of shape $[N, H, W]$.

5 How Does a Segmentation Model Work? (Encoder-Decoder Topology)

Standard CNNs downsample spatial dimensions through strided convolutions and max pooling. For classification, spatial downsampling is beneficial because it builds scale invariance. However, in segmentation, downsampling destroys high-resolution boundary details. An encoder-decoder architecture resolves this tension:

1. Encoder

Progressively extracts semantic visual features while contracting spatial dimensions ($H \times W \to \frac{H}{16} \times \frac{W}{16}$).

2. Bottleneck

The deepest layer holding compact, maximum-receptive-field semantic context (e.g., $512 \times 16 \times 16$).

3. Decoder

Upsamples feature maps back to original resolution ($\frac{H}{16} \to H$) via transposed convolutions or bilinear interpolation.

4. Head

A $1 \times 1$ convolution projecting channel depth to $K$ classes, generating per-pixel raw logits $[N, K, H, W]$.

6 U-Net: The Architecture & Skip Connection Mechanics

Proposed by Olaf Ronneberger, Philipp Fischer, and Thomas Brox in 2015, U-Net became one of the most cited computer vision architectures in deep learning history. Named after its symmetric "U" shape, U-Net solved the fundamental dilemma of segmentation: how to combine broad semantic context with pixel-accurate spatial localization.

The Four Crucial Components of U-Net

Contracting Path (Encoder):

Repeated applications of two $3 \times 3$ convolutions (unpadded in original paper, padded in modern implementations), Batch Normalization, ReLU activations, and $2 \times 2$ Max Pooling with stride 2 for downsampling. At each step, feature channels double.

Expanding Path (Decoder):

At each stage, an upsampling operation ($2 \times 2$ Transposed Convolution or Bilinear Interpolation) halves the number of feature channels while doubling spatial dimensions.

Skip Connections (Concatenation):

Directly bridges the high-resolution feature maps from each encoder stage across to the corresponding decoder stage. The decoder concatenates these tensors along the channel dimension:

\mathbf{X}_{\text{dec\_in}} = \text{Concat}([\mathbf{X}_{\text{upsampled}}, \mathbf{X}_{\text{encoder}}], \text{dim}=1)
1×1 Output Projection Head:

A final $1 \times 1$ convolutional layer maps the 32- or 64-channel refined decoder output directly to the number of classes $K$, producing the final unnormalized logit tensor.

Why Skip Connections Are Indispensable In standard autoencoders without skip connections, low-level spatial coordinates are compressed into a tiny bottleneck tensor. When the decoder tries to reconstruct the image, sharp organ boundaries, fine hair strands, or thin power lines are permanently blurred. U-Net's skip connections act as an information highway: the encoder provides the spatial coordinates (where the boundary is), while the bottleneck provides the semantic context (what the object is).

7 The Mathematics of Pixel-Level Classification

Semantic segmentation reformulates image processing as $H \times W$ simultaneous, independent multi-class classification problems. For each pixel $(i, j)$, the network outputs a vector of raw logits $\mathbf{z}_{i, j} = [z_{i, j, 0}, z_{i, j, 1}, \dots, z_{i, j, K-1}] \in \mathbb{R}^K$.

Pixel-wise Softmax Probability Distribution:
$$P(c \mid i, j) = \frac{e^{z_{i, j, c}}}{\sum_{k=0}^{K-1} e^{z_{i, j, k}}}$$

The final predicted class ID $\hat{y}_{i, j}$ for pixel $(i, j)$ is computed via the argmax operator along the channel dimension:

\hat{y}_{i, j} = \arg\max_{c \in \{0, \dots, K-1\}} P(c \mid i, j) = \arg\max_{c \in \{0, \dots, K-1\}} z_{i, j, c}

8 Loss Functions for Segmentation: Cross-Entropy, Dice & Combined

Choosing the correct loss function determines whether a segmentation model converges or completely fails. Unlike classification, segmentation datasets suffer from massive class imbalance: the background class often accounts for 90%–99% of total pixels, while target entities (tumors, cracks, pedestrians) occupy only 1%–5%.

1. Pixel-wise Cross-Entropy

Evaluates classification accuracy at each pixel independently:

\mathcal{L}_{\text{CE}} = -\frac{1}{HW} \sum_{i, j} \sum_c y_{i, j, c} \log(p_{i, j, c})

Drawback: Overwhelmed by massive background regions.

2. Soft Dice Loss

Measures global region overlap, inherently immune to background pixel count:

\mathcal{L}_{\text{Dice}} = 1 - \frac{2 \sum p_i y_i + \epsilon}{\sum p_i + \sum y_i + \epsilon}

Ideal for small, rare foreground targets (e.g., tumors).

3. Combined Loss

The industry standard: blends smooth pixel probabilities with region overlap:

\mathcal{L}_{\text{Combo}} = \alpha \mathcal{L}_{\text{CE}} + \beta \mathcal{L}_{\text{Dice}}

Provides stable gradients while maximizing mIoU.

Differentiable Soft Dice Loss in PyTorch: torch.nn.Module
import torch
import torch.nn as nn

class SoftDiceLoss(nn.Module):
    def __init__(self, eps: float = 1e-6):
        super().__init__()
        self.eps = eps

    def forward(self, logits: torch.Tensor, targets: torch.Tensor) -> torch.Tensor:
        # logits: [N, 1, H, W] for binary segmentation
        # targets: [N, 1, H, W] float {0.0, 1.0}
        probs = torch.sigmoid(logits)
        
        probs_flat = probs.view(probs.shape[0], -1)
        targets_flat = targets.view(targets.shape[0], -1)

        intersection = (probs_flat * targets_flat).sum(dim=1)
        cardinality = probs_flat.sum(dim=1) + targets_flat.sum(dim=1)

        dice = (2.0 * intersection + self.eps) / (cardinality + self.eps)
        return 1.0 - dice.mean()

9 Evaluation Metrics: Pixel Accuracy, IoU, Mean IoU & Dice

Evaluating segmentation models requires metrics that resist class-imbalance distortions.

Pixel Accuracy (Deceptive)

$$\text{Acc} = \frac{\sum \text{Correct Pixels}}{\text{Total Pixels}}$$ If 95% of the image is background, predicting all zeros achieves 95% accuracy while completely missing the target entity.

Intersection over Union (IoU)

$$\text{IoU}_c = \frac{\text{TP}_c}{\text{TP}_c + \text{FP}_c + \text{FN}_c}$$ Directly penalizes both false alarms and missed regions for class $c$.

Mean IoU (mIoU)

$$\text{mIoU} = \frac{1}{K} \sum_{c=0}^{K-1} \text{IoU}_c$$ The benchmark metric across all competition leaderboards (Cityscapes, ADE20K, Pascal VOC).

Mathematical Identity Between Dice Score & IoU (Jaccard)

The Dice coefficient and IoU are monotonically related functions of the exact same underlying confusion counts:

\text{Dice} = \frac{2 \cdot \text{IoU}}{1 + \text{IoU}}
\text{IoU} = \frac{\text{Dice}}{2 - \text{Dice}}

Notice that $\text{Dice} \ge \text{IoU}$ for all values in $[0, 1]$. For instance, an IoU of $0.70$ equates to a Dice score of $0.824$.

10 Preparing a Segmentation Dataset & The Interpolation Rule

A semantic segmentation dataset consists of matched pairs of images and annotation masks.

CRITICAL RULE: The Mask Resizing Interpolation Trap

When resizing images and segmentation masks to a uniform resolution (e.g., $256 \times 256$):

Input Images: Use Bilinear or Bicubic interpolation (Image.Resampling.BILINEAR) to preserve continuous optical texture.
Label Masks: MUST strictly use Nearest-Neighbor interpolation (Image.Resampling.NEAREST)!

Why? If class 0 is Background and class 2 is Car, bilinear interpolation on the mask creates intermediate floating values like 0.8, which round to class 1 (Road). Bilinear interpolation literally fabricates non-existent classes along object boundaries!

11 Building a U-Net with PyTorch

Let us build a modular, production-ready U-Net in PyTorch. Each stage utilizes a DoubleConv block comprising two $3 \times 3$ convolutions with Batch Normalization and LeakyReLU / ReLU activations.

Python: Full Modular U-Net Implementation unet_model.py
import torch
import torch.nn as nn
import torch.nn.functional as F

class DoubleConv(nn.Module):
    # (Conv2d -> BatchNorm -> ReLU) * 2 with padding=1 to preserve spatial dimensions.
    def __init__(self, in_channels: int, out_channels: int):
        super().__init__()
        self.block = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1, bias=False),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return self.block(x)


class UNet(nn.Module):
    def __init__(self, in_channels: int = 3, num_classes: int = 3):
        super().__init__()
        # Encoder (Contracting Path)
        self.enc1 = DoubleConv(in_channels, 32)
        self.enc2 = DoubleConv(32, 64)
        self.enc3 = DoubleConv(64, 128)
        self.enc4 = DoubleConv(128, 256)
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)

        # Bottleneck
        self.bottleneck = DoubleConv(256, 512)

        # Decoder (Expanding Path) with Transposed Convolutions
        self.up4 = nn.ConvTranspose2d(512, 256, kernel_size=2, stride=2)
        self.dec4 = DoubleConv(512, 256)

        self.up3 = nn.ConvTranspose2d(256, 128, kernel_size=2, stride=2)
        self.dec3 = DoubleConv(256, 128)

        self.up2 = nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2)
        self.dec2 = DoubleConv(128, 64)

        self.up1 = nn.ConvTranspose2d(64, 32, kernel_size=2, stride=2)
        self.dec1 = DoubleConv(64, 32)

        # 1x1 Convolution Segmentation Head
        self.out = nn.Conv2d(32, num_classes, kernel_size=1)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        # Encoder forward pass
        e1 = self.enc1(x)                # [N, 32, H, W]
        e2 = self.enc2(self.pool(e1))    # [N, 64, H/2, W/2]
        e3 = self.enc3(self.pool(e2))    # [N, 128, H/4, W/4]
        e4 = self.enc4(self.pool(e3))    # [N, 256, H/8, W/8]

        # Bottleneck
        b = self.bottleneck(self.pool(e4)) # [N, 512, H/16, W/16]

        # Decoder stage 4 with skip connection from e4
        d4 = self.up4(b)
        if d4.shape[-2:] != e4.shape[-2:]:
            d4 = F.interpolate(d4, size=e4.shape[-2:], mode="bilinear", align_corners=False)
        d4 = self.dec4(torch.cat([d4, e4], dim=1)) # Concat channels: 256 + 256 = 512

        # Decoder stage 3 with skip connection from e3
        d3 = self.up3(d4)
        if d3.shape[-2:] != e3.shape[-2:]:
            d3 = F.interpolate(d3, size=e3.shape[-2:], mode="bilinear", align_corners=False)
        d3 = self.dec3(torch.cat([d3, e3], dim=1)) # 128 + 128 = 256

        # Decoder stage 2 with skip connection from e2
        d2 = self.up2(d3)
        if d2.shape[-2:] != e2.shape[-2:]:
            d2 = F.interpolate(d2, size=e2.shape[-2:], mode="bilinear", align_corners=False)
        d2 = self.dec2(torch.cat([d2, e2], dim=1)) # 64 + 64 = 128

        # Decoder stage 1 with skip connection from e1
        d1 = self.up1(d2)
        if d1.shape[-2:] != e1.shape[-2:]:
            d1 = F.interpolate(d1, size=e1.shape[-2:], mode="bilinear", align_corners=False)
        d1 = self.dec1(torch.cat([d1, e1], dim=1)) # 32 + 32 = 64

        # Output projection head produces raw logits: [N, num_classes, H, W]
        return self.out(d1)

# Sanity check output dimensions
model = UNet(in_channels=3, num_classes=4)
x = torch.randn(2, 3, 256, 256)
logits = model(x)
print(f"Logits shape: {logits.shape}") # Expect: [2, 4, 256, 256]

12 End-to-End Dataset, Training & Validation Pipeline

Here is the complete training pipeline including custom PyTorch Dataset loading, loss computation with CrossEntropyLoss, and validation.

Python: PyTorch Dataset & Training Loop train_segmentation.py
import numpy as np
from PIL import Image
from torch.utils.data import Dataset, DataLoader

class SegmentationDataset(Dataset):
    def __init__(self, image_paths, mask_paths, size=(256, 256)):
        self.image_paths = image_paths
        self.mask_paths = mask_paths
        self.size = size

    def __len__(self):
        return len(self.image_paths)

    def __getitem__(self, idx):
        # 1. Load image and mask
        image = Image.open(self.image_paths[idx]).convert("RGB")
        mask = Image.open(self.mask_paths[idx]).convert("L")

        # 2. Resize with correct interpolation!
        w, h = self.size[1], self.size[0]
        image = image.resize((w, h), Image.Resampling.BILINEAR)
        mask = mask.resize((w, h), Image.Resampling.NEAREST)

        # 3. Convert to PyTorch tensors
        img_np = np.asarray(image, dtype=np.float32) / 255.0  # Normalize to [0, 1]
        mask_np = np.asarray(mask, dtype=np.int64)            # Discrete class IDs

        image_tensor = torch.from_numpy(img_np).permute(2, 0, 1) # [C, H, W]
        mask_tensor = torch.from_numpy(mask_np)                 # [H, W]

        return image_tensor, mask_tensor

# Setup Training Loop
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = UNet(in_channels=3, num_classes=3).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-4)

# Training Epoch
model.train()
for images, masks in train_loader:
    images, masks = images.to(device), masks.to(device)
    
    optimizer.zero_grad(set_to_none=True)
    logits = model(images)               # [N, 3, 256, 256]
    loss = criterion(logits, masks)      # masks is [N, 256, 256] with dtype torch.long
    loss.backward()
    optimizer.step()

13 Calculating Mean IoU via Vectorized Confusion Matrix

Calculating per-pixel IoU iteratively in Python loops causes devastating CPU-GPU synchronization bottlenecks. Production systems compute the $K \times K$ confusion matrix in a single vectorized GPU kernel using torch.bincount:

Python: Vectorized GPU Confusion Matrix & mIoU metrics_miou.py
@torch.no_grad()
def evaluate_miou(model, dataloader, num_classes, device):
    model.eval()
    confusion = torch.zeros(num_classes, num_classes, dtype=torch.long, device=device)

    for images, targets in dataloader:
        images, targets = images.to(device), targets.to(device)
        logits = model(images)
        preds = logits.argmax(dim=1) # [N, H, W]

        # Valid pixel mask excluding ignore_index
        valid = (targets >= 0) & (targets < num_classes)
        indices = num_classes * targets[valid].long() + preds[valid].long()
        confusion += torch.bincount(indices, minlength=num_classes**2).view(num_classes, num_classes)

    # Compute per-class IoU from confusion matrix diagonal
    tp = confusion.diag()
    fp = confusion.sum(dim=0) - tp
    fn = confusion.sum(dim=1) - tp
    denominator = tp + fp + fn

    ious = tp.float() / denominator.clamp(min=1)
    present_classes = denominator > 0
    mean_iou = ious[present_classes].mean().item()

    return ious.cpu().numpy(), mean_iou

14 Modern Segmentation Model Zoo

Architecture Primary Paradigm Core Innovation Ideal Use Case
U-Net Semantic Segmentation Encoder-decoder with dense skip connections Biomedical imaging, small datasets, defect detection
FCN (Fully Conv Net) Semantic Segmentation Replaced dense linear layers with $1 \times 1$ convs Historical baseline, simple dense prediction
DeepLabv3+ Semantic Segmentation Atrous Spatial Pyramid Pooling (ASPP) & depthwise separable convs Urban street scenes, multi-scale complex landscapes
SegFormer Semantic Segmentation Hierarchical Vision Transformer encoder + lightweight MLP decoder State-of-the-art accuracy with zero positional encodings
Mask R-CNN Instance Segmentation Faster R-CNN + FCN mask branch via RoIAlign Object-aware instance tracking, robotics grasping

15 Top 10 Segmentation Pitfalls & Anti-Patterns Matrix

# Pitfall / Anti-Pattern Root Cause Symptom Production Remedy
1 Bilinear Mask Resizing Using default bilinear interpolation when resizing label masks. Phantom intermediate class IDs appear on borders. Always use Image.Resampling.NEAREST for masks.
2 CrossEntropy Target Shape Mismatch Passing $[N, 1, H, W]$ or $[N, K, H, W]$ targets to PyTorch CE loss. Runtime crash: 1D target tensor expected. Squeeze channel dimension: target must be $[N, H, W]$ with dtype torch.long.
3 Sigmoid with BCEWithLogitsLoss Applying torch.sigmoid() before calling BCEWithLogitsLoss. Double sigmoid saturates gradients; loss stagnates. Pass raw unbounded logits directly into BCEWithLogitsLoss.
4 Unsynchronized Augmentation Applying separate random transforms to image and mask independently. Image is flipped horizontally while mask stays unflipped. Use Albumentations or set fixed random seeds for joint spatial transforms.
5 Relying on Pixel Accuracy Evaluating severe class imbalance using plain pixel accuracy. Model reports 97% accuracy while mIoU is 0.05. Always report per-class IoU, mIoU, and Dice score.
6 VRAM Out-of-Memory (OOM) Retaining full-resolution high-channel feature maps during backward pass. CUDA OOM on batch size 4 with $512 \times 512$ inputs. Enable PyTorch mixed precision (torch.cuda.amp.autocast()) and gradient checkpointing.
7 Dice Division by Zero Omitting smoothing constant $\epsilon$ when neither prediction nor GT has foreground. Dice loss returns NaN; weights corrupt. Always add $\epsilon = 10^{-6}$ to both numerator and denominator.
8 Padding Mismatch in U-Net Using unpadded $3 \times 3$ convs causing decoder feature maps to shrink. Shape mismatch during torch.cat([d, e]). Use padding=1 in all conv layers or interpolate decoder maps.
9 Color Augmentation on Masks Applying ColorJitter or Brightness to categorical integer mask. Pixel labels shift to corrupt, arbitrary class IDs. Apply photometric transformations strictly to input images only.
10 Ignoring Missing Classes in mIoU Averaging IoU over all $K$ classes when some classes never appeared in batch. mIoU falsely penalized by unobserved classes. Compute mean only across classes present in GT or prediction.

16 Practical Assignment: Build a Road & Vehicle Segmentation Model

Capstone Task: Train a 3-Class Urban Scene Segmenter

Train a PyTorch U-Net to segment street scenes into Background (0), Road (1), and Vehicle (2).

17 Interactive Image Segmentation Workbench

Explore U-Net skip connections, compare semantic vs instance vs panoptic segmentation, test Dice vs CE loss dynamics, and diagnose mask interpolation errors.

4 Live Simulators

U-Net Architecture & Skip Connection Laboratory

Toggle skip connections ON or OFF to observe how encoder spatial features restore boundary sharpness in the reconstructed mask.

Active Mode With Skip Connections
Boundary Sharpness 98.4% (Sub-Pixel Precision)
Reconstructed mIoU 0.842
Spatial Detail Transfer Preserved [E1..E4]

Click the "Skip Connections" button above to witness the loss of fine boundaries when encoder features are disconnected from the decoder!

18 Key Takeaways

  • Dense Pixel Prediction: Image segmentation predicts a class label or instance identifier for every individual pixel in an $H \times W$ image.
  • Taxonomy: Semantic segmentation classifies pixels without distinguishing instances; Instance segmentation isolates individual countable "things"; Panoptic segmentation unifies both for 100% coverage.
  • U-Net Architecture: Combines an encoder (feature compression), a bottleneck (high-level context), and a decoder (resolution restoration).
  • Skip Connections: Transfer high-resolution spatial feature maps directly from encoder stages to decoder stages via concatenation, preserving sub-pixel boundary accuracy.
  • Loss Landscape: Pixel Cross-Entropy is vulnerable to extreme class imbalance; Soft Dice Loss evaluates global region overlap; Combined Loss ($\mathcal{L}_{\text{CE}} + \mathcal{L}_{\text{Dice}}$) provides the best convergence.
  • Evaluation: Pixel accuracy is dangerously deceptive on imbalanced datasets; always report Mean IoU (mIoU) and per-class IoU.
  • Interpolation Rule: Always use Bilinear interpolation for input images and Nearest-Neighbor interpolation for categorical label masks.
Previous Lesson 15.28 Object Detection: YOLO & Real-Time Vision
Next Lesson 15.30 Vision Transformers (ViT) & Attention in Computer Vision