Pytorch 目标检测和数据集
0. 环境介绍
小技巧:当遇到函数看不懂的时候可以按 Shift+Tab
查看函数详解。
1. 目标检测
1.1 概述
在图像分类任务中,我们假设图像中只有一个主要物体对象,我们只关注如何识别其类别。然而,很多时候图像里有多个我们感兴趣的目标,我们不仅想知道它们的类别,还想得到它们在图像中的具体位置。 在计算机视觉里,我们将这类任务称为目标检测(object detection)或目标识别(object recognition)。
1.2 边缘框
一个边缘框可以通过 4 个数字定义:
- (左上 x,左上 y,右下 x,右下 y)或者(左上 x,左上 y,宽,高)或者(中心 x,中心 y,宽,高)等
数据集标注的时候就是在画框框。
2. 数据集
每行表示一个物体
- 图片文件名,物体类别,边缘框
COCO(cocodataset.org) - 80 物体,330K 图片,1.5M 物体
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)
3.2 边界框
我们定义在这两种表示法之间进行转换的函数:box_corner_to_center
从两角表示法转换为中心宽度表示法,而 box_center_to_corner
反之亦然。 输入参数 boxes
可以是长度为 的张量,也可以是形状为(,)的二维张量,其中
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
定义一个辅助函数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'))
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
图像的小批量的形状为(批量大小,通道数,高度,宽度),与之前图像分类任务中的相同。
标签的小批量的形状为(批量大小,,),其中 是数据集的任何图像中边界框可能出现的最大数量(本数据集中只有 1 个,所以设置为 1),其中 表示每个框的标号(种类)以及
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'])
对边框操作的时候要小心,注意是按照相对位置还是像素设置。
4. Q&A
Q:有哪些标注软件/平台推荐?
A:弹幕上说的:Labeling,Makesense,Vott 等。也可以自己标注,使用半监督学习,迁移学习等。