制作自己的pascal voc数据集

最近跑faster rcnn需要训练自己的数据集,那么首先要跑通一个faster rcnn模型,比如先跑通公共数据集,证明模型是没问题的。第二步就是要自己制作数据集用于训练和测试。制作自己的目标检测数据集有两种方法,第一种是自己写,第二种是把自己的数据集改成和公共数据集一样的结构。当然第二种方法更简单。会了第二种方法,再自己重头开始写数据集的时候就不会那么难受。

系统ubuntu16。假设开始之前你已经有了一个可以跑通的faster rcnn模型,需要用自己的数据集进行训练。

除去语义分割的部分,主要用到VOC2007文件夹下的Annotations文件夹、ImageSets文件件、JPEGImages文件夹。用到的目标检测voc格式大致如下:

PASCAL_VOC07

└──VOCdevkit

├ local

├ results

├ VOC2007

│ ├── Annotations

│ ├── ImageSets

│ │ └── Main

│ └── JPEGImages

└ VOCcode

Annotations文件夹存放的是xml文件,它们是一些标注信息。比如VOC数据集中000001图片的标注信息在<annotation></annotation>中,<filename></filename>中间存放的文件名,图片的长、宽、通道存放在<size></size>中,<segmented>0</segmented>表示这张图片不用来做语义分割,每一个<object></object>表示一个物体,其中<name></name>表示物体名称,<pose></pose>是拍摄角度,<truncated>1</truncated>表示物体被截断,<difficult>0</difficult>表示检测难度为简单。<></>

<annotation>
	<folder>VOC2007</folder>
	<filename>000001.jpg</filename>
	<source>
		<database>The VOC2007 Database</database>
		<annotation>PASCAL VOC2007</annotation>
		<image>flickr</image>
		<flickrid>341012865</flickrid>
	</source>
	<owner>
		<flickrid>Fried Camels</flickrid>
		<name>Jinky the Fruit Bat</name>
	</owner>
	<size>
		<width>353</width>
		<height>500</height>
		<depth>3</depth>
	</size>
	<segmented>0</segmented>
	<object>
		<name>dog</name>
		<pose>Left</pose>
		<truncated>1</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>48</xmin>
			<ymin>240</ymin>
			<xmax>195</xmax>
			<ymax>371</ymax>
		</bndbox>
	</object>
	<object>
		<name>person</name>
		<pose>Left</pose>
		<truncated>1</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>8</xmin>
			<ymin>12</ymin>
			<xmax>352</xmax>
			<ymax>498</ymax>
		</bndbox>
	</object>
</annotation>

最简单办法就是把自己的数据集格式改成voc的格式,最重要的,也是需要改的地方主要是<filename></filename>、<size></size>以及<object></object>中的<name></name>和<bndbox></bndbox>。这些修改可以使用标注工具label me完成。label me可以选择保存为voc的xml格式。

ImageSets文件夹中只使用到Main文件夹。Main文件夹中包含一些txt文件,这些txt以A_test、A_train、A_trainval命名。表示test/train/trainval有哪些图片,test/train/trainval的图片是否有A类。比如在A_test.txt中,000001 -1表示000001 图片属于test,-1表示000001 图片中没有A类。

000001 -1

000002 -1

000003 -1

……

可以写个python小程序把已有的xml信息做成符合A_test.txt、A_train.txt、A_trainval.txt、B_test.txt、B_train.txt、B_trainval.txt的格式。假设已经制作好了voc格式的标注信息,并且放在path下,简单假设只有A、B、C3个类。首先把它们存到不同的list中。

import os
import xml.etree.cElementTree as ET

A , B , C = [] ,[] , []

for file in os.listdir(path):
    tree = ET.parse(os.path.join(path , file))
    root = tree.getroot()
    for obj in root.findall('object'):
        name = obj.find('name').text
        if name == 'A':
            file = root.find('path').text
            file = file.split("\\")
            file = file[-1][:-4]
            A.append(file)
        elif name == 'B':
            file = root.find('path').text
            file = file.split("\\")
            file = file[-1][:-4]
            B.append(file)
        elif name == 'C':
            file = root.find('path').text
            file = file.split("\\")
            file = file[-1][:-4]
            C.append(file)

剩下的是随机将数据集分成train和test,还有一些txt的操作,这些操作和你的数据集有关,代码需要根据自己的数据集来写。上面完成了A_test.txt、A_train.txt、A_trainval.txt的制作。

JPEGImages文件夹中存放的都是图片文件,没什么好说的,把自己的图片放在JPEGImages文件夹中就行。

现在你的voc数据集就制作完成了。

制作完数据集还需要相应的修改代码。如果你的数据集少,没有办法分成train、val、test三部分,可以只分成train和test两部分,factory.py中有一段代码

# Set up voc_<year>_<split>
for year in ['2007', '2012']:
  for split in ['train', 'val', 'trainval', 'test']:
    name = 'voc_{}_{}'.format(year, split)
    __sets[name] = (lambda split=split, year=year: pascal_voc(split, year))

pascal_voc(split, year)生成了pascal_voc子类,在pascal_voc.py文件中根据实际情况需要修改pascal_voc构造函数的路径以及类别。

class pascal_voc(imdb):
    def __init__(self, image_set, year, devkit_path=None):
        imdb.__init__(self, 'voc_' + year + '_' + image_set)
        self._year = year
        self._image_set = image_set
        self._devkit_path = self._get_default_path() if devkit_path is None else devkit_path
        self._data_path = os.path.join(self._devkit_path, 'VOC' + self._year)
        self._classes = ('__background__',  # always index 0
                         'aeroplane', 'bicycle', 'bird', 'boat',
                         'bottle', 'bus', 'car', 'cat', 'chair',
                         'cow', 'diningtable', 'dog', 'horse',
                         'motorbike', 'person', 'pottedplant',
                         'sheep', 'sofa', 'train', 'tvmonitor')
        #balabala....

pascalvoc(‘train’, ‘2007’)表示生成pascal voc2007格式的训练数据集,pascalvoc(‘test’, ‘2007’)表示生成pascal voc2007格式的测试数据集。同时,如果你的数据集只有训练和测试集,ImageSets/Main中的txt文件就不再是一个类有3个txt,而是只有A_train.txt和A_test.txt。所以制作txt的代码也需要修改。

上面基本是跑通自己的目标检测数据集需要修改的地方,如果已经有一个跑通了的目标检测网络,就可以开始训练自己的数据集了。

下面记录一下关于数据集代码的调用关系,实在是太复杂了。

从开始的imdb, roidb, ratio_list, ratio_index = combined_roidb(imdb_name)

在get_roidb函数中,首先用到datasets/factory.py的get_imdb函数,get_imdb函数返回相应的pascalvoc(‘xxx’, ‘yyyy’)的pascalvoc类(imdb基类)。接着get_training_roidb函数从image database获得roi database

然后filter_roidb函数去除没有bounding box的图片,接着rank_roidb_ratio函数按照roi的长宽比排序。

所以返回的imdb, roidb, ratio_list, ratio_index表示image database、roi database、长宽比、长宽比索引。

之后就是写一个torch.utils.data.Dataset子类,将roi database包装成pytorch的Dataset的抽象类。还要写个torch.utils.data.sampler.Sampler基础采样器的子类,控制数据集的采样方式,注意的是如果数据集大小不能被batch_size整除的时候,余下部分的处理。最后,有了上面两个子类,就可以使用torch.utils.data.DataLoader加载数据了。总结就是重写了classtorch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, num_workers=0,……)的dataset和sampler。

剩下的就是送入网络训练。

    原文作者:赵子龙
    原文地址: https://zhuanlan.zhihu.com/p/42980766
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞