Pytorch 目标检测和数据集

0. 环境介绍

小技巧:当遇到函数看不懂的时候可以按 Shift+Tab 查看函数详解。

1. 目标检测

1.1 概述

在图像分类任务中,我们假设图像中只有一个主要物体对象,我们只关注如何识别其类别。然而,很多时候图像里有多个我们感兴趣的目标,我们不仅想知道它们的类别,还想得到它们在图像中的具体位置。 在计算机视觉里,我们将这类任务称为目标检测(object detection)或目标识别(object recognition)。

1.2 边缘框

一个边缘框可以通过 4 个数字定义:

  • (左上 x,左上 y,右下 x,右下 y)或者(左上 x,左上 y,宽,高)或者(中心 x,中心 y,宽,高)等

pytorch目标检测 pytorch目标检测数据集_数据集


数据集标注的时候就是在画框框。

2. 数据集

每行表示一个物体

  • 图片文件名,物体类别,边缘框
    COCO(cocodataset.org
  • 80 物体,330K 图片,1.5M 物体

pytorch目标检测 pytorch目标检测数据集_深度学习_02

3. 代码

3.1 导入图片

!pip install -U d2l
%matplotlib inline
import torch
import os
import requests
from d2l import torch as d2l

从李沐老师的 GitHub 下载图片到本地:

if not os.path.exists('../data'):
    os.mkdir('../data')
url = 'https://raw.githubusercontent.com/d2l-ai/d2l-zh/master/img/catdog.jpg'
r = requests.get(url) 
with open('../data/catdog.jpg', 'wb') as f:
    f.write(r.content)
d2l.set_figsize()
img = d2l.plt.imread('../data/catdog.jpg')
d2l.plt.imshow(img)

pytorch目标检测 pytorch目标检测数据集_深度学习_03

3.2 边界框

我们定义在这两种表示法之间进行转换的函数:box_corner_to_center 从两角表示法转换为中心宽度表示法,而 box_center_to_corner 反之亦然。 输入参数 boxes 可以是长度为 pytorch目标检测 pytorch目标检测数据集_pytorch_04 的张量,也可以是形状为(pytorch目标检测 pytorch目标检测数据集_数据集_05pytorch目标检测 pytorch目标检测数据集_pytorch_04)的二维张量,其中 pytorch目标检测 pytorch目标检测数据集_数据集_05

def box_corner_to_center(boxes):
    """从(左上,右下)转换到(中间,宽度,高度)"""
    x1, y1, x2, y2 = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
    cx = (x1 + x2) / 2
    cy = (y1 + y2) / 2
    w = x2 - x1
    h = y2 - y1
    boxes = torch.stack((cx, cy, w, h), axis=-1)
    return boxes

def box_center_to_corner(boxes):
    """从(中间,宽度,高度)转换到(左上,右下)"""
    cx, cy, w, h = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
    x1 = cx - 0.5 * w
    y1 = cy - 0.5 * h
    x2 = cx + 0.5 * w
    y2 = cy + 0.5 * h
    boxes = torch.stack((x1, y1, x2, y2), axis=-1)
    return boxes

根据坐标信息定义图像中狗和猫的边界框,并通过转换两次来验证边界框转换函数的正确性:

# bbox是边界框的英文缩写
dog_bbox, cat_bbox = [60.0, 45.0, 378.0, 516.0], [400.0, 112.0, 655.0, 493.0]

boxes = torch.tensor((dog_bbox, cat_bbox))
box_center_to_corner(box_corner_to_center(boxes)) == boxes

pytorch目标检测 pytorch目标检测数据集_pytorch目标检测_08

定义一个辅助函数bbox_to_rect。 它将边界框表示成matplotlib的边界框格式:

def bbox_to_rect(bbox, color):
    # 将边界框(左上x,左上y,右下x,右下y)格式转换成matplotlib格式:
    # ((左上x,左上y),宽,高)
    return d2l.plt.Rectangle(
        xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1],
        fill=False, edgecolor=color, linewidth=2)

图像上添加边界框之后,我们可以看到两个物体的主要轮廓基本上在两个框内:

fig = d2l.plt.imshow(img)
fig.axes.add_patch(bbox_to_rect(dog_bbox, 'blue'))
fig.axes.add_patch(bbox_to_rect(cat_bbox, 'red'))

pytorch目标检测 pytorch目标检测数据集_pytorch_09

3.3 数据集

3.3.1 下载数据集

import pandas as pd
import torchvision

d2l.DATA_HUB['banana-detection'] = (
    d2l.DATA_URL + 'banana-detection.zip',
    '5de26c8fce5ccdea9f91267273464dc968d20d72')

3.3.2 读取数据集

该数据集包括一个 CSV 文件,内含目标类别标签和位于左上角和右下角的真实边界框坐标:

def read_data_bananas(is_train=True):
    """读取香蕉检测数据集中的图像和标签"""
    data_dir = d2l.download_extract('banana-detection')
    csv_fname = os.path.join(data_dir, 'bananas_train' if is_train
                             else 'bananas_val', 'label.csv')
    csv_data = pd.read_csv(csv_fname)
    csv_data = csv_data.set_index('img_name')
    images, targets = [], []
    for img_name, target in csv_data.iterrows():
        images.append(torchvision.io.read_image(
            os.path.join(data_dir, 'bananas_train' if is_train else
                         'bananas_val', 'images', f'{img_name}')))
        # 这里的target包含(类别,左上角x,左上角y,右下角x,右下角y),
        # 其中所有图像都具有相同的香蕉类(索引为0)
        targets.append(list(target))
    return images, torch.tensor(targets).unsqueeze(1) / 256

3.3.3 自定义 Dataset 类

class BananasDataset(torch.utils.data.Dataset):
    """一个用于加载香蕉检测数据集的自定义数据集"""
    def __init__(self, is_train):
        self.features, self.labels = read_data_bananas(is_train)
        print('read ' + str(len(self.features)) + (f' training examples' if
              is_train else f' validation examples'))

    def __getitem__(self, idx):
        return (self.features[idx].float(), self.labels[idx])

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

3.3.4 自定义数据加载器

为训练集和测试集返回两个数据加载器实例。对于测试集,无须按随机顺序读取它:

def load_data_bananas(batch_size):
    """加载香蕉检测数据集"""
    train_iter = torch.utils.data.DataLoader(BananasDataset(is_train=True),
                                             batch_size, shuffle=True)
    val_iter = torch.utils.data.DataLoader(BananasDataset(is_train=False),
                                           batch_size)
    return train_iter, val_iter

3.3.5 读取小批量

batch_size, edge_size = 32, 256
train_iter, _ = load_data_bananas(batch_size)
batch = next(iter(train_iter))
batch[0].shape, batch[1].shape

pytorch目标检测 pytorch目标检测数据集_深度学习_10


图像的小批量的形状为(批量大小,通道数,高度,宽度),与之前图像分类任务中的相同。

标签的小批量的形状为(批量大小,pytorch目标检测 pytorch目标检测数据集_pytorch_11pytorch目标检测 pytorch目标检测数据集_pytorch_12),其中 pytorch目标检测 pytorch目标检测数据集_pytorch_11 是数据集的任何图像中边界框可能出现的最大数量(本数据集中只有 1 个,所以设置为 1),其中 pytorch目标检测 pytorch目标检测数据集_pytorch_12 表示每个框的标号(种类)以及 pytorch目标检测 pytorch目标检测数据集_pytorch_04

3.3.6 演示

# permute 交换维度,torch 数值顺序:BCHW,NP 里是:BHWC
imgs = (batch[0][0:10].permute(0, 2, 3, 1)) / 255
axes = d2l.show_images(imgs, 2, 5, scale=2)
for ax, label in zip(axes, batch[1][0:10]):
    d2l.show_bboxes(ax, [label[0][1:5] * edge_size], colors=['w'])

pytorch目标检测 pytorch目标检测数据集_pytorch目标检测_16


对边框操作的时候要小心,注意是按照相对位置还是像素设置。

4. Q&A

Q:有哪些标注软件/平台推荐?
A:弹幕上说的:Labeling,Makesense,Vott 等。也可以自己标注,使用半监督学习,迁移学习等。