{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "85f68775",
   "metadata": {},
   "source": [
    "# 第12章 目标检测"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2127f09",
   "metadata": {},
   "source": [
    "## 12.1 简介"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6bbc1a5",
   "metadata": {},
   "source": [
    "随着我们朝着更完整的图像理解迈进，拥有更精确和详细的目标识别变得至关重要。在这种情况下，人们不仅关心对图像进行分类，还关心精确估计图像中包含的目标的类别和位置，这一问题被称为目标检测。目标检测是一个重要的计算机视觉任务。它由图像分类任务发展而来，区别在于不再只是对一张图像中的单一类型目标进行分类，而是要同时完成一张图像里可能存在的多个目标的分类和定位，其中分类是指给目标分配类别标签，定位是指确定目标的位置也即是外围边界框的中心点坐标和长宽。如图 12-1 所示，目标检测的任务是从该图像中识别出“狗”、“猫”和“鸭子”，并且将其框选出。目标检测也可以作为图像分割、图像描述、动作识别等更复杂的计算机视觉任务的研究基础。在这一章中，我们将一起学习目标检测的相关内容，并动手实现相应的功能。\n",
    "\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic3.zhimg.com/80/v2-94fb384365a9ddd17e05e8a4a6e5fc6a_1440w.jpg\n",
    "\" width=300>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图12-1 目标检测示例。</div>\n",
    "</center>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d97b7824",
   "metadata": {},
   "source": [
    "## 12.2 数据集和评测度规"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef8ca62b",
   "metadata": {},
   "source": [
    "在目标检测中，我们常常会用到以下几种数据集：Pascal Voc、Ms COCO与ImageNet，这些数据集在之前的章节中我们已经介绍过，因此不再赘述。\n",
    "\n",
    "我们通常使用IoU作为目标检测的评价度规。如图 12-2 所示，给定矩形框$A$与$B$，则$IoU(A,B)=\\frac{A\\cap B}{A\\cup B}$，即图中绿色部分的面积除以红色的面积。\n",
    "\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic4.zhimg.com/80/v2-f68491e05d2e55a3d08425cdfe13a317_1440w.webp\n",
    "\" width=300>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图12-2 IoU。</div>\n",
    "</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2bee3e07",
   "metadata": {},
   "source": [
    "## 12.3 目标检测模型——从R-CNN到Faster R-CNN"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b4da1da",
   "metadata": {},
   "source": [
    "目标检测算法主要分为3个步骤：图像特征提取、候选区域生成与候选区域分类。其中，图像特征提取是整个检测流程的基石。在图像分类中我们曾简要的阐述过，随着Alexnet [1]的提出，基于深度学习的特征提取的迎来了研究的热潮。在之后的几年里，VGGNet [2]、GoogLeNet [3]、ResNet [4]等更强大的分类网络相继问世。由于它们能够提出非常强大的表征，因此除了完成图像分类任务之外，还普遍被用作更复杂的计算机视觉任务的骨架网络（backbone），其中就包括目标检测。2014年，Girshick等人提出的R-CNN [5]算法在PASCAL VOC检测数据集上以绝对优获得第一名，为目标检测开启了一个新的里程碑。自此，深度学习算法在目标检测的研究领域里占据了绝对的主导地位，并一直持续至今。\n",
    "\n",
    "基于深度学习的目标检测算法主要分为两个流派：（1）以R-CNN系列为代表的Two-Stage算法；（2）以SSD [6]、YOLO [7]为代表的One-Stage算法。具体来说，Two-Stage算法首先在图像上生成候选区域，然后对每一个候选区域依次进行分类与边界回归；而One-Stage算法则是直接在整张图像上完成所有目标的定位和分类，省去了生成候选区域这一步骤。两种流派各有优势，通常来说，前者精度更高，后者速度更快。在这一章中，我们主要以Two-Stage算法中的R-CNN家族算法为例，详细介绍R-CNN算法的发展历程和算法原理。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "92409e40",
   "metadata": {},
   "source": [
    "### 12.3.1 R-CNN"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6cc8081a",
   "metadata": {},
   "source": [
    "R-CNN（Region CNN，区域卷积神经网络）可以说是利用深度学习进行目标检测的开山之作，其流程如图 12-3 所示，可大致分为以下几个步骤：\n",
    "\n",
    "1. 输入一张图像；\n",
    "2. 为每张图像生成1K~2K个候选区域（ROI，region of interest）；\n",
    "3. 利用深度网络提取每个候选区域的特征\n",
    "4. 利用SVM分类器对提取的特征进行分类；\n",
    "5. 使用回归器精修候选框位置。\n",
    "\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic4.zhimg.com/80/v2-5a8f9899f6809d6b54fca95254f223ab_1440w.webp\n",
    "\" width=600>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图12-3 R-CNN流程。</div>\n",
    "</center>\n",
    "\n",
    "首先，我们利用Selective Search等方法在输入图片上获得1k～2k个候选区域。Selective Search是传统图像处理中的图像分层方法，首先在图像中生成一些初始的区域，再从颜色、纹理等角度对这些区域合并，从而获得最终分割的图像区域。接着，我们先把候选区域归一化成同一尺寸227×227，然后再训练一个CNN网络（如Alexnet、VGG、ResNet等）用于提取这些候选区域的特征。在此之后，对每一个类别我们训练一个SVM二分类器。我们将候选区域的特征与其对应的真实标签输入进SVM分类器中进行学习。经过SVM之后，我们会得到每一个候选区域是某个类别的概率值。因为一张图像出现有上千个物品的可能性微乎其微，所以必定有大量的候选区域是重叠的，所以我们需要去除冗余的候选框，作者在此处使用非极大值抑制（NMS）方法来去除冗余候选框。对于每一个类别，我们先挑选出概率值最大的候选框$A$，并计算其他候选框（如$B$）与该候选框的IoU。若IoU大于给定的阈值，则将对应的候选框（$B$）删去。重复上述过程，我们便得到了每一个类别得分最高的一些候选区域，如图 12-4 所示。\n",
    "\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic1.zhimg.com/80/v2-0ba2b8b03f9461a8776f959f83ab0548_1440w.jpg\n",
    "\" width=600>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图12-4 利用NMS去除冗余的候选区域。</div>\n",
    "</center>\n",
    "\n",
    "最后，我们再使用回归器对候选框位置调整。目标检测问题的衡量标准是重叠面积，许多看似准确的预测结果，往往因为候选框不够准确，重叠面积很小而被判定为假阳性。如图 12-5 所示，红色的框表示真实框的位置, 黑色的框为我们预测得到的候选框。即便黑色的框被分类器识别为猫，但是由于框定位不准(如IOU<0.5)，那么这张图像相当于没有正确的检测出猫。所以需要对黑色的框进行微调，使得经过微调后的窗口跟真实框更接近，这样就可以更准确的定位。因此，我们需要额外训练一个线性回归模型，对候选框的位置进行调整，这一过程被称为候选框回归。\n",
    "\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic3.zhimg.com/80/v2-a000dafdd9cf5cc628cc5a247040cc96_1440w.jpg\n",
    "\" width=300>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图12-5 训练一个线性回归模型对候选框的位置进行精调。</div>\n",
    "</center>\n",
    "\n",
    "接下来，我们来动手编写实现R-CNN。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aac890b2",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "9d48235d",
   "metadata": {},
   "source": [
    "### 12.3.2 Fast R-CNN"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40178b12",
   "metadata": {},
   "source": [
    "显而易见R-CNN存在着许多的问题：1. 对于一张图像中的每个候选区域都需要计算一次特征，而一张图像上通常有着2k个候选区域，计算效率低；2. R-CNN的整个流程是分离的三个部分域，整体是分立的，无法以端到端的方式来训练；3. SVM分类和候选框回归的结果无法反馈给之前的CNN模型中用以更新模型的参数。为了解决上述问题，Ross Girshick在15年推出Fast RCNN [8]。Fast R-CNN构思精巧，流程更为紧凑，大幅提升了目标检测的速度。使用相同最大规模的网络下，与R-CNN相比，Fast R-CNN训练时间从84小时减少为9.5小时，测试时间从47秒减少为0.32秒。\n",
    "\n",
    "\n",
    "Fast R-CNN整体流程如图 12-6 所示，可大致分为三个步骤：\n",
    "1. 对一张输入图像，使用Selective Search生成2K个候选区域；\n",
    "2. 对整张图像进行特征提取得到相应的特征图，并将生成的候选区域映射到特征图中；\n",
    "3. 使用“ROI Pooling”将所有的候选区域特征统一缩放到同一大小，然后将展平之后的特征连接到全连接层上，用于分类与候选框回归。\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic3.zhimg.com/80/v2-c3b7fc34daa7aeaf778e284d2fb1c742_1440w.jpg\n",
    "\" width=600>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图12-6 Fast R-CNN流程。</div>\n",
    "</center>\n",
    "\n",
    "\n",
    "相比于R-CNN，我们可以发现Fast R-CNN产生了了两个重大变化：1. 不再是对每一个候选区域单独提取特征，而是在提取整个图像的特征后，将每一个候选区域映射到特征图上；2. 整个训练流程是一个端到端的方式，不再像R-CNN分成很多部分分开训练。在Fast R-CNN中，我们首先将整张图像通过一个深度网络学习得到了对应的特征图，在此之后，我们将候选区域投影到特征图中。由于这些投影的候选区域大小不同，我们需要先将它们归一化到同一尺寸。这里，我们使用一个叫作“ROI pooling”的网络层来对这些不同大小的候选区域的投影特征图进行处理。简单来说，ROI Pooling层将每个候选区域均匀分成M×N块，对每块进行池化操作。虽然输入的图像尺寸不同，得到的特征图尺寸也不同，但是我们都可以通过ROI pooling层产生一样大小的ROI的特征图。获得了每个ROI的特征之后，我们将这些特征再通过两个全连接层进行区域分类和候选框回归。对于每个输入的ROI，训练的损失函数为:\n",
    "\n",
    "$$L\\left(p, u, t^{u}, v\\right)=L_{c l s}(p, u)+\\lambda[u \\geq 1] L_{l o c}\\left(t^{u}, v\\right)$$\n",
    "\n",
    "其中$p$是分类器预测的类别概率分布，$u$是对应的真实标签，$t^{u}$是回归器预测的类别$u$的候选框对应的参数（即x、y坐标与候选框的宽和高），$v$是对应的真实边界框的参数。很明显，损失函数由两个部分组成，一个是分类的损失，一个是候选框回归损失。\n",
    "\n",
    "接下来，我们来编写Fast R-CNN。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c8d4c2d7",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "53be84d2",
   "metadata": {},
   "source": [
    "### 12.3.3 Faster R-CNN"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21de947a",
   "metadata": {},
   "source": [
    "Fast R-CNN还存在一个显著的问题：Fast RCNN虽然将候选区域提取完成后所有步骤的一体化，但初始候选区域的提取仍然采用的是selective search，而这一方法非常的耗时，是目标检测实时化的一个重要阻碍。那么，我们有没有什么办法能够高效的选择候选区域呢？\n",
    "\n",
    "为了解决这一问题，继2015年推出Fast R-CNN之后，Ross Girshick团队在2015年又推出一力作：Faster R-CNN [9]，使简单网络目标检测速度达到17fps，在Pascal VOC上准确率为59.9%，复杂网络达到5fps，准确率78.8%。Faster R-CNN算是RCNN系列算法的最杰出产物，也是two-stage中最为经典的物体检测算法。\n",
    "\n",
    "Faster R-CNN的整体流程如图 12-7 所示，可大致分为两个步骤：\n",
    "\n",
    "1. 通过区域生成网络（Region Proposal Network，RPN）从图像表征中提取不同的候选区域；\n",
    "2. 对这些候选区域进行分类与候选框回归。\n",
    "\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic2.zhimg.com/80/v2-b84c8709eca08536b3e07598c494d7cd_1440w.jpg\n",
    "\" width=400>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图12-7 Faster R-CNN流程。</div>\n",
    "</center>\n",
    "\n",
    "不难发现，和Fast R-CNN相比，在Faster R-CNN中利用RPN直接从图像的表征中生成若干个候选区域。这一做法取代了之前的Selective Search方法，实现了端到端的训练模式。我们也可以简单的将Faster R-CNN理解成“RPN + Fast R-CNN”的形式。\n",
    "\n",
    "RPN网络是Faster RCNN的精髓所在，它将传统的候选区域生成方法使用卷积运算来代替，而且使用卷积来生成候选区域使得RPN具有一定的“注意力机制”，其框架如图 12-8 所示。首先，我们将一张图像的深度特征图输入进RPN网络，RPN假定了输入特征图上的每个位置都可在原图上对应着$k$个候选区域，如图 12-8 所示，我们以特征图每一个位置为中心画矩形框即可得到候选区域。同时为了体现出多尺度特性，我们使用了不同大小，不同比例的矩形框，这样即使同一物体在图中发生了形变或是缩放，仍然存在合适的候选区域能够框得住，我们把这$k$个候选区域被称作锚点。RPN网络在选择锚点的同时，还对其进行了分类与候选框回归。这里，我们只关注该锚点是前景（即含有需要识别的物体）还是背景。最后，RPN将挑选出的是前景的且已经经过候选框精修的候选区域输入给下一级网络。\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic1.zhimg.com/80/v2-6ac8980a5e6945af0028a2d86ea1310c_1440w.jpg\n",
    "\" width=500>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图12-8 RPN网络结构。</div>\n",
    "</center>\n",
    "\n",
    "接下来，我们来动手编写Faster R-CNN。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9c6265fa",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "da6465df",
   "metadata": {},
   "source": [
    "## 12.4 总结"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a92f9876",
   "metadata": {},
   "source": [
    "在这一章中，我们学习了目标检测的相关知识。我们回顾了目标检测的代表性模型——R-CNN家族的发展脉络，并一起动手实现了相应的功能。作为计算机视觉的重要任务，目标检测为图像分割动作识别等更复杂的计算机视觉任务奠定了基础。接下来，我们将学习图像语义理解中的另一个任务——实例分割。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4fa31d05",
   "metadata": {},
   "source": [
    "## 12.5 习题"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28be677c",
   "metadata": {},
   "source": [
    "1. 什么是目标检测？它和图像分类、语义分割有什么联系？\n",
    "2. 什么是Two-stage的目标检测算法？什么是One-stage算法？你能说出他们的代表性算法吗？\n",
    "3. 与R-CNN相比，Fast R-CNN解决了哪些问题？\n",
    "4. 与Fast R-CNN相比，Faster R-CNN解决了哪些问题？"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ceec9ccb",
   "metadata": {},
   "source": [
    "## 参考文献"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c2a46e9c",
   "metadata": {},
   "source": [
    "[1] Alex Krizhevsky, Ilya Sutskever, Geoffrey E. Hinton: ImageNet Classification with Deep Convolutional Neural Networks. NIPS 2012: 1106-1114\n",
    "\n",
    "[2] Karen Simonyan, Andrew Zisserman: Very Deep Convolutional Networks for Large-Scale Image Recognition. ICLR 2015\n",
    "\n",
    "[3] Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott E. Reed, Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, Andrew Rabinovich: Going deeper with convolutions. CVPR 2015: 1-9\n",
    "\n",
    "[4] Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun: Deep Residual Learning for Image Recognition. CVPR 2016: 770-778\n",
    "\n",
    "[5] Ross B. Girshick, Jeff Donahue, Trevor Darrell, Jitendra Malik: Rich Feature Hierarchies for Accurate Object Detection and Semantic Segmentation. CVPR 2014: 580-587\n",
    "\n",
    "[6] Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, Scott E. Reed, Cheng-Yang Fu, Alexander C. Berg:\n",
    "SSD: Single Shot MultiBox Detector. ECCV (1) 2016: 21-37\n",
    "\n",
    "[7] Joseph Redmon, Santosh Kumar Divvala, Ross B. Girshick, Ali Farhadi: You Only Look Once: Unified, Real-Time Object Detection. CVPR 2016: 779-788\n",
    "\n",
    "[8] Ross B. Girshick: Fast R-CNN. ICCV 2015: 1440-1448\n",
    "\n",
    "[9] Shaoqing Ren, Kaiming He, Ross B. Girshick, Jian Sun: Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks. NIPS 2015: 91-99\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
