使用自己的数据集训练 R-CNN

感谢作者:http://imzack.me/post/training-rcnn-using-my-own-dataset.html

之前已经写了一篇安装 R-CNN 的文章了,现在就要使用自己的数据集训练 R-CNN 了。

这篇文章记录了我用人脸数据集来训练 R-CNN,并且使用训练后的 R-CNN 模型检测出图片中的人脸的过程。

注意:虽然下述内容在我的电脑上测试通过,但是由于此文章是我在做毕业设计期间撰写的,时间紧迫,未能考虑到所有情况,仅是做记录使用,因此下述内容不一定适用于每个人,如果发现错误,请求助于其他博客,敬请留意!

构建数据集

查看 R-CNN 的 demo 程序的源代码 rcnn_demo.m 发现,有两个已经训练好的模型可供使用,它们分别是使用 PASCAL VOC 2007 数据集和 ImageNet ILSVRC 2013 数据集训练好的模型,前者可识别 20 个类型,后者可识别 200 个类型。然而这 220 个类型并不包括人脸,所以需要自己训练。

下载数据集

我下载的是香港中文大学多媒体实验室的 CelebA 数据集,这是一个人脸数据集,一共有 20 多万张图片。官方也提供了某度盘的链接。以下是我下载的文件:

1. 有环境的图片(In-The-Wild Images)

在度盘的 Img/img_celeba.7z 文件夹中。由于图片非常多,这个压缩包很大,所以作者进行了分卷压缩,有 14 个文件(img_celeba.7z.001 到 img_celeba.7z.014),一共 9 个多 G。我在 Windows 用度盘客户端下的,不然速度会时不时地归零。下载下来后也在 Windows 下的命令提示符中进行分卷的合并:

copy /b img_celeba.7z.* img_celeba.7z

很慢啊,因为太大了。合并好了会生成一个新的压缩包 img_celeba.7z,然后解压。

2. 标注信息(Bounding Box Annotations)

训练和测试的时候,需要告诉程序一张图片哪个地方才是人脸,所以需要标注框,标注信息提供了每张图片中人脸标注框的坐标。在度盘的 Anno/list_bbox_celeba.txt,直接下载即可。

转换数据集

其实作者提供了使用自己的数据集训练 R-CNN 的教程,但是写得很粗略,主要就是需要准备三个函数:

  • 返回描述图片和类型的结构体(参考 imdb/imdb_from_voc.m
  • 返回感兴趣区域(即标注框)的结构体(参考 imdb/roidb_from_voc.m
  • 提供评估功能(参考 imdb/imdb_eval_voc.m

也可以把自己的数据集转换成 PASCAL VOC 的格式,这样就可以重用作者的代码了,我就是使用的这种方式。

数据集结构

我以前有下过 PASCAL VOC 2007 数据集,先用 tree -d 命令看下目录结构吧:

PASCAL VOC 2007 数据集结构

接下来仿照此结构,构建自己的数据集。

先把 rcnn/datasets/VOCdevkit2007 链接到存放 CelebA 数据集的目录(我是 ~/pascal_voc/celeba/VOCdevkit):

$ ln -sf ~/pascal_voc/celeba/VOCdevkit  ~/rcnn/datasets/VOCdevkit2007

再在 VOCdevkit 下添加一些文件夹,结构如下:

VOCdevkit
├── results
│   └── VOC2007
│       └── Main
├── VOC2007
│   ├── Annotations
│   ├── ImageSets
│   │   └── Main
│   └── JPEGImages
└── VOCcode

下面是每个文件夹的说明:

  • VOC2007/Annotations 存放 JPEGImages 文件夹中图片的标注信息,与图片文件同名且一一对应,为 xml 格式。

  • VOC2007/ImageSets/Main 存放四个 txt 文件:train.txtval.txttrainval.txttest.txt,分别定义训练、验证、训练和验证、测试四种数据集中的图片,这些文件里的每一行文本都是一个 image identifier,其实也就是图片的文件名。

  • VOC2007/JPEGImages 存放所有图片文件。

  • VOCcode 存放加载、处理数据集的代码。直接将 PASCAL VOC 2007 数据集中的 VOCdevkit/VOCcode文件夹复制到这里就行。

  • results/VOC2007/Main 测试的时候会在此文件夹下临时存放一个文件,没有这个路径的话会报错:

Error using fprintf
Invalid file identifier.  Use fopen to generate a valid file identifier.

准备图片

PASCAL VOC 2007 数据集中包含 20 类图片,但是我只需要检测一类,就是人脸,所以到底使用多少张图片合适呢?我自己写了个 Python 脚本,读取 Annotations 文件夹中的 xml 文件分析了一下每个类型在所有图片中出现的次数,结果如图:

每个类型出现的次数

感觉每个类型出现的次数也不多,那我就选 CelebA 数据集中前 1000 张图片吧,应该够用了。把 CelebA 数据集中文件名为 000001.jpg 到 001000.jpg 的图片放到 VOCdevkit/VOC2007/JPEGImages 里。

准备标注文件

PASCAL VOC 2007 数据集的标注文件为 xml 格式的,以 000002.xml 为例:

<annotation>
  <folder>VOC2007</folder>
  <!-- 图片的文件名 -->
  <filename>000002.jpg</filename>
  <source>
    <database>The VOC2007 Database</database>
    <annotation>PASCAL VOC2007</annotation>
    <image>flickr</image>
    <flickrid>329145082</flickrid>
  </source>
  <owner>
    <flickrid>hiromori2</flickrid>
    <name>Hiroyuki Mori</name>
  </owner>
  <!-- 图片宽、高、深度 -->
  <size>
    <width>335</width>
    <height>500</height>
    <depth>3</depth>
  </size>
  <segmented>0</segmented>
  <object>
    <name>train</name>
    <pose>Unspecified</pose>
    <truncated>0</truncated>
    <difficult>0</difficult>
    <!-- 标注框信息 -->
    <bndbox>
      <xmin>139</xmin>
      <ymin>200</ymin>
      <xmax>207</xmax>
      <ymax>301</ymax>
    </bndbox>
  </object>
</annotation>

CelebA 提供的标注信息都存放在 list_bbox_celeba.txt 中。此文本除 1、2 行外,其余行均为各图片的标注信息,格式为

列数 内容 例子
1 图片文件名 000002.jpg
2 标注框左上角x坐标 72
3 标注框左上角y坐标 94
4 标注框宽 221
5 标注框高 306

需要利用这些信息来构建 xml 格式的标注文件,当然,手动转换是不现实的,所以我写了个 Python 脚本来转换,使用了 PASCAL VOC 2007 数据集中的标注文件 000002.xml 作为模板,仅仅将模板中的 filenamesizebndbox 元素修改了就行,其他的不动。代码如下:

import xml.etree.cElementTree as ET
import cv2

# 使用000002.xml作为模板,因为它只有一个object元素
template_file = '/home/zack/pascal_voc/2007/VOCdevkit/VOC2007/Annotations/000002.xml'
# 生成的xml文件保存的路径
target_dir = '/home/zack/pascal_voc/celeba/VOCdevkit/VOC2007/Annotations/'
# 所有图片的路径
image_dir = '/home/zack/pascal_voc/celeba/VOCdevkit/VOC2007/JPEGImages/'
# 给出所有标注信息的文件,由CelebA提供
bbox_file = '/home/zack/celeba/list_bbox_celeba.txt'
# 图片数量
image_num = 1000

bboxes = open(bbox_file, 'r')

for bbox in bboxes.readlines()[2 : image_num + 2]:
    bb_info = bbox.split()
    image_file = bb_info[0]
    x_1 = int(bb_info[1])
    y_1 = int(bb_info[2])
    width = int(bb_info[3])
    height = int(bb_info[4])

    tree = ET.parse(template_file)
    root = tree.getroot()

    # filename
    root.find('filename').text = image_file

    # size
    sz = root.find('size')
    im = cv2.imread(image_dir + image_file)
    sz.find('width').text = str(im.shape[0])
    sz.find('height').text = str(im.shape[1])
    sz.find('depth').text = str(im.shape[2])

    # object
    obj = root.find('object')
    obj.find('name').text = 'face'
    bb = obj.find('bndbox')
    bb.find('xmin').text = str(x_1)
    bb.find('ymin').text = str(y_1)
    bb.find('xmax').text = str(x_1 + width)
    bb.find('ymax').text = str(y_1 + height)

    xml_file = image_file.replace('jpg', 'xml')

    tree.write(target_dir + xml_file)

    print xml_file

bboxes.close()

print 'Done'

执行以上脚本后,所有的标注框文件就已经生成了。如果出现 ImportError: No module named cv2 的错误,执行以下命令安装 OpenCV 的 Python 接口:

$ sudo apt-get install python-opencv`

准备数据集配置文件

配置文件即 ImageSets/Main 下的四个文件,定义四种数据集,这四种数据集中的图片会被分别用来做训练、验证、训练和验证、测试。我看了看 PASCAL VOC 2007 中四个数据集的分配,发现每个数据集中的图片应该是随机抽取的,但需要满足一定的条件: trainval 和 test 约为总图片数的一半,但不重合,即选出其中一个数据集的图片后,另一个数据集的图片就是选剩下的图片了,而 train 和 val 约占 trainval 的一半,同样不重合。所以我也用这个比例来制作这四个数据集吧。依然写了个 Python 脚本(Python 真是太好用了

猜你喜欢

转载自blog.csdn.net/gqixf/article/details/80477445
今日推荐