一、VOC2007介绍

在利用诸如Faster R-CNN等深度学习网络进行目标检测的时候一定需要训练自己的数据集,一般有两种方法:

  • 按照VOC2007的格式修改自己的数据格式
  • 根据自己的数据格式修改代码

一般推荐第一种方法,因为第一种方法比较简单而且不容易出错,在制作为VOC2007格式的数据集之前,这里可以下载原始VOC2007数据集:VOC2007数据集,来看看这个数据集到底是什么样的:

Screenshot from 2020-02-02 11-09-38.png

1. 注释

**Annotations文件夹中存放的是xml格式的标签文件,每一个xml文件都对应于JPEGImages文件夹中的一张图片。xml文件的解析如下:

  1. <annotation>
  2. <folder>VOC2007</folder>
  3. <filename>2007_000392.jpg</filename> //文件名
  4. <source> //图像来源(不重要)
  5. <database>The VOC2007 Database</database>
  6. <annotation>PASCAL VOC2007</annotation>
  7. <image>flickr</image>
  8. </source>
  9. <size> //图像尺寸(长宽以及通道数)
  10. <width>500</width>
  11. <height>332</height>
  12. <depth>3</depth>
  13. </size>
  14. <segmented>1</segmented> //是否用于分割(在图像物体识别中01无所谓)
  15. <object> //检测到的物体
  16. <name>horse</name> //物体类别
  17. <pose>Right</pose> //拍摄角度
  18. <truncated>0</truncated> //是否被截断(0表示完整)
  19. <difficult>0</difficult> //目标是否难以识别(0表示容易识别)
  20. <bndbox> //bounding-box(包含左下角和右上角xy坐标)
  21. <xmin>100</xmin>
  22. <ymin>96</ymin>
  23. <xmax>355</xmax>
  24. <ymax>324</ymax>
  25. </bndbox>
  26. </object>
  27. <object> //检测到多个物体
  28. <name>person</name>
  29. <pose>Unspecified</pose>
  30. <truncated>0</truncated>
  31. <difficult>0</difficult>
  32. <bndbox>
  33. <xmin>198</xmin>
  34. <ymin>58</ymin>
  35. <xmax>286</xmax>
  36. <ymax>197</ymax>
  37. </bndbox>
  38. </object>
  39. </annotation>

2.JPEGImages

**

  • JPEGImages内部存放了PASCAL VOC所提供的所有的图片信息,包括了训练图片与测试图片

  • 图片的像素尺寸不一样。(在我的数据中由于是裁剪的图片,所以图片大小都是600x1000)

3.ImageSets

**
由于我们只是关注目标检测,所以只需要知道Main文件夹中各个文件的含义:

Main文件夹下包含了每个分类的train.txt、val.txt和trainval.txt。

Screenshot from 2020-02-02 11-26-50.png

我们制作数据集时也是需要将图片的名称划分为这几个。

Faster R-CNN主要使用的就是这几个文件。
**

二、制作PASCAL VOC形式的数据集

制作自己的VOC2007格式数据集其实不需要那么多内容,我们只要做三个部分即可:Annotations文件夹、JPEGImages文件夹、ImageSets文件夹下的Main文件。

Step 1 : JPEGImages

**
首先我们需要在faster-rcnn.pytorch-pytorch-1.0这个文件下面创建一个data文件夹,然后在data文件夹下面创建一个VOCdevkit2007的文件夹,然后再在这个文件夹中创建VOC2007的文件夹。

/**faster-rcnn.pytorch-pytorch-1.0/data/VOCdevkit2007/**VOC2007/
**
然后再VOC2007这个文件夹下面,创建Annotations、JPEGImages、ImageSets三个文件夹,最后在ImageSets文件夹下再创建一个Main文件夹。

Screenshot from 2020-02-02 11-36-46.png

创建好所有文件夹后,我们将自己的数据集图片都放到JPEGImages文件夹下。

注意:按照习惯,最好将所有的图片按照一定的规则重写命名,这样更好进行处理。一般,如果名称在后续没有什么特殊使用,可以按照原数据集里的名称规则命名。

代码:

  1. import os
  2. path = "E:\\image"  # 写你图片存储的位置
  3. filelist = os.listdir(path) #该文件夹下所有的文件(包括文件夹)
  4. count=0
  5. for file in filelist:
  6. print(file)
  7. for file in filelist: #遍历所有文件
  8. Olddir=os.path.join(path,file) #原来的文件路径
  9. if os.path.isdir(Olddir): #如果是文件夹则跳过
  10. continue
  11. filename=os.path.splitext(file)[0] #文件名
  12. filetype=os.path.splitext(file)[1] #文件扩展名
  13. Newdir=os.path.join(path,str(count).zfill(6)+filetype)
  14. #用字符串函数zfill 以0补全所需位数
  15. os.rename(Olddir,Newdir)#重命名
  16. count+=1

Step 2 : Annotations

**
标注工具我推荐:LabelImg

github地址:https://github.com/tzutalin/labelImg,有详细安装教程。

下载后进入下载目录,copy到home目录下解压,解压后进入目录:

  1. cd labelImg
  2. python labelImg.py

即可打开界面,进入你存储图片的文件夹,设置标注文件的保存位置Annotations。

image.png

此软件的使用方法:
**

  • 修改默认的XML文件保存位置,使用快捷键“Ctrl+R”,改为自定义位置,这里的路径一定不能包含中文,否则无法保存。

  • 源码文件夹中使用notepad++打开data/predefined_classes.txt,修改默认类别,比如改成person、car、motorcycle三个类别。

  • “Open Dir”打开图片文件夹,选择第一张图片开始进行标注,使用“Create RectBox”或者“Ctrl+N”开始画框,单击结束画框,再双击选择类别。完成一张图片后点击“Save”保存,此时XML文件已经保存到本地了。点击“Next Image”转到下一张图片。

  • 标注过程中可随时返回进行修改,后保存的文件会覆盖之前的。

  • 完成标注后打开XML文件,发现确实和PASCAL VOC所用格式一样。

step 3:ImageSets

接下来为制作ImageSets文件夹下Main文件夹中的4个文件(test.txt、train.txt、trainval.txt、val.txt)。

首先说明一下这四个文件到底是干什么用的:
test.txt:测试集
train.txt:训练集
val.txt:验证集
trainval.txt:训练和验证集

在原始VOC2007数据集中,trainval大约占整个数据集的50%,test大约为整个数据集的50%;train大约是trainval的50%,val大约为trainval的50%。所以我们可参考以下代码来生成这4个txt文件:

  1. # !/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. import os
  4. import random
  5. path = '/home/hans/Documents/optical surface data/Pre_processing data/'
  6. trainval_percent = 0.8 # trainval占比例多少
  7. train_percent = 0.7 # test数据集占比例多少
  8. xmlfilepath = path + 'Annotations'
  9. txtsavepath = path + 'ImageSets\Main'
  10. total_xml = os.listdir(xmlfilepath)
  11. num = len(total_xml)
  12. list = range(num)
  13. tv = int(num * trainval_percent)
  14. tr = int(tv * train_percent)
  15. trainval = random.sample(list, tv)
  16. train = random.sample(trainval, tr)
  17. ftrainval = open(path + 'ImageSets/Main/trainval.txt', 'w')
  18. ftest = open(path + 'ImageSets/Main/test.txt', 'w')
  19. ftrain = open(path + 'ImageSets/Main/train.txt', 'w')
  20. fval = open(path + 'ImageSets/Main/val.txt', 'w')
  21. for i in list:
  22. name = total_xml[i][:-4] + '\n'
  23. if i in trainval:
  24. ftrainval.write(name)
  25. if i in train:
  26. ftrain.write(name)
  27. else:
  28. fval.write(name)
  29. else:
  30. ftest.write(name)
  31. ftrainval.close()
  32. ftrain.close()
  33. fval.close()
  34. ftest.close()

Step 4 : 统计相关数据

在制作自己的数据集成功过后,如果想要统计以下你图片中的标注种类,以及Bounding Box的数量时,可以使用并且修改以下代码进行统计:

  1. import re
  2. import os
  3. import xml.etree.ElementTree as ET
  4. class1 = 'xxxxx'
  5. class2 = 'xxxxx'
  6. annotation_folder = '/Annotations'
  7. def file_name(file_dir):
  8. L = []
  9. for root, dirs, files in os.walk(file_dir):
  10. for file in files:
  11. if os.path.splitext(file)[1] == '.xml':
  12. L.append(os.path.join(root, file))
  13. return L
  14. total_number1 = 0
  15. total_number2 = 0
  16. total = 0
  17. total_pic = 0
  18. pic_num1 = 0
  19. pic_num2 = 0
  20. flag1 = 0
  21. flag2 = 0
  22. xml_dirs = file_name(annotation_folder)
  23. for i in range(0, len(xml_dirs)):
  24. # print(xml_dirs[i])
  25. annotation_folder = open(xml_dirs[i]).read()
  26. root = ET.fromstring(annotation_folder)
  27. total_pic = total_pic + 1
  28. for obj in root.findall('object'):
  29. label = obj.find('name').text
  30. if label == class1:
  31. total_number1 = total_number1 + 1
  32. flag1 = 1
  33. total = total + 1
  34. if label == class2:
  35. total_number2 = total_number2 + 1
  36. flag2 = 1
  37. total = total + 1
  38. if flag1 == 1:
  39. pic_num1 = pic_num1 + 1
  40. flag1 = 0
  41. if flag2 == 1:
  42. pic_num2 = pic_num2 + 1
  43. flag2 = 0
  44. print("class:", class1, "pic_num:", pic_num1, "BB_num", total_number1)
  45. print("class:", class2, "pic_num:", pic_num2, "BB_num", total_number2)
  46. print("total_pic", total_pic, "total_BB:", total)

三、使用Pytorch版Faster R-CNN训练自己数据集

使用代码来自:https://github.com/jwyang/faster-rcnn.pytorch/tree/pytorch-1.0

确保已经编译好相关的代码,能够直接训练。

我们在回到/**faster-rcnn.pytorch-pytorch-1.0/data**文件夹中,在这个文件夹里面主要存储的是

  1. 训练数据集  ----VOCdevkit2007
  2. 骨干网络的预训练模型 ---- pretrained_model
  3. Faster R-CNN模型参数的保存 ---- cache (进行训练之后才会被创建)

Screenshot from 2020-02-02 11-58-29.png

骨干网络的预训练模型下载:

vgg16 :https://filebox.ece.vt.edu/~jw2yang/faster-rcnn/pretrained-base-models/vgg16_caffe.pth
Resnet101:https://filebox.ece.vt.edu/~jw2yang/faster-rcnn/pretrained-basemodels/resnet101_caffe.pth

训练命令示例(单GPU训练):

  1. CUDA_VISIBLE_DEVICES=$GPU_ID python trainval_net.py \
  2. --dataset pascal_voc --net res101 \
  3. --bs $BATCH_SIZE --nw $WORKER_NUMBER \
  4. --lr $LEARNING_RATE --lr_decay_step $DECAY_STEP \
  5. --cuda
  6. '''
  7. GPU_ID : 有的电脑默认为0,有点电脑默认为1
  8. --dataset pascal_voc : 训练数据集类型pascal_voc
  9. --net : 骨干网络选择VGG16,ResNet101
  10. --bs : batch_size 默认为1
  11. –nw : worker number,取决于你的Gpu能力
  12. --lr : learning rate : 一般选取默认值就行,也可以自己选取
  13. --lr_decay_step : 同上
  14. '''

如果使用多GPU训练:

  1. python trainval_net.py --dataset pascal_voc --net vgg16 \
  2. --bs 24 --nw 8 \
  3. --lr $LEARNING_RATE --lr_decay_step $DECAY_STEP \
  4. --cuda --mGPUs

训好的model会存到models文件夹底下。

如果想要训练自己的数据集,开始前一定要确保data/cache中没有文件,因为这里要保存一些训练用的中间数据。

最后我们只需要将/faster-rcnn.pytorch/lib/datasets/pascal_voc.py中的这行代码:

  1. //类别
  2. self._classes = ('__background__', # always index 0
  3. 'aeroplane', 'bicycle', 'bird', 'boat',
  4. 'bottle', 'bus', 'car', 'cat', 'chair',
  5. 'cow', 'diningtable', 'dog', 'horse',
  6. 'motorbike', 'person', 'pottedplant',
  7. 'sheep', 'sofa', 'train', 'tvmonitor','plane')

除了第一个background之外,将自己数据的标注类别填写上去,就能够进行训练了。

Screenshot from 2020-02-02 15-26-27.png