{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c418f9a3",
   "metadata": {
    "origin_pos": 0
   },
   "source": [
    "# 语义分割和数据集\n",
    ":label:`sec_semantic_segmentation`\n",
    "\n",
    "在 :numref:`sec_bbox`— :numref:`sec_rcnn`中讨论的目标检测问题中，我们一直使用方形边界框来标注和预测图像中的目标。\n",
    "本节将探讨*语义分割*（semantic segmentation）问题，它重点关注于如何将图像分割成属于不同语义类别的区域。\n",
    "与目标检测不同，语义分割可以识别并理解图像中每一个像素的内容：其语义区域的标注和预测是像素级的。\n",
    " :numref:`fig_segmentation`展示了语义分割中图像有关狗、猫和背景的标签。\n",
    "与目标检测相比，语义分割标注的像素级的边框显然更加精细。\n",
    "\n",
    "![语义分割中图像有关狗、猫和背景的标签](../img/segmentation.svg)\n",
    ":label:`fig_segmentation`\n",
    "\n",
    "## 图像分割和实例分割\n",
    "\n",
    "计算机视觉领域还有2个与语义分割相似的重要问题，即*图像分割*（image segmentation）和*实例分割*（instance segmentation）。\n",
    "我们在这里将它们同语义分割简单区分一下。\n",
    "\n",
    "* *图像分割*将图像划分为若干组成区域，这类问题的方法通常利用图像中像素之间的相关性。它在训练时不需要有关图像像素的标签信息，在预测时也无法保证分割出的区域具有我们希望得到的语义。以 :numref:`fig_segmentation`中的图像作为输入，图像分割可能会将狗分为两个区域：一个覆盖以黑色为主的嘴和眼睛，另一个覆盖以黄色为主的其余部分身体。\n",
    "* *实例分割*也叫*同时检测并分割*（simultaneous detection and segmentation），它研究如何识别图像中各个目标实例的像素级区域。与语义分割不同，实例分割不仅需要区分语义，还要区分不同的目标实例。例如，如果图像中有两条狗，则实例分割需要区分像素属于的两条狗中的哪一条。\n",
    "\n",
    "## Pascal VOC2012 语义分割数据集\n",
    "\n",
    "[**最重要的语义分割数据集之一是[Pascal VOC2012](http://host.robots.ox.ac.uk/pascal/VOC/voc2012/)。**]\n",
    "下面我们深入了解一下这个数据集。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "daac4844",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:56:37.603612Z",
     "iopub.status.busy": "2023-08-18T06:56:37.603086Z",
     "iopub.status.idle": "2023-08-18T06:56:39.534129Z",
     "shell.execute_reply": "2023-08-18T06:56:39.533314Z"
    },
    "origin_pos": 2,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import os\n",
    "import torch\n",
    "import torchvision\n",
    "from d2l import torch as d2l"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d328b1a",
   "metadata": {
    "origin_pos": 4
   },
   "source": [
    "数据集的tar文件大约为2GB，所以下载可能需要一段时间。\n",
    "提取出的数据集位于`../data/VOCdevkit/VOC2012`。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "bd79e9c2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:56:39.538309Z",
     "iopub.status.busy": "2023-08-18T06:56:39.537665Z",
     "iopub.status.idle": "2023-08-18T06:58:05.677522Z",
     "shell.execute_reply": "2023-08-18T06:58:05.676613Z"
    },
    "origin_pos": 5,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading ../data/VOCtrainval_11-May-2012.tar from http://d2l-data.s3-accelerate.amazonaws.com/VOCtrainval_11-May-2012.tar...\n"
     ]
    }
   ],
   "source": [
    "#@save\n",
    "d2l.DATA_HUB['voc2012'] = (d2l.DATA_URL + 'VOCtrainval_11-May-2012.tar',\n",
    "                           '4e443f8a2eca6b1dac8a6c57641b67dd40621a49')\n",
    "\n",
    "voc_dir = d2l.download_extract('voc2012', 'VOCdevkit/VOC2012')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6ef71ce7",
   "metadata": {
    "origin_pos": 6
   },
   "source": [
    "进入路径`../data/VOCdevkit/VOC2012`之后，我们可以看到数据集的不同组件。\n",
    "`ImageSets/Segmentation`路径包含用于训练和测试样本的文本文件，而`JPEGImages`和`SegmentationClass`路径分别存储着每个示例的输入图像和标签。\n",
    "此处的标签也采用图像格式，其尺寸和它所标注的输入图像的尺寸相同。\n",
    "此外，标签中颜色相同的像素属于同一个语义类别。\n",
    "下面将`read_voc_images`函数定义为[**将所有输入的图像和标签读入内存**]。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b39cdcf9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:58:05.681499Z",
     "iopub.status.busy": "2023-08-18T06:58:05.681220Z",
     "iopub.status.idle": "2023-08-18T06:58:11.042974Z",
     "shell.execute_reply": "2023-08-18T06:58:11.041446Z"
    },
    "origin_pos": 8,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [],
   "source": [
    "#@save\n",
    "def read_voc_images(voc_dir, is_train=True):\n",
    "    \"\"\"读取所有VOC图像并标注\"\"\"\n",
    "    txt_fname = os.path.join(voc_dir, 'ImageSets', 'Segmentation',\n",
    "                             'train.txt' if is_train else 'val.txt')\n",
    "    mode = torchvision.io.image.ImageReadMode.RGB\n",
    "    with open(txt_fname, 'r') as f:\n",
    "        images = f.read().split()\n",
    "    features, labels = [], []\n",
    "    for i, fname in enumerate(images):\n",
    "        features.append(torchvision.io.read_image(os.path.join(\n",
    "            voc_dir, 'JPEGImages', f'{fname}.jpg')))\n",
    "        labels.append(torchvision.io.read_image(os.path.join(\n",
    "            voc_dir, 'SegmentationClass' ,f'{fname}.png'), mode))\n",
    "    return features, labels\n",
    "\n",
    "train_features, train_labels = read_voc_images(voc_dir, True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24c325e8",
   "metadata": {
    "origin_pos": 10
   },
   "source": [
    "下面我们[**绘制前5个输入图像及其标签**]。\n",
    "在标签图像中，白色和黑色分别表示边框和背景，而其他颜色则对应不同的类别。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6de5f355",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:58:11.058847Z",
     "iopub.status.busy": "2023-08-18T06:58:11.057829Z",
     "iopub.status.idle": "2023-08-18T06:58:11.690152Z",
     "shell.execute_reply": "2023-08-18T06:58:11.689328Z"
    },
    "origin_pos": 12,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 64632 characters]\n",
      "text/plain": [
       "<Figure size 540x216 with 10 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "n = 5\n",
    "imgs = train_features[0:n] + train_labels[0:n]\n",
    "imgs = [img.permute(1,2,0) for img in imgs]\n",
    "d2l.show_images(imgs, 2, n);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3eb5e755",
   "metadata": {
    "origin_pos": 14
   },
   "source": [
    "接下来，我们[**列举RGB颜色值和类名**]。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8127e683",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:58:11.694111Z",
     "iopub.status.busy": "2023-08-18T06:58:11.693331Z",
     "iopub.status.idle": "2023-08-18T06:58:11.700642Z",
     "shell.execute_reply": "2023-08-18T06:58:11.699767Z"
    },
    "origin_pos": 15,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [],
   "source": [
    "#@save\n",
    "VOC_COLORMAP = [[0, 0, 0], [128, 0, 0], [0, 128, 0], [128, 128, 0],\n",
    "                [0, 0, 128], [128, 0, 128], [0, 128, 128], [128, 128, 128],\n",
    "                [64, 0, 0], [192, 0, 0], [64, 128, 0], [192, 128, 0],\n",
    "                [64, 0, 128], [192, 0, 128], [64, 128, 128], [192, 128, 128],\n",
    "                [0, 64, 0], [128, 64, 0], [0, 192, 0], [128, 192, 0],\n",
    "                [0, 64, 128]]\n",
    "\n",
    "#@save\n",
    "VOC_CLASSES = ['background', 'aeroplane', 'bicycle', 'bird', 'boat',\n",
    "               'bottle', 'bus', 'car', 'cat', 'chair', 'cow',\n",
    "               'diningtable', 'dog', 'horse', 'motorbike', 'person',\n",
    "               'potted plant', 'sheep', 'sofa', 'train', 'tv/monitor']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46f61b54",
   "metadata": {
    "origin_pos": 16
   },
   "source": [
    "通过上面定义的两个常量，我们可以方便地[**查找标签中每个像素的类索引**]。\n",
    "我们定义了`voc_colormap2label`函数来构建从上述RGB颜色值到类别索引的映射，而`voc_label_indices`函数将RGB值映射到在Pascal VOC2012数据集中的类别索引。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "36949805",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:58:11.704036Z",
     "iopub.status.busy": "2023-08-18T06:58:11.703567Z",
     "iopub.status.idle": "2023-08-18T06:58:11.710356Z",
     "shell.execute_reply": "2023-08-18T06:58:11.709284Z"
    },
    "origin_pos": 18,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [],
   "source": [
    "#@save\n",
    "def voc_colormap2label():\n",
    "    \"\"\"构建从RGB到VOC类别索引的映射\"\"\"\n",
    "    colormap2label = torch.zeros(256 ** 3, dtype=torch.long)\n",
    "    for i, colormap in enumerate(VOC_COLORMAP):\n",
    "        colormap2label[\n",
    "            (colormap[0] * 256 + colormap[1]) * 256 + colormap[2]] = i\n",
    "    return colormap2label\n",
    "\n",
    "#@save\n",
    "def voc_label_indices(colormap, colormap2label):\n",
    "    \"\"\"将VOC标签中的RGB值映射到它们的类别索引\"\"\"\n",
    "    colormap = colormap.permute(1, 2, 0).numpy().astype('int32')\n",
    "    idx = ((colormap[:, :, 0] * 256 + colormap[:, :, 1]) * 256\n",
    "           + colormap[:, :, 2])\n",
    "    return colormap2label[idx]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93e6e896",
   "metadata": {
    "origin_pos": 20
   },
   "source": [
    "[**例如**]，在第一张样本图像中，飞机头部区域的类别索引为1，而背景索引为0。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "8aea3591",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:58:11.713954Z",
     "iopub.status.busy": "2023-08-18T06:58:11.713226Z",
     "iopub.status.idle": "2023-08-18T06:58:11.740434Z",
     "shell.execute_reply": "2023-08-18T06:58:11.739332Z"
    },
    "origin_pos": 21,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 1],\n",
       "         [0, 0, 0, 0, 0, 0, 0, 1, 1, 1],\n",
       "         [0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
       "         [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],\n",
       "         [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],\n",
       "         [0, 0, 0, 0, 1, 1, 1, 1, 1, 1],\n",
       "         [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],\n",
       "         [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],\n",
       "         [0, 0, 0, 0, 0, 0, 1, 1, 1, 1],\n",
       "         [0, 0, 0, 0, 0, 0, 0, 0, 1, 1]]),\n",
       " 'aeroplane')"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = voc_label_indices(train_labels[0], voc_colormap2label())\n",
    "y[105:115, 130:140], VOC_CLASSES[1]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18bcf344",
   "metadata": {
    "origin_pos": 22
   },
   "source": [
    "### 预处理数据\n",
    "\n",
    "在之前的实验，例如 :numref:`sec_alexnet`— :numref:`sec_googlenet`中，我们通过再缩放图像使其符合模型的输入形状。\n",
    "然而在语义分割中，这样做需要将预测的像素类别重新映射回原始尺寸的输入图像。\n",
    "这样的映射可能不够精确，尤其在不同语义的分割区域。\n",
    "为了避免这个问题，我们将图像裁剪为固定尺寸，而不是再缩放。\n",
    "具体来说，我们[**使用图像增广中的随机裁剪，裁剪输入图像和标签的相同区域**]。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "747378f7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:58:11.745126Z",
     "iopub.status.busy": "2023-08-18T06:58:11.744585Z",
     "iopub.status.idle": "2023-08-18T06:58:11.749914Z",
     "shell.execute_reply": "2023-08-18T06:58:11.749104Z"
    },
    "origin_pos": 24,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [],
   "source": [
    "#@save\n",
    "def voc_rand_crop(feature, label, height, width):\n",
    "    \"\"\"随机裁剪特征和标签图像\"\"\"\n",
    "    rect = torchvision.transforms.RandomCrop.get_params(\n",
    "        feature, (height, width))\n",
    "    feature = torchvision.transforms.functional.crop(feature, *rect)\n",
    "    label = torchvision.transforms.functional.crop(label, *rect)\n",
    "    return feature, label"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "19f90bad",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:58:11.754060Z",
     "iopub.status.busy": "2023-08-18T06:58:11.753536Z",
     "iopub.status.idle": "2023-08-18T06:58:12.181252Z",
     "shell.execute_reply": "2023-08-18T06:58:12.180465Z"
    },
    "origin_pos": 27,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 60068 characters]\n",
      "text/plain": [
       "<Figure size 540x216 with 10 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "imgs = []\n",
    "for _ in range(n):\n",
    "    imgs += voc_rand_crop(train_features[0], train_labels[0], 200, 300)\n",
    "\n",
    "imgs = [img.permute(1, 2, 0) for img in imgs]\n",
    "d2l.show_images(imgs[::2] + imgs[1::2], 2, n);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63a6b7aa",
   "metadata": {
    "origin_pos": 29
   },
   "source": [
    "### [**自定义语义分割数据集类**]\n",
    "\n",
    "我们通过继承高级API提供的`Dataset`类，自定义了一个语义分割数据集类`VOCSegDataset`。\n",
    "通过实现`__getitem__`函数，我们可以任意访问数据集中索引为`idx`的输入图像及其每个像素的类别索引。\n",
    "由于数据集中有些图像的尺寸可能小于随机裁剪所指定的输出尺寸，这些样本可以通过自定义的`filter`函数移除掉。\n",
    "此外，我们还定义了`normalize_image`函数，从而对输入图像的RGB三个通道的值分别做标准化。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "82139d78",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:58:12.184986Z",
     "iopub.status.busy": "2023-08-18T06:58:12.184395Z",
     "iopub.status.idle": "2023-08-18T06:58:12.192756Z",
     "shell.execute_reply": "2023-08-18T06:58:12.192015Z"
    },
    "origin_pos": 31,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [],
   "source": [
    "#@save\n",
    "class VOCSegDataset(torch.utils.data.Dataset):\n",
    "    \"\"\"一个用于加载VOC数据集的自定义数据集\"\"\"\n",
    "\n",
    "    def __init__(self, is_train, crop_size, voc_dir):\n",
    "        self.transform = torchvision.transforms.Normalize(\n",
    "            mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n",
    "        self.crop_size = crop_size\n",
    "        features, labels = read_voc_images(voc_dir, is_train=is_train)\n",
    "        self.features = [self.normalize_image(feature)\n",
    "                         for feature in self.filter(features)]\n",
    "        self.labels = self.filter(labels)\n",
    "        self.colormap2label = voc_colormap2label()\n",
    "        print('read ' + str(len(self.features)) + ' examples')\n",
    "\n",
    "    def normalize_image(self, img):\n",
    "        return self.transform(img.float() / 255)\n",
    "\n",
    "    def filter(self, imgs):\n",
    "        return [img for img in imgs if (\n",
    "            img.shape[1] >= self.crop_size[0] and\n",
    "            img.shape[2] >= self.crop_size[1])]\n",
    "\n",
    "    def __getitem__(self, idx):\n",
    "        feature, label = voc_rand_crop(self.features[idx], self.labels[idx],\n",
    "                                       *self.crop_size)\n",
    "        return (feature, voc_label_indices(label, self.colormap2label))\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.features)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d079ac1f",
   "metadata": {
    "origin_pos": 33
   },
   "source": [
    "### [**读取数据集**]\n",
    "\n",
    "我们通过自定义的`VOCSegDataset`类来分别创建训练集和测试集的实例。\n",
    "假设我们指定随机裁剪的输出图像的形状为$320\\times 480$，\n",
    "下面我们可以查看训练集和测试集所保留的样本个数。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "79b88eeb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:58:12.196249Z",
     "iopub.status.busy": "2023-08-18T06:58:12.195713Z",
     "iopub.status.idle": "2023-08-18T06:58:24.376223Z",
     "shell.execute_reply": "2023-08-18T06:58:24.375033Z"
    },
    "origin_pos": 34,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "read 1114 examples\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "read 1078 examples\n"
     ]
    }
   ],
   "source": [
    "crop_size = (320, 480)\n",
    "voc_train = VOCSegDataset(True, crop_size, voc_dir)\n",
    "voc_test = VOCSegDataset(False, crop_size, voc_dir)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74e8ab37",
   "metadata": {
    "origin_pos": 35
   },
   "source": [
    "设批量大小为64，我们定义训练集的迭代器。\n",
    "打印第一个小批量的形状会发现：与图像分类或目标检测不同，这里的标签是一个三维数组。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "0c447d3b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:58:24.381208Z",
     "iopub.status.busy": "2023-08-18T06:58:24.380885Z",
     "iopub.status.idle": "2023-08-18T06:58:26.002445Z",
     "shell.execute_reply": "2023-08-18T06:58:26.000841Z"
    },
    "origin_pos": 37,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([64, 3, 320, 480])\n",
      "torch.Size([64, 320, 480])\n"
     ]
    }
   ],
   "source": [
    "batch_size = 64\n",
    "train_iter = torch.utils.data.DataLoader(voc_train, batch_size, shuffle=True,\n",
    "                                    drop_last=True,\n",
    "                                    num_workers=d2l.get_dataloader_workers())\n",
    "for X, Y in train_iter:\n",
    "    print(X.shape)\n",
    "    print(Y.shape)\n",
    "    break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b3c5ab46",
   "metadata": {
    "origin_pos": 39
   },
   "source": [
    "### [**整合所有组件**]\n",
    "\n",
    "最后，我们定义以下`load_data_voc`函数来下载并读取Pascal VOC2012语义分割数据集。\n",
    "它返回训练集和测试集的数据迭代器。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "ce15ca32",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-08-18T06:58:26.008578Z",
     "iopub.status.busy": "2023-08-18T06:58:26.007726Z",
     "iopub.status.idle": "2023-08-18T06:58:26.017217Z",
     "shell.execute_reply": "2023-08-18T06:58:26.015893Z"
    },
    "origin_pos": 41,
    "tab": [
     "pytorch"
    ]
   },
   "outputs": [],
   "source": [
    "#@save\n",
    "def load_data_voc(batch_size, crop_size):\n",
    "    \"\"\"加载VOC语义分割数据集\"\"\"\n",
    "    voc_dir = d2l.download_extract('voc2012', os.path.join(\n",
    "        'VOCdevkit', 'VOC2012'))\n",
    "    num_workers = d2l.get_dataloader_workers()\n",
    "    train_iter = torch.utils.data.DataLoader(\n",
    "        VOCSegDataset(True, crop_size, voc_dir), batch_size,\n",
    "        shuffle=True, drop_last=True, num_workers=num_workers)\n",
    "    test_iter = torch.utils.data.DataLoader(\n",
    "        VOCSegDataset(False, crop_size, voc_dir), batch_size,\n",
    "        drop_last=True, num_workers=num_workers)\n",
    "    return train_iter, test_iter"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2ea20dfd",
   "metadata": {
    "origin_pos": 43
   },
   "source": [
    "## 小结\n",
    "\n",
    "* 语义分割通过将图像划分为属于不同语义类别的区域，来识别并理解图像中像素级别的内容。\n",
    "* 语义分割的一个重要的数据集叫做Pascal VOC2012。\n",
    "* 由于语义分割的输入图像和标签在像素上一一对应，输入图像会被随机裁剪为固定尺寸而不是缩放。\n",
    "\n",
    "## 练习\n",
    "\n",
    "1. 如何在自动驾驶和医疗图像诊断中应用语义分割？还能想到其他领域的应用吗？\n",
    "1. 回想一下 :numref:`sec_image_augmentation`中对数据增强的描述。图像分类中使用的哪种图像增强方法是难以用于语义分割的？\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17f10497",
   "metadata": {
    "origin_pos": 45,
    "tab": [
     "pytorch"
    ]
   },
   "source": [
    "[Discussions](https://discuss.d2l.ai/t/3295)\n"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  },
  "required_libs": []
 },
 "nbformat": 4,
 "nbformat_minor": 5
}