天天看点

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎

📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃

🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​

📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】  深度学习【DL】

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

 🖍foreword

✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。

如果你对这个系列感兴趣的话,可以关注订阅哟👋

文章目录

物体检测简介

对象检测方法

使用 YOLOv3 进行对象检测

YOLOv3 与 OpenCV 的代码示例

使用 Faster R-CNN 进行目标检测

区域提案网络

检测网络

使用 PyTorch 实现 Faster R-CNN

介绍图像分割

使用 U-Net 进行语义分割

使用 Mask R-CNN 进行实例分割

使用 PyTorch 实现 Mask R-CNN

概括

在第 3 章,高级卷积网络中,我们讨论了一些最流行和性能最好的卷积神经网络(CNN) 楷模。为了关注每个网络的架构细节,我们在分类问题的直接上下文中查看了模型。在计算机视觉任务的领域中,分类相当简单,因为它为图像分配了一个标签。在本章中,我们将把重点转移到两个更有趣的计算机视觉任务上——对象检测和语义分割,而网络架构将退居二线。可以说,这些任务比分类更复杂,因为模型必须对图像有更全面的理解。它必须能够检测不同的对象以及它们在图像上的位置。同时,任务的复杂性允许更多创造性的解决方案。在本章中,我们将讨论其中的一些。

本章将涵盖以下主题:

  • 物体检测简介:
  • 对象检测方法
  • YOLO
  • 更快的 R-CNN
  • 图像分割:
  • 网络
  • 掩码 R-CNN

物体检测简介

对象检测是在图像或视频中查找特定类别的对象实例的过程,例如人脸、汽车和树木。与分类不同,对象检测可以检测多个对象以及它们在图像中的位置。

对象检测器将返回检测到的对象列表,其中包含每个对象的以下信息:

  • 对象的类别(人、车、树等)。
  • [0, 1] 范围内的概率(或置信度分数),它传达了检测器对物体存在于该位置的置信度。这类似于常规分类器的输出。
  • 对象所在的图像矩形区域的坐标。这个 矩形 称为边界框。

我们可以在下面的照片中看到目标检测算法的典型输出。对象类型和置信度得分在每个边界框上方:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

物体检测器的输出

接下来,让我们概述解决对象检测任务的不同方法。

对象检测方法

在本节中,我们将概述 三种 方法:

  • 经典滑动窗口: 在这里,我们将 使用 常规分类网络(分类器)。这种方法可以用于任何类型的分类算法,但它相对较慢且容易出错:
    1. 构建图像金字塔:这是同一图像的不同比例的组合(见下图)。例如,每个缩放的图像可以比前一个小两倍。通过这种方式,我们将能够检测到物体,而不管它们在原始图像中的大小。
    2. 在整个图像上滑动分类器:也就是说,我们将使用图像的每个位置作为分类器的输入,结果将确定该位置中对象的类型。位置的边界框就是我们用作输入的图像区域。
    3. 我们将为每个对象有多个重叠的边界框:我们将使用一些启发式方法将它们组合在一个预测中。

这是滑动窗口方法的示意图:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

滑动窗口加图像金字塔物体检测

  • 两阶段检测方法: 这些方法 非常 准确,但相对较慢。顾名思义,它们涉及两个步骤:
    1. 一种特殊类型的 CNN,称为区域提议网络( RPN ),它扫描图像并提出许多可能的边界框,或感兴趣的区域 ( RoI ),对象可能位于这些区域。但是,该网络不检测对象的类型,而只检测该区域中是否存在对象。
    2. 感兴趣的区域被发送到第二阶段进行对象分类,该阶段确定每个边界框中的实际对象。
  • 单阶段(或单次)检测方法: 在这里,单个 CNN 生成对象类型和边界框。与两阶段方法相比,这些方法通常更快,但准确性较低。

在下一节中,我们将介绍 YOLO——一种准确但高效的单阶段检测算法。

使用 YOLOv3 进行对象检测

在本节中,我们将讨论一种最流行的检测算法,称为 YOLO。这个 名字 是流行格言的首字母缩写,你只活一次,它反映了算法的单阶段性质。作者发布 了三个 版本,对算法进行了增量改进。我们将只讨论最新的 v3(有关更多详细信息,请参阅YOLOv3:增量改进,https ://arxiv.org/abs/1804.02767 )。

该算法从称为Darknet-53的所谓骨干网络开始(在卷积层数之后)。它被训练来对 ImageNet 数据集进行分类,就像第 3 章,高级卷积网络中的网络一样。它是完全卷积的(没有池化层)并使用残差连接。

下图显示了主干架构:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

                         Darknet-53 模型(来源:https://arxiv.org/abs/1804.02767)

一旦网络经过训练,它将作为接下来的目标检测训练阶段的基础。这是特征提取迁移学习的一个案例,我们在第 2 章“理解卷积网络”中进行了描述。主干的全连接层被新的随机初始化的卷积和全连接层替换。新的全连接层将在一次传递中输出所有检测到的对象的边界框、对象类别和置信度分数。

例如,本节开头的人行横道上的人的图像中的边界框是使用单个网络通道生成的。YOLOv3 预测三个不同尺度的框。该系统使用与特征金字塔网络类似的概念从这些尺度中提取特征(有关更多信息,请参阅用于对象检测的特征金字塔网络,https://arxiv.org/abs/1612.03144)。在检测阶段,网络使用上下文中的常见对象(Microsoft COCO:Common Objects in Context,https : //arxiv.org/abs/1405.0312,http: //cocodataset.org)对象检测数据集进行训练。

接下来,让我们看看 YOLO 是如何工作的:

  1. 将图像分割成S × S单元格的网格(在下图中,我们可以看到一个 3 × 3 的网格):
    • 网络将每个网格单元的中心视为区域的中心,对象可能位于该区域的中心。
    • 一个对象可能完全位于一个单元格内。然后,它的边界框将小于单元格。或者,它可以跨越多个单元格,并且边界框会更大。YOLO 涵盖了这两种情况。
    • 该算法可以在锚框的帮助下检测网格单元格中的多个对象(稍后会详细介绍),但一个对象仅与一个单元格相关联(一对一的关系)。也就是说,如果对象的边界框覆盖多个单元格,我们会将对象与边界框中心所在的单元格相关联。例如,下图中的两个对象跨越多个单元格,但它们都被分配到中心单元格,因为它们的中心位于其中。
    • 一些单元格可能包含对象,而其他单元格可能不包含。我们只对那些这样做的人感兴趣。

下图显示了一个 3 × 3 单元格网格,其中包含 2 个对象及其边界框(虚线)。这两个对象都与中间单元格相关联,因为它们的边界框的中心位于该单元格中:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

具有 3x3 单元格网格和 2 个对象的对象检测 YOLO 示例

  1. Тhe 网络将为每个网格单元输出多个可能检测到的对象。例如,如果网格是 3 × 3,那么输出将包含 9 个可能检测到的对象。为了清楚起见,让我们讨论单个网格单元/检测到的对象的输出数据(及其相应的标签)。它是一个数组,其值为[b x , b y , b h , b w , p c , c 1 , c 2 , ..., c n ],其中的值如下:
    • b x , b y , b h , b w 描述物体边界框,如果物体存在,则b x和b y是框中心的坐标。它们相对于图像的大小在 [0, 1] 范围内进行归一化。也就是说,如果图像的大小为 100 x 100 并且b x = 20和b y = 50,它们的归一化值将是 0.2 和 0.5。基本上, b h和b w代表盒子的高度和宽度。 它们相对于网格单元进行了归一化。如果边界框大于单元格,则其值将大于 1。预测框参数是一个回归任务。
    • p c 是 [0, 1] 范围内的置信度分数。置信度分数的标签是 0(不存在)或 1(存在),这使得这部分输出成为分类任务。如果一个对象不存在,我们可以丢弃其余的数组值。
    • c 1 , c 2 , ..., c n 是对象类的 one-hot 编码。例如,如果我们有 car、person、tree、cat 和 dog 类,并且当前对象是 cat 类型,则其编码将为[0, 0, 0, 1, 0]。如果我们有n 个可能的类别,则一个单元格的输出数组的大小将为5 + n(在我们的示例中为 9)。

网络输出/标签将包含S × S 个这样的数组。例如,3 × 3 单元格网格和四个类别的 YOLO 输出长度为3*3*9 = 81。

  1. 让我们 解决在同一个单元格中有多个对象的场景。值得庆幸的是,YOLO 为这个问题提出了一个优雅的解决方案。我们将有多个候选框(称为锚框或先验),每个单元格的形状略有不同。在下图中,我们可以看到网格单元(正方形,不间断的线)和两个锚框——垂直和水平(虚线)。如果我们在同一个单元格中有多个对象,我们会将每个对象与一个锚框相关联。相反,如果锚框没有关联的对象,则其置信度得分为零。这种安排也会改变网络输出。每个网格单元有多个输出数组(每个锚框一个输出数组)。为了扩展我们之前的示例,假设我们有一个3 × 3的单元格网格,每个单元格有 4 个类和 2 个锚框。然后,我们将有3*3*2 = 18 个输出边界框,总输出长度为3*3*2*9 = 162。因为我们有固定数量的细胞(S × S ) 并且每个单元有固定数量的锚框,网络输出的大小不会随着检测到的对象的数量而变化。相反,输出将指示对象是否存在于所有可能的锚框中。

在下图中,我们可以看到一个带有两个锚框的网格单元:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

带有两个锚框(虚线)的网格单元(正方形,不间断的线)

现在唯一的问题是如何在训练期间为对象选择合适的锚框(在推理期间,网络将自行选择)。我们将在Intersection over Union ( IoU ) 的帮助下做到这一点。这只是对象边界框/锚框的交集面积与其并集面积之间的比率:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

联合交叉口

我们将每个对象的边界框与所有锚框进行比较,并将对象分配给具有最高 IoU 的锚框。由于锚框具有不同的大小和形状,IoU 确保将对象分配给与其在图像上的足迹最相似的锚框。

  1. 现在我们(希望)知道 YOLO 是如何工作的,我们可以用它来进行预测。然而,网络的输出可能是有噪声的——也就是说,输出包括每个单元格的所有可能的锚框,无论其中是否存在 对象 。许多框将重叠并实际上预测相同的对象。我们将使用非最大抑制来消除噪声。以下是它的工作原理:
    1. 丢弃所有置信度分数小于或等于 0.6 的边界框。
    2. 从剩余的边界框中,选择具有最高置信度分数的边界框。
    3. 用我们在上一步中选择的框丢弃任何 IoU >= 0.5 的框。

如果您担心网络输出/groundtruth 数据会变得过于复杂或庞大,请不要担心。CNN 与 ImageNet 数据集配合良好,该数据集有 1,000 个类别,因此有 1,000 个输出。

有关 YOLO 的更多信息,请查看论文的原始序列:

  • 你只看一次:统一的实时对象检测( https://arxiv.org/abs/1506.02640 ) Joseph Redmon、Santosh Divvala、Ross Girshick 和 Ali Farhadi
  • YOLO9000:更好、更快、更强( https://arxiv.org/abs/1612.08242 ) 作者 Joseph Redmon 和 Ali Farhadi
  • YOLOv3: Joseph Redmon 和 Ali Farhadi的增量改进( https://arxiv.org/abs/1804.02767 )

现在我们已经介绍了 YOLO 算法的理论,在下一节中,我们将讨论如何在实践中使用它。

YOLOv3 与 OpenCV 的代码示例

在本节中,我们将演示如何 将 YOLOv3 对象检测器与 OpenCV 结合使用。对于此示例,您需要opencv-python4.1.1 或更高版本以及 250 MB 的磁盘空间用于预训练的 YOLO 网络。让我们从以下步骤开始:

1.从导入开始:

import os.path 

import cv2   # opencv import 
import numpy as np 
import requests
           

2.添加一些样板代码,用于下载和存储多个配置和数据文件。我们将从 YOLOv3 网络配置yolo_config和开始weights,我们将使用它们来初始化net网络。我们将使用 YOLO 作者的 GitHub 和个人网站来做到这一点:

# 下载 YOLO 网络配置文件
# 我们会从 YOLO 作者的 github repo
yolo_config = 'yolov3.cfg'
if not os.path.isfile(yolo_config):
   url = 'https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3.cfg'
    r = requests.get(url)
    with open(yolo_config, 'wb') as f:
        f.write(r.content)

# 下载 YOLO 净权重
# 我们会从YOLO 作者的网站
yolo_weights = 'yolov3.weights'
if not os.path.isfile(yolo_weights):
    url = 'https://pjreddie.com/media/files/yolov3.weights'
    r = requests.get(url)
    with open(yolo_weights, 'wb') as f:
        f.write(r.content)

# 加载网络
net = cv2.dnn.readNet(yolo_weights, yolo_config)
           

3.接下来,我们将下载网络可以检测到的 COCO 数据集类的名称。我们还将从文件中加载它们。COCO 论文中提供的数据集包含 91 个类别。但是,网站上的数据集只包含 80 个。YOLO 使用的是 80-category 版本:

# Download class names file
# Contains the names of the classes the network can detect
classes_file = 'coco.names'
if not os.path.isfile(classes_file):
    url = 'https://raw.githubusercontent.com/pjreddie/darknet/master/data/coco.names'
    r = requests.get(url)
    with open(classes_file, 'wb') as f:
        f.write(r.content)

# load class names
with open(classes_file, 'r') as f:
    classes = [line.strip() for line in f.readlines()]
           

4.然后,从 Wikipedia 下载测试图像。我们还将从blob变量中的文件加载图像:

# Download object detection image
image_file = 'source_1.png'
if not os.path.isfile(image_file):
    url = "https://github.com/ivan-vasilev/advanced-deep-learning-with-python/blob/master/chapter04-detection-segmentation/source_1.png"
    r = requests.get(url)
    with open(image_file, 'wb') as f:
        f.write(r.content)

# read and normalize image
image = cv2.imread(image_file)
blob = cv2.dnn.blobFromImage(image, 1 / 255, (416, 416), (0, 0, 0), True, crop=False)
           

5.将图像输入网络并进行推理:

# set as input to the net
net.setInput(blob)

# get network output layers
layer_names = net.getLayerNames()
output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]

# inference
# the network outputs multiple lists of anchor boxes,
# one for each detected class
outs = net.forward(output_layers)
           

6.遍历类和锚框并为下一步做准备:

# extract bounding boxes
class_ids = list()
confidences = list()
boxes = list()

# iterate over all classes
for out in outs:
    # iterate over the anchor boxes for each class
    for detection in out:
        # bounding box
        center_x = int(detection[0] * image.shape[1])
        center_y = int(detection[1] * image.shape[0])
        w, h = int(detection[2] * image.shape[1]), int(detection[3] * image.shape[0])
        x, y = center_x - w // 2, center_y - h // 2
        boxes.append([x, y, w, h])

        # confidence
        confidences.append(float(detection[4]))

        # class
        class_ids.append(np.argmax(detection[5:]))
           

7.使用非最大抑制去除噪声。您可以尝试使用不同的值来查看检测到的对象如何变化: score_threshold nms_threshold

# non-max suppression
ids = cv2.dnn.NMSBoxes(boxes, confidences, score_threshold=0.75, nms_threshold=0.5)
           

8.在图像上绘制边界框及其标题:

for i in ids:
    i = i[0]
    x, y, w, h = boxes[i]
    class_id = class_ids[i]

    color = colors[class_id]

    cv2.rectangle(img=image,
                  pt1=(round(x), round(y)),
                  pt2=(round(x + w), round(y + h)),
                  color=color,
                  thickness=3)

    cv2.putText(img=image,
                text=f"{classes[class_id]}: {confidences[i]:.2f}",
                org=(x - 10, y - 10),
                fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                fontScale=0.8,
                color=color,
                thickness=2)
           

9.最后,我们可以使用以下代码显示检测到的对象:

cv2.imshow("Object detection", image)
cv2.waitKey()
           

如果一切顺利,此代码块将生成我们在对象检测简介部分开头看到的相同图像。

我们对 YOLO 的讨论到此结束。在下一节中,我们将介绍一个名为 Faster R-CNN 的两阶段目标检测器(R-CNN 代表带有 CNN 的区域)。

使用 Faster R-CNN 进行目标检测

在本节中,我们将讨论一种称为 Faster R-CNN 的两阶段目标检测算法(Faster R-CNN:Towards Real-Time Object Detection with Region Proposal Networks,https://arxiv.org/abs/1506.01497)。它是早期两阶段检测器 Fast R-CNN(https://arxiv.org/abs/1504.08083)和 R-CNN(用于准确对象检测和语义分割的丰富特征层次结构,https://arxiv. org/abs/1311.2524)。

我们将首先概述 Faster R-CNN 的一般结构,如下图所示:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

Faster R-CNN的结构;来源:https://arxiv.org/abs/1506.01497

在我们解释算法时,让我们记住这个数字。与 YOLO 一样,Faster R-CNN 从在 ImageNet 上训练的骨干分类网络开始,该网络作为模型不同模块的基础。论文作者对 VGG16 和 ZF 网络(Visualizing and Understanding Convolutional Networks , https://cs.nyu.edu/~fergus/papers/zeilerECCV2014.pdf )主干进行了实验。然而,最近的实现使用了更现代的架构,例如 ResNets。主干网络充当模型的其他两个组件——区域提议网络(RPN)和检测网络的主干(明白吗?)。在下一节中,我们将讨论 RPN。

区域提案网络

在第一阶段,RPN 将图像(任意大小)作为输入,并将输出一组可能位于对象的矩形感兴趣区域 (RoI)。RPN 本身是通过使用主干模型的第一个p(VGG 为 13,ZF net 为 5 )卷积层创建的(参见上图)。一旦输入图像传播到最后一个共享卷积层,算法就会获取该层的特征图,并在特征图的每个位置上滑动另一个小网络。小网络输出对象是否存在于任何k个每个位置上的锚框(锚框的概念与 YOLO 中的相同)。下图的左侧图像说明了这个概念,该图显示了RPN 的单个位置在最后一个卷积层的单个特征图上滑动:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

 左:单个位置的 RPN 提案;右图:使用 RPN 提议的示例检测(标签被人为增强)。来源: https ://arxiv.org/abs/1506.01497

小网络完全连接到 所有输入特征图上相同位置的n × n区域(根据论文,n = 3 ) 。例如,如果最终卷积层有 512 个特征图,那么一个位置的小网络输入大小为 512 x 3 x 3 = 4,608。每个滑动窗口都映射到较低维(VGG 为 512,ZF 网络为 256)向量。该向量本身用作以下两个并行全连接层的输入:

1.具有2k个单元的分类层,组织成k个 2 单元二进制 softmax 输出。每个 softmax 的输出表示对象是否位于k个锚框中的每一个中的置信度分数。论文将置信度分数称为objectness,它衡量anchor box内容是否属于一组对象与背景。在训练过程中,与 YOLO 中一样,根据 IoU 公式将对象分配给锚框。

2.具有4k个单元的回归层,组织成k个 4 单元的 RoI 坐标。4个单位中的2个表示[0:1]范围内RoI中心相对于整个图像的坐标。其他两个坐标代表区域的高度和宽度,相对于整个图像(同样,类似于 YOLO)。

该论文的作者尝试了三种尺度和三种纵横比,在每个位置上产生了九个可能的锚框。最终特征图的典型 H × W 大小约为 2,400,即 2,400*9 = 21,600 个锚框。

理论上,我们将小网络滑过最后一个卷积层的特征图。然而,小净重沿所有位置共享。因此,滑动可以实现为跨通道卷积。因此,网络可以在单个图像通道中为所有锚框生成输出。这是对 Fast R-CNN 的改进,Fast R-CNN 需要对每个锚框进行单独的网络传递。

RPN 通过反向传播和随机梯度下降进行训练(真是令人惊讶!)。共享卷积层使用主干网络的权重进行初始化,其余的则随机初始化。每个小批量的样本都是从单个图像中提取的,该图像包含许多正(对象)和负(背景)锚框。两种类型的采样比例为 1:1。每个锚点都分配有一个二进制类标签(是否为对象)。有两种带有正标签的锚点:IoU 最高的anchor/anchors 与groundtruth box 重叠,或者与任何groundtruth box 的IoU 重叠高于0.7 的anchor。如果anchor的IoU比率低于0.3,则为该框分配负标签。既不是正面也不是负面的锚不参与训练。

由于 RPN 有两个输出层(分类和回归),因此训练使用以下复合成本函数:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

让我们详细讨论一下:

  • i是小批量中锚点的索引。
  • p i是分类输出,代表anchor i作为对象的预测概率。注意p i *是相同的目标数据(0 或 1)。
  • t i是大小为 4 的回归输出向量,表示 RoI 参数。与 YOLO 一样, t i *是相同的目标向量。
  • L cls是分类层的交叉熵损失。N cls 是一个归一化项,等于小批量大小。
  • L reg是回归损失。
    【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括
    ,其中 R 是平均绝对误差(参见第 1 章中的成本函数部分 ,神经网络的基本要素)。N reg是一个归一化项,等于锚位置的总数(大约 2,400)。

 最后,在λ参数的帮助下,将成本函数的分类和回归分量结合起来。由于N reg ~ 2400 和N cls = 256,λ设置为 10 以保持两个损失之间的平衡。

检测网络

现在我们已经讨论了 RPN,让我们关注检测网络。为此,我们将回到“使用 Faster R-CNN 进行对象检测”部分开头的图表Faster R-CNN 的结构。让我们回想一下,在第一阶段,RPN 已经生成了 RoI 坐标。检测网络是一个常规分类器,它确定当前 RoI 中对象(或背景)的类型。RPN 和检测网络共享它们的第一个卷积层,从主干网络借用。但是检测网络还结合了来自 RPN 的建议区域,以及最后一个共享层的特征图。

但是我们如何组合输入呢?我们可以借助感兴趣区域( RoI ) 最大池化来做到这一点,它是检测网络第二部分的第一层。下图显示了此操作的示例:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

具有 10 × 7 特征图和 5 × 5 感兴趣区域(蓝色矩形)的2 × 2 RoI 最大池化示例

为了简单起见,我们假设我们有一个10 × 7的特征图和一个 RoI。正如我们在Region proposal network部分中了解到的,RoI 由其坐标、宽度和高度定义。该操作将这些参数转换为特征图上的实际坐标。在此示例中,区域大小为h × w = 5 × 5。RoI 最大池进一步由其输出高度H和宽度W定义。在这个例子中,H × W = 2 × 2,但实际上值可能更大,例如 7 ×7. 该操作将h × w RoI 拆分为具有 ( h / H) × (w / W)子区域的网格。

正如我们从示例中看到的,子区域可能具有不同的大小。完成此操作后,通过取该区域的最大值,将每个子区域下采样到单个输出单元。换句话说,RoI pooling 可以将任意大小的输入转换为固定大小的输出窗口。通过这种方式,转换后的数据可以以一致的格式在网络中传播。

正如我们在使用 Faster R-CNN的对象检测部分中提到的,RPN 和检测网络共享它们的初始层。然而,他们以独立的网络开始他们的生活。培训在四步过程中在两者之间交替进行:

  1. 训练 RPN,它使用主干网络的 ImageNet 权重进行初始化。
  2. 使用来自步骤 1的新训练的 RPN 的建议来训练检测网络。训练还从 ImageNet 主干网络的权重开始。此时,两个网络不共享权重。
  3. 使用检测网络共享层来初始化 RPN 的权重。然后,再次训练 RPN,但冻结共享层并仅微调 RPN 特定层。这两个网络现在共享权重。
  4. 通过冻结共享层和仅微调检测网络特定层来训练检测网络。

现在我们已经介绍了 Faster R-CNN,在下一节中,我们将讨论如何借助预训练的 PyTorch 模型在实践中使用它。

使用 PyTorch 实现 Faster R-CNN

在本节中,我们将使用带有 ResNet50 主干的预训练 PyTorch Faster R-CNN 进行对象检测。此示例需要 PyTorch 1.3.1、0.4.2torchvision和python-opencv4.1.1:

1.我们将从导入开始:

import os.path

import cv2
import numpy as np
import requests
import torchvision
import torchvision.transforms as transforms
           

2.接下来,我们将继续下载输入图像,并在 COCO 数据集中定义类名。此步骤与我们在YOLOv3 with OpenCV 的 A 代码示例部分中实现的步骤相同。下载图像的路径存储在 image_file = 'source_2.png'变量中,类名存储在classes列表中。此实现使用完整的 91 个 COCO 类别。

3.我们将加载预训练的 Faster R-CNN 模型,并将其设置为评估模式:

# load the pytorch model
model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True)

# set the model in evaluation mode
model.eval()
           

4.然后,我们将使用 OpenCV 读取图像文件:

img = cv2.imread(image_file)
           

5.我们将定义 PyTorchtransform 序列,将图像转换为 PyTorch 兼容的张量,并将其馈送到网络。网络输出存储在output变量中。正如我们在区域提议网络部分中讨论的那样,output它包含三个组件:boxes边界框参数、classes对象类和scores置信度分数。模型内部应用了NMS,代码中不需要做:

transform = transforms.Compose([transforms.ToPILImage(), transforms.ToTensor()])
nn_input = transform(img)
output = model([nn_input])
           

6.在继续显示检测到的对象之前,我们将为 COCO 数据集的每个类定义一组随机颜色:

colors = np.random.uniform(0, 255, size=(len(classes), 3))
           

7.我们遍历每个边界框并将其绘制在图像上:

# iterate over the network output for all boxes
for box, box_class, score in zip(output[0]['boxes'].detach().numpy(),
                                 output[0]['labels'].detach().numpy(),
                                 output[0]['scores'].detach().numpy()):

    # filter the boxes by score
    if score > 0.5:
        # transform bounding box format
        box = [(box[0], box[1]), (box[2], box[3])]

        # select class color
        color = colors[box_class]

        # extract class name
        class_name = classes[box_class]

        # draw the bounding box
        cv2.rectangle(img=img, pt1=box[0], pt2=box[1], color=color, thickness=2)

        # display the box class label
        cv2.putText(img=img, text=class_name, org=box[0], 
                    fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=color, thickness=2)
           

绘制边界框涉及以下步骤:

  • 过滤置信度分数小于 0.5 的框以防止噪声检测。
  • 边界box参数(从 中提取)包含图像上边界框的左上角和右下角绝对(像素)坐标。它们仅在元组中进行转换以适应 OpenCV 格式。 output['boxes']
  • 提取边界框的类名和颜色。
  • 绘制边界框和类名。

8.最后,我们可以用下面的代码展示检测结果:

cv2.imshow("Object detection", image)
cv2.waitKey()
           

此代码将产生以下结果(还检测到公交车上的乘客):

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

更快的 R-CNN 对象检测

关于对象检测的部分到此结束。总而言之,我们讨论了两个最流行的检测模型——YOLO 和 Faster R-CNN。在下一节中,我们将讨论图像分割——您可以将其视为像素级别的分类。

介绍图像分割

图像 分割 是为图像的每个像素 分配 类别标签(例如人、汽车或树)的过程。您可以将其视为分类,但在像素级别上——我们将分别对每个像素进行分类,而不是将整个图像分类在一个标签下。有两种类型的分段:

  • 语义分割:这为每个像素分配一个类,但不区分对象实例。例如,以下屏幕截图中的中间图像显示了一个语义分割掩码,其中每辆车的像素具有相同的值。语义分割可以告诉我们一个像素是车辆的一部分,但不能区分两辆车。
  • 实例分割:这为每个像素分配一个类并区分对象实例。例如,以下屏幕截图中右侧的图像显示了一个实例分割掩码,其中每辆车都被分割为一个单独的对象。

以下屏幕截图显示了语义和实例分割的示例:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

左:输入图像;中:语义分割;右:实例分割;来源:http://sceneparsing.csail.mit.edu/

为了训练 分割 算法,我们需要一种特殊类型的真实数据,其中每张图像的标签都是图像的分割版本。

分割图像的最简单方法是使用熟悉的滑动窗口技术,我们在“目标检测方法”部分中对此进行了描述。也就是说,我们将使用常规分类器,并以步长 1 向任一方向滑动它。在获得位置预测后,我们将获取位于输入区域中间的像素,然后我们将其分配给预测的类。可以预见的是,由于图像中有大量像素(即使是 1024 × 1024 的图像也有超过 100 万像素),这种方法非常慢。值得庆幸的是,有更快、更准确的算法,我们将在以下部分讨论。

使用 U-Net 进行语义分割

我们将讨论的第一种分割方法称为 U-Net(U-Net:用于生物医学图像分割的卷积网络,https ://arxiv.org/abs/1505.04597 )。这个名字来源于网络架构的可视化。U-Net 是一种全卷积网络(FCN),之所以这么称呼是因为它只包含卷积层,没有任何全连接层。FCN 将整个图像作为输入,并一次输出其分割图。我们可以将 FCN 分成两个虚拟组件(实际上,这只是一个网络):

  • 编码器是网络的第一部分。它类似于常规的 CNN,最后没有完全连接的层。编码器的作用是学习输入图像的高度抽象的表示(这里没有什么新东西)。
  • 解码器是网络的第二部分。它在编码器之后开始并将其用作输入。解码器的作用是将这些抽象表示转换为分段的真实数据。为此,解码器使用与编码器相反的操作。这包括转置卷积(与卷积相反)和反池化(与池化相反)。

有了这个介绍,这就是 U-Net 的所有荣耀:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

U-Net 架构;来源:https://arxiv.org/abs/1505.04597

每个蓝色 框对应一个多通道特征图。通道数在框的顶部表示,特征图大小在框的左下角。白框代表复制的特征图。箭头表示不同的操作(也显示在图例上)。U的左边部分是编码器,右边部分是解码器。

接下来,让我们分割(明白了吗?)U-Net 模块:

  • 编码器:网络将 572 × 572 RGB 图像作为输入。从那里开始,它像常规的 CNN 一样继续,具有交替的卷积层和最大池化层。编码器由以下层的四个块组成。
    • 两个连续的跨通道未填充的 3 × 3 卷积,步幅为 1。
    • 一个 2 × 2 最大池化层。
    • ReLU 激活。
    • 每个下采样步骤都会使特征图的数量翻倍。
    • 最终的编码器卷积以 1,024 个 28 × 28 特征图结束。
  • 解码器:这与编码器对称。解码器采用最里面的 28 × 28 特征图,同时上采样并将它们转换为 388 × 388 的分割图。它包含四个上采样块:
    • 上采样使用步长为 2 的 2 × 2 转置卷积(第 2 章,理解卷积网络),用绿色垂直箭头表示。
    • 每个上采样步骤的输出与相应编码器步骤的裁剪高分辨率特征图(灰色水平箭头)连接。裁剪是必要的,因为在每个卷积中都会丢失边界像素。
    • 每个转置卷积后跟两个常规卷积以平滑扩展图像。
    • 上采样步骤将特征图的数量减半。最终输出使用 1×1 瓶颈卷积将 64 分量的特征图张量映射到所需的类数。该论文的作者已经证明了细胞医学图像的二元分割。
    • 网络输出是每个像素上的 softmax。也就是说,输出包含与像素数一样多的独立 softmax 操作。一个像素的 softmax 输出决定了像素类别。U-Net 像常规分类网络一样被训练。然而,成本函数是 softmax 输出在所有像素上的交叉熵损失的组合。

我们可以看到,由于网络的有效(未填充)卷积,输出分割图小于输入图像(388 对 572)。但是,输出地图不是输入图像的重新缩放版本。相反,它与输入相比具有一对一的比例,但仅覆盖输入图块的中心部分。

如下图所示:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

用于分割大图像的重叠平铺策略;来源: https ://arxiv.org/abs/1505.04597

未填充的卷积是必要的,因此网络不会在分割图的边界处产生噪声伪影。这使得使用所谓的重叠平铺策略分割具有任意大尺寸的图像成为可能。输入图像被分割成重叠的输入图块,如上图左侧的图块。右图小光区的分割图需要左图大光区(一瓦片)作为输入。

下一个输入图块与前一个图块重叠,使得它们的分割图覆盖图像的相邻区域。为了预测图像边界区域的像素,通过镜像输入图像来推断缺失的上下文。在下一节中,我们将讨论 Mask R-CNN——一个模型,它扩展了 Faster R-CNN 的实例分割。

使用 Mask R-CNN 进行实例分割

Mask R-CNN ( https://arxiv.org/abs/1703.06870 ) 是 Faster R-CNN 用于实例分割的扩展。Faster R-CNN 对每个候选对象都有两个输出:边界框参数和类标签。除了这些,Mask R-CNN 添加了第三个输出——一个 FCN,它为每个 RoI 生成一个二进制分割掩码。下图展示了Mask R-CNN的结构:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

掩码 R-CNN

RPN 产生五个尺度和三个纵横比的锚点。分割和分类路径都使用 RPN 的 RoI 预测,但在其他方面相互独立。分割路径产生I m×m二进制分割掩码,每个I类一个。在训练或推理时,只考虑与分类路径的预测类相关的掩码,其余的被丢弃。类预测和分割是并行解耦的——分类路径预测被分割对象的类,分割路径决定掩码。

Mask R-CNN 用更准确的 RoI 对齐层代替了 RoI 最大池化操作。RPN 将锚框中心及其高度和宽度输出为四个浮点数。然后,RoI 池化层将它们转换为整数特征图单元坐标(量化)。此外,将 RoI 划分为H × W bins 还涉及量化。Object detection with Faster R-CNN部分的 RoI 示例显示 bin 具有不同的大小(3 × 3、3 × 2、2 × 3、2 ×2)。这两个量化级别可能会在 RoI 和提取的特征之间引入错位。下图展示了 RoI 对齐如何解决这个问题:

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

投资回报率对齐示例;来源:https://arxiv.org/abs/1703.06870

虚线表示特征图单元。中间有实线的区域是覆盖在特征图上的2 × 2 RoI。请注意,它与单元格不完全匹配。相反,它是根据没有量化的RPN预测定位的。同样,RoI 的一个单元格(黑点)与特征图的一个特定单元格不匹配。RoI 对齐操作使用相邻单元的双线性插值计算 RoI 单元的值。这样,RoI align 比 RoI pooling 更准确。

在训练时,如果 RoI 的 IoU 与 groundtruth box 至少为 0.5,则为 RoI 分配正标签,否则为负标签。掩码目标是 RoI 与其关联的真实掩码之间的交集。只有正的 RoI 参与分割路径训练。

使用 PyTorch 实现 Mask R-CNN

在本节中,我们将使用带有 ResNet50 主干的预训练 PyTorch Mask R-CNN 进行实例分割。此示例需要 PyTorch 1.1.0、torchvision 0.3.0 和 OpenCV 3.4.2。此示例与我们在使用 PyTorch 实现更快的 R-CNN部分中实现的示例非常相似。因此,我们将省略部分代码以避免重复。开始吧:

1.导入、classes和image_file与 Faster R-CNN 示例中的相同。

2.这两个示例之间的第一个区别是我们将加载 Mask R-CNN 预训练模型:

model = torchvision.models.detection.maskrcnn_resnet50_fpn(pretrained=True)
model.eval()
           

3.我们将输入图像输入网络并获得output变量:

# read the image file
img = cv2.imread(image_file)

# transform the input to tensor
transform = transforms.Compose([transforms.ToPILImage(), transforms.ToTensor()])
nn_input = transform(img)
output = model([nn_input])
           

此外boxes,classes和scores,output包含预测分割掩码的附加masks组件。

4.我们迭代蒙版并将它们覆盖在图像上。图像和掩码是numpy数组,我们可以将叠加实现为矢量操作。我们将同时显示边界框和分割掩码:

# iterate over the network output for all boxes
for mask, box, score in zip(output[0]['masks'].detach().numpy(),
                            output[0]['boxes'].detach().numpy(),
                            output[0]['scores'].detach().numpy()):

    # filter the boxes by score
    if score > 0.5:
        # transform bounding box format
        box = [(box[0], box[1]), (box[2], box[3])]

        # overlay the segmentation mask on the image with random color
        img[(mask > 0.5).squeeze(), :] = np.random.uniform(0, 255, size=3)

        # draw the bounding box
        cv2.rectangle(img=img,
                      pt1=box[0],
                      pt2=box[1],
                      color=(255, 255, 255),
                      thickness=2)
           

5.最后,我们可以显示分割结果如下:

cv2.imshow("Object detection", img)
cv2.waitKey()
           

这个例子会产生右边的图像如下(左边的原图是为了对比):

【DL】第 4 章:目标检测和图像分割物体检测简介对象检测方法使用 YOLOv3 进行对象检测YOLOv3 与 OpenCV 的代码示例使用 Faster R-CNN 进行目标检测区域提案网络检测网络使用 PyTorch 实现 Faster R-CNN介绍图像分割使用 U-Net 进行语义分割使用 Mask R-CNN 进行实例分割使用 PyTorch 实现 Mask R-CNN概括

Mask R-CNN 实例分割

我们可以看到,每个分割掩码仅在其边界框内定义,其中分割掩码的所有值都大于零。为了获得属于对象的实际像素,我们仅对分割置信度分数大于 0.5 的像素应用掩码(此代码片段是 Mask R-CNN 代码示例的第 4 步的一部分):

img[(mask > 0.5).squeeze(), :] = np.random.uniform(0, 255, size=3)
           

本章专门讨论图像分割的部分到此结束(实际上,它结束了本章本身)。

概括

在本章中,我们讨论了对象检测和图像分割。我们从 one-shot 检测算法 YOLO 开始,然后我们继续使用两阶段 Faster R-CNN 算法。接下来,我们讨论了语义分割网络架构 U-Net。最后,我们讨论了 Mask R-CNN——Faster R-CNN 的扩展,用于实例分割。

在下一章中,我们将探索称为生成模型的新型ML 算法。我们可以使用它们来生成新的内容,例如图像。请继续关注——它会很有趣!

继续阅读