41 物体检测和数据集【动手学深度学习v2】

在视觉领域,目标检测比图片分类问题要广泛的多。
二者区别:
目标检测除了要对目标进行分类,还要标出目标所在位置。

边缘框:一个边缘框可以用四个数字来表示。标注物体边缘框的工作通常是人工进行的。所以物体识别的数据集通常要比图片分类要小很多,因为标注成本太高

目标检测数据集常见结构:
文本文件,每行表示一个物体,数据通常由图片文件名、物体类别和边缘框组成。(当一个图片里有多个物体时,这些物体数据中的图片文件名是相同的)
目标检测常用数据集:COCO


代码实现
(图片在img文件夹里,直接引用就可以了,slides是没有img文件夹的,你可以去d2l里copy一下)
%matplotlib inline import torch from d2l import torch as d2l d2l.set_figsize() img = d2l.plt.imread('../img/catdog.jpg') d2l.plt.imshow(img);
在这里,我们定义在这两种表示法之间进行转换的函数:box_corner_to_center
从两角表示法转换为中心宽度表示法,而box_center_to_corner
反之亦然。 输入参数boxes
可以是长度为4的张量,也可以是形状为(n,4)的二维张量,其中n是边界框的数量。
#@save 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 #@save 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
根据坐标信息定义图像中狗和猫的边界框。 图像中坐标的原点是图像的左上角,向右的方向为x轴的正方向,向下的方向为y轴的正方向。
# bbox是边界框的英文缩写 dog_bbox, cat_bbox = [60.0, 45.0, 378.0, 516.0], [400.0, 112.0, 655.0, 493.0]
将边界框在图中画出,以检查其是否准确。 画之前,我们定义一个辅助函数bbox_to_rect
。 它将边界框表示成matplotlib
的边界框格式。
#@save 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'));

目标检测数据集没有很小的数据集,所以训练起来比较慢。
下载数据集
包含所有图像和CSV标签文件的香蕉检测数据集可以直接从互联网下载。
MXNET
PYTORCH
PADDLE
%matplotlib inline import os import pandas as pd import torch import torchvision from d2l import torch as d2l #@save d2l.DATA_HUB['banana-detection'] = ( d2l.DATA_URL + 'banana-detection.zip', '5de26c8fce5ccdea9f91267273464dc968d20d72')
读取数据集
通过read_data_bananas
函数,我们读取香蕉检测数据集。 该数据集包括一个的CSV文件,内含目标类别标签和位于左上角和右下角的真实边界框坐标。
#@save 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
通过使用read_data_bananas
函数读取图像和标签,以下BananasDataset
类别将允许我们创建一个自定义Dataset
实例来加载香蕉检测数据集。
#@save 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)
最后,我们定义load_data_bananas
函数,来为训练集和测试集返回两个数据加载器实例。对于测试集,无须按随机顺序读取它。
#@save 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
让我们读取一个小批量,并打印其中的图像和标签的形状。 图像的小批量的形状为(批量大小、通道数、高度、宽度),看起来很眼熟:它与我们之前图像分类任务中的相同。 标签的小批量的形状为(批量大小,m,5),其中m
是数据集的任何图像中边界框可能出现的最大数量。
小批量计算虽然高效,但它要求每张图像含有相同数量的边界框,以便放在同一个批量中。 通常来说,图像可能拥有不同数量个边界框;因此,在达到m之前,边界框少于m的图像将被非法边界框填充。 这样,每个边界框的标签将被长度为5的数组表示。 数组中的第一个元素是边界框中对象的类别,其中-1表示用于填充的非法边界框。 数组的其余四个元素是边界框左上角和右下角的(x,y)坐标值(值域在0~1之间)。 对于香蕉数据集而言,由于每张图像上只有一个边界框,因此m=1。
batch_size, edge_size = 32, 256 train_iter, _ = load_data_bananas(batch_size) batch = next(iter(train_iter)) batch[0].shape, batch[1].shape
Downloading ../data/banana-detection.zip from http://d2l-data.s3-accelerate.amazonaws.com/banana-detection.zip... read 1000 training examples read 100 validation examples (torch.Size([32, 3, 256, 256]), torch.Size([32, 1, 5]))
展示10幅带有真实边界框的图像。 我们可以看到在所有这些图像中香蕉的旋转角度、大小和位置都有所不同。 当然,这只是一个简单的人工数据集,实践中真实世界的数据集通常要复杂得多。
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'])

知识补充: