目标检测(一)数据集处理与读取

目标检测(一)数据集处理与读取

  • 开始
  • 一· VOC数据集简介
  • 二· Dataloader的构建
    • 1.对于pytorch自带的数据集
    • 2.需要自己构建的数据集
      • 数据集准备
      • 构建dataloader
  • 总结

开始

账号注册的比较晚,本来准备好好记录平时的一些有价值的问题,结果一直没写,这次为了督促自己学习,报名参加了Datawhale12月组队学习,以后会开始坚持记录,这几篇虽是打卡但尽量不限于组队学习的内容

内容参考:Datawhale Task01: 两个年轻人-目标检测基础和VOC数据集

一· VOC数据集简介

做深度学习目标检测都会接触到VOC这个数据集,一般很少用到整个数据集,但是一般都会按照它的格式准备自己的数据集。VOC数据集是目标检测领域最常用的标准数据集之一,几乎所有检测方向的论文,如faster_rcnn、yolo、SSD等都会给出其在VOC数据集上训练并评测的效果。下面是下载VOC数据集后的目录构成以及各个文件夹的内容格式:

VOC2012
├── Annotations	# 每张图片相关的标注信息,xml格式
├── ImageSets
│   ├── Layout	# train,valid,test和train+valid数据集的文件名
│   ├── Main	# 各个类别所在图片的文件名
│   └── Segmentation	# 分割所用数据集的文件名
├── JPEGImages	# 包括训练验证测试用到的所有图片   
├── SegmentationClass	# 存放语义分割相关图片
└── SegmentationObject	# 存放实例分割相关图片

如下图为Annotations中xml文件中包裹的图片标注信息
目标检测(一)数据集处理与读取_第1张图片

二· Dataloader的构建

torch.utils.data.dataset这样的抽象类可以用来创建数据集。因为抽象类不能实例化,因此我们需要构造这个抽象类的子类来创建数据集。类中最重要的就是len和getitem这两个函数,前者给出数据集的大小,后者是用于查找数据和标签。torch.utils.data.DataLoader是一个迭代器,方便我们去多线程地读取数据,并且可以实现batch以及shuffle的读取等。

对pytorch读取数据一般化的流程:
图像数据 ➡ 图像索引文件 ➡ 使用Dataset构建数据集 ➡ 使用DataLoader读取数据

1.对于pytorch自带的数据集

pytorch帮你下载好了数据并制作了数据标签,然后通过使用Dataset和DataLoader两个类完成了数据集的构建和读取
以MNIST 手写数字数据集为例:

train_dataset = datasets.FashionMNIST(
	root='../datasets', # 数据加载后保存的相对目录
	train=True, # 是否加载数据库的训练集,false的时候加载测试集
	transform=transforms.ToTensor(), # 是否需要对数据做预处理,None表示不需要
	download=True)  # 是否自动下载数据集
  
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)

2.需要自己构建的数据集

除了pytorch自带的数据集外,在实际应用中,我们可能还需要从其他各种不同的数据集或自己构建的数据集(将其统称为自定义数据集)中读取图像,这些图像可能来自于开源数据集网站,也可能是我们自己整理得到的。对于这样的图像数据:首先,我们要确定是否包含标签文件,如果没有就要自己先创建标签文件;然后,我们就可以使用pytorch来读取数据集。

数据集准备

针对VOC数据集,datawhale准备了一个预处理脚本create_data_lists.py。该脚本的作用是进行一系列的数据准备工作,主要是提前将记录标注信息的xml文件(Annotations)进行解析,并将信息整理到json文件之中,这样在运行训练脚本时,只需简单的从json文件中读取已经按想要的格式存储好的标签信息即可。

from utils import create_data_lists

if __name__ == '__main__':
    # voc07_path,voc12_path为我们训练测试所需要用到的数据集,output_folder为我们生成构建dataloader所需文件的路径
    create_data_lists(voc07_path='../../../dataset/VOCdevkit/VOC2007',
                      voc12_path='../../../dataset/VOCdevkit/VOC2012',
                      output_folder='../../../dataset/VOCdevkit')

具体方法的代码这里不放,可以根据链接去看datawhale团队教程,总体就是对xml文件做了解析,提取了后面要用到的数据保存。

构建dataloader

在这里重写了dataset的类和方法,为dataloader迭代遍历数据做好铺垫

"""python
    PascalVOCDataset具体实现过程
"""
import torch
from torch.utils.data import Dataset
import json
import os
from PIL import Image
from utils import transform


class PascalVOCDataset(Dataset):
    """
    A PyTorch Dataset class to be used in a PyTorch DataLoader to create batches.
    """

    #初始化相关变量
    #读取images和objects标注信息
    def __init__(self, data_folder, split, keep_difficult=False):
        """
        :param data_folder: folder where data files are stored
        :param split: split, one of 'TRAIN' or 'TEST'
        :param keep_difficult: keep or discard objects that are considered difficult to detect?
        """
        self.split = split.upper()    #保证输入为纯大写字母,便于匹配{'TRAIN', 'TEST'}

        assert self.split in {'TRAIN', 'TEST'}

        self.data_folder = data_folder
        self.keep_difficult = keep_difficult

        # Read data files
        with open(os.path.join(data_folder, self.split + '_images.json'), 'r') as j:
            self.images = json.load(j)
        with open(os.path.join(data_folder, self.split + '_objects.json'), 'r') as j:
            self.objects = json.load(j)

        assert len(self.images) == len(self.objects)

    #循环读取image及对应objects
    #对读取的image及objects进行tranform操作(数据增广)
    #返回PIL格式图像,标注框,标注框对应的类别索引,对应的difficult标志(True or False)
    def __getitem__(self, i):
        # Read image
        #*需要注意,在pytorch中,图像的读取要使用Image.open()读取成PIL格式,不能使用opencv
        #*由于Image.open()读取的图片是四通道的(RGBA),因此需要.convert('RGB')转换为RGB通道
        image = Image.open(self.images[i], mode='r')
        image = image.convert('RGB')

        # Read objects in this image (bounding boxes, labels, difficulties)
        objects = self.objects[i]
        boxes = torch.FloatTensor(objects['boxes'])  # (n_objects, 4)
        labels = torch.LongTensor(objects['labels'])  # (n_objects)
        difficulties = torch.ByteTensor(objects['difficulties'])  # (n_objects)

        # Discard difficult objects, if desired
        #如果self.keep_difficult为False,即不保留difficult标志为True的目标
        #那么这里将对应的目标删去
        if not self.keep_difficult:
            boxes = boxes[1 - difficulties]
            labels = labels[1 - difficulties]
            difficulties = difficulties[1 - difficulties]

        # Apply transformations
        #对读取的图片应用transform
        image, boxes, labels, difficulties = transform(image, boxes, labels, difficulties, split=self.split)

        return image, boxes, labels, difficulties

    #获取图片的总数,用于计算batch数
    def __len__(self):
        return len(self.images)

    #我们知道,我们输入到网络中训练的数据通常是一个batch一起输入,而通过__getitem__我们只读取了一张图片及其objects信息
    #如何将读取的一张张图片及其object信息整合成batch的形式呢?
    #collate_fn就是做这个事情,
    #对于一个batch的images,collate_fn通过torch.stack()将其整合成4维tensor,对应的objects信息分别用一个list存储
    def collate_fn(self, batch):
        """
        Since each image may have a different number of objects, we need a collate function (to be passed to the DataLoader).
        This describes how to combine these tensors of different sizes. We use lists.
        Note: this need not be defined in this Class, can be standalone.
        :param batch: an iterable of N sets from __getitem__()
        :return: a tensor of images, lists of varying-size tensors of bounding boxes, labels, and difficulties
        """

        images = list()
        boxes = list()
        labels = list()
        difficulties = list()

        for b in batch:
            images.append(b[0])
            boxes.append(b[1])
            labels.append(b[2])
            difficulties.append(b[3])

        #(3,224,224) -> (N,3,224,224)
        images = torch.stack(images, dim=0)

        return images, boxes, labels, difficulties  # tensor (N, 3, 224, 224), 3 lists of N tensors each

这样在最终训练开始的部分,我们使用的dataset和dataloader如下:

    #train_dataset和train_loader的实例化
    train_dataset = PascalVOCDataset(data_folder,
                                     split='train',
                                     keep_difficult=keep_difficult)
    train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True,
                                               collate_fn=train_dataset.collate_fn, num_workers=workers,
                                               pin_memory=True)  # note that we're passing the collate function here

总结

由于现在只能做到读懂代码,所以第一次的文章里面没有自己的原创代码,代码段来源于Datawhale教程,总体上对主动构建数据集有了一定的理解,之后有了新的理解再补充

你可能感兴趣的:(Datawhale学习,深度学习,计算机视觉,python)